Skip to content

Commit 2136989

Browse files
authored
[kernel-1310] browser telemetry - CLI integration (#168)
Adds browser telemetry to the CLI: - `kernel browsers create --telemetry=all|off|<list>` - `kernel browsers update <id> --telemetry=all|off|<list>` (partial — unspecified categories retain state) - `kernel browsers telemetry stream <id>` with `--categories`, `--types`, `--seq` (Last-Event-ID resume), `-o json` (NDJSON) Full test matrix → https://gist.github.com/archandatta/f27eb3ea93d58e932b9fd9a7b7b9090f ## Essential run steps to validate Build: ```bash go build -o /tmp/kernel ./cmd/kernel export KERNEL_API_KEY=<your key> ``` **1. Config (create / update / get):** ```bash # create with all categories enabled ID=$(/tmp/kernel browsers create --telemetry=all -o json | jq -r .session_id) # verify on the session /tmp/kernel browsers get $ID -o json | jq .telemetry # partial update — only network is changed, others retain state /tmp/kernel browsers update $ID --telemetry=network=off /tmp/kernel browsers get $ID -o json | jq .telemetry # disable everything /tmp/kernel browsers update $ID --telemetry=off ``` **2. Live stream (text + JSON):** ```bash # in terminal A — start streaming /tmp/kernel browsers update $ID --telemetry=all /tmp/kernel browsers telemetry stream $ID # in terminal B — drive traffic via CDP (use any Playwright/CDP client) to https://kernel.sh # events appear in terminal A: 15:04:05<TAB>[network]<TAB>network_response ``` **3. Filters:** ```bash /tmp/kernel browsers telemetry stream $ID --categories=network /tmp/kernel browsers telemetry stream $ID --categories=system # monitor_* events /tmp/kernel browsers telemetry stream $ID --types=network_response /tmp/kernel browsers telemetry stream $ID -o json # NDJSON envelopes ``` **4. Resume from sequence:** ```bash # pull a seq from JSON output, then resume /tmp/kernel browsers telemetry stream $ID --seq=1 # earliest replay (resumes after seq 1; event #1 itself is not replayable) /tmp/kernel browsers telemetry stream $ID --seq=574 # from a specific event ``` **5. Validation paths (all should error cleanly):** ```bash /tmp/kernel browsers create --telemetry=garbage # invalid assignment /tmp/kernel browsers create --telemetry=foo=on # unknown category /tmp/kernel browsers create --telemetry=network=yes # invalid value /tmp/kernel browsers telemetry stream $ID --seq=-50 # negative seq rejected /tmp/kernel browsers telemetry stream $ID --output=yaml # unsupported output ``` Cleanup: ```bash /tmp/kernel browsers delete $ID ``` ## Tests - 14 unit tests under `cmd/browsers_telemetry_test.go` (`go test ./cmd/...`) - Full end-to-end matrix run against prod API → see linked gist <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > CLI-only feature wiring to existing browser APIs; no auth or core infra changes, with broad unit test coverage for parsing and streaming behavior. > > **Overview** > Adds **browser telemetry** to the Kernel CLI: configure it on session **create** and **update**, and **stream** live events from a running session. > > **Configuration** — New `--telemetry` on `browsers create` and `browsers update` accepts `all`, `off`, or per-category lists (`network=on,page=off`). Per-category updates only send named categories so the API can merge; `all`/`off` set the global `Enabled` flag. `browsers update` now allows telemetry-only updates (no proxy/profile/viewport required). > > **Streaming** — New `browsers telemetry stream <id>` uses the SDK SSE client, resolves the session ID, supports `--categories`, `--types`, `--seq` (Last-Event-ID resume), human-readable lines or `-o json` NDJSON via new `PrintCompactJSONLine`. Client-side filtering and category inference (wire `category` or type-prefix, with `monitor_*` → `system`). > > **Docs & tests** — README documents flags and telemetry section; unit tests cover param wire shapes, stream validation, filters, and create/update telemetry mapping. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0e33313. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 68c4770 commit 2136989

6 files changed

Lines changed: 729 additions & 5 deletions

File tree

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ Commands with JSON output support:
130130
- **Deploy**: `deploy` (JSONL streaming), `history`
131131
- **Invoke**: `invoke` (JSONL streaming), `history`
132132
- **Browser Sub-commands**: `replays list/start`, `process exec/spawn`, `fs file-info/list-files`
133+
- **Browser NDJSON streaming**: `telemetry stream`
133134

134135
### Authentication
135136

@@ -212,13 +213,21 @@ Commands with JSON output support:
212213
- `--start-url <url>` - Initial page to open on launch
213214
- `--pool-id <id>` - Acquire a browser from the specified pool (mutually exclusive with --pool-name; ignores other session flags)
214215
- `--pool-name <name>` - Acquire a browser from the pool name (mutually exclusive with --pool-id; ignores other session flags)
216+
- `--telemetry=all` - Enable telemetry for all categories
217+
- `--telemetry=off` - Disable telemetry
218+
- `--telemetry=<list>` - Per-category config, e.g. `--telemetry=network=on,page=off`
215219
- `--output json`, `-o json` - Output raw JSON object
216220
- _Note: When a pool is specified, omit other session configuration flags—pool settings determine profile, proxy, viewport, etc._
217221
- `kernel browsers delete <id>` - Delete a browser
218222
- `kernel browsers view <id>` - Get live view URL for a browser
219223
- `--output json`, `-o json` - Output JSON with liveViewUrl
220224
- `kernel browsers get <id>` - Get detailed browser session info
221225
- `--output json`, `-o json` - Output raw JSON object
226+
- `kernel browsers update <id>` - Update a running browser session
227+
- `--telemetry=all` - Enable telemetry for all categories
228+
- `--telemetry=off` - Disable telemetry
229+
- `--telemetry=<list>` - Per-category config, e.g. `--telemetry=network=on,page=off`
230+
- `--output json`, `-o json` - Output raw JSON object
222231
- `kernel browsers curl <id> <url>` - Make HTTP requests through a browser session's Chrome network stack
223232
- `-X, --request <method>` - HTTP method (default: GET; defaults to POST when `--data` is set)
224233
- `-H, --header <header>` - HTTP header, repeatable (`"Key: Value"` format)
@@ -281,6 +290,23 @@ Commands with JSON output support:
281290
- `kernel browsers replays download <id> <replay-id>` - Download a replay video
282291
- `-f, --output-file <path>` - Output file path for the replay video
283292

293+
### Browser Telemetry
294+
295+
Telemetry config is a sub-field of the browser session. Use `browsers create` or `browsers update` to enable, disable, or configure it, and `browsers get` to inspect the current state.
296+
297+
- Enable all categories: `kernel browsers update <id> --telemetry=all`
298+
- Disable: `kernel browsers update <id> --telemetry=off`
299+
- Per-category: `kernel browsers update <id> --telemetry=network=on,page=off` (valid: `console`, `interaction`, `network`, `page`; `system` always emits and cannot be toggled)
300+
301+
Per-category updates are partial — only categories you name are changed; others retain their current state. `--telemetry=all` and `--telemetry=off` reset the entire config.
302+
303+
- `kernel browsers telemetry stream <id>` - Stream live telemetry events (NDJSON with `-o json`)
304+
- `--categories <list>` - Filter by event category (`api`, `console`, `interaction`, `network`, `page`, `system`); `system` matches `monitor_*` and `cdp_*` event types
305+
- `--types <list>` - Filter by event type (e.g. `network_response`, `console_error`)
306+
- `--seq <n>` - Resume after sequence number N (Last-Event-ID); replays events with `seq > N`. Omit to stream from now.
307+
- `-o, --output json` - Output newline-delimited JSON envelopes
308+
- Default output: tab-separated `<time>\t[<category>]\t<type>`, e.g. `15:04:05 [network] network_response`
309+
284310
### Browser Process Control
285311

286312
- `kernel browsers process exec <id> [--] [command...]` - Execute a command synchronously

cmd/browsers.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ type BrowsersCreateInput struct {
175175
StartURL string
176176
Extensions []string
177177
Viewport string
178+
Telemetry string
178179
Output string
179180
}
180181

@@ -202,6 +203,7 @@ type BrowsersUpdateInput struct {
202203
ProfileSaveChanges BoolFlag
203204
Viewport string
204205
Force bool
206+
Telemetry string
205207
Output string
206208
}
207209

@@ -215,6 +217,7 @@ type BrowsersCmd struct {
215217
logs BrowserLogService
216218
computer BrowserComputerService
217219
playwright BrowserPlaywrightService
220+
telemetry BrowserTelemetryService
218221
}
219222

220223
type BrowsersListInput struct {
@@ -331,9 +334,6 @@ func (b BrowsersCmd) Create(ctx context.Context, in BrowsersCreateInput) error {
331334
return err
332335
}
333336

334-
if in.Output != "json" {
335-
pterm.Info.Println("Creating browser session...")
336-
}
337337
params := kernel.BrowserNewParams{}
338338
if in.TimeoutSeconds > 0 {
339339
params.TimeoutSeconds = kernel.Opt(int64(in.TimeoutSeconds))
@@ -410,6 +410,17 @@ func (b BrowsersCmd) Create(ctx context.Context, in BrowsersCreateInput) error {
410410
}
411411
}
412412

413+
if in.Telemetry != "" {
414+
t, err := buildNewTelemetryParam(in.Telemetry)
415+
if err != nil {
416+
return err
417+
}
418+
params.Telemetry = t
419+
}
420+
421+
if in.Output != "json" {
422+
pterm.Info.Println("Creating browser session...")
423+
}
413424
browser, err := b.browsers.New(ctx, params)
414425
if err != nil {
415426
return util.CleanedUpSdkError{Err: err}
@@ -576,8 +587,8 @@ func (b BrowsersCmd) Update(ctx context.Context, in BrowsersUpdateInput) error {
576587
}
577588

578589
// Validate that at least one update option is provided
579-
if !hasProxyChange && !hasProfileChange && !hasViewportChange {
580-
return fmt.Errorf("must specify at least one of: --proxy-id, --clear-proxy, --profile-id, --profile-name, or --viewport")
590+
if !hasProxyChange && !hasProfileChange && !hasViewportChange && in.Telemetry == "" {
591+
return fmt.Errorf("must specify at least one of: --proxy-id, --clear-proxy, --profile-id, --profile-name, --viewport, or --telemetry")
581592
}
582593

583594
params := kernel.BrowserUpdateParams{}
@@ -602,6 +613,15 @@ func (b BrowsersCmd) Update(ctx context.Context, in BrowsersUpdateInput) error {
602613
}
603614
}
604615

616+
// Handle telemetry changes
617+
if in.Telemetry != "" {
618+
t, err := buildUpdateTelemetryParam(in.Telemetry)
619+
if err != nil {
620+
return err
621+
}
622+
params.Telemetry = t
623+
}
624+
605625
// Handle viewport changes
606626
if hasViewportChange {
607627
width, height, refreshRate, err := parseViewport(in.Viewport)
@@ -2229,6 +2249,7 @@ func init() {
22292249
browsersUpdateCmd.Flags().Bool("save-changes", false, "If set, save changes back to the profile when the session ends")
22302250
browsersUpdateCmd.Flags().String("viewport", "", "Browser viewport size (e.g., 1920x1080@25). Supported: 2560x1440@10, 1920x1080@25, 1920x1200@25, 1440x900@25, 1024x768@60, 1200x800@60, 1280x800@60")
22312251
browsersUpdateCmd.Flags().Bool("force", false, "Force viewport resize even when a live view or recording/replay is active")
2252+
browsersUpdateCmd.Flags().String("telemetry", "", "Update telemetry: --telemetry=all to enable, --telemetry=off to disable, --telemetry=network=on,page=off for per-category")
22322253

22332254
browsersCmd.AddCommand(browsersListCmd)
22342255
browsersCmd.AddCommand(browsersCreateCmd)
@@ -2494,6 +2515,7 @@ func init() {
24942515
browsersCreateCmd.Flags().Bool("viewport-interactive", false, "Interactively select viewport size from list")
24952516
browsersCreateCmd.Flags().String("pool-id", "", "Browser pool ID to acquire from (mutually exclusive with --pool-name)")
24962517
browsersCreateCmd.Flags().String("pool-name", "", "Browser pool name to acquire from (mutually exclusive with --pool-id)")
2518+
browsersCreateCmd.Flags().String("telemetry", "", "Configure telemetry: --telemetry=all to enable, --telemetry=off to disable, --telemetry=network=on,page=off for per-category")
24972519

24982520
// curl
24992521
curlCmd := &cobra.Command{
@@ -2520,6 +2542,15 @@ followed automatically by Chromium.`,
25202542
curlCmd.Flags().BoolP("silent", "s", false, "Suppress progress output")
25212543
browsersCmd.AddCommand(curlCmd)
25222544

2545+
telemetryRoot := &cobra.Command{Use: "telemetry", Short: "Browser telemetry operations"}
2546+
telemetryStream := &cobra.Command{Use: "stream <id>", Short: "Stream live telemetry events", Args: cobra.ExactArgs(1), RunE: runBrowsersTelemetryStream}
2547+
telemetryStream.Flags().StringSlice("categories", []string{}, "Filter by API event category (api,console,interaction,network,page,system); system covers monitor_* and cdp_* events")
2548+
telemetryStream.Flags().StringSlice("types", []string{}, "Filter by event type (e.g. network_response,console_error)")
2549+
telemetryStream.Flags().Int64("seq", -1, "Resume after sequence number N (Last-Event-ID); replays events with seq > N. Default -1 streams from now")
2550+
telemetryStream.Flags().StringP("output", "o", "", "Output format: json for newline-delimited JSON envelopes")
2551+
telemetryRoot.AddCommand(telemetryStream)
2552+
browsersCmd.AddCommand(telemetryRoot)
2553+
25232554
// no flags for view; it takes a single positional argument
25242555
}
25252556

@@ -2563,6 +2594,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
25632594
viewportInteractive, _ := cmd.Flags().GetBool("viewport-interactive")
25642595
poolID, _ := cmd.Flags().GetString("pool-id")
25652596
poolName, _ := cmd.Flags().GetString("pool-name")
2597+
telemetry, _ := cmd.Flags().GetString("telemetry")
25662598
output, _ := cmd.Flags().GetString("output")
25672599

25682600
if poolID != "" && poolName != "" {
@@ -2672,6 +2704,7 @@ func runBrowsersCreate(cmd *cobra.Command, args []string) error {
26722704
StartURL: startURL,
26732705
Extensions: extensions,
26742706
Viewport: viewport,
2707+
Telemetry: telemetry,
26752708
Output: output,
26762709
}
26772710

@@ -2730,6 +2763,7 @@ func runBrowsersUpdate(cmd *cobra.Command, args []string) error {
27302763
saveChanges, _ := cmd.Flags().GetBool("save-changes")
27312764
viewport, _ := cmd.Flags().GetString("viewport")
27322765
force, _ := cmd.Flags().GetBool("force")
2766+
telemetry, _ := cmd.Flags().GetString("telemetry")
27332767

27342768
svc := client.Browsers
27352769
b := BrowsersCmd{browsers: &svc}
@@ -2742,6 +2776,7 @@ func runBrowsersUpdate(cmd *cobra.Command, args []string) error {
27422776
ProfileSaveChanges: BoolFlag{Set: cmd.Flags().Changed("save-changes"), Value: saveChanges},
27432777
Viewport: viewport,
27442778
Force: force,
2779+
Telemetry: telemetry,
27452780
Output: out,
27462781
})
27472782
}

0 commit comments

Comments
 (0)