Skip to content

Commit e85eacc

Browse files
authored
fix: use SDK RawJSON() for -o json output to avoid zero values (#69)
## Summary - When using `-o json`, the CLI was re-marshaling SDK response structs with `json.MarshalIndent()`, which included Go zero values for fields not present in the API response (e.g., `deleted_at`, `persistence`, `profile`, `viewport`) - Now uses the SDK's `RawJSON()` method which preserves the original API response, only outputting fields that were actually returned - Adds `PrintPrettyJSON` and `PrintPrettyJSONSlice` helpers in `pkg/util/json.go` that pretty-print the raw JSON from SDK response types ### Before ```json { "cdp_ws_url": "wss://...", "created_at": "2026-01-12T02:41:42.658978979Z", "session_id": "wvmx4yt5afram2xobclje52x", "timeout_seconds": 60, "deleted_at": "0001-01-01T00:00:00Z", "kiosk_mode": false, "persistence": {"id": ""}, "profile": {"id": "", "created_at": "0001-01-01T00:00:00Z", ...}, "viewport": {"height": 0, "width": 0, "refresh_rate": 0} } ``` ### After ```json { "browser_live_view_url": "https://...", "cdp_ws_url": "wss://...", "created_at": "2026-01-12T02:49:16.736473188Z", "headless": false, "session_id": "tpuq0sl28j5d8par13mqo79d", "stealth": false, "timeout_seconds": 60 } ``` ## Test plan - [x] All existing tests pass (`make test`) - [x] Tested `kernel browsers create --output json` - no zero-value fields - [x] Tested `kernel browsers list --output json` - no zero-value fields <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Standardizes `--output json` to pretty-print the original API response using SDK `RawJSON()` instead of re-marshaling Go structs. > > - Adds `pkg/util/json.go` with `PrintPrettyJSON` and `PrintPrettyJSONSlice` helpers that indent raw SDK JSON > - Refactors JSON output across commands (`app`, `deploy history`, `browser-pools`, `browsers` incl. replays/process/fs, `extensions`, `proxies`) to use the new helpers; consistent empty slice handling > - Special-case `browsers view` to emit `{ "liveViewUrl": ... }` with proper JSON escaping > - Updates tests (`browsers_test.go`) to validate pretty JSON output using populated `RawJSON()` > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit beccbd7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 090e90d commit e85eacc

File tree

12 files changed

+109
-180
lines changed

12 files changed

+109
-180
lines changed

cmd/app.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package cmd
22

33
import (
4-
"encoding/json"
54
"fmt"
65
"strings"
76

@@ -107,12 +106,7 @@ func runAppList(cmd *cobra.Command, args []string) error {
107106
fmt.Println("[]")
108107
return nil
109108
}
110-
bs, err := json.MarshalIndent(apps.Items, "", " ")
111-
if err != nil {
112-
return err
113-
}
114-
fmt.Println(string(bs))
115-
return nil
109+
return util.PrintPrettyJSONSlice(apps.Items)
116110
}
117111

118112
if apps == nil || len(apps.Items) == 0 {
@@ -242,12 +236,7 @@ func runAppHistory(cmd *cobra.Command, args []string) error {
242236
fmt.Println("[]")
243237
return nil
244238
}
245-
bs, err := json.MarshalIndent(deployments.Items, "", " ")
246-
if err != nil {
247-
return err
248-
}
249-
fmt.Println(string(bs))
250-
return nil
239+
return util.PrintPrettyJSONSlice(deployments.Items)
251240
}
252241

253242
if deployments == nil || len(deployments.Items) == 0 {

cmd/browser_pools.go

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cmd
22

33
import (
44
"context"
5-
"encoding/json"
65
"fmt"
76
"strings"
87

@@ -44,16 +43,11 @@ func (c BrowserPoolsCmd) List(ctx context.Context, in BrowserPoolsListInput) err
4443
}
4544

4645
if in.Output == "json" {
47-
if pools == nil {
46+
if pools == nil || len(*pools) == 0 {
4847
fmt.Println("[]")
4948
return nil
5049
}
51-
bs, err := json.MarshalIndent(*pools, "", " ")
52-
if err != nil {
53-
return err
54-
}
55-
fmt.Println(string(bs))
56-
return nil
50+
return util.PrintPrettyJSONSlice(*pools)
5751
}
5852

5953
if pools == nil || len(*pools) == 0 {
@@ -155,12 +149,7 @@ func (c BrowserPoolsCmd) Create(ctx context.Context, in BrowserPoolsCreateInput)
155149
}
156150

157151
if in.Output == "json" {
158-
bs, err := json.MarshalIndent(pool, "", " ")
159-
if err != nil {
160-
return err
161-
}
162-
fmt.Println(string(bs))
163-
return nil
152+
return util.PrintPrettyJSON(pool)
164153
}
165154

166155
if pool.Name != "" {
@@ -187,12 +176,7 @@ func (c BrowserPoolsCmd) Get(ctx context.Context, in BrowserPoolsGetInput) error
187176
}
188177

189178
if in.Output == "json" {
190-
bs, err := json.MarshalIndent(pool, "", " ")
191-
if err != nil {
192-
return err
193-
}
194-
fmt.Println(string(bs))
195-
return nil
179+
return util.PrintPrettyJSON(pool)
196180
}
197181

198182
cfg := pool.BrowserPoolConfig
@@ -301,12 +285,7 @@ func (c BrowserPoolsCmd) Update(ctx context.Context, in BrowserPoolsUpdateInput)
301285
}
302286

303287
if in.Output == "json" {
304-
bs, err := json.MarshalIndent(pool, "", " ")
305-
if err != nil {
306-
return err
307-
}
308-
fmt.Println(string(bs))
309-
return nil
288+
return util.PrintPrettyJSON(pool)
310289
}
311290

312291
if pool.Name != "" {
@@ -364,12 +343,7 @@ func (c BrowserPoolsCmd) Acquire(ctx context.Context, in BrowserPoolsAcquireInpu
364343
}
365344

366345
if in.Output == "json" {
367-
bs, err := json.MarshalIndent(resp, "", " ")
368-
if err != nil {
369-
return err
370-
}
371-
fmt.Println(string(bs))
372-
return nil
346+
return util.PrintPrettyJSON(resp)
373347
}
374348

375349
tableData := pterm.TableData{

cmd/browsers.go

Lines changed: 17 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,7 @@ func (b BrowsersCmd) List(ctx context.Context, in BrowsersListInput) error {
226226
}
227227

228228
if in.Output == "json" {
229-
if len(browsers) == 0 {
230-
fmt.Println("[]")
231-
return nil
232-
}
233-
bs, err := json.MarshalIndent(browsers, "", " ")
234-
if err != nil {
235-
return err
236-
}
237-
fmt.Println(string(bs))
238-
return nil
229+
return util.PrintPrettyJSONSlice(browsers)
239230
}
240231

241232
if len(browsers) == 0 {
@@ -371,12 +362,7 @@ func (b BrowsersCmd) Create(ctx context.Context, in BrowsersCreateInput) error {
371362
}
372363

373364
if in.Output == "json" {
374-
bs, err := json.MarshalIndent(browser, "", " ")
375-
if err != nil {
376-
return err
377-
}
378-
fmt.Println(string(bs))
379-
return nil
365+
return util.PrintPrettyJSON(browser)
380366
}
381367

382368
printBrowserSessionResult(browser.SessionID, browser.CdpWsURL, browser.BrowserLiveViewURL, browser.Persistence, browser.Profile)
@@ -482,9 +468,13 @@ func (b BrowsersCmd) View(ctx context.Context, in BrowsersViewInput) error {
482468
}
483469

484470
if in.Output == "json" {
485-
result := map[string]string{"liveViewUrl": browser.BrowserLiveViewURL}
486-
bs, _ := json.MarshalIndent(result, "", " ")
487-
fmt.Println(string(bs))
471+
// View command returns a custom response, not the full browser object
472+
// Use json.Marshal to ensure proper JSON escaping of the URL
473+
urlBytes, err := json.Marshal(browser.BrowserLiveViewURL)
474+
if err != nil {
475+
return err
476+
}
477+
fmt.Printf("{\n \"liveViewUrl\": %s\n}\n", urlBytes)
488478
return nil
489479
}
490480

@@ -511,12 +501,7 @@ func (b BrowsersCmd) Get(ctx context.Context, in BrowsersGetInput) error {
511501
return util.CleanedUpSdkError{Err: err}
512502
}
513503
if in.Output == "json" {
514-
bs, err := json.MarshalIndent(browser, "", " ")
515-
if err != nil {
516-
return err
517-
}
518-
fmt.Println(string(bs))
519-
return nil
504+
return util.PrintPrettyJSON(browser)
520505
}
521506

522507
// Build table starting with common browser fields
@@ -922,12 +907,7 @@ func (b BrowsersCmd) ReplaysList(ctx context.Context, in BrowsersReplaysListInpu
922907
fmt.Println("[]")
923908
return nil
924909
}
925-
bs, err := json.MarshalIndent(*items, "", " ")
926-
if err != nil {
927-
return err
928-
}
929-
fmt.Println(string(bs))
930-
return nil
910+
return util.PrintPrettyJSONSlice(*items)
931911
}
932912

933913
if items == nil || len(*items) == 0 {
@@ -964,12 +944,7 @@ func (b BrowsersCmd) ReplaysStart(ctx context.Context, in BrowsersReplaysStartIn
964944
}
965945

966946
if in.Output == "json" {
967-
bs, err := json.MarshalIndent(res, "", " ")
968-
if err != nil {
969-
return err
970-
}
971-
fmt.Println(string(bs))
972-
return nil
947+
return util.PrintPrettyJSON(res)
973948
}
974949

975950
rows := pterm.TableData{{"Property", "Value"}, {"Replay ID", res.ReplayID}, {"View URL", res.ReplayViewURL}, {"Started At", util.FormatLocal(res.StartedAt)}}
@@ -1148,12 +1123,7 @@ func (b BrowsersCmd) ProcessExec(ctx context.Context, in BrowsersProcessExecInpu
11481123
}
11491124

11501125
if in.Output == "json" {
1151-
bs, err := json.MarshalIndent(res, "", " ")
1152-
if err != nil {
1153-
return err
1154-
}
1155-
fmt.Println(string(bs))
1156-
return nil
1126+
return util.PrintPrettyJSON(res)
11571127
}
11581128

11591129
rows := pterm.TableData{{"Property", "Value"}, {"Exit Code", fmt.Sprintf("%d", res.ExitCode)}, {"Duration (ms)", fmt.Sprintf("%d", res.DurationMs)}}
@@ -1220,12 +1190,7 @@ func (b BrowsersCmd) ProcessSpawn(ctx context.Context, in BrowsersProcessSpawnIn
12201190
}
12211191

12221192
if in.Output == "json" {
1223-
bs, err := json.MarshalIndent(res, "", " ")
1224-
if err != nil {
1225-
return err
1226-
}
1227-
fmt.Println(string(bs))
1228-
return nil
1193+
return util.PrintPrettyJSON(res)
12291194
}
12301195

12311196
rows := pterm.TableData{{"Property", "Value"}, {"Process ID", res.ProcessID}, {"PID", fmt.Sprintf("%d", res.Pid)}, {"Started At", util.FormatLocal(res.StartedAt)}}
@@ -1508,12 +1473,7 @@ func (b BrowsersCmd) FSFileInfo(ctx context.Context, in BrowsersFSFileInfoInput)
15081473
}
15091474

15101475
if in.Output == "json" {
1511-
bs, err := json.MarshalIndent(res, "", " ")
1512-
if err != nil {
1513-
return err
1514-
}
1515-
fmt.Println(string(bs))
1516-
return nil
1476+
return util.PrintPrettyJSON(res)
15171477
}
15181478

15191479
rows := pterm.TableData{{"Property", "Value"}, {"Path", res.Path}, {"Name", res.Name}, {"Mode", res.Mode}, {"IsDir", fmt.Sprintf("%t", res.IsDir)}, {"SizeBytes", fmt.Sprintf("%d", res.SizeBytes)}, {"ModTime", util.FormatLocal(res.ModTime)}}
@@ -1544,12 +1504,7 @@ func (b BrowsersCmd) FSListFiles(ctx context.Context, in BrowsersFSListFilesInpu
15441504
fmt.Println("[]")
15451505
return nil
15461506
}
1547-
bs, err := json.MarshalIndent(*res, "", " ")
1548-
if err != nil {
1549-
return err
1550-
}
1551-
fmt.Println(string(bs))
1552-
return nil
1507+
return util.PrintPrettyJSONSlice(*res)
15531508
}
15541509

15551510
if res == nil || len(*res) == 0 {
@@ -2227,12 +2182,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
22272182
return nil
22282183
}
22292184
if output == "json" {
2230-
bs, err := json.MarshalIndent(resp, "", " ")
2231-
if err != nil {
2232-
return err
2233-
}
2234-
fmt.Println(string(bs))
2235-
return nil
2185+
return util.PrintPrettyJSON(resp)
22362186
}
22372187
printBrowserSessionResult(resp.SessionID, resp.CdpWsURL, resp.BrowserLiveViewURL, resp.Persistence, resp.Profile)
22382188
return nil

cmd/browsers_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,10 +407,13 @@ func TestBrowsersGet_JSONOutput(t *testing.T) {
407407

408408
fake := &FakeBrowsersService{
409409
GetFunc: func(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.BrowserGetResponse, error) {
410-
return &kernel.BrowserGetResponse{
411-
SessionID: "sess-json",
412-
CdpWsURL: "ws://cdp",
413-
}, nil
410+
// Unmarshal JSON to populate RawJSON() properly
411+
jsonData := `{"session_id": "sess-json", "cdp_ws_url": "ws://cdp", "created_at": "2024-01-01T00:00:00Z", "headless": false, "stealth": false, "timeout_seconds": 60}`
412+
var resp kernel.BrowserGetResponse
413+
if err := json.Unmarshal([]byte(jsonData), &resp); err != nil {
414+
t.Fatalf("failed to unmarshal test response: %v", err)
415+
}
416+
return &resp, nil
414417
},
415418
}
416419
b := BrowsersCmd{browsers: fake}

cmd/deploy.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -433,12 +433,7 @@ func runDeployHistory(cmd *cobra.Command, args []string) error {
433433
fmt.Println("[]")
434434
return nil
435435
}
436-
bs, err := json.MarshalIndent(deployments.Items, "", " ")
437-
if err != nil {
438-
return err
439-
}
440-
fmt.Println(string(bs))
441-
return nil
436+
return util.PrintPrettyJSONSlice(deployments.Items)
442437
}
443438

444439
if deployments == nil || len(deployments.Items) == 0 {

cmd/extensions.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cmd
33
import (
44
"bytes"
55
"context"
6-
"encoding/json"
76
"fmt"
87
"io"
98
"net/http"
@@ -77,12 +76,7 @@ func (e ExtensionsCmd) List(ctx context.Context, in ExtensionsListInput) error {
7776
fmt.Println("[]")
7877
return nil
7978
}
80-
bs, err := json.MarshalIndent(*items, "", " ")
81-
if err != nil {
82-
return err
83-
}
84-
fmt.Println(string(bs))
85-
return nil
79+
return util.PrintPrettyJSONSlice(*items)
8680
}
8781

8882
if items == nil || len(*items) == 0 {
@@ -326,12 +320,7 @@ func (e ExtensionsCmd) Upload(ctx context.Context, in ExtensionsUploadInput) err
326320
}
327321

328322
if in.Output == "json" {
329-
bs, err := json.MarshalIndent(item, "", " ")
330-
if err != nil {
331-
return err
332-
}
333-
fmt.Println(string(bs))
334-
return nil
323+
return util.PrintPrettyJSON(item)
335324
}
336325

337326
name := item.Name

cmd/invoke.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -379,12 +379,7 @@ func runInvocationHistory(cmd *cobra.Command, args []string) error {
379379
fmt.Println("[]")
380380
return nil
381381
}
382-
bs, err := json.MarshalIndent(invocations.Items, "", " ")
383-
if err != nil {
384-
return err
385-
}
386-
fmt.Println(string(bs))
387-
return nil
382+
return util.PrintPrettyJSONSlice(invocations.Items)
388383
}
389384

390385
table := pterm.TableData{{"Invocation ID", "App Name", "Action", "Version", "Status", "Started At", "Duration", "Output"}}

0 commit comments

Comments
 (0)