diff --git a/server/cmd/api/api/events.go b/server/cmd/api/api/events.go index 27dfc50a..33e2b88d 100644 --- a/server/cmd/api/api/events.go +++ b/server/cmd/api/api/events.go @@ -122,6 +122,9 @@ func (s *ApiService) StreamTelemetryEvents(ctx context.Context, req oapi.StreamT } if result.Dropped > 0 { + // The client's next Last-Event-ID will skip this gap, so record it: + // a silent skip reads as a quiet stream rather than a lost one. + s.telemetrySession.RecordDropped(result.Dropped) continue } diff --git a/server/cmd/api/api/telemetry.go b/server/cmd/api/api/telemetry.go index a45ca3ba..27826453 100644 --- a/server/cmd/api/api/telemetry.go +++ b/server/cmd/api/api/telemetry.go @@ -52,7 +52,7 @@ func (s *ApiService) PutTelemetry(ctx context.Context, req oapi.PutTelemetryRequ s.telemetrySession.Stop() s.stopTelemetryState() } - return oapi.PutTelemetry200JSONResponse(oapi.TelemetryState{Config: disabledConfig(), Seq: int64(s.telemetrySession.Seq())}), nil + return oapi.PutTelemetry200JSONResponse(s.stoppedTelemetryResponse()), nil } // Commit the config first so the filter is live before the collector emits, @@ -102,7 +102,7 @@ func (s *ApiService) PatchTelemetry(ctx context.Context, req oapi.PatchTelemetry if allDisabled { s.telemetrySession.Stop() s.stopTelemetryState() - return oapi.PatchTelemetry200JSONResponse(oapi.TelemetryState{Config: disabledConfig(), Seq: int64(s.telemetrySession.Seq())}), nil + return oapi.PatchTelemetry200JSONResponse(s.stoppedTelemetryResponse()), nil } // Commit first so the filter is live before the collector emits, then @@ -199,8 +199,9 @@ func (s *ApiService) stopTelemetryState() { // buildTelemetryResponse constructs a TelemetryState response from the current configuration. func (s *ApiService) buildTelemetryResponse() oapi.TelemetryState { resp := oapi.TelemetryState{ - Config: telemetryConfigToOAPI(s.telemetrySession.Config()), - Seq: int64(s.telemetrySession.Seq()), + Config: telemetryConfigToOAPI(s.telemetrySession.Config()), + Seq: int64(s.telemetrySession.Seq()), + DroppedEvents: lo.ToPtr(int64(s.telemetrySession.DroppedEvents())), } if appliedAt := s.telemetrySession.AppliedAt(); !appliedAt.IsZero() { resp.AppliedAt = &appliedAt @@ -208,28 +209,59 @@ func (s *ApiService) buildTelemetryResponse() oapi.TelemetryState { return resp } -// categoryField pairs a category with its config field so the helpers can walk -// the configurable categories without enumerating them inline. +// stoppedTelemetryResponse reports the cleared configuration. Seq and the +// dropped count are process-scoped, so they survive a session ending. +func (s *ApiService) stoppedTelemetryResponse() oapi.TelemetryState { + return oapi.TelemetryState{ + Config: disabledConfig(), + Seq: int64(s.telemetrySession.Seq()), + DroppedEvents: lo.ToPtr(int64(s.telemetrySession.DroppedEvents())), + } +} + +// categoryField pairs a category with its enabled flag so the helpers can walk +// the configurable categories without enumerating them inline. The flag rather +// than the config, because control carries settings the others do not. type categoryField struct { category oapi.TelemetryEventCategory - config *oapi.BrowserTelemetryCategoryConfig + enabled *bool } func categoryFields(b *oapi.BrowserTelemetryCategoriesConfig) []categoryField { + flag := func(c *oapi.BrowserTelemetryCategoryConfig) *bool { + if c == nil { + return nil + } + return c.Enabled + } + var control *bool + if b.Control != nil { + control = b.Control.Enabled + } return []categoryField{ - {events.Console, b.Console}, - {events.Network, b.Network}, - {events.Page, b.Page}, - {events.Interaction, b.Interaction}, - {events.Control, b.Control}, - {events.Platform, b.Platform}, - {events.Connection, b.Connection}, - {events.System, b.System}, - {events.Screenshot, b.Screenshot}, - {events.Captcha, b.Captcha}, + {events.Console, flag(b.Console)}, + {events.Network, flag(b.Network)}, + {events.Page, flag(b.Page)}, + {events.Interaction, flag(b.Interaction)}, + {events.Control, control}, + {events.Platform, flag(b.Platform)}, + {events.Connection, flag(b.Connection)}, + {events.System, flag(b.System)}, + {events.Screenshot, flag(b.Screenshot)}, + {events.Captcha, flag(b.Captcha)}, } } +// excludedCdpMethodsFromOAPI reads the cdp_command exclusion list, which only +// the control category carries. +func excludedCdpMethodsFromOAPI(cfg *oapi.BrowserTelemetryConfig) []oapi.BrowserCdpCommandMethod { + if cfg == nil || cfg.Browser == nil || cfg.Browser.Control == nil || + cfg.Browser.Control.Cdp == nil || cfg.Browser.Control.Cdp.ExcludedMethods == nil { + return nil + } + return *cfg.Browser.Control.Cdp.ExcludedMethods +} + func categorySetOf(cats []oapi.TelemetryEventCategory) map[oapi.TelemetryEventCategory]bool { set := make(map[oapi.TelemetryEventCategory]bool, len(cats)) for _, c := range cats { @@ -262,14 +294,18 @@ func telemetryConfigFromOAPI(cfg *oapi.BrowserTelemetryConfig) (telemetry.Teleme cats := make([]oapi.TelemetryEventCategory, 0, len(events.UserCategories)) for _, f := range categoryFields(cfg.Browser) { - if f.config != nil && f.config.Enabled != nil && *f.config.Enabled { + if f.enabled != nil && *f.enabled { cats = append(cats, f.category) } } if len(cats) == 0 { return telemetry.TelemetryConfig{}, true, nil } - return telemetry.TelemetryConfig{Categories: cats, ExportOTLP: exportOTLP}, false, nil + return telemetry.TelemetryConfig{ + Categories: cats, + ExportOTLP: exportOTLP, + ExcludedCdpMethods: excludedCdpMethodsFromOAPI(cfg), + }, false, nil } // exportOTLPFromOAPI reads the OTLP export toggle from a config, defaulting to @@ -295,10 +331,10 @@ func mergeTelemetryConfig(current telemetry.TelemetryConfig, patch *oapi.Browser if patch.Browser != nil { for _, f := range categoryFields(patch.Browser) { - if f.config == nil || f.config.Enabled == nil { + if f.enabled == nil { continue // not mentioned in patch; keep current state } - if *f.config.Enabled { + if *f.enabled { active[f.category] = struct{}{} } else { delete(active, f.category) @@ -312,6 +348,13 @@ func mergeTelemetryConfig(current telemetry.TelemetryConfig, patch *oapi.Browser exportOTLP = *patch.Export.Otlp.Enabled } + // So do the cdp_command exclusions: an omitted list is unchanged, an empty + // one clears them. + excluded := current.ExcludedCdpMethods + if patched := excludedCdpMethodsFromOAPI(patch); patched != nil { + excluded = patched + } + if len(active) == 0 { return telemetry.TelemetryConfig{}, true } @@ -319,7 +362,7 @@ func mergeTelemetryConfig(current telemetry.TelemetryConfig, patch *oapi.Browser for c := range active { cats = append(cats, c) } - return telemetry.TelemetryConfig{Categories: cats, ExportOTLP: exportOTLP}, false + return telemetry.TelemetryConfig{Categories: cats, ExportOTLP: exportOTLP, ExcludedCdpMethods: excluded}, false } // disabledConfig returns a BrowserTelemetryConfig with every configurable category explicitly disabled. @@ -333,7 +376,7 @@ func disabledConfig() oapi.BrowserTelemetryConfig { Network: off(), Page: off(), Interaction: off(), - Control: off(), + Control: &oapi.BrowserTelemetryControlConfig{Enabled: lo.ToPtr(false)}, Platform: off(), Connection: off(), System: off(), @@ -359,13 +402,17 @@ func telemetryConfigToOAPI(cfg telemetry.TelemetryConfig) oapi.BrowserTelemetryC on := active[cat] return &oapi.BrowserTelemetryCategoryConfig{Enabled: &on} } + control := &oapi.BrowserTelemetryControlConfig{Enabled: lo.ToPtr(active[events.Control])} + if len(cfg.ExcludedCdpMethods) > 0 { + control.Cdp = &oapi.BrowserTelemetryCdpControlConfig{ExcludedMethods: &cfg.ExcludedCdpMethods} + } return oapi.BrowserTelemetryConfig{ Browser: &oapi.BrowserTelemetryCategoriesConfig{ Console: enabled(events.Console), Network: enabled(events.Network), Page: enabled(events.Page), Interaction: enabled(events.Interaction), - Control: enabled(events.Control), + Control: control, Platform: enabled(events.Platform), Connection: enabled(events.Connection), System: enabled(events.System), diff --git a/server/cmd/api/api/telemetry_test.go b/server/cmd/api/api/telemetry_test.go index ec69d07b..8db4243c 100644 --- a/server/cmd/api/api/telemetry_test.go +++ b/server/cmd/api/api/telemetry_test.go @@ -12,6 +12,7 @@ import ( oapi "github.com/kernel/kernel-images/server/lib/oapi" "github.com/kernel/kernel-images/server/lib/recorder" "github.com/kernel/kernel-images/server/lib/scaletozero" + "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,7 +29,7 @@ func allCategoriesDisabled() *oapi.BrowserTelemetryCategoriesConfig { Network: off(), Page: off(), Interaction: off(), - Control: off(), + Control: &oapi.BrowserTelemetryControlConfig{Enabled: lo.ToPtr(false)}, Platform: off(), Connection: off(), System: off(), @@ -207,7 +208,7 @@ func TestTelemetryHandlersDriveMiddlewareToggle(t *testing.T) { _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{ Body: &oapi.BrowserTelemetryConfig{ Browser: &oapi.BrowserTelemetryCategoriesConfig{ - Control: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}, + Control: &oapi.BrowserTelemetryControlConfig{Enabled: &tr}, }, }, }) @@ -217,7 +218,7 @@ func TestTelemetryHandlersDriveMiddlewareToggle(t *testing.T) { _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{ Body: &oapi.BrowserTelemetryConfig{ Browser: &oapi.BrowserTelemetryCategoriesConfig{ - Control: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Control: &oapi.BrowserTelemetryControlConfig{Enabled: &f}, }, }, }) @@ -254,7 +255,7 @@ func TestTelemetryHandlersEnableMiddlewareForPlatformOnly(t *testing.T) { _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{ Body: &oapi.BrowserTelemetryConfig{ Browser: &oapi.BrowserTelemetryCategoriesConfig{ - Control: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Control: &oapi.BrowserTelemetryControlConfig{Enabled: &f}, }, }, }) @@ -293,6 +294,10 @@ func TestGetTelemetry(t *testing.T) { r200, ok := resp.(oapi.GetTelemetry200JSONResponse) require.True(t, ok) assert.Equal(t, started.Config, r200.Config) + // Optional in the schema so an older image's response still validates, + // but always set here: absent would mean "not reported", not zero. + require.NotNil(t, r200.DroppedEvents) + assert.Zero(t, *r200.DroppedEvents) }) } @@ -624,3 +629,78 @@ func (e *blockingStopExporter) Running() bool { defer e.mu.Unlock() return e.running } + +func TestCdpExcludedMethodsRoundTrip(t *testing.T) { + ctx := context.Background() + excluded := []oapi.BrowserCdpCommandMethod{"Input.dispatchMouseEvent", "Page.captureScreenshot"} + withExclusions := func() *oapi.BrowserTelemetryConfig { + return &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Control: &oapi.BrowserTelemetryControlConfig{ + Enabled: lo.ToPtr(true), + Cdp: &oapi.BrowserTelemetryCdpControlConfig{ExcludedMethods: &excluded}, + }, + }, + } + } + + t.Run("put stores them and the session exposes them to the proxy", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: withExclusions()}) + require.NoError(t, err) + created := resp.(oapi.PutTelemetry201JSONResponse) + require.NotNil(t, created.Config.Browser.Control.Cdp) + assert.Equal(t, excluded, *created.Config.Browser.Control.Cdp.ExcludedMethods) + + // The proxy reads this set per command, so it has to reflect the config. + assert.Equal(t, map[string]struct{}{ + "Input.dispatchMouseEvent": {}, + "Page.captureScreenshot": {}, + }, svc.telemetrySession.ExcludedCdpMethods()) + }) + + t.Run("patch leaves an omitted list alone and an empty list clears it", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{Body: withExclusions()}) + require.NoError(t, err) + + // Category toggle only: the exclusions are not mentioned, so they stand. + _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + System: &oapi.BrowserTelemetryCategoryConfig{Enabled: lo.ToPtr(true)}, + }, + }}) + require.NoError(t, err) + assert.Len(t, svc.telemetrySession.ExcludedCdpMethods(), 2) + + empty := []oapi.BrowserCdpCommandMethod{} + _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Control: &oapi.BrowserTelemetryControlConfig{ + Cdp: &oapi.BrowserTelemetryCdpControlConfig{ExcludedMethods: &empty}, + }, + }, + }}) + require.NoError(t, err) + assert.Empty(t, svc.telemetrySession.ExcludedCdpMethods()) + }) +} + +// dropped_events was added to TelemetryState after it shipped, so it stays +// optional: a response from an image that predates it must still decode, and +// an old client's control block must still be a valid request. +func TestTelemetryStateStaysCompatibleWithOlderImages(t *testing.T) { + var state oapi.TelemetryState + err := json.Unmarshal([]byte(`{"config":{},"seq":42}`), &state) + require.NoError(t, err) + assert.Nil(t, state.DroppedEvents, "absent means not reported, which is not zero") + assert.EqualValues(t, 42, state.Seq) + + // A client that predates control.cdp sends only enabled, and still parses. + var cfg oapi.BrowserTelemetryConfig + err = json.Unmarshal([]byte(`{"browser":{"control":{"enabled":true}}}`), &cfg) + require.NoError(t, err) + require.NotNil(t, cfg.Browser.Control) + assert.True(t, *cfg.Browser.Control.Enabled) + assert.Nil(t, cfg.Browser.Control.Cdp) +} diff --git a/server/cmd/api/main.go b/server/cmd/api/main.go index 3a4a1461..30155ede 100644 --- a/server/cmd/api/main.go +++ b/server/cmd/api/main.go @@ -108,8 +108,12 @@ func main() { } // Construct events pipeline + // Sized for the control stream's event rate rather than the operational + // signals it started with: browser-control CDP commands are one event per + // keystroke and two per click, so a form-filling session produces thousands + // where a session used to produce tens. eventStream, err := events.NewEventStream(events.EventStreamConfig{ - RingCapacity: 1024, + RingCapacity: 8192, }) if err != nil { slogger.Error("failed to create event stream", "err", err) @@ -321,8 +325,11 @@ func main() { rDevtools.Get("/json/", jsonTargetHandler) rDevtools.Get("/json/list", jsonTargetHandler) rDevtools.Get("/json/list/", jsonTargetHandler) + // Checked once per forwarded client frame, so it reads the session's + // lock-free view rather than taking the telemetry lock. + controlEnabled := func() bool { return telemetrySession.CategoryEnabled(events.Control) } rDevtools.Get("/*", func(w http.ResponseWriter, r *http.Request) { - devtoolsproxy.WebSocketProxyHandler(upstreamMgr, slogger, config.LogCDPMessages, stz, telemetrySession.Publish, wsRegistry).ServeHTTP(w, r) + devtoolsproxy.WebSocketProxyHandler(upstreamMgr, slogger, config.LogCDPMessages, stz, telemetrySession.Publish, controlEnabled, telemetrySession.ExcludedCdpMethods, wsRegistry).ServeHTTP(w, r) }) srvDevtools := &http.Server{ diff --git a/server/e2e/e2e_otlp_storage_test.go b/server/e2e/e2e_otlp_storage_test.go index 8610e07d..c4c66b3e 100644 --- a/server/e2e/e2e_otlp_storage_test.go +++ b/server/e2e/e2e_otlp_storage_test.go @@ -106,7 +106,7 @@ func enableControlExport(t *testing.T, ctx context.Context, client *instanceoapi tr := true resp, err := client.PutTelemetryWithResponse(ctx, instanceoapi.PutTelemetryJSONRequestBody{ Browser: &instanceoapi.BrowserTelemetryCategoriesConfig{ - Control: &instanceoapi.BrowserTelemetryCategoryConfig{Enabled: &tr}, + Control: &instanceoapi.BrowserTelemetryControlConfig{Enabled: &tr}, }, Export: &instanceoapi.BrowserTelemetryExportConfig{ Otlp: &instanceoapi.BrowserTelemetryOTLPExportConfig{Enabled: &tr}, diff --git a/server/lib/devtoolsproxy/cdpcommand.go b/server/lib/devtoolsproxy/cdpcommand.go new file mode 100644 index 00000000..3d7ca9c4 --- /dev/null +++ b/server/lib/devtoolsproxy/cdpcommand.go @@ -0,0 +1,100 @@ +package devtoolsproxy + +import ( + "encoding/json" + + "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" +) + +// cdpCommandMethod is the first of two decodes: the method alone. With no +// Params field, encoding/json walks the arguments without copying them, so +// deciding that a large Runtime.callFunctionOn is not browser control costs a +// scan rather than a megabyte. It is a real decode rather than a scan for the +// method name, so an escaped name like "Input.\u0064ispatchMouseEvent" +// resolves to the method it actually names. +type cdpCommandMethod struct { + Method string `json:"method"` +} + +// cdpCommand is the JSON-RPC envelope of a client command, matching the shape +// the rest of the repo uses (cdpmonitor, cdpclient). Params stays raw so the +// per-method sanitizer decides what is worth decoding. Only a frame that +// already named a supported method is decoded this far. +type cdpCommand struct { + ID *int64 `json:"id"` + Method string `json:"method"` + SessionID string `json:"sessionId"` + Params json.RawMessage `json:"params"` + + // connectionID names the proxy connection the command arrived on. It comes + // from the observer rather than the frame, so concurrent clients driving one + // browser can be told apart. + connectionID string +} + +func (c cdpCommand) sessionID() *string { + if c.SessionID == "" { + return nil + } + return clipIDPtr(&c.SessionID) +} + +func (c cdpCommand) connID() *string { + if c.connectionID == "" { + return nil + } + return clipIDPtr(&c.connectionID) +} + +// supportedMethod reports the browser-control method a frame names, if any. It +// is the admission test, so it copies nothing out of the frame: with no Params +// field, encoding/json walks the arguments without materializing them, and a +// large Runtime.callFunctionOn costs a scan rather than a megabyte. +func supportedMethod(msg []byte) (string, bool) { + var probe cdpCommandMethod + if err := json.Unmarshal(msg, &probe); err != nil { + return "", false + } + if _, ok := sanitizers[probe.Method]; !ok { + return "", false + } + return probe.Method, true +} + +// cdpCommandEvent builds the cdp_command event for a frame whose method +// supportedMethod already resolved, or reports false when the arguments do not +// decode. ts is when the command reached Chromium, passed in so time spent +// queued for classification does not show up as event time. +// +// Every supported command gets one event. Subtypes like mouseMoved and keyUp +// are commands in their own right — a mouseMoved with buttons held is a drag +// path, a keyUp releases a modifier — so the stream is never coalesced down to +// what looks like the interesting phases. +func cdpCommandEvent(msg []byte, ts int64, connectionID, method string) (events.Event, bool) { + sanitize, ok := sanitizers[method] + if !ok { + return events.Event{}, false + } + // Only now is the frame worth copying arguments out of. A browser-control + // command is small, so this second pass is cheap. + cmd := cdpCommand{connectionID: connectionID} + if err := json.Unmarshal(msg, &cmd); err != nil { + return events.Event{}, false + } + data, err := sanitize(cmd) + if err != nil { + return events.Event{}, false + } + payload, err := json.Marshal(data) + if err != nil { + return events.Event{}, false + } + return events.Event{ + Ts: ts, + Type: "cdp_command", + Category: events.Control, + Source: oapi.BrowserEventSource{Kind: oapi.KernelApi}, + Data: payload, + }, true +} diff --git a/server/lib/devtoolsproxy/cdpcommand_test.go b/server/lib/devtoolsproxy/cdpcommand_test.go new file mode 100644 index 00000000..2747e913 --- /dev/null +++ b/server/lib/devtoolsproxy/cdpcommand_test.go @@ -0,0 +1,851 @@ +package devtoolsproxy + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + "testing" + + "github.com/ghodss/yaml" + + serverpkg "github.com/kernel/kernel-images/server" + "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" +) + +// bs is a single backslash, kept out of the fixtures below so an editing +// pass cannot silently strip the escape they are testing. +const bs = `\` + +// testForwardTs stands in for the time a command reached Chromium. +const testForwardTs int64 = 1_700_000_000_000_000 + +// payloadOf classifies a frame and returns its event payload as a plain map. +// Asserting on the wire shape rather than the generated union type is the +// point: the payload is what a reader sees. +func payloadOf(t *testing.T, frame string) map[string]any { + t.Helper() + ev, ok := classifyFrame(t, frame, nil) + if !ok { + t.Fatalf("frame produced no event: %s", frame) + } + if ev.Type != "cdp_command" { + t.Fatalf("type = %q, want cdp_command", ev.Type) + } + if ev.Category != events.Control { + t.Fatalf("category = %q, want control", ev.Category) + } + if ev.Ts != testForwardTs { + t.Fatalf("ts = %d, want the forward time %d", ev.Ts, testForwardTs) + } + var got map[string]any + if err := json.Unmarshal(ev.Data, &got); err != nil { + t.Fatalf("unmarshal data: %v", err) + } + return got +} + +func TestCdpCommandEventClassification(t *testing.T) { + tests := []struct { + name string + frame string + want map[string]any + }{ + { + name: "click keeps the arguments that describe it", + frame: `{"id":1,"sessionId":"S1","method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":10.5,"y":20,"button":"left","clickCount":2,"modifiers":8,"buttons":1,"pointerType":"mouse"}}`, + want: map[string]any{ + "command_id": 1.0, "method": "Input.dispatchMouseEvent", "session_id": "S1", "event_type": "mousePressed", + "x": 10.5, "y": 20.0, "button": "left", "click_count": 2.0, + "modifiers": 8.0, "buttons": 1.0, "pointer_type": "mouse", + }, + }, + { + name: "mouseMoved with buttons held is a drag path, not a duplicate phase", + frame: `{"id":2,"method":"Input.dispatchMouseEvent","params":{"type":"mouseMoved","x":9,"y":9,"buttons":1}}`, + want: map[string]any{ + "command_id": 2.0, "method": "Input.dispatchMouseEvent", "event_type": "mouseMoved", + "x": 9.0, "y": 9.0, "buttons": 1.0, + }, + }, + { + name: "wheel keeps its deltas", + frame: `{"id":3,"method":"Input.dispatchMouseEvent","params":{"type":"mouseWheel","x":1,"y":2,"deltaX":0,"deltaY":-400}}`, + want: map[string]any{ + "command_id": 3.0, "method": "Input.dispatchMouseEvent", "event_type": "mouseWheel", + "x": 1.0, "y": 2.0, "delta_x": 0.0, "delta_y": -400.0, + }, + }, + { + name: "keyUp releases a held modifier", + frame: `{"id":4,"method":"Input.dispatchKeyEvent","params":{"type":"keyUp","key":"Shift"}}`, + want: map[string]any{"command_id": 4.0, "method": "Input.dispatchKeyEvent", "event_type": "keyUp", "named_key": "Shift"}, + }, + { + name: "char is the command that inserts the character", + frame: `{"id":5,"method":"Input.dispatchKeyEvent","params":{"type":"char","text":"a"}}`, + want: map[string]any{"command_id": 5.0, "method": "Input.dispatchKeyEvent", "event_type": "char", "text_length": 1.0}, + }, + { + name: "a typed key is counted, never named", + frame: `{"id":6,"method":"Input.dispatchKeyEvent","params":{"type":"keyDown","key":"é","text":"é","code":"KeyE"}}`, + want: map[string]any{"command_id": 6.0, "method": "Input.dispatchKeyEvent", "event_type": "keyDown", "text_length": 1.0}, + }, + { + name: "scroll gesture keeps its distance", + frame: `{"id":7,"method":"Input.synthesizeScrollGesture","params":{"x":1,"y":2,"xDistance":0,"yDistance":-500,"speed":800}}`, + want: map[string]any{ + "command_id": 7.0, "method": "Input.synthesizeScrollGesture", "x": 1.0, "y": 2.0, + "x_distance": 0.0, "y_distance": -500.0, "speed": 800.0, + }, + }, + { + name: "touch reports its point count and the primary point", + frame: `{"id":8,"method":"Input.dispatchTouchEvent","params":{"type":"touchStart","touchPoints":[{"x":100,"y":200},{"x":300,"y":400}]}}`, + want: map[string]any{ + "command_id": 8.0, "method": "Input.dispatchTouchEvent", "event_type": "touchStart", + "touch_point_count": 2.0, "x": 100.0, "y": 200.0, + }, + }, + { + name: "drag reports counts and mime categories, not contents", + frame: `{"id":9,"method":"Input.dispatchDragEvent","params":{"type":"drop","x":5,"y":6,"data":{"items":[{"mimeType":"text/plain","data":"secret"},{"mimeType":"image/png","data":"secret"}],"files":["/tmp/a.pdf"],"dragOperationsMask":1}}}`, + want: map[string]any{ + "command_id": 9.0, "method": "Input.dispatchDragEvent", "event_type": "drop", "x": 5.0, "y": 6.0, + "drag_item_count": 2.0, "drag_file_count": 1.0, + "drag_mime_categories": []any{"image", "text"}, "drag_operations_mask": 1.0, + }, + }, + { + name: "navigation reports the scheme, never the host or the path", + frame: `{"id":10,"method":"Page.navigate","params":{"url":"https://example.com/reset?token=abc","referrer":"https://mail.example.com/x","transitionType":"typed"}}`, + want: map[string]any{ + "command_id": 10.0, "method": "Page.navigate", "url_scheme": "https", + "transition_type": "typed", "referrer_present": true, + }, + }, + { + name: "dialog reports the decision", + frame: `{"id":11,"method":"Page.handleJavaScriptDialog","params":{"accept":true,"promptText":"hunter2"}}`, + want: map[string]any{"command_id": 11.0, "method": "Page.handleJavaScriptDialog", "accept": true, "prompt_text_length": 7.0}, + }, + { + name: "file selection reports the count, never the paths", + frame: `{"id":12,"method":"DOM.setFileInputFiles","params":{"files":["/tmp/a.pdf","/tmp/b.pdf"],"backendNodeId":7}}`, + want: map[string]any{"command_id": 12.0, "method": "DOM.setFileInputFiles", "file_count": 2.0, "backend_node_id": 7.0}, + }, + { + name: "screenshot reports its options and clip", + frame: `{"id":13,"method":"Page.captureScreenshot","params":{"format":"png","quality":80,"clip":{"x":0,"y":0,"width":800,"height":600,"scale":1}}}`, + want: map[string]any{ + "command_id": 13.0, "method": "Page.captureScreenshot", "format": "png", "quality": 80.0, + "clip_x": 0.0, "clip_y": 0.0, "clip_width": 800.0, "clip_height": 600.0, "clip_scale": 1.0, + }, + }, + { + name: "autofill reports which kind of value was filled", + frame: `{"id":14,"method":"Autofill.trigger","params":{"fieldId":3,"card":{"number":"4111111111111111","cvc":"123"}}}`, + want: map[string]any{"command_id": 14.0, "method": "Autofill.trigger", "field_id": 3.0, "mode": "card"}, + }, + { + name: "a command with no arguments reports its name", + frame: `{"id":15,"method":"Page.bringToFront"}`, + want: map[string]any{"command_id": 15.0, "method": "Page.bringToFront"}, + }, + { + name: "window bounds are flattened out of the bounds object", + frame: `{"id":16,"method":"Browser.setWindowBounds","params":{"windowId":1,"bounds":{"left":0,"top":0,"width":1280,"height":720,"windowState":"normal"}}}`, + want: map[string]any{ + "command_id": 16.0, "method": "Browser.setWindowBounds", "window_id": 1.0, "left": 0.0, "top": 0.0, + "width": 1280.0, "height": 720.0, "window_state": "normal", + }, + }, + {name: "library bookkeeping is not browser control", frame: `{"id":17,"method":"Runtime.callFunctionOn","params":{"functionDeclaration":"() => 1","objectId":"x"}}`}, + {name: "runtime evaluation is not browser control", frame: `{"id":18,"method":"Runtime.evaluate","params":{"expression":"1+1"}}`}, + {name: "configuration is not browser control", frame: `{"id":19,"method":"Emulation.setDeviceMetricsOverride","params":{"width":1920,"height":1080}}`}, + {name: "chrome ui commands stay out", frame: `{"id":20,"method":"Browser.executeBrowserCommand","params":{"commandId":"openTabSearch"}}`}, + {name: "command results carry no method", frame: `{"id":21,"result":{"nodeId":42}}`}, + {name: "upstream events are not commands", frame: `{"method":"Page.frameNavigated","params":{"frame":{"url":"https://example.com"}}}`}, + {name: "a nested method cannot spoof a control command", frame: `{"id":22,"method":"Runtime.callFunctionOn","params":{"method":"Input.dispatchMouseEvent","x":1}}`}, + {name: "malformed frames are dropped", frame: `{"id":23,"method":"Input.insertText","params":`}, + {name: "malformed params are dropped", frame: `{"id":24,"method":"Input.insertText","params":{"text":5}}`}, + {name: "an empty frame is dropped", frame: ``}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.want == nil { + if ev, ok := classifyFrame(t, tc.frame, nil); ok { + t.Fatalf("frame produced an event, want none: %s", ev.Data) + } + return + } + got := payloadOf(t, tc.frame) + wantJSON, _ := json.Marshal(tc.want) + gotJSON, _ := json.Marshal(got) + if string(wantJSON) != string(gotJSON) { + t.Fatalf("payload mismatch:\n want %s\n got %s", wantJSON, gotJSON) + } + }) + } +} + +// An escaped method name is still that method. The classifier decodes the +// frame rather than scanning it for a literal, so "Input.dispatch..." +// cannot slip a command past the stream. +func TestCdpCommandEventDecodesEscapedMethodNames(t *testing.T) { + // The "d" arrives as a unicode escape. A byte scan for the literal method + // name misses this; a decode does not. + escaped := `{"id":1,"method":"Input.\u0064ispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}` + if !strings.Contains(escaped, bs+`u0064`) { + t.Fatal("the fixture lost its escape, so this test proves nothing") + } + got := payloadOf(t, escaped) + if got["method"] != "Input.dispatchMouseEvent" { + t.Fatalf("method = %v, want Input.dispatchMouseEvent", got["method"]) + } + if got["event_type"] != "mousePressed" { + t.Fatalf("event_type = %v, want mousePressed", got["event_type"]) + } +} + +// sensitiveParams stuffs a unique sentinel into every argument that must never +// reach an event, across every supported method at once. A sanitizer only +// decodes its own arguments, so the superset is safe to send to all of them and +// catches a sanitizer that passes one of these through. +const sensitiveParams = `{ + "text":"SENTINELtext", + "unmodifiedText":"SENTINELunmodified", + "key":"SENTINELkey", + "code":"SENTINELcode", + "keyIdentifier":"SENTINELkeyident", + "url":"https://SENTINELhost.example/SENTINELpath?token=SENTINELquery#SENTINELfragment", + "referrer":"https://SENTINELhost.example/SENTINELreferrer", + "scriptToEvaluateOnLoad":"SENTINELscript", + "headerTemplate":"SENTINELheader", + "footerTemplate":"SENTINELfooter", + "pageRanges":"SENTINELranges", + "promptText":"SENTINELprompt", + "interactionMarkerName":"SENTINELmarker", + "files":["/tmp/SENTINELfile.pdf"], + "proxyServer":"http://SENTINELproxy:8080", + "proxyBypassList":"SENTINELbypass", + "originsWithUniversalNetworkAccess":["https://SENTINELorigin"], + "card":{"number":"SENTINELcard","cvc":"SENTINELcvc"}, + "address":{"fields":[{"name":"SENTINELname","value":"SENTINELaddress"}]}, + "data":{"items":[{"mimeType":"text/SENTINELsubtype","data":"SENTINELdrag","baseURL":"https://SENTINELhost.example/SENTINELbase","title":"SENTINELtitle"}],"files":["/tmp/SENTINELdragfile"]}, + "type":"SENTINELtype", + "button":"SENTINELbutton", + "pointerType":"SENTINELpointer", + "format":"SENTINELformat", + "state":"SENTINELstate", + "transferMode":"SENTINELtransfer", + "gestureSourceType":"SENTINELgesture", + "transitionType":"SENTINELtransition", + "referrerPolicy":"SENTINELpolicy", + "windowState":"SENTINELwindowstate", + "bounds":{"windowState":"SENTINELboundsstate"}, + "clip":{}, + "touchPoints":[{"x":1,"y":2}] +}` + +func TestSanitizersNeverEmitSensitiveValues(t *testing.T) { + for method := range sanitizers { + t.Run(method, func(t *testing.T) { + frame := fmt.Sprintf(`{"id":1,"sessionId":"S","method":%q,"params":%s}`, method, sensitiveParams) + ev, ok := classifyFrame(t, frame, nil) + if !ok { + t.Fatalf("supported method produced no event") + } + payload := string(ev.Data) + if strings.Contains(payload, "SENTINEL") { + t.Fatalf("payload leaked a sensitive value: %s", payload) + } + }) + } +} + +// The map key and the payload's method must agree, or a copy-paste between two +// similar sanitizers would silently mislabel a command. +func TestSanitizersReportTheMethodTheyAreKeyedBy(t *testing.T) { + for method := range sanitizers { + t.Run(method, func(t *testing.T) { + got := payloadOf(t, fmt.Sprintf(`{"id":1,"method":%q}`, method)) + if got["method"] != method { + t.Fatalf("payload method = %v, want %s", got["method"], method) + } + }) + } +} + +// The schema has to describe what the sanitizers emit, so a method reported +// with no variant to decode it, or a variant nothing emits, fails here. +// Detecting a canonical method or argument that nobody handled is a different +// question, and neither side of this comparison can answer it — that is what +// the pinned-protocol checks in cdpmanifest_test.go are for. +func TestSanitizersMatchTheSchemaMethodEnum(t *testing.T) { + want := specCommandMethods(t) + got := make([]string, 0, len(sanitizers)) + for method := range sanitizers { + got = append(got, method) + } + sort.Strings(got) + sort.Strings(want) + if strings.Join(got, ",") != strings.Join(want, ",") { + t.Fatalf("sanitizers and BrowserCdpCommandMethod disagree:\n sanitizers: %v\n schema: %v", got, want) + } +} + +func TestSessionIdIsReportedWhenAddressed(t *testing.T) { + got := payloadOf(t, `{"id":1,"sessionId":"ABC","method":"Page.reload","params":{"ignoreCache":true}}`) + if got["session_id"] != "ABC" { + t.Fatalf("session_id = %v, want ABC", got["session_id"]) + } + got = payloadOf(t, `{"id":2,"method":"Browser.close"}`) + if _, ok := got["session_id"]; ok { + t.Fatal("browser-level command reported a session_id") + } +} + +func FuzzCdpCommandEvent(f *testing.F) { + f.Add(`{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}`) + f.Add(`{"id":1,"method":"Input.insertText","params":{"text":"hunter2"}}`) + f.Add(`{"id":1,"method":"Page.navigate","params":{"url":"https://example.com/a?token=b"}}`) + f.Add(`{"id":1,"method":"Input.dispatchDragEvent","params":{"data":{"items":[{"mimeType":"x"}]}}}`) + f.Add(`{"id":1,"method":"Autofill.trigger","params":{"fieldId":1,"address":{}}}`) + f.Add(`{"id":1,"method":`) + f.Add("\x00\xff\xfe") + + f.Fuzz(func(t *testing.T, frame string) { + ev, ok := classifyFrameOrSkip(frame) + if !ok { + return + } + // Whatever the input, the output must be a payload naming a supported + // method: an event that cannot be discriminated is worse than none. + var payload struct { + Method string `json:"method"` + } + if err := json.Unmarshal(ev.Data, &payload); err != nil { + t.Fatalf("emitted unparseable payload %q for frame %q", ev.Data, frame) + } + if _, ok := sanitizers[payload.Method]; !ok { + t.Fatalf("emitted method %q that is not supported, for frame %q", payload.Method, frame) + } + }) +} + +func BenchmarkCdpCommandEventClick(b *testing.B) { + frame := []byte(`{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2,"button":"left","clickCount":1}}`) + b.ReportAllocs() + for b.Loop() { + classifyFrameOrSkip(string(frame)) + } +} + +func BenchmarkCdpCommandEventUnsupported(b *testing.B) { + frame := []byte(`{"id":1,"method":"Runtime.callFunctionOn","params":{"functionDeclaration":"() => 1","objectId":"x"}}`) + b.ReportAllocs() + for b.Loop() { + classifyFrameOrSkip(string(frame)) + } +} + +// specCommandMethods reads the BrowserCdpCommandMethod enum out of the +// embedded spec, so the test compares against the schema rather than a second +// copy of the list. +func specCommandMethods(t *testing.T) []string { + t.Helper() + raw, err := yaml.YAMLToJSON(serverpkg.OpenAPIYAML) + if err != nil { + t.Fatalf("convert spec: %v", err) + } + var spec struct { + Components struct { + Schemas struct { + BrowserCdpCommandMethod struct { + Enum []string `json:"enum"` + } `json:"BrowserCdpCommandMethod"` + } `json:"schemas"` + } `json:"components"` + } + if err := json.Unmarshal(raw, &spec); err != nil { + t.Fatalf("parse spec: %v", err) + } + methods := spec.Components.Schemas.BrowserCdpCommandMethod.Enum + if len(methods) == 0 { + t.Fatal("spec has no BrowserCdpCommandMethod enum") + } + return methods +} + +// Excluding a method suppresses only its event. The raw command reached +// Chromium before classification ran, so exclusion can never change what the +// browser was told to do. +func TestExcludedMethodsSuppressOnlyTheirOwnEvents(t *testing.T) { + click := `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}` + nav := `{"id":2,"method":"Page.navigate","params":{"url":"https://example.com/"}}` + excluded := map[string]struct{}{"Input.dispatchMouseEvent": {}} + + if _, ok := classifyFrame(t, click, excluded); ok { + t.Fatal("excluded method produced an event") + } + if _, ok := classifyFrame(t, nav, excluded); !ok { + t.Fatal("a method that was not excluded produced no event") + } + if _, ok := classifyFrame(t, click, nil); !ok { + t.Fatal("no exclusions configured, but the command produced no event") + } +} + +// The scheme is the only part of a URL the control category carries. A reader +// who needs the destination opts into the page category, where navigation +// events report the URL itself. +func TestNavigationReportsSchemeOnly(t *testing.T) { + for _, tc := range []struct { + frame string + want string + }{ + {`{"id":1,"method":"Page.navigate","params":{"url":"https://internal.acme.example/admin?token=abc"}}`, "https"}, + {`{"id":2,"method":"Page.navigate","params":{"url":"data:text/html,

hi

"}}`, "data"}, + {`{"id":3,"method":"Target.createTarget","params":{"url":"about:blank"}}`, "about"}, + } { + got := payloadOf(t, tc.frame) + if got["url_scheme"] != tc.want { + t.Fatalf("url_scheme = %v, want %v", got["url_scheme"], tc.want) + } + if _, ok := got["url_host"]; ok { + t.Fatalf("payload carried a url_host: %v", got) + } + for key, value := range got { + if str, isStr := value.(string); isStr && strings.Contains(str, "acme") { + t.Fatalf("payload leaked the host in %s: %v", key, value) + } + } + } +} + +// A relative or unparseable URL has no scheme, and the event says so rather +// than inventing one. +func TestNavigationOmitsSchemeWhenThereIsNone(t *testing.T) { + got := payloadOf(t, `{"id":1,"method":"Page.navigate","params":{"url":"/relative/path"}}`) + if _, ok := got["url_scheme"]; ok { + t.Fatalf("relative URL reported a scheme: %v", got) + } +} + +// The failure this guards: a client-controlled string was copied into the +// payload verbatim, so a 1.1 MB button produced a 1.1 MB event, and +// truncateIfNeeded nulls the whole event rather than clipping the field. Every +// such value is now either a protocol enum or a clipped identifier, so the +// payload is bounded whatever the client sends. +func TestPayloadStaysBoundedWhateverTheClientSends(t *testing.T) { + huge := strings.Repeat("z", 1_100_000) + for _, frame := range []string{ + `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","button":"` + huge + `"}}`, + `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"` + huge + `"}}`, + `{"id":1,"sessionId":"` + huge + `","method":"Page.bringToFront"}`, + `{"id":1,"method":"Target.activateTarget","params":{"targetId":"` + huge + `"}}`, + `{"id":1,"method":"Page.navigate","params":{"url":"https://h.example/","transitionType":"` + huge + `"}}`, + `{"id":1,"method":"Page.captureScreenshot","params":{"format":"` + huge + `"}}`, + `{"id":1,"method":"Browser.setWindowBounds","params":{"windowId":1,"bounds":{"windowState":"` + huge + `"}}}`, + } { + ev, ok := classifyFrame(t, frame, nil) + if !ok { + t.Fatalf("no event for %.60s", frame) + } + // Comfortably inside the 1 MB envelope limit, so the event survives whole. + if len(ev.Data) > 4096 { + t.Fatalf("payload is %d bytes for a frame with one huge value: %.200s", len(ev.Data), ev.Data) + } + // An enum is replaced outright; an identifier is clipped. Either way no + // single value carries more than the identifier bound. + var fields map[string]any + if err := json.Unmarshal(ev.Data, &fields); err != nil { + t.Fatal(err) + } + for name, value := range fields { + str, isStr := value.(string) + if isStr && len(str) > maxOpaqueIDBytes { + t.Fatalf("%s carries %d bytes of client value", name, len(str)) + } + } + } +} + +// A value the protocol does not define is reported, but as `other` rather than +// whatever the client chose to send. +func TestUnknownEnumValuesReportAsOther(t *testing.T) { + got := payloadOf(t, `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"teleported","button":"elbow","pointerType":"nose"}}`) + for field, want := range map[string]string{"event_type": "other", "button": "other", "pointer_type": "other"} { + if got[field] != want { + t.Fatalf("%s = %v, want %v", field, got[field], want) + } + } +} + +// An identifier past the bound is clipped, not dropped: the field is still +// there and still partially readable. +func TestOpaqueIdentifiersAreClipped(t *testing.T) { + got := payloadOf(t, `{"id":7,"sessionId":"`+strings.Repeat("S", 500)+`","method":"Page.reload"}`) + sid, _ := got["session_id"].(string) + if len(sid) != maxOpaqueIDBytes { + t.Fatalf("session_id length = %d, want %d", len(sid), maxOpaqueIDBytes) + } +} + +// The JSON-RPC id and the connection id are what let a reader join a command to +// the result the browser returned, and attribute it to one of several clients. +func TestCommandAndConnectionIdsAreReported(t *testing.T) { + ev, ok := classifyFrameConn(t, `{"id":42,"method":"Page.reload"}`, "conn-abc") + if !ok { + t.Fatal("no event") + } + var got map[string]any + if err := json.Unmarshal(ev.Data, &got); err != nil { + t.Fatal(err) + } + if got["command_id"] != 42.0 { + t.Fatalf("command_id = %v, want 42", got["command_id"]) + } + if got["connection_id"] != "conn-abc" { + t.Fatalf("connection_id = %v, want conn-abc", got["connection_id"]) + } + // A notification carries no id, and the event says so rather than inventing one. + ev2, _ := classifyFrameConn(t, `{"method":"Page.reload"}`, "conn-abc") + var got2 map[string]any + json.Unmarshal(ev2.Data, &got2) + if _, ok := got2["command_id"]; ok { + t.Fatalf("command_id present for a frame with no id: %v", got2) + } +} + +// classifyFrame runs a frame through the two steps production uses: resolve the +// method at admission, then build the event. A frame whose method is not +// supported or is excluded never reaches the second step, exactly as it does +// not reach the queue. +func classifyFrame(t *testing.T, frame string, excluded map[string]struct{}) (events.Event, bool) { + t.Helper() + return classifyFrameWith(frame, "", excluded) +} + +func classifyFrameConn(t *testing.T, frame, connectionID string) (events.Event, bool) { + t.Helper() + return classifyFrameWith(frame, connectionID, nil) +} + +// classifyFrameOrSkip is classifyFrame for callers with no *testing.T to hand, +// such as the fuzz target. +func classifyFrameOrSkip(frame string) (events.Event, bool) { + return classifyFrameWith(frame, "", nil) +} + +func classifyFrameWith(frame, connectionID string, excluded map[string]struct{}) (events.Event, bool) { + method, supported := supportedMethod([]byte(frame)) + if !supported { + return events.Event{}, false + } + if _, skip := excluded[method]; skip { + return events.Event{}, false + } + return cdpCommandEvent([]byte(frame), testForwardTs, connectionID, method) +} + +// specMaxLengthFields returns, per command method, the payload fields the +// schema bounds. Reading it from the spec rather than listing them here is the +// point: a field that gains a maxLength is covered without anyone remembering +// to extend this test. +func specMaxLengthFields(t *testing.T) map[string]map[string]int { + t.Helper() + raw, err := yaml.YAMLToJSON(serverpkg.OpenAPIYAML) + if err != nil { + t.Fatalf("convert spec: %v", err) + } + var spec struct { + Components struct { + Schemas map[string]struct { + Properties map[string]struct { + Const string `json:"const"` + MaxLength *int `json:"maxLength"` + } `json:"properties"` + } `json:"schemas"` + } `json:"components"` + } + if err := json.Unmarshal(raw, &spec); err != nil { + t.Fatalf("parse spec: %v", err) + } + out := map[string]map[string]int{} + for name, schema := range spec.Components.Schemas { + if !strings.HasSuffix(name, "CommandData") { + continue + } + method := schema.Properties["method"].Const + if method == "" { + continue + } + for field, prop := range schema.Properties { + if prop.MaxLength == nil { + continue + } + if out[method] == nil { + out[method] = map[string]int{} + } + out[method][field] = *prop.MaxLength + } + } + if len(out) == 0 { + t.Fatal("spec declares no bounded fields, so this test proves nothing") + } + return out +} + +// paramForField names a CDP argument that lands in a given payload field, so +// the bound can be exercised through a real frame. +var paramForField = map[string]string{ + "session_id": "", // envelope, not params + "connection_id": "", // supplied by the proxy + "object_id": "objectId", + "frame_id": "frameId", + "target_id": "targetId", + "browser_context_id": "browserContextId", + "loader_id": "loaderId", + "download_guid": "guid", + "panel_id": "panelId", + "url_scheme": "url", // derived via urlScheme(); test value is scheme://x +} + +// Every field the schema bounds has to be bounded in the payload too, on every +// command that carries it. Five of these were emitted raw against a +// maxLength: 128 schema, which put the whole-event nulling back in reach. +func TestBoundedFieldsAreClippedOnEveryCommand(t *testing.T) { + huge := strings.Repeat("Q", 512) + for method, fields := range specMaxLengthFields(t) { + for field, max := range fields { + t.Run(method+"/"+field, func(t *testing.T) { + param, known := paramForField[field] + if !known { + t.Fatalf("no CDP argument mapped for bounded field %q; add it to paramForField", field) + } + var frame string + switch field { + case "session_id": + frame = fmt.Sprintf(`{"id":1,"sessionId":%q,"method":%q,"params":{}}`, huge, method) + case "connection_id": + t.Skip("supplied by the proxy, covered by TestConnectionIdIsClipped") + case "url_scheme": + frame = fmt.Sprintf(`{"id":1,"method":%q,"params":{%q:%q}}`, method, param, huge+"://x") + default: + frame = fmt.Sprintf(`{"id":1,"method":%q,"params":{%q:%q}}`, method, param, huge) + } + got := payloadOf(t, frame) + v, present := got[field].(string) + if !present { + t.Fatalf("%s did not report %s at all", method, field) + } + if len(v) > max { + t.Fatalf("%s.%s is %d bytes, over the schema's maxLength %d", method, field, len(v), max) + } + }) + } + } +} + +// The connection id is ours rather than the client's, but it is bounded by the +// same schema and reaches every command. +func TestConnectionIdIsClipped(t *testing.T) { + ev, ok := classifyFrameConn(t, `{"id":1,"method":"Page.reload"}`, strings.Repeat("C", 512)) + if !ok { + t.Fatal("no event") + } + var got map[string]any + if err := json.Unmarshal(ev.Data, &got); err != nil { + t.Fatal(err) + } + if v, _ := got["connection_id"].(string); len(v) != maxOpaqueIDBytes { + t.Fatalf("connection_id is %d bytes, want %d", len(v), maxOpaqueIDBytes) + } +} + +// specPayloadEnums returns the value set of every enum a cdp_command payload +// field can carry. Membership comes from what the CommandData schemas actually +// reference, not from a naming convention, so the config-only method enum is +// not swept in and a new payload enum is covered without extending this test. +func specPayloadEnums(t *testing.T) map[string]map[string]bool { + t.Helper() + raw, err := yaml.YAMLToJSON(serverpkg.OpenAPIYAML) + if err != nil { + t.Fatalf("convert spec: %v", err) + } + var spec struct { + Components struct { + Schemas map[string]struct { + Enum []string `json:"enum"` + Properties map[string]struct { + Ref string `json:"$ref"` + Items *struct { + Ref string `json:"$ref"` + } `json:"items"` + } `json:"properties"` + } `json:"schemas"` + } `json:"components"` + } + if err := json.Unmarshal(raw, &spec); err != nil { + t.Fatalf("parse spec: %v", err) + } + + referenced := map[string]bool{} + note := func(ref string) { + if name := strings.TrimPrefix(ref, "#/components/schemas/"); name != ref && name != "" { + referenced[name] = true + } + } + for name, schema := range spec.Components.Schemas { + if !strings.HasSuffix(name, "CommandData") { + continue + } + for _, prop := range schema.Properties { + note(prop.Ref) + if prop.Items != nil { + note(prop.Items.Ref) + } + } + } + + out := map[string]map[string]bool{} + for name := range referenced { + schema := spec.Components.Schemas[name] + if len(schema.Enum) == 0 { + continue + } + members := map[string]bool{} + for _, v := range schema.Enum { + members[v] = true + } + out[name] = members + } + return out +} + +// Every enum a payload can carry must have a fallback the schema accepts. The +// drag MIME category emitted "other" while its enum did not list it, so a +// client sending an unusual MIME type produced a payload the spec rejects. +func TestEveryPayloadEnumHasItsFallbackInTheSchema(t *testing.T) { + members := specPayloadEnums(t) + if len(members) == 0 { + t.Fatal("spec declares no payload enums, so this test proves nothing") + } + // Autofill mode is the one enum the proxy derives rather than copying, so + // it has no unknown case to fall back on. + derived := map[string]bool{"BrowserCdpAutofillMode": true} + for name, values := range members { + if derived[name] { + continue + } + if !values[unknownEnumValue] { + t.Errorf("%s cannot represent an unrecognised value: %q is not in its enum", name, unknownEnumValue) + } + } +} + +// An unusual value on any enum-bearing argument must still produce a payload +// whose every enum passes the generated Valid(). +func TestUnrecognisedEnumValuesStaySchemaValid(t *testing.T) { + for _, frame := range []string{ + `{"id":1,"method":"Input.dispatchDragEvent","params":{"type":"drop","data":{"items":[{"mimeType":"weirdtype/x-thing"},{"mimeType":"no-slash"}]}}}`, + `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"teleported","button":"elbow","pointerType":"nose"}}`, + `{"id":1,"method":"Page.captureScreenshot","params":{"format":"holograph"}}`, + `{"id":1,"method":"Page.navigate","params":{"url":"https://h.example/","transitionType":"osmosis","referrerPolicy":"whatever"}}`, + `{"id":1,"method":"Browser.setWindowBounds","params":{"windowId":1,"bounds":{"windowState":"sideways"}}}`, + `{"id":1,"method":"Page.setWebLifecycleState","params":{"state":"marinating"}}`, + // Missing type field: enumOf must produce the fallback, not "". + `{"id":1,"method":"Input.dispatchMouseEvent","params":{}}`, + `{"id":1,"method":"Input.dispatchKeyEvent","params":{}}`, + `{"id":1,"method":"Input.dispatchTouchEvent","params":{"touchPoints":[]}}`, + `{"id":1,"method":"Input.dispatchDragEvent","params":{}}`, + } { + ev, ok := classifyFrame(t, frame, nil) + if !ok { + t.Fatalf("no event for %.70s", frame) + } + assertPayloadEnumsValid(t, ev.Data) + } +} + +// assertPayloadEnumsValid round-trips the payload through the generated union, +// whose enum types vet their own values. +func assertPayloadEnumsValid(t *testing.T, payload []byte) { + t.Helper() + var data oapi.BrowserCdpCommandEventData + if err := json.Unmarshal(payload, &data); err != nil { + t.Fatalf("payload does not decode into the union: %v", err) + } + var probe struct { + Method string `json:"method"` + } + json.Unmarshal(payload, &probe) + + switch probe.Method { + case "Input.dispatchDragEvent": + v, err := data.AsBrowserCdpInputDispatchDragEventCommandData() + if err != nil { + t.Fatalf("decode drag payload: %v", err) + } + if !v.EventType.Valid() { + t.Errorf("event_type %q fails Valid()", v.EventType) + } + if v.DragMimeCategories != nil { + for _, c := range *v.DragMimeCategories { + if !c.Valid() { + t.Errorf("drag_mime_categories %q fails Valid(): payload violates its own schema", c) + } + } + } + case "Input.dispatchMouseEvent": + v, _ := data.AsBrowserCdpInputDispatchMouseEventCommandData() + if !v.EventType.Valid() { + t.Errorf("event_type %q fails Valid()", v.EventType) + } + if v.Button != nil && !v.Button.Valid() { + t.Errorf("button %q fails Valid()", *v.Button) + } + if v.PointerType != nil && !v.PointerType.Valid() { + t.Errorf("pointer_type %q fails Valid()", *v.PointerType) + } + case "Page.captureScreenshot": + v, _ := data.AsBrowserCdpPageCaptureScreenshotCommandData() + if v.Format != nil && !v.Format.Valid() { + t.Errorf("format %q fails Valid()", *v.Format) + } + case "Page.navigate": + v, _ := data.AsBrowserCdpPageNavigateCommandData() + if v.TransitionType != nil && !v.TransitionType.Valid() { + t.Errorf("transition_type %q fails Valid()", *v.TransitionType) + } + if v.ReferrerPolicy != nil && !v.ReferrerPolicy.Valid() { + t.Errorf("referrer_policy %q fails Valid()", *v.ReferrerPolicy) + } + case "Browser.setWindowBounds": + v, _ := data.AsBrowserCdpBrowserSetWindowBoundsCommandData() + if v.WindowState != nil && !v.WindowState.Valid() { + t.Errorf("window_state %q fails Valid()", *v.WindowState) + } + case "Input.dispatchKeyEvent": + v, _ := data.AsBrowserCdpInputDispatchKeyEventCommandData() + if !v.EventType.Valid() { + t.Errorf("event_type %q fails Valid()", v.EventType) + } + case "Input.dispatchTouchEvent": + v, _ := data.AsBrowserCdpInputDispatchTouchEventCommandData() + if !v.EventType.Valid() { + t.Errorf("event_type %q fails Valid()", v.EventType) + } + case "Page.setWebLifecycleState": + v, _ := data.AsBrowserCdpPageSetWebLifecycleStateCommandData() + if !v.State.Valid() { + t.Errorf("state %q fails Valid()", v.State) + } + default: + t.Fatalf("no enum assertions for %s", probe.Method) + } +} diff --git a/server/lib/devtoolsproxy/cdpmanifest_test.go b/server/lib/devtoolsproxy/cdpmanifest_test.go new file mode 100644 index 00000000..120808d2 --- /dev/null +++ b/server/lib/devtoolsproxy/cdpmanifest_test.go @@ -0,0 +1,261 @@ +package devtoolsproxy + +import ( + "encoding/json" + "os" + "reflect" + "sort" + "strings" + "testing" + + "github.com/ghodss/yaml" +) + +var rawMessageType = reflect.TypeOf(json.RawMessage{}) + +// The inventory test compares the sanitizers against this repo's own OpenAPI +// enum, which is a second copy of the same list and so cannot notice a +// canonical method or argument nobody handled. These tests compare against the +// pinned protocol instead, by way of two checked-in files: +// +// - testdata/cdp_protocol_pinned.json, generated by scripts/cdpmanifest from +// devtools-protocol at the pinned commit: every argument of every +// supported command, including the fields of the object types they carry. +// - testdata/cdp_arguments.yaml, the decision for each of those arguments. +// +// Between them, an argument added upstream becomes a decision someone has to +// record, and a decision that stops matching the code fails here. + +const ( + protocolSnapshotPath = "testdata/cdp_protocol_pinned.json" + argumentManifestPath = "testdata/cdp_arguments.yaml" +) + +type protocolSnapshot struct { + Commit string `json:"commit"` + Permalink string `json:"permalink"` + Commands map[string][]string `json:"commands"` +} + +// argumentDecision is "retained" as a bare string, or a mapping carrying the +// reason it is redacted. +type argumentDecision struct { + Redacted string `json:"redacted"` +} + +func loadProtocolSnapshot(t *testing.T) protocolSnapshot { + t.Helper() + raw, err := os.ReadFile(protocolSnapshotPath) + if err != nil { + t.Fatalf("read protocol snapshot: %v", err) + } + var snap protocolSnapshot + if err := json.Unmarshal(raw, &snap); err != nil { + t.Fatalf("parse protocol snapshot: %v", err) + } + if len(snap.Commands) == 0 { + t.Fatal("protocol snapshot is empty, so these tests prove nothing") + } + return snap +} + +// loadArgumentManifest returns method -> argument -> decision, with retained +// arguments mapping to an empty decision. +func loadArgumentManifest(t *testing.T) map[string]map[string]argumentDecision { + t.Helper() + raw, err := os.ReadFile(argumentManifestPath) + if err != nil { + t.Fatalf("read argument manifest: %v", err) + } + var loose map[string]map[string]json.RawMessage + if err := yaml.Unmarshal(raw, &loose); err != nil { + t.Fatalf("parse argument manifest: %v", err) + } + out := make(map[string]map[string]argumentDecision, len(loose)) + for method, args := range loose { + out[method] = map[string]argumentDecision{} + for arg, value := range args { + var word string + if err := json.Unmarshal(value, &word); err == nil { + if word != "retained" { + t.Fatalf("%s.%s: decision %q is neither retained nor a redacted reason", method, arg, word) + } + out[method][arg] = argumentDecision{} + continue + } + var decision argumentDecision + if err := json.Unmarshal(value, &decision); err != nil { + t.Fatalf("%s.%s: unreadable decision: %v", method, arg, err) + } + if strings.TrimSpace(decision.Redacted) == "" { + t.Fatalf("%s.%s: redacted without a reason", method, arg) + } + out[method][arg] = decision + } + } + return out +} + +// decodedArguments walks a canonical input type for the JSON paths it reads, +// in the dotted form the snapshot uses: touchPoints[].radiusX, bounds.left. +func decodedArguments(t reflect.Type, prefix string) map[string]bool { + out := map[string]bool{} + if t == nil || t.Kind() != reflect.Struct { + return out + } + for i := range t.NumField() { + field := t.Field(i) + tag := strings.Split(field.Tag.Get("json"), ",")[0] + + // A raw message is read whole to test its presence, not for any + // argument inside it, so it decodes no canonical argument. + if field.Type == rawMessageType { + continue + } + + inner := field.Type + suffix := "" + for inner.Kind() == reflect.Ptr { + inner = inner.Elem() + } + if inner.Kind() == reflect.Slice { + inner = inner.Elem() + for inner.Kind() == reflect.Ptr { + inner = inner.Elem() + } + suffix = "[]" + } + + // An embedded struct contributes its fields at this level. + if field.Anonymous && tag == "" { + for path := range decodedArguments(inner, prefix) { + out[path] = true + } + continue + } + if tag == "" || tag == "-" { + continue + } + path := prefix + tag + + // A struct read for nothing but its shape — the empty element type used + // to count a list — yields no canonical argument either. + if inner.Kind() == reflect.Struct { + for p := range decodedArguments(inner, path+suffix+".") { + out[p] = true + } + continue + } + out[path] = true + } + return out +} + +func decodedArgumentsFor(method string) map[string]bool { + proto, ok := commandParams[method] + if !ok { + return nil + } + return decodedArguments(reflect.TypeOf(proto), "") +} + +// Every command the proxy reports must appear in both checked-in files, and +// nothing else may. +func TestManifestCoversExactlyTheSupportedMethods(t *testing.T) { + snap := loadProtocolSnapshot(t) + manifest := loadArgumentManifest(t) + + for method := range sanitizers { + if _, ok := snap.Commands[method]; !ok { + t.Errorf("%s is reported but absent from the pinned protocol snapshot", method) + } + if _, ok := manifest[method]; !ok { + t.Errorf("%s is reported but has no argument decisions", method) + } + if _, ok := commandParams[method]; !ok { + t.Errorf("%s has no canonical input type registered", method) + } + } + for method := range snap.Commands { + if _, ok := sanitizers[method]; !ok { + t.Errorf("%s is in the protocol snapshot but nothing reports it", method) + } + } + for method := range manifest { + if _, ok := sanitizers[method]; !ok { + t.Errorf("%s has argument decisions but nothing reports it", method) + } + } +} + +// The check the OpenAPI-enum comparison could not make: every argument the +// protocol defines for a supported command has an explicit decision, so an +// argument added upstream cannot be silently absent. +func TestEveryCanonicalArgumentHasADecision(t *testing.T) { + snap := loadProtocolSnapshot(t) + manifest := loadArgumentManifest(t) + + var undecided []string + for method, args := range snap.Commands { + for _, arg := range args { + if _, ok := manifest[method][arg]; !ok { + undecided = append(undecided, method+"."+arg) + } + } + } + sort.Strings(undecided) + if len(undecided) > 0 { + t.Fatalf("%d canonical arguments have no retained/redacted decision in %s:\n %s", + len(undecided), argumentManifestPath, strings.Join(undecided, "\n ")) + } + + // And nothing invented: a decision for an argument the protocol does not + // define is a stale entry, which would hide a rename. + for method, args := range manifest { + canonical := map[string]bool{} + for _, a := range snap.Commands[method] { + canonical[a] = true + } + for arg := range args { + if !canonical[arg] { + t.Errorf("%s.%s has a decision but the pinned protocol has no such argument", method, arg) + } + } + } +} + +// The decisions have to describe the code: an argument marked retained must be +// decoded, and anything the code decodes must be declared. +func TestManifestDecisionsMatchWhatTheSanitizersDecode(t *testing.T) { + manifest := loadArgumentManifest(t) + + for method, args := range manifest { + decoded := decodedArgumentsFor(method) + for arg, decision := range args { + retained := decision.Redacted == "" + switch { + case retained && !decoded[arg]: + t.Errorf("%s.%s is marked retained but no canonical input type decodes it", method, arg) + case !retained && decoded[arg]: + t.Errorf("%s.%s is marked redacted (%s) but the code decodes it", method, arg, decision.Redacted) + } + } + for arg := range decoded { + if _, ok := args[arg]; !ok { + t.Errorf("%s decodes %s, which has no decision recorded", method, arg) + } + } + } +} + +// The snapshot names the commit it came from, so a reader can check the +// definitions the decisions were made against. +func TestProtocolSnapshotIsPinned(t *testing.T) { + snap := loadProtocolSnapshot(t) + if len(snap.Commit) != 40 { + t.Fatalf("commit %q is not a full sha, so the snapshot is not pinned", snap.Commit) + } + if !strings.Contains(snap.Permalink, snap.Commit) { + t.Fatalf("permalink %q does not point at the pinned commit", snap.Permalink) + } +} diff --git a/server/lib/devtoolsproxy/cdpobserver.go b/server/lib/devtoolsproxy/cdpobserver.go new file mode 100644 index 00000000..411e054f --- /dev/null +++ b/server/lib/devtoolsproxy/cdpobserver.go @@ -0,0 +1,240 @@ +package devtoolsproxy + +import ( + "context" + "log/slog" + "sync/atomic" + "time" +) + +// ControlEnabledFunc reports whether control-category telemetry is currently +// captured. The proxy calls it once per forwarded client frame, so it must be +// cheap: telemetry.TelemetrySession.CategoryEnabled is lock-free for this. +type ControlEnabledFunc func() bool + +// ExcludedMethodsFunc returns the browser-control methods configured out of the +// cdp_command stream, or nil when none are. Consulted at admission, once the +// method is known, so an excluded command never occupies the queue. +type ExcludedMethodsFunc func() map[string]struct{} + +const ( + // cdpObserverQueueDepth bounds how many forwarded frames may be waiting for + // classification. Deep enough to absorb a burst of input gestures, shallow + // enough that a stalled publisher cannot accumulate unbounded garbage. + cdpObserverQueueDepth = 256 + // cdpObserverMaxQueuedBytes bounds the memory frames awaiting classification + // can hold. Only supported methods reach the queue, so this bounds real + // commands rather than arbitrary traffic; a per-frame cap would instead + // reject a large paste, which is a command, while admitting many small + // frames that together cost more. + cdpObserverMaxQueuedBytes = 8 << 20 + // cdpObserverDrainWait bounds how long connection teardown waits for the + // worker to finish the queue. + cdpObserverDrainWait = time.Second +) + +// cdpObserver turns forwarded client frames into cdp_command events on its own +// goroutine. Observe runs on the pump, so it does only what is needed to decide +// the frame is not worth queuing; classification, sanitation and publication +// all happen on the worker, where they cannot delay CDP or kill the process. +type cdpObserver struct { + frames chan observedFrame + drained chan struct{} + publish EventPublisher + controlEnabled ControlEnabledFunc + excludedMethods ExcludedMethodsFunc + logger *slog.Logger + + // connectionID names this proxy connection on every event it produces, so + // concurrent clients driving one browser can be told apart. + connectionID string + + // queuedBytes tracks what the queue is holding, so admission can be decided + // on bytes rather than frame count alone. + queuedBytes atomic.Int64 + droppedQueued atomic.Int64 + droppedPanicked atomic.Int64 + droppedMalformed atomic.Int64 + excluded atomic.Int64 +} + +// observedFrame is a client frame that reached Chromium, with the time the +// forward completed. The timestamp travels with the frame so queue latency +// does not show up as event time. The method is resolved at admission, so the +// worker does not decode the envelope a second time to learn it. +type observedFrame struct { + msg []byte + ts int64 + method string +} + +// newCdpObserver starts the classification worker. It stops when ctx is done. +// A nil publish or controlEnabled disables observation entirely. +func newCdpObserver(ctx context.Context, connectionID string, publish EventPublisher, controlEnabled ControlEnabledFunc, excludedMethods ExcludedMethodsFunc, logger *slog.Logger) *cdpObserver { + if publish == nil || controlEnabled == nil { + return nil + } + if excludedMethods == nil { + excludedMethods = func() map[string]struct{} { return nil } + } + o := &cdpObserver{ + frames: make(chan observedFrame, cdpObserverQueueDepth), + drained: make(chan struct{}), + connectionID: connectionID, + publish: publish, + controlEnabled: controlEnabled, + excludedMethods: excludedMethods, + logger: logger, + } + go o.run(ctx) + return o +} + +// Observe admits a forwarded client frame for classification. It never blocks, +// and it runs only after Chromium has already accepted the frame, so nothing it +// does can delay the command it is looking at. +// +// It resolves the method first. Queue capacity exists for browser-control +// commands, and a client library issues far more DOM and Runtime bookkeeping +// than gestures; admitting that traffic lets it fill the queue and push out a +// real command, and makes the drop count a tally of arbitrary CDP traffic +// rather than of lost control events. +// +// Deciding here costs a scan of the frame that copies none of its arguments: +// 273 B and 7 allocations whatever the frame's size, per +// BenchmarkObserveLibraryTraffic and BenchmarkObserveLargeLibraryTraffic. The +// scan itself is proportional to the bytes, which is inherent to reading JSON, +// and it runs after the forward, so it delays the next frame rather than this +// one. A frame this package does not report is dropped here, never retained. +func (o *cdpObserver) Observe(msg []byte, ts int64) { + if o == nil || !o.controlEnabled() { + return + } + method, supported := supportedMethod(msg) + if !supported { + return + } + if _, skip := o.excludedMethods()[method]; skip { + o.excluded.Add(1) + return + } + size := int64(len(msg)) + if o.queuedBytes.Add(size) > cdpObserverMaxQueuedBytes { + o.queuedBytes.Add(-size) + o.droppedQueued.Add(1) + return + } + select { + case o.frames <- observedFrame{msg: msg, ts: ts, method: method}: + default: + o.queuedBytes.Add(-size) + o.droppedQueued.Add(1) + } +} + +// Excluded reports how many supported commands produced no event because +// excluded_methods named their method. Counted, as the review asked, but apart +// from Dropped: a reader who configured an exclusion has not lost anything. +func (o *cdpObserver) Excluded() int64 { + if o == nil { + return 0 + } + return o.excluded.Load() +} + +// Dropped reports how many supported commands the classifier never saw or +// could not read: queue saturation, classification panics, commands whose +// arguments did not decode, and anything still queued once the worker has +// stopped. Reported on cdp_disconnect so a reader sees the loss rather than +// only the VM's log. Every increment is a real lost command — unsupported +// and excluded methods are filtered before admission. +func (o *cdpObserver) Dropped() int64 { + if o == nil { + return 0 + } + dropped := o.droppedQueued.Load() + o.droppedPanicked.Load() + o.droppedMalformed.Load() + // Pump calls onClose as soon as one direction fails, while the other may + // still be forwarding, so a frame can be queued after the final drain. Once + // the worker has stopped nothing will read it, which makes it as lost as one + // the queue turned away — and silently so, unless it is counted here. + select { + case <-o.drained: + dropped += int64(len(o.frames)) + default: + } + return dropped +} + +func (o *cdpObserver) run(ctx context.Context) { + defer close(o.drained) + for { + select { + case <-ctx.Done(): + o.drain() + o.logDrops() + return + case f := <-o.frames: + o.handle(f) + } + } +} + +// drain classifies what is already queued once the pump is done, so a client's +// last commands still produce events rather than dying with the connection. +// The queue is bounded, so this is too. +func (o *cdpObserver) drain() { + for { + select { + case f := <-o.frames: + o.handle(f) + default: + return + } + } +} + +// WaitDrained blocks until the worker has finished the queue. Bounded, so a +// wedged publisher delays connection teardown by at most timeout. +func (o *cdpObserver) WaitDrained(timeout time.Duration) { + if o == nil { + return + } + timer := time.NewTimer(timeout) + defer timer.Stop() + select { + case <-o.drained: + case <-timer.C: + } +} + +// handle classifies one frame. The recover is what keeps a malformed-input bug +// in a sanitizer, or a panicking publisher, from taking the VM down: the pump +// is a bare goroutine and so is this one. +func (o *cdpObserver) handle(f observedFrame) { + defer o.queuedBytes.Add(-int64(len(f.msg))) + defer func() { + if r := recover(); r != nil { + o.droppedPanicked.Add(1) + o.logger.Error("cdp command telemetry panicked", slog.Any("err", r)) + } + }() + ev, ok := cdpCommandEvent(f.msg, f.ts, o.connectionID, f.method) + if !ok { + // The command reached the browser but nothing readable reached the + // stream, which is a loss however malformed the arguments were. + o.droppedMalformed.Add(1) + return + } + o.publish(ev) +} + +func (o *cdpObserver) logDrops() { + queued, panicked, malformed := o.droppedQueued.Load(), o.droppedPanicked.Load(), o.droppedMalformed.Load() + if queued+panicked+malformed == 0 { + return + } + o.logger.Warn("cdp command telemetry dropped frames", + slog.Int64("queue_full", queued), + slog.Int64("panicked", panicked), + slog.Int64("malformed", malformed)) +} diff --git a/server/lib/devtoolsproxy/cdpobserver_test.go b/server/lib/devtoolsproxy/cdpobserver_test.go new file mode 100644 index 00000000..94df1f4a --- /dev/null +++ b/server/lib/devtoolsproxy/cdpobserver_test.go @@ -0,0 +1,406 @@ +package devtoolsproxy + +import ( + "context" + "strings" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/kernel/kernel-images/server/lib/events" +) + +const clickFrame = `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}` + +// countingPublisher records how many events reached the bus. +type countingPublisher struct { + n atomic.Int64 +} + +func (c *countingPublisher) publish(ev events.Event) (events.Envelope, bool) { + c.n.Add(1) + return events.Envelope{Event: ev}, true +} + +func newTestObserver(t *testing.T, publish EventPublisher, enabled ControlEnabledFunc) *cdpObserver { + t.Helper() + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + o := newCdpObserver(ctx, testConnID, publish, enabled, nil, silentLogger()) + if o == nil { + t.Fatal("observer was not created") + } + return o +} + +// The gate is what makes telemetry free when it is off: a frame observed with +// control disabled must not be retained, parsed or queued. +func TestObserverDoesNoWorkWhenControlIsDisabled(t *testing.T) { + pub := &countingPublisher{} + o := newTestObserver(t, pub.publish, func() bool { return false }) + + for range 100 { + o.Observe([]byte(clickFrame), testForwardTs) + } + if queued := len(o.frames); queued != 0 { + t.Fatalf("queued %d frames with control disabled, want 0", queued) + } + if got := pub.n.Load(); got != 0 { + t.Fatalf("published %d events with control disabled, want 0", got) + } + if got := o.Dropped(); got != 0 { + t.Fatalf("counted %d drops with control disabled, want 0: a frame nobody wanted is not a loss", got) + } +} + +func TestObserveAllocatesNothingWhenControlIsDisabled(t *testing.T) { + o := newTestObserver(t, (&countingPublisher{}).publish, func() bool { return false }) + frame := []byte(clickFrame) + allocs := testing.AllocsPerRun(1000, func() { o.Observe(frame, testForwardTs) }) + if allocs != 0 { + t.Fatalf("Observe allocated %v times per call with control disabled, want 0", allocs) + } +} + +// A panicking publisher is the failure the pump must survive: before this the +// panic unwound through the message transform and took the process with it. +func TestPanickingPublisherIsContainedAndCounted(t *testing.T) { + var published atomic.Int64 + panicking := func(ev events.Event) (events.Envelope, bool) { + published.Add(1) + panic("publisher exploded") + } + o := newTestObserver(t, panicking, controlOn) + + for range 5 { + o.Observe([]byte(clickFrame), testForwardTs) + } + waitFor(t, func() bool { return o.Dropped() == 5 }) + if got := published.Load(); got != 5 { + t.Fatalf("publisher called %d times, want 5: the worker must keep going after a panic", got) + } +} + +// Saturation is an acceptable loss, but only a counted one. +func TestQueueSaturationIsCounted(t *testing.T) { + blocked := make(chan struct{}) + var released sync.Once + t.Cleanup(func() { released.Do(func() { close(blocked) }) }) + + blocking := func(ev events.Event) (events.Envelope, bool) { + <-blocked + return events.Envelope{Event: ev}, true + } + o := newTestObserver(t, blocking, controlOn) + + // One frame occupies the worker, the queue absorbs cdpObserverQueueDepth + // more, and everything past that is dropped rather than blocking the pump. + const overshoot = 50 + for range cdpObserverQueueDepth + overshoot + 1 { + o.Observe([]byte(clickFrame), testForwardTs) + } + waitFor(t, func() bool { return o.Dropped() > 0 }) + if got := o.Dropped(); got > overshoot+1 { + t.Fatalf("dropped %d frames, want at most %d: the queue should absorb the rest", got, overshoot+1) + } +} + +// A big frame is admitted on its merits, not rejected for its size: a large +// paste is a real command, and rejecting it as a lost command is wrong. Only +// the queue's byte budget turns one away. +func TestLargeFramesAreClassifiedRatherThanRejected(t *testing.T) { + pub := &countingPublisher{} + o := newTestObserver(t, pub.publish, controlOn) + + big := `{"id":1,"method":"Input.insertText","params":{"text":"` + + strings.Repeat("x", 256<<10) + `"}}` + o.Observe([]byte(big), testForwardTs) + waitFor(t, func() bool { return pub.n.Load() == 1 }) + + if got := o.Dropped(); got != 0 { + t.Fatalf("dropped = %d, want 0: the frame was classified, not lost", got) + } +} + +// Library traffic never reaches the queue, so it neither occupies capacity nor +// counts as a loss, however large it is. +func TestLibraryTrafficIsNeverAdmitted(t *testing.T) { + pub := &countingPublisher{} + o := newTestObserver(t, pub.publish, controlOn) + + big := `{"id":1,"method":"Runtime.callFunctionOn","params":{"functionDeclaration":"` + + strings.Repeat("x", 256<<10) + `","objectId":"x"}}` + o.Observe([]byte(big), testForwardTs) + + if queued := len(o.frames); queued != 0 { + t.Fatalf("queued %d library frames, want 0", queued) + } + if got := o.queuedBytes.Load(); got != 0 { + t.Fatalf("library traffic held %d queued bytes, want 0", got) + } + if got := pub.n.Load(); got != 0 { + t.Fatalf("published %d events for library traffic, want 0", got) + } + if got := o.Dropped(); got != 0 { + t.Fatalf("dropped = %d, want 0: a frame that would never be an event is not a loss", got) + } +} + +// The failure raf reported: queue capacity exists for control commands, so +// library traffic must not be able to fill it and push a real one out. +func TestLibraryTrafficCannotCrowdOutCommands(t *testing.T) { + blocked := make(chan struct{}) + var released sync.Once + t.Cleanup(func() { released.Do(func() { close(blocked) }) }) + + pub := &countingPublisher{} + blocking := func(ev events.Event) (events.Envelope, bool) { + pub.n.Add(1) + <-blocked + return events.Envelope{Event: ev}, true + } + o := newTestObserver(t, blocking, controlOn) + + // One real command wedges the worker, then far more library frames than the + // queue could hold arrive. + o.Observe([]byte(clickFrame), testForwardTs) + waitFor(t, func() bool { return pub.n.Load() == 1 }) + junk := []byte(`{"id":9,"method":"Runtime.callFunctionOn","params":{"functionDeclaration":"` + + strings.Repeat("x", 1024) + `","objectId":"x"}}`) + for range cdpObserverQueueDepth * 2 { + o.Observe(junk, testForwardTs) + } + + // A real navigation arriving now still gets a slot. + before := o.Dropped() + o.Observe([]byte(`{"id":100,"method":"Page.navigate","params":{"url":"https://x.example/"}}`), testForwardTs) + if o.Dropped() != before { + t.Fatalf("a real command was dropped after %d library frames", cdpObserverQueueDepth*2) + } +} + +// An excluded method is turned away at admission, so it does not occupy the +// queue either, and is still counted apart from the drops. +func TestExcludedMethodsAreNotAdmitted(t *testing.T) { + pub := &countingPublisher{} + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + excluded := func() map[string]struct{} { + return map[string]struct{}{"Input.dispatchMouseEvent": {}} + } + o := newCdpObserver(ctx, testConnID, pub.publish, controlOn, excluded, silentLogger()) + + for range 3 { + o.Observe([]byte(clickFrame), testForwardTs) + } + if queued := len(o.frames); queued != 0 { + t.Fatalf("queued %d excluded frames, want 0", queued) + } + if got := o.Excluded(); got != 3 { + t.Fatalf("excluded = %d, want 3", got) + } + if got := o.Dropped(); got != 0 { + t.Fatalf("dropped = %d, want 0: an exclusion is not a loss", got) + } +} + +// The byte budget is what bounds memory, since there is no per-frame cap. +func TestQueueByteBudgetTurnsAwayWhatItCannotHold(t *testing.T) { + blocked := make(chan struct{}) + var released sync.Once + t.Cleanup(func() { released.Do(func() { close(blocked) }) }) + + blocking := func(ev events.Event) (events.Envelope, bool) { + <-blocked + return events.Envelope{Event: ev}, true + } + o := newTestObserver(t, blocking, controlOn) + + // Each frame is a sixteenth of the budget, so the budget binds well before + // the queue depth does. + frame := []byte(`{"id":1,"method":"Input.insertText","params":{"text":"` + + strings.Repeat("x", cdpObserverMaxQueuedBytes/16) + `"}}`) + for range 32 { + o.Observe(frame, testForwardTs) + } + waitFor(t, func() bool { return o.Dropped() > 0 }) + if got := o.queuedBytes.Load(); got > cdpObserverMaxQueuedBytes { + t.Fatalf("queued %d bytes, over the %d budget", got, cdpObserverMaxQueuedBytes) + } +} + +// Teardown must not lose the commands a client sent last. +func TestObserverDrainsQueuedFramesOnShutdown(t *testing.T) { + pub := &countingPublisher{} + ctx, cancel := context.WithCancel(context.Background()) + o := newCdpObserver(ctx, testConnID, pub.publish, controlOn, nil, silentLogger()) + + const sent = 20 + for range sent { + o.Observe([]byte(clickFrame), testForwardTs) + } + cancel() + o.WaitDrained(5 * time.Second) + + if got := pub.n.Load(); got != sent { + t.Fatalf("published %d events, want %d: queued commands must survive teardown", got, sent) + } +} + +func TestObserverAppliesMethodExclusions(t *testing.T) { + pub := &countingPublisher{} + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + excluded := func() map[string]struct{} { + return map[string]struct{}{"Input.dispatchMouseEvent": {}} + } + o := newCdpObserver(ctx, testConnID, pub.publish, controlOn, excluded, silentLogger()) + + o.Observe([]byte(clickFrame), testForwardTs) + o.Observe([]byte(`{"id":2,"method":"Page.reload"}`), testForwardTs) + waitFor(t, func() bool { return pub.n.Load() == 1 }) + + // An excluded method is not a drop: nothing was lost, it was configured out. + if got := o.Dropped(); got != 0 { + t.Fatalf("dropped = %d, want 0", got) + } +} + +func waitFor(t *testing.T, cond func() bool) { + t.Helper() + deadline := time.Now().Add(5 * time.Second) + for time.Now().Before(deadline) { + if cond() { + return + } + time.Sleep(2 * time.Millisecond) + } + t.Fatal("condition not met within 5s") +} + +// Pump reports a disconnect as soon as one direction fails, while the other can +// still forward, so a frame can reach the queue after the worker has drained +// and stopped. Nothing will classify it, so it has to be counted rather than +// quietly left behind: telemetry_dropped is what tells a reader the tail of the +// session is incomplete. +func TestFramesQueuedAfterTeardownAreCountedAsLoss(t *testing.T) { + pub := &countingPublisher{} + ctx, cancel := context.WithCancel(context.Background()) + o := newCdpObserver(ctx, testConnID, pub.publish, controlOn, nil, silentLogger()) + + o.Observe([]byte(clickFrame), testForwardTs) + cancel() + o.WaitDrained(5 * time.Second) + if got := pub.n.Load(); got != 1 { + t.Fatalf("published %d events before teardown, want 1", got) + } + if got := o.Dropped(); got != 0 { + t.Fatalf("dropped = %d before the late frame, want 0", got) + } + + // The straggler the other pump direction forwarded on its way out. + o.Observe([]byte(clickFrame), testForwardTs) + + if got := pub.n.Load(); got != 1 { + t.Fatalf("published %d events, want 1: the worker has stopped", got) + } + if got := o.Dropped(); got != 1 { + t.Fatalf("dropped = %d, want 1: a frame nothing will read is a loss", got) + } +} + +// testConnID names the connection in observer tests. +const testConnID = "conn-test" + +// An excluded method is configuration, not loss, so it is counted apart from +// the drops. The review asked for it to be counted either way. +func TestExcludedMethodsAreCountedApartFromDrops(t *testing.T) { + pub := &countingPublisher{} + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + excluded := func() map[string]struct{} { + return map[string]struct{}{"Input.dispatchMouseEvent": {}} + } + o := newCdpObserver(ctx, testConnID, pub.publish, controlOn, excluded, silentLogger()) + + for range 3 { + o.Observe([]byte(clickFrame), testForwardTs) + } + waitFor(t, func() bool { return o.Excluded() == 3 }) + if got := o.Dropped(); got != 0 { + t.Fatalf("dropped = %d, want 0: an exclusion is not a loss", got) + } +} + +// A supported command whose arguments do not decode reached the browser but +// produced no event, so it is a loss and has to be counted as one. +func TestMalformedParamsAreCountedAsLoss(t *testing.T) { + pub := &countingPublisher{} + o := newTestObserver(t, pub.publish, controlOn) + + // x is a string where the protocol defines a number. + o.Observe([]byte(`{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":"nope"}}`), testForwardTs) + waitFor(t, func() bool { return o.Dropped() == 1 }) + if got := pub.n.Load(); got != 0 { + t.Fatalf("published %d events, want 0", got) + } +} + +// Library traffic is still not a loss, so the malformed counter must not catch +// frames that were never browser control to begin with. +func TestUnsupportedMethodsAreNotCountedAsLoss(t *testing.T) { + pub := &countingPublisher{} + o := newTestObserver(t, pub.publish, controlOn) + + o.Observe([]byte(`{"id":1,"method":"Runtime.callFunctionOn","params":{"objectId":5}}`), testForwardTs) + o.Observe([]byte(`not json at all`), testForwardTs) + waitFor(t, func() bool { return o.queuedBytes.Load() == 0 }) + if got := o.Dropped(); got != 0 { + t.Fatalf("dropped = %d, want 0", got) + } +} + +// Admission runs on the pump goroutine, so its cost is the cost of every +// forwarded client frame. It must not grow with the frame: the method decode +// copies no arguments. +func BenchmarkObserveControlCommand(b *testing.B) { + o := benchObserver(b) + frame := []byte(clickFrame) + b.ReportAllocs() + for b.Loop() { + o.Observe(frame, testForwardTs) + } +} + +func BenchmarkObserveLibraryTraffic(b *testing.B) { + o := benchObserver(b) + frame := []byte(`{"id":1,"method":"Runtime.callFunctionOn","params":{"functionDeclaration":"() => 1","objectId":"x"}}`) + b.ReportAllocs() + for b.Loop() { + o.Observe(frame, testForwardTs) + } +} + +func BenchmarkObserveLargeLibraryTraffic(b *testing.B) { + o := benchObserver(b) + frame := []byte(`{"id":1,"method":"Runtime.callFunctionOn","params":{"functionDeclaration":"` + + strings.Repeat("x", 64<<10) + `","objectId":"x"}}`) + b.SetBytes(int64(len(frame))) + b.ReportAllocs() + for b.Loop() { + o.Observe(frame, testForwardTs) + } +} + +// benchObserver drains continuously so the benchmark measures admission rather +// than a queue filling up. +func benchObserver(b *testing.B) *cdpObserver { + b.Helper() + ctx, cancel := context.WithCancel(context.Background()) + b.Cleanup(cancel) + o := newCdpObserver(ctx, testConnID, func(events.Event) (events.Envelope, bool) { + return events.Envelope{}, true + }, controlOn, nil, silentLogger()) + return o +} diff --git a/server/lib/devtoolsproxy/cdpparams.go b/server/lib/devtoolsproxy/cdpparams.go new file mode 100644 index 00000000..32b086ac --- /dev/null +++ b/server/lib/devtoolsproxy/cdpparams.go @@ -0,0 +1,1332 @@ +package devtoolsproxy + +// Sanitizers for the browser-control CDP commands the proxy reports. Each +// supported method has a canonical input type mirroring its parameters, and +// produces a separate output type generated from the OpenAPI schema. The split +// is what keeps the two jobs apart: the input names what the client sent, the +// output names what is safe to publish. +// +// The canonical definitions are devtools-protocol at 2d019e73, pinned here: +// https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json +// +// The rule for every field: an argument that can carry a secret — typed and +// composition text, URLs, referrers, scripts, templates, file paths, drag +// contents, autofill values — is replaced by a length, a count, a presence +// flag, an enum or a URL scheme. Everything else is reported as it arrived, +// because an event that omits the click count or the scroll distance cannot +// answer what the agent did. +// +// Fields a canonical input type does not name are not decoded and cannot reach +// an event, so a protocol addition is privacy-safe until someone deliberately +// adds it here. Which arguments those are is not left implicit: every argument +// of every supported command carries a retained or redacted decision in +// testdata/cdp_arguments.yaml, checked against a snapshot of the pinned +// protocol by the tests in cdpmanifest_test.go. + +import ( + "encoding/json" + "net/url" + "sort" + "strings" + "unicode/utf8" + + oapi "github.com/kernel/kernel-images/server/lib/oapi" +) + +// sanitizer turns one command's raw params into its sanitized payload. +type sanitizer func(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) + +// sanitizers is the supported-method inventory: a method is reported if and +// only if it has an entry here. +var sanitizers = map[string]sanitizer{ + "Input.dispatchMouseEvent": sanitizeInputDispatchMouseEvent, + "Input.dispatchKeyEvent": sanitizeInputDispatchKeyEvent, + "Input.insertText": sanitizeInputInsertText, + "Input.imeSetComposition": sanitizeInputImeSetComposition, + "Input.dispatchTouchEvent": sanitizeInputDispatchTouchEvent, + "Input.dispatchDragEvent": sanitizeInputDispatchDragEvent, + "Input.cancelDragging": sanitizeInputCancelDragging, + "Input.emulateTouchFromMouseEvent": sanitizeInputEmulateTouchFromMouseEvent, + "Input.synthesizePinchGesture": sanitizeInputSynthesizePinchGesture, + "Input.synthesizeScrollGesture": sanitizeInputSynthesizeScrollGesture, + "Input.synthesizeTapGesture": sanitizeInputSynthesizeTapGesture, + "DOM.setFileInputFiles": sanitizeDomSetFileInputFiles, + "DOM.focus": sanitizeDomFocus, + "DOM.scrollIntoViewIfNeeded": sanitizeDomScrollIntoViewIfNeeded, + "Page.bringToFront": sanitizePageBringToFront, + "Page.captureScreenshot": sanitizePageCaptureScreenshot, + "Page.captureSnapshot": sanitizePageCaptureSnapshot, + "Page.handleJavaScriptDialog": sanitizePageHandleJavaScriptDialog, + "Page.navigate": sanitizePageNavigate, + "Page.navigateToHistoryEntry": sanitizePageNavigateToHistoryEntry, + "Page.reload": sanitizePageReload, + "Page.printToPDF": sanitizePagePrintToPDF, + "Page.startScreencast": sanitizePageStartScreencast, + "Page.stopScreencast": sanitizePageStopScreencast, + "Page.stopLoading": sanitizePageStopLoading, + "Page.close": sanitizePageClose, + "Page.setWebLifecycleState": sanitizePageSetWebLifecycleState, + "Target.activateTarget": sanitizeTargetActivateTarget, + "Target.closeTarget": sanitizeTargetCloseTarget, + "Target.createTarget": sanitizeTargetCreateTarget, + "Target.createBrowserContext": sanitizeTargetCreateBrowserContext, + "Target.disposeBrowserContext": sanitizeTargetDisposeBrowserContext, + "Target.openDevTools": sanitizeTargetOpenDevTools, + "Browser.cancelDownload": sanitizeBrowserCancelDownload, + "Browser.close": sanitizeBrowserClose, + "Browser.setWindowBounds": sanitizeBrowserSetWindowBounds, + "Browser.setContentsSize": sanitizeBrowserSetContentsSize, + "Autofill.trigger": sanitizeAutofillTrigger, +} + +// commandParams is the canonical input type each supported method decodes its +// arguments into. It exists so the drift check can compare what the code reads +// against testdata/cdp_arguments.yaml: an argument decoded here but not +// declared there, or declared retained but never decoded, is a disagreement +// between the sanitizers and the record of what they are meant to do. +var commandParams = map[string]any{ + "Input.dispatchMouseEvent": inputDispatchMouseEventParams{}, + "Input.dispatchKeyEvent": inputDispatchKeyEventParams{}, + "Input.insertText": inputInsertTextParams{}, + "Input.imeSetComposition": inputImeSetCompositionParams{}, + "Input.dispatchTouchEvent": inputDispatchTouchEventParams{}, + "Input.dispatchDragEvent": inputDispatchDragEventParams{}, + "Input.cancelDragging": struct{}{}, + "Input.emulateTouchFromMouseEvent": inputEmulateTouchFromMouseEventParams{}, + "Input.synthesizePinchGesture": inputSynthesizePinchGestureParams{}, + "Input.synthesizeScrollGesture": inputSynthesizeScrollGestureParams{}, + "Input.synthesizeTapGesture": inputSynthesizeTapGestureParams{}, + "DOM.setFileInputFiles": domSetFileInputFilesParams{}, + "DOM.focus": domNodeRef{}, + "DOM.scrollIntoViewIfNeeded": domScrollIntoViewIfNeededParams{}, + "Page.bringToFront": struct{}{}, + "Page.captureScreenshot": pageCaptureScreenshotParams{}, + "Page.captureSnapshot": pageCaptureSnapshotParams{}, + "Page.handleJavaScriptDialog": pageHandleJavaScriptDialogParams{}, + "Page.navigate": pageNavigateParams{}, + "Page.navigateToHistoryEntry": pageNavigateToHistoryEntryParams{}, + "Page.reload": pageReloadParams{}, + "Page.printToPDF": pagePrintToPDFParams{}, + "Page.startScreencast": pageStartScreencastParams{}, + "Page.stopScreencast": struct{}{}, + "Page.stopLoading": struct{}{}, + "Page.close": struct{}{}, + "Page.setWebLifecycleState": pageSetWebLifecycleStateParams{}, + "Target.activateTarget": targetIdParams{}, + "Target.closeTarget": targetIdParams{}, + "Target.createTarget": targetCreateTargetParams{}, + "Target.createBrowserContext": targetCreateBrowserContextParams{}, + "Target.disposeBrowserContext": browserContextIdParams{}, + "Target.openDevTools": targetOpenDevToolsParams{}, + "Browser.cancelDownload": browserCancelDownloadParams{}, + "Browser.close": struct{}{}, + "Browser.setWindowBounds": browserSetWindowBoundsParams{}, + "Browser.setContentsSize": browserSetContentsSizeParams{}, + "Autofill.trigger": autofillTriggerParams{}, +} + +// namedKeys are the KeyboardEvent.key values worth reading back: keys that +// command the page rather than type into it. This is an allowlist rather than a +// "more than one character" rule because key for typed input can itself be +// multi-rune — a decomposed "é" is two runes and is the letter someone typed, +// so a length rule would publish it. +var namedKeys = lookup(` + Enter Tab Escape Backspace Delete Insert + Home End PageUp PageDown ArrowUp ArrowDown ArrowLeft ArrowRight + Shift Control Alt Meta CapsLock NumLock ScrollLock + ContextMenu Pause PrintScreen + F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 +`) + +// mimeCategories are the top-level MIME types a drag payload may report. A +// subtype names the file ("application/vnd.acme.invoice-2024"), so only the +// category survives, and one outside this set reports as "other". +var mimeCategories = lookup(`text image audio video application font model multipart message`) + +// A client controls the enum strings below, and an event is only as bounded as +// the values it copies: a 1 MB button would push the payload past the envelope +// limit, and truncateIfNeeded drops the whole event rather than the field. So a +// value is passed through only when the generated Valid() — the spec's own enum +// membership — accepts it, and is reported as "other" otherwise. +const unknownEnumValue = "other" + +// maxOpaqueIDBytes bounds identifiers the protocol leaves as free-form strings. +// Real ones are well under this; anything longer is a broken or hostile client, +// and clipping keeps one from taking the event down with it. +const maxOpaqueIDBytes = 128 + +// enumValue is a generated enum type that can vet its own value. +type enumValue interface { + ~string + Valid() bool +} + +// enumOf narrows a client string to a generated enum, for a field the schema +// always carries. An empty or unrecognised value maps to the "other" fallback +// so the emitted payload is always schema-valid. +func enumOf[T enumValue](v string) T { + if v != "" { + if out := T(v); out.Valid() { + return out + } + } + return T(unknownEnumValue) +} + +// optionalEnumOf is enumOf for a field the schema omits when absent. +func optionalEnumOf[T enumValue](v *string) *T { + if v == nil || *v == "" { + return nil + } + out := enumOf[T](*v) + return &out +} + +// clipID bounds an opaque identifier. Clipping rather than dropping keeps a +// required field present and a long-but-real id partially readable. +func clipID(v string) string { + if len(v) <= maxOpaqueIDBytes { + return v + } + return v[:maxOpaqueIDBytes] +} + +func clipIDPtr(v *string) *string { + if v == nil { + return nil + } + clipped := clipID(*v) + return &clipped +} + +func mouseEventType(v string) oapi.BrowserCdpMouseEventType { + return enumOf[oapi.BrowserCdpMouseEventType](v) +} + +func keyEventType(v string) oapi.BrowserCdpKeyEventType { + return enumOf[oapi.BrowserCdpKeyEventType](v) +} + +func touchEventType(v string) oapi.BrowserCdpTouchEventType { + return enumOf[oapi.BrowserCdpTouchEventType](v) +} + +func dragEventType(v string) oapi.BrowserCdpDragEventType { + return enumOf[oapi.BrowserCdpDragEventType](v) +} + +func webLifecycleState(v string) oapi.BrowserCdpWebLifecycleState { + return enumOf[oapi.BrowserCdpWebLifecycleState](v) +} + +func mouseButton(v *string) *oapi.BrowserCdpMouseButton { + return optionalEnumOf[oapi.BrowserCdpMouseButton](v) +} + +func pointerType(v *string) *oapi.BrowserCdpPointerType { + return optionalEnumOf[oapi.BrowserCdpPointerType](v) +} + +func gestureSourceType(v *string) *oapi.BrowserCdpGestureSourceType { + return optionalEnumOf[oapi.BrowserCdpGestureSourceType](v) +} + +func screenshotFormat(v *string) *oapi.BrowserCdpScreenshotFormat { + return optionalEnumOf[oapi.BrowserCdpScreenshotFormat](v) +} + +func snapshotFormat(v *string) *oapi.BrowserCdpSnapshotFormat { + return optionalEnumOf[oapi.BrowserCdpSnapshotFormat](v) +} + +func screencastFormat(v *string) *oapi.BrowserCdpScreencastFormat { + return optionalEnumOf[oapi.BrowserCdpScreencastFormat](v) +} + +func pdfTransferMode(v *string) *oapi.BrowserCdpPdfTransferMode { + return optionalEnumOf[oapi.BrowserCdpPdfTransferMode](v) +} + +func windowState(v *string) *oapi.BrowserCdpWindowState { + return optionalEnumOf[oapi.BrowserCdpWindowState](v) +} + +func transitionType(v *string) *oapi.BrowserCdpTransitionType { + return optionalEnumOf[oapi.BrowserCdpTransitionType](v) +} + +func referrerPolicy(v *string) *oapi.BrowserCdpReferrerPolicy { + return optionalEnumOf[oapi.BrowserCdpReferrerPolicy](v) +} + +// lookup builds a membership set from a whitespace-separated list, so the lists +// above read as lists. +func lookup(words string) map[string]struct{} { + out := make(map[string]struct{}) + for _, word := range strings.Fields(words) { + out[word] = struct{}{} + } + return out +} + +// decodeParams fills p from a command's params. Several control commands take +// no arguments, so an absent params object means "all defaults", not an error. +func decodeParams(raw json.RawMessage, p any) error { + if len(raw) == 0 || string(raw) == "null" { + return nil + } + return json.Unmarshal(raw, p) +} + +func runeLen(s *string) *int { + if s == nil { + return nil + } + n := utf8.RuneCountInString(*s) + return &n +} + +func present(s *string) *bool { + p := s != nil && *s != "" + return &p +} + +func count[T any](items []T) *int { + n := len(items) + return &n +} + +// namedKey passes through only a key that commands the page. A key that +// produces a character is the character someone typed. +func namedKey(key *string) *string { + if key == nil { + return nil + } + if _, ok := namedKeys[*key]; !ok { + return nil + } + return key +} + +// urlScheme reduces a URL to its scheme. The host names the site the agent +// went to and the path and query can carry a reset token, so neither leaves +// the VM through the control category; the page category is where a reader +// opts in to navigation URLs. +const maxURLSchemeBytes = 32 + +func urlScheme(raw string) *string { + if raw == "" { + return nil + } + parsed, err := url.Parse(raw) + if err != nil || parsed.Scheme == "" { + return nil + } + scheme := parsed.Scheme + if len(scheme) > maxURLSchemeBytes { + scheme = scheme[:maxURLSchemeBytes] + } + return &scheme +} + +// ---- Input ---- + +type inputDispatchMouseEventParams struct { + Type string `json:"type"` + X *float64 `json:"x"` + Y *float64 `json:"y"` + Modifiers *int `json:"modifiers"` + Button *string `json:"button"` + Buttons *int `json:"buttons"` + ClickCount *int `json:"clickCount"` + Force *float64 `json:"force"` + TangentialPressure *float64 `json:"tangentialPressure"` + TiltX *float64 `json:"tiltX"` + TiltY *float64 `json:"tiltY"` + Twist *int `json:"twist"` + DeltaX *float64 `json:"deltaX"` + DeltaY *float64 `json:"deltaY"` + PointerType *string `json:"pointerType"` +} + +func sanitizeInputDispatchMouseEvent(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputDispatchMouseEventParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpInputDispatchMouseEventCommandData(oapi.BrowserCdpInputDispatchMouseEventCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + EventType: mouseEventType(p.Type), + X: p.X, + Y: p.Y, + Modifiers: p.Modifiers, + Button: mouseButton(p.Button), + Buttons: p.Buttons, + ClickCount: p.ClickCount, + DeltaX: p.DeltaX, + DeltaY: p.DeltaY, + PointerType: pointerType(p.PointerType), + Force: p.Force, + TangentialPressure: p.TangentialPressure, + TiltX: p.TiltX, + TiltY: p.TiltY, + Twist: p.Twist, + }) +} + +type inputDispatchKeyEventParams struct { + Type string `json:"type"` + Modifiers *int `json:"modifiers"` + Text *string `json:"text"` + Key *string `json:"key"` + Location *int `json:"location"` + AutoRepeat *bool `json:"autoRepeat"` + IsKeypad *bool `json:"isKeypad"` + IsSystemKey *bool `json:"isSystemKey"` + Commands []string `json:"commands"` +} + +func sanitizeInputDispatchKeyEvent(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputDispatchKeyEventParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + data := oapi.BrowserCdpInputDispatchKeyEventCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + EventType: keyEventType(p.Type), + Modifiers: p.Modifiers, + TextLength: runeLen(p.Text), + NamedKey: namedKey(p.Key), + Location: p.Location, + AutoRepeat: p.AutoRepeat, + IsKeypad: p.IsKeypad, + IsSystemKey: p.IsSystemKey, + } + // code, keyIdentifier and the virtual key codes all name the character as + // surely as text does, so they are never decoded. + if p.Commands != nil { + data.CommandCount = count(p.Commands) + } + return out, out.FromBrowserCdpInputDispatchKeyEventCommandData(data) +} + +type inputInsertTextParams struct { + Text string `json:"text"` +} + +func sanitizeInputInsertText(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputInsertTextParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpInputInsertTextCommandData(oapi.BrowserCdpInputInsertTextCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + TextLength: utf8.RuneCountInString(p.Text), + }) +} + +type inputImeSetCompositionParams struct { + Text string `json:"text"` + SelectionStart *int `json:"selectionStart"` + SelectionEnd *int `json:"selectionEnd"` + ReplacementStart *int `json:"replacementStart"` + ReplacementEnd *int `json:"replacementEnd"` +} + +func sanitizeInputImeSetComposition(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputImeSetCompositionParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpInputImeSetCompositionCommandData(oapi.BrowserCdpInputImeSetCompositionCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + TextLength: utf8.RuneCountInString(p.Text), + SelectionStart: p.SelectionStart, + SelectionEnd: p.SelectionEnd, + ReplacementStart: p.ReplacementStart, + ReplacementEnd: p.ReplacementEnd, + }) +} + +type touchPoint struct { + X *float64 `json:"x"` + Y *float64 `json:"y"` + RadiusX *float64 `json:"radiusX"` + RadiusY *float64 `json:"radiusY"` + RotationAngle *float64 `json:"rotationAngle"` + Force *float64 `json:"force"` + TangentialPressure *float64 `json:"tangentialPressure"` + TiltX *float64 `json:"tiltX"` + TiltY *float64 `json:"tiltY"` + Twist *int `json:"twist"` +} + +type inputDispatchTouchEventParams struct { + Type string `json:"type"` + TouchPoints []touchPoint `json:"touchPoints"` + Modifiers *int `json:"modifiers"` +} + +func sanitizeInputDispatchTouchEvent(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputDispatchTouchEventParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + data := oapi.BrowserCdpInputDispatchTouchEventCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + EventType: touchEventType(p.Type), + TouchPointCount: len(p.TouchPoints), + Modifiers: p.Modifiers, + } + // A touch dispatch carries its per-point detail inside touchPoints rather + // than at the top level, so the primary point stands in for the gesture. + if len(p.TouchPoints) > 0 { + primary := p.TouchPoints[0] + data.X, data.Y = primary.X, primary.Y + data.RadiusX, data.RadiusY = primary.RadiusX, primary.RadiusY + data.RotationAngle = primary.RotationAngle + data.Force = primary.Force + data.TangentialPressure = primary.TangentialPressure + data.TiltX, data.TiltY = primary.TiltX, primary.TiltY + data.Twist = primary.Twist + } + return out, out.FromBrowserCdpInputDispatchTouchEventCommandData(data) +} + +type dragDataItem struct { + MimeType string `json:"mimeType"` +} + +type dragData struct { + Items []dragDataItem `json:"items"` + Files []string `json:"files"` + DragOperationsMask *int `json:"dragOperationsMask"` +} + +type inputDispatchDragEventParams struct { + Type string `json:"type"` + X *float64 `json:"x"` + Y *float64 `json:"y"` + Modifiers *int `json:"modifiers"` + Data dragData `json:"data"` +} + +func sanitizeInputDispatchDragEvent(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputDispatchDragEventParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + data := oapi.BrowserCdpInputDispatchDragEventCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + EventType: dragEventType(p.Type), + X: p.X, + Y: p.Y, + Modifiers: p.Modifiers, + DragItemCount: count(p.Data.Items), + DragFileCount: count(p.Data.Files), + DragOperationsMask: p.Data.DragOperationsMask, + } + if cats := mimeCategoriesOf(p.Data.Items); len(cats) > 0 { + data.DragMimeCategories = &cats + } + return out, out.FromBrowserCdpInputDispatchDragEventCommandData(data) +} + +// mimeCategoriesOf reduces drag item MIME types to their distinct top-level +// categories. The subtype names the file, so it does not survive. +func mimeCategoriesOf(items []dragDataItem) []oapi.BrowserCdpDragMimeCategory { + seen := make(map[string]struct{}, len(items)) + for _, item := range items { + category, _, _ := strings.Cut(item.MimeType, "/") + category = strings.ToLower(strings.TrimSpace(category)) + if _, ok := mimeCategories[category]; !ok { + category = "other" + } + seen[category] = struct{}{} + } + out := make([]oapi.BrowserCdpDragMimeCategory, 0, len(seen)) + for category := range seen { + out = append(out, oapi.BrowserCdpDragMimeCategory(category)) + } + sort.Slice(out, func(i, j int) bool { return out[i] < out[j] }) + return out +} + +func sanitizeInputCancelDragging(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var out oapi.BrowserCdpCommandEventData + return out, out.FromBrowserCdpInputCancelDraggingCommandData(oapi.BrowserCdpInputCancelDraggingCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + }) +} + +type inputEmulateTouchFromMouseEventParams struct { + Type string `json:"type"` + X *float64 `json:"x"` + Y *float64 `json:"y"` + Button *string `json:"button"` + Modifiers *int `json:"modifiers"` + ClickCount *int `json:"clickCount"` + DeltaX *float64 `json:"deltaX"` + DeltaY *float64 `json:"deltaY"` +} + +func sanitizeInputEmulateTouchFromMouseEvent(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputEmulateTouchFromMouseEventParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpInputEmulateTouchFromMouseEventCommandData(oapi.BrowserCdpInputEmulateTouchFromMouseEventCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + EventType: mouseEventType(p.Type), + X: p.X, + Y: p.Y, + Button: mouseButton(p.Button), + Modifiers: p.Modifiers, + ClickCount: p.ClickCount, + DeltaX: p.DeltaX, + DeltaY: p.DeltaY, + }) +} + +type inputSynthesizePinchGestureParams struct { + X *float64 `json:"x"` + Y *float64 `json:"y"` + ScaleFactor *float64 `json:"scaleFactor"` + RelativeSpeed *int `json:"relativeSpeed"` + GestureSourceType *string `json:"gestureSourceType"` +} + +func sanitizeInputSynthesizePinchGesture(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputSynthesizePinchGestureParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpInputSynthesizePinchGestureCommandData(oapi.BrowserCdpInputSynthesizePinchGestureCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + X: p.X, + Y: p.Y, + ScaleFactor: p.ScaleFactor, + RelativeSpeed: p.RelativeSpeed, + GestureSourceType: gestureSourceType(p.GestureSourceType), + }) +} + +type inputSynthesizeScrollGestureParams struct { + X *float64 `json:"x"` + Y *float64 `json:"y"` + XDistance *float64 `json:"xDistance"` + YDistance *float64 `json:"yDistance"` + XOverscroll *float64 `json:"xOverscroll"` + YOverscroll *float64 `json:"yOverscroll"` + PreventFling *bool `json:"preventFling"` + Speed *int `json:"speed"` + GestureSourceType *string `json:"gestureSourceType"` + RepeatCount *int `json:"repeatCount"` + RepeatDelayMs *int `json:"repeatDelayMs"` +} + +func sanitizeInputSynthesizeScrollGesture(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputSynthesizeScrollGestureParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + // interactionMarkerName is a caller-supplied label, so it is not decoded. + return out, out.FromBrowserCdpInputSynthesizeScrollGestureCommandData(oapi.BrowserCdpInputSynthesizeScrollGestureCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + X: p.X, + Y: p.Y, + XDistance: p.XDistance, + YDistance: p.YDistance, + XOverscroll: p.XOverscroll, + YOverscroll: p.YOverscroll, + PreventFling: p.PreventFling, + Speed: p.Speed, + GestureSourceType: gestureSourceType(p.GestureSourceType), + RepeatCount: p.RepeatCount, + RepeatDelayMs: p.RepeatDelayMs, + }) +} + +type inputSynthesizeTapGestureParams struct { + X *float64 `json:"x"` + Y *float64 `json:"y"` + Duration *int `json:"duration"` + TapCount *int `json:"tapCount"` + GestureSourceType *string `json:"gestureSourceType"` +} + +func sanitizeInputSynthesizeTapGesture(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p inputSynthesizeTapGestureParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpInputSynthesizeTapGestureCommandData(oapi.BrowserCdpInputSynthesizeTapGestureCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + X: p.X, + Y: p.Y, + Duration: p.Duration, + TapCount: p.TapCount, + GestureSourceType: gestureSourceType(p.GestureSourceType), + }) +} + +// ---- DOM ---- + +// domNodeRef is the three-way node reference DOM commands take. It is shared +// because it is one canonical argument group, not because the commands are. +type domNodeRef struct { + NodeId *int `json:"nodeId"` + BackendNodeId *int `json:"backendNodeId"` + ObjectId *string `json:"objectId"` +} + +type domSetFileInputFilesParams struct { + domNodeRef + Files []string `json:"files"` +} + +func sanitizeDomSetFileInputFiles(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p domSetFileInputFilesParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpDomSetFileInputFilesCommandData(oapi.BrowserCdpDomSetFileInputFilesCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + FileCount: len(p.Files), + NodeId: p.NodeId, + BackendNodeId: p.BackendNodeId, + ObjectId: clipIDPtr(p.ObjectId), + }) +} + +func sanitizeDomFocus(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p domNodeRef + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpDomFocusCommandData(oapi.BrowserCdpDomFocusCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + NodeId: p.NodeId, + BackendNodeId: p.BackendNodeId, + ObjectId: clipIDPtr(p.ObjectId), + }) +} + +// rect is DOM.Rect: an offset and a size, none of it sensitive. +type rect struct { + X *float64 `json:"x"` + Y *float64 `json:"y"` + Width *float64 `json:"width"` + Height *float64 `json:"height"` +} + +type domScrollIntoViewIfNeededParams struct { + domNodeRef + Rect *rect `json:"rect"` +} + +func sanitizeDomScrollIntoViewIfNeeded(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p domScrollIntoViewIfNeededParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + data := oapi.BrowserCdpDomScrollIntoViewIfNeededCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + NodeId: p.NodeId, + BackendNodeId: p.BackendNodeId, + ObjectId: clipIDPtr(p.ObjectId), + } + if p.Rect != nil { + data.RectX, data.RectY = p.Rect.X, p.Rect.Y + data.RectWidth, data.RectHeight = p.Rect.Width, p.Rect.Height + } + return out, out.FromBrowserCdpDomScrollIntoViewIfNeededCommandData(data) +} + +// ---- Page ---- + +func sanitizePageBringToFront(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var out oapi.BrowserCdpCommandEventData + return out, out.FromBrowserCdpPageBringToFrontCommandData(oapi.BrowserCdpPageBringToFrontCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + }) +} + +type viewport struct { + X *float64 `json:"x"` + Y *float64 `json:"y"` + Width *float64 `json:"width"` + Height *float64 `json:"height"` + Scale *float64 `json:"scale"` +} + +type pageCaptureScreenshotParams struct { + Format *string `json:"format"` + Quality *int `json:"quality"` + Clip *viewport `json:"clip"` + FromSurface *bool `json:"fromSurface"` + CaptureBeyondViewport *bool `json:"captureBeyondViewport"` + OptimizeForSpeed *bool `json:"optimizeForSpeed"` +} + +func sanitizePageCaptureScreenshot(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageCaptureScreenshotParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + data := oapi.BrowserCdpPageCaptureScreenshotCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + Format: screenshotFormat(p.Format), + Quality: p.Quality, + FromSurface: p.FromSurface, + CaptureBeyondViewport: p.CaptureBeyondViewport, + OptimizeForSpeed: p.OptimizeForSpeed, + } + if p.Clip != nil { + data.ClipX, data.ClipY = p.Clip.X, p.Clip.Y + data.ClipWidth, data.ClipHeight, data.ClipScale = p.Clip.Width, p.Clip.Height, p.Clip.Scale + } + return out, out.FromBrowserCdpPageCaptureScreenshotCommandData(data) +} + +type pageCaptureSnapshotParams struct { + Format *string `json:"format"` +} + +func sanitizePageCaptureSnapshot(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageCaptureSnapshotParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPageCaptureSnapshotCommandData(oapi.BrowserCdpPageCaptureSnapshotCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + Format: snapshotFormat(p.Format), + }) +} + +type pageHandleJavaScriptDialogParams struct { + Accept bool `json:"accept"` + PromptText *string `json:"promptText"` +} + +func sanitizePageHandleJavaScriptDialog(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageHandleJavaScriptDialogParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPageHandleJavaScriptDialogCommandData(oapi.BrowserCdpPageHandleJavaScriptDialogCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + Accept: p.Accept, + PromptTextLength: runeLen(p.PromptText), + }) +} + +type pageNavigateParams struct { + Url string `json:"url"` + Referrer *string `json:"referrer"` + TransitionType *string `json:"transitionType"` + FrameId *string `json:"frameId"` + ReferrerPolicy *string `json:"referrerPolicy"` +} + +func sanitizePageNavigate(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageNavigateParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPageNavigateCommandData(oapi.BrowserCdpPageNavigateCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + UrlScheme: urlScheme(p.Url), + TransitionType: transitionType(p.TransitionType), + ReferrerPresent: present(p.Referrer), + ReferrerPolicy: referrerPolicy(p.ReferrerPolicy), + FrameId: clipIDPtr(p.FrameId), + }) +} + +type pageNavigateToHistoryEntryParams struct { + EntryId int `json:"entryId"` +} + +func sanitizePageNavigateToHistoryEntry(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageNavigateToHistoryEntryParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPageNavigateToHistoryEntryCommandData(oapi.BrowserCdpPageNavigateToHistoryEntryCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + EntryId: p.EntryId, + }) +} + +type pageReloadParams struct { + IgnoreCache *bool `json:"ignoreCache"` + ScriptToEvaluateOnLoad *string `json:"scriptToEvaluateOnLoad"` + LoaderId *string `json:"loaderId"` +} + +func sanitizePageReload(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageReloadParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPageReloadCommandData(oapi.BrowserCdpPageReloadCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + IgnoreCache: p.IgnoreCache, + ScriptLength: runeLen(p.ScriptToEvaluateOnLoad), + LoaderId: clipIDPtr(p.LoaderId), + }) +} + +type pagePrintToPDFParams struct { + Landscape *bool `json:"landscape"` + DisplayHeaderFooter *bool `json:"displayHeaderFooter"` + PrintBackground *bool `json:"printBackground"` + Scale *float64 `json:"scale"` + PaperWidth *float64 `json:"paperWidth"` + PaperHeight *float64 `json:"paperHeight"` + PageRanges *string `json:"pageRanges"` + HeaderTemplate *string `json:"headerTemplate"` + FooterTemplate *string `json:"footerTemplate"` + MarginTop *float64 `json:"marginTop"` + MarginBottom *float64 `json:"marginBottom"` + MarginLeft *float64 `json:"marginLeft"` + MarginRight *float64 `json:"marginRight"` + PreferCSSPageSize *bool `json:"preferCSSPageSize"` + TransferMode *string `json:"transferMode"` + GenerateTaggedPDF *bool `json:"generateTaggedPDF"` + GenerateDocumentOutline *bool `json:"generateDocumentOutline"` +} + +func sanitizePagePrintToPDF(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pagePrintToPDFParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPagePrintToPdfCommandData(oapi.BrowserCdpPagePrintToPdfCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + Landscape: p.Landscape, + Scale: p.Scale, + PaperWidth: p.PaperWidth, + PaperHeight: p.PaperHeight, + DisplayHeaderFooter: p.DisplayHeaderFooter, + PrintBackground: p.PrintBackground, + MarginTop: p.MarginTop, + MarginBottom: p.MarginBottom, + MarginLeft: p.MarginLeft, + MarginRight: p.MarginRight, + PreferCssPageSize: p.PreferCSSPageSize, + TransferMode: pdfTransferMode(p.TransferMode), + GenerateTaggedPdf: p.GenerateTaggedPDF, + GenerateDocumentOutline: p.GenerateDocumentOutline, + PageRangesPresent: present(p.PageRanges), + HeaderTemplatePresent: present(p.HeaderTemplate), + FooterTemplatePresent: present(p.FooterTemplate), + }) +} + +type pageStartScreencastParams struct { + Format *string `json:"format"` + Quality *int `json:"quality"` + MaxWidth *int `json:"maxWidth"` + MaxHeight *int `json:"maxHeight"` + EveryNthFrame *int `json:"everyNthFrame"` +} + +func sanitizePageStartScreencast(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageStartScreencastParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPageStartScreencastCommandData(oapi.BrowserCdpPageStartScreencastCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + Format: screencastFormat(p.Format), + Quality: p.Quality, + MaxWidth: p.MaxWidth, + MaxHeight: p.MaxHeight, + EveryNthFrame: p.EveryNthFrame, + }) +} + +func sanitizePageStopScreencast(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var out oapi.BrowserCdpCommandEventData + return out, out.FromBrowserCdpPageStopScreencastCommandData(oapi.BrowserCdpPageStopScreencastCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + }) +} + +func sanitizePageStopLoading(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var out oapi.BrowserCdpCommandEventData + return out, out.FromBrowserCdpPageStopLoadingCommandData(oapi.BrowserCdpPageStopLoadingCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + }) +} + +func sanitizePageClose(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var out oapi.BrowserCdpCommandEventData + return out, out.FromBrowserCdpPageCloseCommandData(oapi.BrowserCdpPageCloseCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + }) +} + +type pageSetWebLifecycleStateParams struct { + State string `json:"state"` +} + +func sanitizePageSetWebLifecycleState(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p pageSetWebLifecycleStateParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpPageSetWebLifecycleStateCommandData(oapi.BrowserCdpPageSetWebLifecycleStateCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + State: webLifecycleState(p.State), + }) +} + +// ---- Target ---- + +type targetIdParams struct { + TargetId string `json:"targetId"` +} + +// Target.openDevTools is the only one of the three that takes a panel. +type targetOpenDevToolsParams struct { + TargetId string `json:"targetId"` + PanelId string `json:"panelId"` +} + +func sanitizeTargetActivateTarget(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p targetIdParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpTargetActivateTargetCommandData(oapi.BrowserCdpTargetActivateTargetCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + TargetId: clipID(p.TargetId), + }) +} + +func sanitizeTargetCloseTarget(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p targetIdParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpTargetCloseTargetCommandData(oapi.BrowserCdpTargetCloseTargetCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + TargetId: clipID(p.TargetId), + }) +} + +func sanitizeTargetOpenDevTools(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p targetOpenDevToolsParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + data := oapi.BrowserCdpTargetOpenDevToolsCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + TargetId: clipID(p.TargetId), + } + if p.PanelId != "" { + clipped := clipID(p.PanelId) + data.PanelId = &clipped + } + return out, out.FromBrowserCdpTargetOpenDevToolsCommandData(data) +} + +type targetCreateTargetParams struct { + Url string `json:"url"` + Left *int `json:"left"` + Top *int `json:"top"` + Width *int `json:"width"` + Height *int `json:"height"` + WindowState *string `json:"windowState"` + BrowserContextId *string `json:"browserContextId"` + EnableBeginFrameControl *bool `json:"enableBeginFrameControl"` + NewWindow *bool `json:"newWindow"` + Background *bool `json:"background"` + ForTab *bool `json:"forTab"` + Hidden *bool `json:"hidden"` + Focus *bool `json:"focus"` +} + +func sanitizeTargetCreateTarget(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p targetCreateTargetParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpTargetCreateTargetCommandData(oapi.BrowserCdpTargetCreateTargetCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + UrlScheme: urlScheme(p.Url), + Left: p.Left, + Top: p.Top, + Width: p.Width, + Height: p.Height, + WindowState: windowState(p.WindowState), + BrowserContextId: clipIDPtr(p.BrowserContextId), + NewWindow: p.NewWindow, + Background: p.Background, + ForTab: p.ForTab, + Hidden: p.Hidden, + EnableBeginFrameControl: p.EnableBeginFrameControl, + Focus: p.Focus, + }) +} + +type targetCreateBrowserContextParams struct { + DisposeOnDetach *bool `json:"disposeOnDetach"` + ProxyServer *string `json:"proxyServer"` + ProxyBypassList *string `json:"proxyBypassList"` + OriginsWithUniversalNetworkAccess []string `json:"originsWithUniversalNetworkAccess"` +} + +func sanitizeTargetCreateBrowserContext(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p targetCreateBrowserContextParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpTargetCreateBrowserContextCommandData(oapi.BrowserCdpTargetCreateBrowserContextCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + DisposeOnDetach: p.DisposeOnDetach, + ProxyServerPresent: present(p.ProxyServer), + ProxyBypassListPresent: present(p.ProxyBypassList), + UniversalNetworkAccessOriginCount: count(p.OriginsWithUniversalNetworkAccess), + }) +} + +type browserContextIdParams struct { + BrowserContextId string `json:"browserContextId"` +} + +func sanitizeTargetDisposeBrowserContext(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p browserContextIdParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpTargetDisposeBrowserContextCommandData(oapi.BrowserCdpTargetDisposeBrowserContextCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + BrowserContextId: clipID(p.BrowserContextId), + }) +} + +// ---- Browser ---- + +type browserCancelDownloadParams struct { + Guid string `json:"guid"` + BrowserContextId *string `json:"browserContextId"` +} + +func sanitizeBrowserCancelDownload(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p browserCancelDownloadParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpBrowserCancelDownloadCommandData(oapi.BrowserCdpBrowserCancelDownloadCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + DownloadGuid: clipID(p.Guid), + BrowserContextId: clipIDPtr(p.BrowserContextId), + }) +} + +func sanitizeBrowserClose(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var out oapi.BrowserCdpCommandEventData + return out, out.FromBrowserCdpBrowserCloseCommandData(oapi.BrowserCdpBrowserCloseCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + }) +} + +type windowBounds struct { + Left *int `json:"left"` + Top *int `json:"top"` + Width *int `json:"width"` + Height *int `json:"height"` + WindowState *string `json:"windowState"` +} + +type browserSetWindowBoundsParams struct { + WindowId int `json:"windowId"` + Bounds windowBounds `json:"bounds"` +} + +func sanitizeBrowserSetWindowBounds(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p browserSetWindowBoundsParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpBrowserSetWindowBoundsCommandData(oapi.BrowserCdpBrowserSetWindowBoundsCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + WindowId: p.WindowId, + Left: p.Bounds.Left, + Top: p.Bounds.Top, + Width: p.Bounds.Width, + Height: p.Bounds.Height, + WindowState: windowState(p.Bounds.WindowState), + }) +} + +type browserSetContentsSizeParams struct { + WindowId int `json:"windowId"` + Width *int `json:"width"` + Height *int `json:"height"` +} + +func sanitizeBrowserSetContentsSize(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p browserSetContentsSizeParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + return out, out.FromBrowserCdpBrowserSetContentsSizeCommandData(oapi.BrowserCdpBrowserSetContentsSizeCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + WindowId: p.WindowId, + Width: p.Width, + Height: p.Height, + }) +} + +// ---- Autofill ---- + +// autofillAddress counts the fields an address carries. Their names are +// caller-supplied strings and their values are the address itself, so neither +// is decoded. +type autofillAddress struct { + Fields []struct{} `json:"fields"` +} + +type autofillTriggerParams struct { + FieldId int `json:"fieldId"` + FrameId *string `json:"frameId"` + Card json.RawMessage `json:"card"` + Address *autofillAddress `json:"address"` +} + +func sanitizeAutofillTrigger(cmd cdpCommand) (oapi.BrowserCdpCommandEventData, error) { + var p autofillTriggerParams + var out oapi.BrowserCdpCommandEventData + if err := decodeParams(cmd.Params, &p); err != nil { + return out, err + } + data := oapi.BrowserCdpAutofillTriggerCommandData{ + SessionId: cmd.sessionID(), + CommandId: cmd.ID, + ConnectionId: cmd.connID(), + FieldId: p.FieldId, + FrameId: clipIDPtr(p.FrameId), + } + // The card number and the address lines are the whole payload, so only + // which of the two was filled survives. + switch { + case len(p.Card) > 0 && string(p.Card) != "null": + mode := oapi.Card + data.Mode = &mode + case p.Address != nil: + mode := oapi.Address + data.Mode = &mode + data.AddressFieldCount = count(p.Address.Fields) + } + return out, out.FromBrowserCdpAutofillTriggerCommandData(data) +} diff --git a/server/lib/devtoolsproxy/cdpproxy_test.go b/server/lib/devtoolsproxy/cdpproxy_test.go new file mode 100644 index 00000000..c4fc5f45 --- /dev/null +++ b/server/lib/devtoolsproxy/cdpproxy_test.go @@ -0,0 +1,300 @@ +package devtoolsproxy + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "strings" + "testing" + "time" + + "github.com/coder/websocket" + "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" + "github.com/kernel/kernel-images/server/lib/scaletozero" +) + +// echoProxy stands up a proxy in front of an echoing upstream and returns a +// connected client. The upstream echoes every frame back, so a test that +// counted the upstream direction as commands would fail. +func echoProxy(t *testing.T, publish EventPublisher, controlEnabled ControlEnabledFunc) (*websocket.Conn, context.Context) { + t.Helper() + upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c, err := websocket.Accept(w, r, &websocket.AcceptOptions{OriginPatterns: []string{"*"}}) + if err != nil { + return + } + defer c.Close(websocket.StatusNormalClosure, "") + c.SetReadLimit(100 * 1024 * 1024) + for { + mt, msg, err := c.Read(r.Context()) + if err != nil { + return + } + if err := c.Write(r.Context(), mt, msg); err != nil { + return + } + } + })) + t.Cleanup(upstream.Close) + + u, _ := url.Parse(upstream.URL) + u.Scheme = "ws" + u.Path = "/devtools/browser/x" + + logger := silentLogger() + mgr := NewUpstreamManager("/dev/null", logger) + mgr.setCurrent(u.String()) + + proxy := httptest.NewServer(WebSocketProxyHandler( + mgr, logger, false, scaletozero.NewNoopController(), publish, controlEnabled, nil, nil)) + t.Cleanup(proxy.Close) + + pu, _ := url.Parse(proxy.URL) + pu.Scheme = "ws" + + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + t.Cleanup(cancel) + conn, _, err := websocket.Dial(ctx, pu.String(), nil) + if err != nil { + t.Fatalf("dial proxy failed: %v", err) + } + conn.SetReadLimit(100 * 1024 * 1024) + t.Cleanup(func() { conn.Close(websocket.StatusNormalClosure, "") }) + return conn, ctx +} + +// roundTrip writes each frame and reads the echo back, so the test only +// proceeds once the proxy has relayed in both directions. +func roundTrip(t *testing.T, conn *websocket.Conn, ctx context.Context, frames ...string) []string { + t.Helper() + echoes := make([]string, 0, len(frames)) + for i, frame := range frames { + if err := conn.Write(ctx, websocket.MessageText, []byte(frame)); err != nil { + t.Fatalf("write %d: %v", i, err) + } + _, echo, err := conn.Read(ctx) + if err != nil { + t.Fatalf("read %d: %v", i, err) + } + echoes = append(echoes, string(echo)) + } + return echoes +} + +func commandEvents(evs []events.Event) []events.Event { + out := make([]events.Event, 0, len(evs)) + for _, ev := range evs { + if ev.Type == "cdp_command" { + out = append(out, ev) + } + } + return out +} + +// waitForDisconnect blocks until the proxy has published cdp_disconnect, which +// happens after the observer has drained. Anything the upstream direction +// wrongly produced has been recorded by then. +func waitForDisconnect(t *testing.T, rp *recordingPublisher) events.Event { + t.Helper() + var found events.Event + if !waitForCondition(10*time.Second, func() bool { + for _, ev := range rp.snapshot() { + if ev.Type == "cdp_disconnect" { + found = ev + return true + } + } + return false + }) { + t.Fatal("proxy never published cdp_disconnect") + } + return found +} + +func TestProxyEmitsOneEventPerClientControlCommand(t *testing.T) { + rp := &recordingPublisher{} + conn, ctx := echoProxy(t, rp.publish, controlOn) + + roundTrip(t, conn, ctx, + `{"id":1,"method":"Runtime.callFunctionOn","params":{"functionDeclaration":"() => 1"}}`, + `{"id":2,"method":"Input.dispatchMouseEvent","params":{"type":"mouseMoved","x":9,"y":9}}`, + `{"id":3,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":10,"y":20,"button":"left"}}`, + `{"id":4,"method":"Input.dispatchMouseEvent","params":{"type":"mouseReleased","x":10,"y":20,"button":"left"}}`, + ) + _ = conn.Close(websocket.StatusNormalClosure, "bye") + + disconnect := waitForDisconnect(t, rp) + commands := commandEvents(rp.snapshot()) + if len(commands) != 3 { + t.Fatalf("cdp_command count = %d, want 3 (the move and both click phases; nothing for Runtime.callFunctionOn or the echoes)", len(commands)) + } + + var data map[string]any + if err := json.Unmarshal(disconnect.Data, &data); err != nil { + t.Fatalf("unmarshal disconnect: %v", err) + } + if data["telemetry_dropped"] != 0.0 { + t.Fatalf("telemetry_dropped = %v, want 0", data["telemetry_dropped"]) + } + // Optional in the schema for compatibility, but this image always sets it, + // so a reader can tell "nothing lost" from "not reported". + if _, ok := data["telemetry_dropped"]; !ok { + t.Fatal("cdp_disconnect omitted telemetry_dropped") + } +} + +func TestProxyEmitsNothingWhenControlIsDisabled(t *testing.T) { + rp := &recordingPublisher{} + conn, ctx := echoProxy(t, rp.publish, func() bool { return false }) + + roundTrip(t, conn, ctx, + `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}`, + `{"id":2,"method":"Page.navigate","params":{"url":"https://example.com/"}}`, + ) + _ = conn.Close(websocket.StatusNormalClosure, "bye") + + waitForDisconnect(t, rp) + if got := len(commandEvents(rp.snapshot())); got != 0 { + t.Fatalf("cdp_command count = %d with control disabled, want 0", got) + } +} + +// The failure that motivated moving classification off the pump: a publisher +// that never returns used to stall the message transform, and with it the +// browser. +// +// Only cdp_command misbehaves here. cdp_connect and cdp_disconnect are +// published once per connection from the request goroutine, which chi's +// Recoverer covers and which forwards nothing; the pump is the path under +// test. +func TestBlockedPublisherDoesNotStallForwarding(t *testing.T) { + release := make(chan struct{}) + defer close(release) + blocking := func(ev events.Event) (events.Envelope, bool) { + if ev.Type == "cdp_command" { + <-release + } + return events.Envelope{Event: ev}, true + } + conn, ctx := echoProxy(t, blocking, controlOn) + + // Far more commands than the queue holds, so the publisher is wedged and + // the queue is full well before the last one. Forwarding must not notice. + frames := make([]string, 0, cdpObserverQueueDepth*2) + for i := range cap(frames) { + frames = append(frames, `{"id":`+strconv.Itoa(i)+`,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}`) + } + echoes := roundTrip(t, conn, ctx, frames...) + for i, echo := range echoes { + if echo != frames[i] { + t.Fatalf("frame %d came back changed:\n sent %s\n got %s", i, frames[i], echo) + } + } +} + +// A panicking publisher used to unwind through the pump goroutine, which no +// Recoverer covers, and take the process down with it. Forwarding must survive +// it, in both bytes and order. +func TestPanickingPublisherDoesNotBreakForwarding(t *testing.T) { + panicking := func(ev events.Event) (events.Envelope, bool) { + if ev.Type == "cdp_command" { + panic("publisher exploded") + } + return events.Envelope{Event: ev}, true + } + conn, ctx := echoProxy(t, panicking, controlOn) + + frames := []string{ + `{"id":1,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}`, + `{"id":2,"method":"Page.navigate","params":{"url":"https://example.com/"}}`, + `{"id":3,"method":"Input.insertText","params":{"text":"still forwarding"}}`, + } + echoes := roundTrip(t, conn, ctx, frames...) + for i, echo := range echoes { + if echo != frames[i] { + t.Fatalf("frame %d came back changed:\n sent %s\n got %s", i, frames[i], echo) + } + } +} + +// Telemetry looks at frames; it must not change them. Whatever the client +// sends — malformed JSON, binary, invalid UTF-8, a large paste — the browser +// gets the same bytes in the same order. +func TestForwardingPreservesBytesAndOrderForAwkwardTraffic(t *testing.T) { + rp := &recordingPublisher{} + conn, ctx := echoProxy(t, rp.publish, controlOn) + + big := `{"id":9,"method":"Input.insertText","params":{"text":"` + + strings.Repeat("x", 256<<10) + `"}}` + frames := []string{ + `{"id":1,"method":"Input.dispatchMouseEvent","params":`, + `{"id":2,"method":"Input.insertText","params":{"text":"\ud800"}}`, + `{"id":3,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2},"method":"Page.close"}`, + `{"id":4,"method":"Input.dispatchMouseEvent","params":{"type":"mousePressed","x":1,"y":2}}`, + big, + `{"id":5,"method":"Page.reload"}`, + } + echoes := roundTrip(t, conn, ctx, frames...) + for i, echo := range echoes { + if echo != frames[i] { + t.Fatalf("frame %d came back changed (len sent %d, len got %d)", i, len(frames[i]), len(echo)) + } + } + + _ = conn.Close(websocket.StatusNormalClosure, "bye") + disconnect := waitForDisconnect(t, rp) + + // Awkward traffic is classified or discarded, never counted as loss. + var data map[string]any + if err := json.Unmarshal(disconnect.Data, &data); err != nil { + t.Fatalf("unmarshal disconnect: %v", err) + } + if data["telemetry_dropped"] != 0.0 { + t.Fatalf("telemetry_dropped = %v, want 0", data["telemetry_dropped"]) + } +} + +// Binary frames are not CDP commands, so they are relayed and ignored. +func TestBinaryFramesAreForwardedAndNotClassified(t *testing.T) { + rp := &recordingPublisher{} + conn, ctx := echoProxy(t, rp.publish, controlOn) + + payload := []byte{0x00, 0xff, 0xfe, 0x7b, 0x22} + if err := conn.Write(ctx, websocket.MessageBinary, payload); err != nil { + t.Fatalf("write binary: %v", err) + } + mt, echo, err := conn.Read(ctx) + if err != nil { + t.Fatalf("read binary: %v", err) + } + if mt != websocket.MessageBinary || string(echo) != string(payload) { + t.Fatalf("binary frame came back as %v %q", mt, echo) + } + + _ = conn.Close(websocket.StatusNormalClosure, "bye") + waitForDisconnect(t, rp) + if got := len(commandEvents(rp.snapshot())); got != 0 { + t.Fatalf("cdp_command count = %d for binary traffic, want 0", got) + } +} + +// telemetry_dropped was added to cdp_disconnect after the event type shipped, +// so it stays optional: a payload from an image that predates it must still +// decode, and must be distinguishable from one reporting zero. +func TestDisconnectPayloadFromAnOlderImageStillDecodes(t *testing.T) { + old := `{"duration_ms":12.5,"message_count":3,"reason":"client_close"}` + var data oapi.BrowserCdpDisconnectEventData + if err := json.Unmarshal([]byte(old), &data); err != nil { + t.Fatalf("payload without telemetry_dropped failed to decode: %v", err) + } + if data.TelemetryDropped != nil { + t.Fatalf("telemetry_dropped = %v, want absent", *data.TelemetryDropped) + } + if data.MessageCount != 3 || data.Reason != oapi.ClientClose { + t.Fatalf("decoded the rest wrong: %+v", data) + } +} diff --git a/server/lib/devtoolsproxy/proxy.go b/server/lib/devtoolsproxy/proxy.go index df8b47eb..50cdde31 100644 --- a/server/lib/devtoolsproxy/proxy.go +++ b/server/lib/devtoolsproxy/proxy.go @@ -24,6 +24,7 @@ import ( "github.com/kernel/kernel-images/server/lib/scaletozero" "github.com/kernel/kernel-images/server/lib/wsdrain" "github.com/kernel/kernel-images/server/lib/wsproxy" + "github.com/nrednav/cuid2" ) var devtoolsListeningRegexp = regexp.MustCompile(`DevTools listening on (ws://\S+)`) @@ -309,9 +310,14 @@ type EventPublisher func(ev events.Event) (events.Envelope, bool) // proxies them to the current upstream websocket URL. It expects only websocket requests. // If logCDPMessages is true, all CDP messages will be logged with their direction. // publish is invoked on accept (cdp_connect) and on teardown (cdp_disconnect); pass -// nil to disable emission. -func WebSocketProxyHandler(mgr *UpstreamManager, logger *slog.Logger, logCDPMessages bool, ctrl scaletozero.Controller, publish EventPublisher, reg *wsdrain.Registry) http.Handler { +// nil to disable emission. controlEnabled gates cdp_command classification and is +// checked once per forwarded client frame; pass nil to disable it. excludedMethods +// names the control methods configured out of the stream; nil reports them all. +func WebSocketProxyHandler(mgr *UpstreamManager, logger *slog.Logger, logCDPMessages bool, ctrl scaletozero.Controller, publish EventPublisher, controlEnabled ControlEnabledFunc, excludedMethods ExcludedMethodsFunc, reg *wsdrain.Registry) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Names this connection on every event it produces, so a reader can tell + // two clients driving the same browser apart. + connectionID := cuid2.Generate() // Counts every relayed message so cdp_disconnect can report message_count. var msgCount atomic.Int64 var transform wsproxy.MessageTransform = func(direction string, mt websocket.MessageType, msg []byte) []byte { @@ -353,7 +359,7 @@ func WebSocketProxyHandler(mgr *UpstreamManager, logger *slog.Logger, logCDPMess untrack := reg.Track(clientConn) defer untrack() - publishCdpConnect(publish) + publishCdpConnect(publish, connectionID) connectedAt := time.Now() // Dial upstream. If the URL is stale (Chromium just restarted), first @@ -364,11 +370,11 @@ func WebSocketProxyHandler(mgr *UpstreamManager, logger *slog.Logger, logCDPMess switch { case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded), errors.Is(r.Context().Err(), context.Canceled), errors.Is(r.Context().Err(), context.DeadlineExceeded): clientConn.Close(websocket.StatusGoingAway, "request cancelled") - publishCdpDisconnect(publish, oapi.ContextCancelled, connectedAt, time.Now(), msgCount.Load()) + publishCdpDisconnect(publish, connectionID, oapi.ContextCancelled, connectedAt, time.Now(), msgCount.Load(), 0, 0) default: logger.Error("failed to connect to upstream", slog.String("err", err.Error())) clientConn.Close(websocket.StatusInternalError, "upstream unavailable") - publishCdpDisconnect(publish, oapi.UpstreamError, connectedAt, time.Now(), msgCount.Load()) + publishCdpDisconnect(publish, connectionID, oapi.UpstreamError, connectedAt, time.Now(), msgCount.Load(), 0, 0) } return } @@ -378,6 +384,20 @@ func WebSocketProxyHandler(mgr *UpstreamManager, logger *slog.Logger, logCDPMess pumpCtx, pumpCancel := context.WithCancel(r.Context()) + // Classification of client commands runs behind the pump, not inside it: + // a frame is observed only once Chromium has accepted it. + observer := newCdpObserver(pumpCtx, connectionID, publish, controlEnabled, excludedMethods, logger) + var observe wsproxy.Observer + if observer != nil { + observe = func(direction string, mt websocket.MessageType, msg []byte, ts int64) { + // Client-to-upstream only: commands are what the caller drives the + // browser with, and upstream frames are events and command results. + if direction == "->" && mt == websocket.MessageText { + observer.Observe(msg, ts) + } + } + } + // Force clients off a stale upstream as soon as UpstreamManager // publishes a different DevTools URL. Closing upstreamConn (rather // than cancelling pumpCtx) makes the pump exit PumpExitUpstream so @@ -416,21 +436,35 @@ func WebSocketProxyHandler(mgr *UpstreamManager, logger *slog.Logger, logCDPMess pumpCancel() upstreamConn.Close(websocket.StatusNormalClosure, "") clientConn.Close(websocket.StatusNormalClosure, "") - reason := resolveDisconnectReason(cause, r.Context(), mgr, upstreamURL, restartConfirmWait, logger) - publishCdpDisconnect(publish, reason, connectedAt, disconnectedAt, msgCount.Load()) + reason := resolveDisconnectReason(cause, r.Context(), mgr, upstreamURL, getRestartConfirmWait(), logger) + // Let the worker finish the queue before the disconnect, so the + // client's last commands land ahead of it and telemetry_dropped + // is final. + observer.WaitDrained(cdpObserverDrainWait) + publishCdpDisconnect(publish, connectionID, reason, connectedAt, disconnectedAt, msgCount.Load(), observer.Dropped(), observer.Excluded()) }) } - wsproxy.Pump(pumpCtx, clientConn, upstreamConn, cleanup, logger, transform) + wsproxy.Pump(pumpCtx, clientConn, upstreamConn, cleanup, logger, transform, observe) }) } // restartConfirmWait is how long cleanup waits for a new upstream URL after // the upstream side of the pump dies before classifying the disconnect as // upstream_error vs upstream_changed. Sized for Chromium's typical cold -// restart (~5-8s on Unikraft Cloud) with headroom. var (not const) so tests -// can temporarily shrink it. -var restartConfirmWait = 10 * time.Second +// restart (~5-8s on Unikraft Cloud) with headroom. Atomic rather than a plain +// var because tests shrink it while other handlers are still reading it. +var restartConfirmWait atomic.Int64 + +func init() { setRestartConfirmWait(10 * time.Second) } + +func getRestartConfirmWait() time.Duration { + return time.Duration(restartConfirmWait.Load()) +} + +func setRestartConfirmWait(d time.Duration) { + restartConfirmWait.Store(int64(d)) +} // resolveDisconnectReason picks the cdp_disconnect reason from which side // caused the pump to exit. On upstream cause it polls mgr.Current() for up @@ -468,26 +502,35 @@ func resolveDisconnectReason(cause wsproxy.PumpExitCause, reqCtx context.Context } } -func publishCdpConnect(publish EventPublisher) { +func publishCdpConnect(publish EventPublisher, connectionID string) { if publish == nil { return } + data, _ := json.Marshal(oapi.BrowserCdpConnectEventData{ConnectionId: &connectionID}) publish(events.Event{ Ts: time.Now().UnixMicro(), Type: "cdp_connect", Category: events.Connection, Source: oapi.BrowserEventSource{Kind: oapi.KernelApi}, + Data: data, }) } -func publishCdpDisconnect(publish EventPublisher, reason oapi.BrowserCdpDisconnectEventDataReason, connectedAt, disconnectedAt time.Time, msgCount int64) { +func publishCdpDisconnect(publish EventPublisher, connectionID string, reason oapi.BrowserCdpDisconnectEventDataReason, connectedAt, disconnectedAt time.Time, msgCount, telemetryDropped, telemetryExcluded int64) { if publish == nil { return } + // Optional in the schema so an event from an image that predates the field + // still validates, but always set here: absent means "not reported", which + // is not the same as zero. + dropped, excluded := int(telemetryDropped), int(telemetryExcluded) data, _ := json.Marshal(oapi.BrowserCdpDisconnectEventData{ - DurationMs: float32(disconnectedAt.Sub(connectedAt).Microseconds()) / 1000.0, - MessageCount: int(msgCount), - Reason: reason, + ConnectionId: &connectionID, + DurationMs: float32(disconnectedAt.Sub(connectedAt).Microseconds()) / 1000.0, + MessageCount: int(msgCount), + TelemetryDropped: &dropped, + TelemetryExcluded: &excluded, + Reason: reason, }) publish(events.Event{ Ts: disconnectedAt.UnixMicro(), diff --git a/server/lib/devtoolsproxy/proxy_test.go b/server/lib/devtoolsproxy/proxy_test.go index 5956a555..eb2ee285 100644 --- a/server/lib/devtoolsproxy/proxy_test.go +++ b/server/lib/devtoolsproxy/proxy_test.go @@ -133,7 +133,7 @@ func TestWebSocketProxyHandler_ProxiesEcho(t *testing.T) { // seed current upstream to echo server including path/query (bypass tailing) mgr.setCurrent((&url.URL{Scheme: u.Scheme, Host: u.Host, Path: u.Path, RawQuery: u.RawQuery}).String()) - proxy := WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), nil, nil) + proxy := WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), nil, nil, nil, nil) proxySrv := httptest.NewServer(proxy) defer proxySrv.Close() @@ -191,7 +191,7 @@ func TestWebSocketProxyHandler_RegistryClosesClientWithGoingAway(t *testing.T) { mgr.setCurrent((&url.URL{Scheme: "ws", Host: u.Host, Path: "/echo"}).String()) reg := wsdrain.New() - proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), nil, reg)) + proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), nil, nil, nil, reg)) defer proxySrv.Close() pu, _ := url.Parse(proxySrv.URL) @@ -520,7 +520,7 @@ func TestWebSocketProxyHandler_EmitsConnectAndDisconnect(t *testing.T) { mgr.setCurrent(u.String()) rp := &recordingPublisher{} - proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, nil)) + proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, controlOn, nil, nil)) defer proxySrv.Close() pu, _ := url.Parse(proxySrv.URL) @@ -664,9 +664,9 @@ func TestResolveDisconnectReason(t *testing.T) { func TestWebSocketProxyHandler_EmitsUpstreamChangedOnMidStreamRestart(t *testing.T) { // Shorten the resolve wait so the test doesn't pay the production 10s. - prev := restartConfirmWait - restartConfirmWait = 1 * time.Second - defer func() { restartConfirmWait = prev }() + prev := getRestartConfirmWait() + setRestartConfirmWait(1 * time.Second) + defer setRestartConfirmWait(prev) // Upstream A: echoes once, then closes (simulates Chromium dying mid-session). upstreamA := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -695,7 +695,7 @@ func TestWebSocketProxyHandler_EmitsUpstreamChangedOnMidStreamRestart(t *testing mgr.setCurrent(urlA.String()) rp := &recordingPublisher{} - proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, nil)) + proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, controlOn, nil, nil)) defer proxySrv.Close() pu, _ := url.Parse(proxySrv.URL) @@ -749,9 +749,9 @@ func TestWebSocketProxyHandler_EmitsUpstreamChangedOnMidStreamRestart(t *testing } func TestWebSocketProxyHandler_KicksClientOffStaleUpstreamOnURLChange(t *testing.T) { - prev := restartConfirmWait - restartConfirmWait = 500 * time.Millisecond - defer func() { restartConfirmWait = prev }() + prev := getRestartConfirmWait() + setRestartConfirmWait(500 * time.Millisecond) + defer setRestartConfirmWait(prev) // Upstream stays alive until the proxy closes it from the watcher path. upstreamSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -779,7 +779,7 @@ func TestWebSocketProxyHandler_KicksClientOffStaleUpstreamOnURLChange(t *testing mgr.setCurrent(urlA.String()) rp := &recordingPublisher{} - proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, nil)) + proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, controlOn, nil, nil)) defer proxySrv.Close() pu, _ := url.Parse(proxySrv.URL) @@ -831,7 +831,7 @@ func TestWebSocketProxyHandler_EmitsUpstreamErrorOnDialFailure(t *testing.T) { mgr.setCurrent(deadURL) rp := &recordingPublisher{} - proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, nil)) + proxySrv := httptest.NewServer(WebSocketProxyHandler(mgr, logger, false, scaletozero.NewNoopController(), rp.publish, controlOn, nil, nil)) defer proxySrv.Close() pu, _ := url.Parse(proxySrv.URL) @@ -863,3 +863,6 @@ func TestWebSocketProxyHandler_EmitsUpstreamErrorOnDialFailure(t *testing.T) { t.Fatalf("disconnect reason = %q, want %q", disconnect.Reason, oapi.UpstreamError) } } + +// controlOn is the gate a proxy test needs to see cdp_command events at all. +func controlOn() bool { return true } diff --git a/server/lib/devtoolsproxy/testdata/cdp_arguments.yaml b/server/lib/devtoolsproxy/testdata/cdp_arguments.yaml new file mode 100644 index 00000000..fd2dca37 --- /dev/null +++ b/server/lib/devtoolsproxy/testdata/cdp_arguments.yaml @@ -0,0 +1,311 @@ +# Every argument of every browser-control command the proxy reports, with an +# explicit decision: retained in the cdp_command payload, or redacted and why. +# +# The argument list comes from testdata/cdp_protocol_pinned.json, generated from +# the pinned protocol by scripts/cdpmanifest. TestEveryCanonicalArgumentHasADecision +# fails when an argument here has no decision, when a retained argument is not +# actually decoded, or when the code decodes something this file does not declare. +# An argument added upstream therefore surfaces as a decision someone must make, +# rather than as a field that is quietly absent. +# +# retained means the argument reaches the payload, directly or as a derived value +# (a length, a count, a presence flag, an enum, a URL scheme). The schema names the +# field it becomes. +# +# Pinned protocol: https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json + +Autofill.trigger: + "address.fields[].name": + redacted: caller-supplied field names with no protocol-defined set to allowlist against + "address.fields[].value": + redacted: the address the agent filled + "card.cvc": + redacted: the card security code + "card.expiryMonth": + redacted: part of the card the agent filled + "card.expiryYear": + redacted: part of the card the agent filled + "card.name": + redacted: the cardholder name + "card.number": + redacted: the card number + "fieldId": retained + "frameId": retained + +Browser.cancelDownload: + "browserContextId": retained + "guid": retained + +Browser.close: + {} + +Browser.setContentsSize: + "height": retained + "width": retained + "windowId": retained + +Browser.setWindowBounds: + "bounds.height": retained + "bounds.left": retained + "bounds.top": retained + "bounds.width": retained + "bounds.windowState": retained + "windowId": retained + +DOM.focus: + "backendNodeId": retained + "nodeId": retained + "objectId": retained + +DOM.scrollIntoViewIfNeeded: + "backendNodeId": retained + "nodeId": retained + "objectId": retained + "rect.height": retained + "rect.width": retained + "rect.x": retained + "rect.y": retained + +DOM.setFileInputFiles: + "backendNodeId": retained + "files": retained + "nodeId": retained + "objectId": retained + +Input.cancelDragging: + {} + +Input.dispatchDragEvent: + "data.dragOperationsMask": retained + "data.files": retained + "data.items[].baseURL": + redacted: the document the drag came from + "data.items[].data": + redacted: the drag payload itself + "data.items[].mimeType": retained + "data.items[].title": + redacted: caller-supplied text describing the drag payload + "modifiers": retained + "type": retained + "x": retained + "y": retained + +Input.dispatchKeyEvent: + "autoRepeat": retained + "code": + redacted: names the character typed as surely as text does + "commands": retained + "isKeypad": retained + "isSystemKey": retained + "key": retained + "keyIdentifier": + redacted: names the character typed as surely as text does + "location": retained + "modifiers": retained + "nativeVirtualKeyCode": + redacted: names the character typed as surely as text does + "text": retained + "timestamp": + redacted: a clock reading supplied by the client, not part of what the agent did + "type": retained + "unmodifiedText": + redacted: the character typed, before modifiers applied + "windowsVirtualKeyCode": + redacted: names the character typed as surely as text does + +Input.dispatchMouseEvent: + "button": retained + "buttons": retained + "clickCount": retained + "deltaX": retained + "deltaY": retained + "force": retained + "modifiers": retained + "pointerType": retained + "tangentialPressure": retained + "tiltX": retained + "tiltY": retained + "timestamp": + redacted: a clock reading supplied by the client, not part of what the agent did + "twist": retained + "type": retained + "x": retained + "y": retained + +Input.dispatchTouchEvent: + "modifiers": retained + "timestamp": + redacted: a clock reading supplied by the client, not part of what the agent did + "touchPoints[].force": retained + "touchPoints[].id": + redacted: identifies one finger across frames, which only the full point list could use; the event reports the primary point + "touchPoints[].radiusX": retained + "touchPoints[].radiusY": retained + "touchPoints[].rotationAngle": retained + "touchPoints[].tangentialPressure": retained + "touchPoints[].tiltX": retained + "touchPoints[].tiltY": retained + "touchPoints[].twist": retained + "touchPoints[].x": retained + "touchPoints[].y": retained + "type": retained + +Input.emulateTouchFromMouseEvent: + "button": retained + "clickCount": retained + "deltaX": retained + "deltaY": retained + "modifiers": retained + "timestamp": + redacted: a clock reading supplied by the client, not part of what the agent did + "type": retained + "x": retained + "y": retained + +Input.imeSetComposition: + "replacementEnd": retained + "replacementStart": retained + "selectionEnd": retained + "selectionStart": retained + "text": retained + +Input.insertText: + "text": retained + +Input.synthesizePinchGesture: + "gestureSourceType": retained + "relativeSpeed": retained + "scaleFactor": retained + "x": retained + "y": retained + +Input.synthesizeScrollGesture: + "gestureSourceType": retained + "interactionMarkerName": + redacted: a caller-supplied label with no protocol-defined values + "preventFling": retained + "repeatCount": retained + "repeatDelayMs": retained + "speed": retained + "x": retained + "xDistance": retained + "xOverscroll": retained + "y": retained + "yDistance": retained + "yOverscroll": retained + +Input.synthesizeTapGesture: + "duration": retained + "gestureSourceType": retained + "tapCount": retained + "x": retained + "y": retained + +Page.bringToFront: + {} + +Page.captureScreenshot: + "captureBeyondViewport": retained + "clip.height": retained + "clip.scale": retained + "clip.width": retained + "clip.x": retained + "clip.y": retained + "format": retained + "fromSurface": retained + "optimizeForSpeed": retained + "quality": retained + +Page.captureSnapshot: + "format": retained + +Page.close: + {} + +Page.handleJavaScriptDialog: + "accept": retained + "promptText": retained + +Page.navigate: + "frameId": retained + "referrer": retained + "referrerPolicy": retained + "transitionType": retained + "url": retained + +Page.navigateToHistoryEntry: + "entryId": retained + +Page.printToPDF: + "displayHeaderFooter": retained + "footerTemplate": retained + "generateDocumentOutline": retained + "generateTaggedPDF": retained + "headerTemplate": retained + "landscape": retained + "marginBottom": retained + "marginLeft": retained + "marginRight": retained + "marginTop": retained + "pageRanges": retained + "paperHeight": retained + "paperWidth": retained + "preferCSSPageSize": retained + "printBackground": retained + "scale": retained + "transferMode": retained + +Page.reload: + "ignoreCache": retained + "loaderId": retained + "scriptToEvaluateOnLoad": retained + +Page.setWebLifecycleState: + "state": retained + +Page.startScreencast: + "everyNthFrame": retained + "format": retained + "maxHeight": retained + "maxWidth": retained + "quality": retained + +Page.stopLoading: + {} + +Page.stopScreencast: + {} + +Target.activateTarget: + "targetId": retained + +Target.closeTarget: + "targetId": retained + +Target.createBrowserContext: + "disposeOnDetach": retained + "originsWithUniversalNetworkAccess": retained + "proxyBypassList": retained + "proxyServer": retained + +Target.createTarget: + "background": retained + "browserContextId": retained + "enableBeginFrameControl": retained + "focus": retained + "forTab": retained + "height": retained + "hidden": retained + "left": retained + "newWindow": retained + "top": retained + "url": retained + "width": retained + "windowState": retained + +Target.disposeBrowserContext: + "browserContextId": retained + +Target.openDevTools: + "panelId": retained + "targetId": retained diff --git a/server/lib/devtoolsproxy/testdata/cdp_protocol_pinned.json b/server/lib/devtoolsproxy/testdata/cdp_protocol_pinned.json new file mode 100644 index 00000000..a7dd0f85 --- /dev/null +++ b/server/lib/devtoolsproxy/testdata/cdp_protocol_pinned.json @@ -0,0 +1,270 @@ +{ + "_comment": "Generated by scripts/cdpmanifest from the pinned protocol. Every argument here needs a retained or redacted decision in cdp_arguments.yaml.", + "commit": "2d019e73eb371d1d6985d26d395d78bd8f8a22ba", + "permalink": "https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json", + "commands": { + "Autofill.trigger": [ + "address.fields[].name", + "address.fields[].value", + "card.cvc", + "card.expiryMonth", + "card.expiryYear", + "card.name", + "card.number", + "fieldId", + "frameId" + ], + "Browser.cancelDownload": [ + "browserContextId", + "guid" + ], + "Browser.close": [], + "Browser.setContentsSize": [ + "height", + "width", + "windowId" + ], + "Browser.setWindowBounds": [ + "bounds.height", + "bounds.left", + "bounds.top", + "bounds.width", + "bounds.windowState", + "windowId" + ], + "DOM.focus": [ + "backendNodeId", + "nodeId", + "objectId" + ], + "DOM.scrollIntoViewIfNeeded": [ + "backendNodeId", + "nodeId", + "objectId", + "rect.height", + "rect.width", + "rect.x", + "rect.y" + ], + "DOM.setFileInputFiles": [ + "backendNodeId", + "files", + "nodeId", + "objectId" + ], + "Input.cancelDragging": [], + "Input.dispatchDragEvent": [ + "data.dragOperationsMask", + "data.files", + "data.items[].baseURL", + "data.items[].data", + "data.items[].mimeType", + "data.items[].title", + "modifiers", + "type", + "x", + "y" + ], + "Input.dispatchKeyEvent": [ + "autoRepeat", + "code", + "commands", + "isKeypad", + "isSystemKey", + "key", + "keyIdentifier", + "location", + "modifiers", + "nativeVirtualKeyCode", + "text", + "timestamp", + "type", + "unmodifiedText", + "windowsVirtualKeyCode" + ], + "Input.dispatchMouseEvent": [ + "button", + "buttons", + "clickCount", + "deltaX", + "deltaY", + "force", + "modifiers", + "pointerType", + "tangentialPressure", + "tiltX", + "tiltY", + "timestamp", + "twist", + "type", + "x", + "y" + ], + "Input.dispatchTouchEvent": [ + "modifiers", + "timestamp", + "touchPoints[].force", + "touchPoints[].id", + "touchPoints[].radiusX", + "touchPoints[].radiusY", + "touchPoints[].rotationAngle", + "touchPoints[].tangentialPressure", + "touchPoints[].tiltX", + "touchPoints[].tiltY", + "touchPoints[].twist", + "touchPoints[].x", + "touchPoints[].y", + "type" + ], + "Input.emulateTouchFromMouseEvent": [ + "button", + "clickCount", + "deltaX", + "deltaY", + "modifiers", + "timestamp", + "type", + "x", + "y" + ], + "Input.imeSetComposition": [ + "replacementEnd", + "replacementStart", + "selectionEnd", + "selectionStart", + "text" + ], + "Input.insertText": [ + "text" + ], + "Input.synthesizePinchGesture": [ + "gestureSourceType", + "relativeSpeed", + "scaleFactor", + "x", + "y" + ], + "Input.synthesizeScrollGesture": [ + "gestureSourceType", + "interactionMarkerName", + "preventFling", + "repeatCount", + "repeatDelayMs", + "speed", + "x", + "xDistance", + "xOverscroll", + "y", + "yDistance", + "yOverscroll" + ], + "Input.synthesizeTapGesture": [ + "duration", + "gestureSourceType", + "tapCount", + "x", + "y" + ], + "Page.bringToFront": [], + "Page.captureScreenshot": [ + "captureBeyondViewport", + "clip.height", + "clip.scale", + "clip.width", + "clip.x", + "clip.y", + "format", + "fromSurface", + "optimizeForSpeed", + "quality" + ], + "Page.captureSnapshot": [ + "format" + ], + "Page.close": [], + "Page.handleJavaScriptDialog": [ + "accept", + "promptText" + ], + "Page.navigate": [ + "frameId", + "referrer", + "referrerPolicy", + "transitionType", + "url" + ], + "Page.navigateToHistoryEntry": [ + "entryId" + ], + "Page.printToPDF": [ + "displayHeaderFooter", + "footerTemplate", + "generateDocumentOutline", + "generateTaggedPDF", + "headerTemplate", + "landscape", + "marginBottom", + "marginLeft", + "marginRight", + "marginTop", + "pageRanges", + "paperHeight", + "paperWidth", + "preferCSSPageSize", + "printBackground", + "scale", + "transferMode" + ], + "Page.reload": [ + "ignoreCache", + "loaderId", + "scriptToEvaluateOnLoad" + ], + "Page.setWebLifecycleState": [ + "state" + ], + "Page.startScreencast": [ + "everyNthFrame", + "format", + "maxHeight", + "maxWidth", + "quality" + ], + "Page.stopLoading": [], + "Page.stopScreencast": [], + "Target.activateTarget": [ + "targetId" + ], + "Target.closeTarget": [ + "targetId" + ], + "Target.createBrowserContext": [ + "disposeOnDetach", + "originsWithUniversalNetworkAccess", + "proxyBypassList", + "proxyServer" + ], + "Target.createTarget": [ + "background", + "browserContextId", + "enableBeginFrameControl", + "focus", + "forTab", + "height", + "hidden", + "left", + "newWindow", + "top", + "url", + "width", + "windowState" + ], + "Target.disposeBrowserContext": [ + "browserContextId" + ], + "Target.openDevTools": [ + "panelId", + "targetId" + ] + } +} diff --git a/server/lib/events/category_gen.go b/server/lib/events/category_gen.go index 9d23bdc2..2729d421 100644 --- a/server/lib/events/category_gen.go +++ b/server/lib/events/category_gen.go @@ -7,6 +7,7 @@ import oapi "github.com/kernel/kernel-images/server/lib/oapi" var categoryByType = map[string]oapi.TelemetryEventCategory{ "api_call": oapi.TelemetryEventCategory("control"), "captcha_solve_result": oapi.TelemetryEventCategory("captcha"), + "cdp_command": oapi.TelemetryEventCategory("control"), "cdp_connect": oapi.TelemetryEventCategory("connection"), "cdp_disconnect": oapi.TelemetryEventCategory("connection"), "console_error": oapi.TelemetryEventCategory("console"), diff --git a/server/lib/events/events_test.go b/server/lib/events/events_test.go index 86a5744f..c630ff5e 100644 --- a/server/lib/events/events_test.go +++ b/server/lib/events/events_test.go @@ -128,7 +128,7 @@ func cdpEvent(typ string, cat oapi.TelemetryEventCategory) Event { func newTestRingBuffer(t *testing.T, capacity int) *ringBuffer { t.Helper() - rb, err := newRingBuffer(capacity) + rb, err := newRingBuffer(capacity, DefaultRingMaxBytes) require.NoError(t, err) return rb } @@ -361,9 +361,64 @@ func TestRingBufferResetWithActiveReader(t *testing.T) { func TestNewRingBufferRejectsNonPositiveCapacity(t *testing.T) { for _, cap := range []int{0, -1} { - rb, err := newRingBuffer(cap) + rb, err := newRingBuffer(cap, DefaultRingMaxBytes) assert.Nil(t, rb) require.Error(t, err) assert.Contains(t, err.Error(), "capacity must be > 0") } } + +// Capacity in envelopes bounds the count, not the memory: one slot holds +// anything from a small control event to a base64 screenshot. The byte budget +// is what keeps a run of large events from costing capacity times the largest +// one, and a reader it evicts past sees a gap exactly as it would for eviction +// by count. +func TestRingBufferEvictsOnByteBudget(t *testing.T) { + const payload = 4096 + // Room for four payloads, against a ring that would otherwise hold 64. + rb, err := newRingBuffer(64, 4*(payload+envelopeOverheadBytes)) + require.NoError(t, err) + + reader := rb.newReader(0) + big := make([]byte, payload) + for i := range big { + big[i] = 'x' + } + for seq := uint64(1); seq <= 20; seq++ { + rb.publish(Envelope{Seq: seq, Event: Event{Data: append([]byte(nil), big...)}}) + } + + assert.LessOrEqual(t, rb.bytes, rb.maxBytes, "ring held more than its byte budget") + assert.Greater(t, rb.oldestSeq(), uint64(1), "byte eviction should have moved the floor") + + // The reader started at the beginning, so it is told what it missed. + res, ok := reader.TryRead() + require.True(t, ok) + assert.NotZero(t, res.Dropped, "a reader evicted past must see a gap") + + // And the newest event is always still there. + res, ok = reader.TryRead() + require.True(t, ok) + require.NotNil(t, res.Envelope) + assert.LessOrEqual(t, res.Envelope.Seq, uint64(20)) +} + +// Small events are what the capacity is for: the byte budget must not evict +// them early. +func TestRingBufferKeepsSmallEventsUpToCapacity(t *testing.T) { + rb, err := newRingBuffer(64, DefaultRingMaxBytes) + require.NoError(t, err) + for seq := uint64(1); seq <= 64; seq++ { + rb.publish(Envelope{Seq: seq, Event: Event{Data: []byte(`{"method":"Page.reload"}`)}}) + } + assert.Equal(t, uint64(1), rb.oldestSeq(), "nothing should have been evicted") +} + +func TestRingBufferResetClearsByteAccounting(t *testing.T) { + rb, err := newRingBuffer(8, 1024) + require.NoError(t, err) + rb.publish(Envelope{Seq: 1, Event: Event{Data: make([]byte, 512)}}) + rb.reset() + assert.Zero(t, rb.bytes) + assert.Equal(t, uint64(1), rb.oldestSeq()) +} diff --git a/server/lib/events/eventsstorage.go b/server/lib/events/eventsstorage.go index c14cf063..93ee3ddb 100644 --- a/server/lib/events/eventsstorage.go +++ b/server/lib/events/eventsstorage.go @@ -19,6 +19,7 @@ type Storage interface { // available event in the ring, not the current tail. Delivery is // at-least-once; consumers should dedupe by env.Seq. type StorageWriter struct { + es *EventStream reader *Reader storage Storage log *slog.Logger @@ -38,6 +39,7 @@ func NewStorageWriter(es *EventStream, storage Storage, log *slog.Logger) *Stora // rebuilt on demand does not replay the ring. func NewStorageWriterAfter(es *EventStream, storage Storage, log *slog.Logger, afterSeq uint64) *StorageWriter { return &StorageWriter{ + es: es, reader: es.NewReader(afterSeq), storage: storage, log: log, @@ -89,6 +91,7 @@ func (w *StorageWriter) Drain(ctx context.Context) error { func (w *StorageWriter) processResult(ctx context.Context, res ReadResult) error { if res.Dropped > 0 { + w.es.RecordDropped(res.Dropped) w.log.Warn("storage writer: dropped events", "count", res.Dropped) return nil } diff --git a/server/lib/events/eventstream.go b/server/lib/events/eventstream.go index 371061d4..68b91aac 100644 --- a/server/lib/events/eventstream.go +++ b/server/lib/events/eventstream.go @@ -3,6 +3,7 @@ package events import ( "fmt" "sync" + "sync/atomic" ) // EventStream is the process-lifetime event bus. It owns the ring buffer and @@ -11,15 +12,32 @@ type EventStream struct { mu sync.Mutex seq uint64 ring *ringBuffer + // dropped counts envelopes a consumer missed because it fell behind the + // ring, summed across consumers and sessions. Loss is per-consumer, so this + // is a pressure signal rather than a count of distinct lost events. + dropped atomic.Uint64 } type EventStreamConfig struct { // RingCapacity is the number of envelopes the ring buffer holds. RingCapacity int + // RingMaxBytes bounds the memory those envelopes may occupy. Zero uses + // DefaultRingMaxBytes. Capacity alone does not bound memory: a slot holds + // anything from a small control event to a base64 screenshot. + RingMaxBytes uint64 } +// DefaultRingMaxBytes bounds the ring when a caller does not choose. Sized to +// hold a full ring of control events comfortably while keeping a run of +// screenshot-sized ones from costing capacity times the largest envelope. +const DefaultRingMaxBytes = 64 << 20 + func NewEventStream(cfg EventStreamConfig) (*EventStream, error) { - rb, err := newRingBuffer(cfg.RingCapacity) + maxBytes := cfg.RingMaxBytes + if maxBytes == 0 { + maxBytes = DefaultRingMaxBytes + } + rb, err := newRingBuffer(cfg.RingCapacity, maxBytes) if err != nil { return nil, fmt.Errorf("event stream: %w", err) } @@ -39,6 +57,19 @@ func (es *EventStream) Publish(env Envelope) Envelope { return env } +// RecordDropped notes that a consumer found a gap of n envelopes. Consumers +// report it rather than the ring detecting it, because only a consumer knows +// what it had already read. +func (es *EventStream) RecordDropped(n uint64) { + es.dropped.Add(n) +} + +// DroppedEvents returns the cumulative gap count across consumers, so a reader +// can tell a quiet stream from one it is falling behind. +func (es *EventStream) DroppedEvents() uint64 { + return es.dropped.Load() +} + // NewReader returns a Reader positioned after afterSeq. Pass 0 to start from // the oldest buffered event. func (es *EventStream) NewReader(afterSeq uint64) *Reader { diff --git a/server/lib/events/ringbuffer.go b/server/lib/events/ringbuffer.go index e5dfec84..5e31f555 100644 --- a/server/lib/events/ringbuffer.go +++ b/server/lib/events/ringbuffer.go @@ -14,19 +14,43 @@ type ringBuffer struct { cap uint64 latestSeq uint64 // highest envelope.Seq published readerWake chan struct{} // closed-and-replaced on each Publish to wake blocked readers + + // A slot holds anything from a 200-byte cdp_command to a base64 screenshot, + // so a capacity in envelopes bounds the count and not the memory. maxBytes + // bounds the memory: enough slots for a burst of small events, without a + // run of large ones costing capacity times the largest envelope. + maxBytes uint64 + bytes uint64 + // floorSeq is the oldest seq still held after byte eviction. Readers below + // it get a gap, exactly as they do for eviction by count. + floorSeq uint64 } -func newRingBuffer(capacity int) (*ringBuffer, error) { +func newRingBuffer(capacity int, maxBytes uint64) (*ringBuffer, error) { if capacity <= 0 { return nil, fmt.Errorf("events: ring buffer capacity must be > 0, got %d", capacity) } + if maxBytes == 0 { + return nil, fmt.Errorf("events: ring buffer byte budget must be > 0") + } return &ringBuffer{ buf: make([]Envelope, capacity), cap: uint64(capacity), + maxBytes: maxBytes, + floorSeq: 1, readerWake: make(chan struct{}), }, nil } +// envelopeBytes approximates what an envelope costs to hold. The payload +// dominates; the rest is a fixed handful of scalars and short strings. +func envelopeBytes(env Envelope) uint64 { + if env.Seq == 0 { + return 0 + } + return uint64(len(env.Event.Data)) + envelopeOverheadBytes +} + // reset clears the buffer and wakes any blocked readers so they re-evaluate // against the new (empty) state. Readers will reposition to seq 1 on the next // Read call and block until fresh publishes arrive. @@ -36,28 +60,56 @@ func (rb *ringBuffer) reset() { rb.buf[i] = Envelope{} } rb.latestSeq = 0 + rb.bytes = 0 + rb.floorSeq = 1 old := rb.readerWake rb.readerWake = make(chan struct{}) rb.mu.Unlock() close(old) } -// publish adds an envelope to the ring, evicting the oldest on overflow. +// publish adds an envelope to the ring, evicting the oldest on overflow of +// either bound: the slot count, or the byte budget. func (rb *ringBuffer) publish(env Envelope) { rb.mu.Lock() - rb.buf[env.Seq%rb.cap] = env + slot := env.Seq % rb.cap + // The slot may already hold an envelope this publish is evicting by count. + rb.bytes -= envelopeBytes(rb.buf[slot]) + rb.buf[slot] = env + rb.bytes += envelopeBytes(env) rb.latestSeq = env.Seq + rb.evictForBytesLocked() old := rb.readerWake rb.readerWake = make(chan struct{}) rb.mu.Unlock() close(old) } +// evictForBytesLocked drops the oldest envelopes until the ring is inside its +// byte budget, always keeping the newest so a publish is never a no-op. +// Requires rb.mu. +func (rb *ringBuffer) evictForBytesLocked() { + for rb.bytes > rb.maxBytes && rb.floorSeq < rb.latestSeq { + slot := rb.floorSeq % rb.cap + // A slot whose seq has moved on was already evicted by count, and its + // bytes left the total when it was overwritten. + if rb.buf[slot].Seq == rb.floorSeq { + rb.bytes -= envelopeBytes(rb.buf[slot]) + rb.buf[slot] = Envelope{} + } + rb.floorSeq++ + } +} + func (rb *ringBuffer) oldestSeq() uint64 { - if rb.latestSeq <= rb.cap { - return 1 + oldest := uint64(1) + if rb.latestSeq > rb.cap { + oldest = rb.latestSeq - rb.cap + 1 } - return rb.latestSeq - rb.cap + 1 + if rb.floorSeq > oldest { + oldest = rb.floorSeq + } + return oldest } // newReader returns a Reader. afterSeq == 0 starts from the oldest available @@ -149,3 +201,7 @@ func (r *Reader) Read(ctx context.Context) (ReadResult, error) { } } } + +// envelopeOverheadBytes approximates the non-payload cost of a held envelope: +// the seq, timestamps, type and category strings, and the source metadata. +const envelopeOverheadBytes = 256 diff --git a/server/lib/oapi/oapi.go b/server/lib/oapi/oapi.go index a5fb587e..6b16e727 100644 --- a/server/lib/oapi/oapi.go +++ b/server/lib/oapi/oapi.go @@ -140,1626 +140,4546 @@ func (e BrowserCaptchaSolveResultEventDataStatus) Valid() bool { } } -// Defines values for BrowserCdpConnectEventCategory. +// Defines values for BrowserCdpAutofillMode. const ( - BrowserCdpConnectEventCategoryConnection BrowserCdpConnectEventCategory = "connection" + Address BrowserCdpAutofillMode = "address" + Card BrowserCdpAutofillMode = "card" ) -// Valid indicates whether the value is a known member of the BrowserCdpConnectEventCategory enum. -func (e BrowserCdpConnectEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpAutofillMode enum. +func (e BrowserCdpAutofillMode) Valid() bool { switch e { - case BrowserCdpConnectEventCategoryConnection: + case Address: + return true + case Card: return true default: return false } } -// Defines values for BrowserCdpConnectEventType. +// Defines values for BrowserCdpAutofillTriggerCommandDataMethod. const ( - CdpConnect BrowserCdpConnectEventType = "cdp_connect" + BrowserCdpAutofillTriggerCommandDataMethodAutofillTrigger BrowserCdpAutofillTriggerCommandDataMethod = "Autofill.trigger" ) -// Valid indicates whether the value is a known member of the BrowserCdpConnectEventType enum. -func (e BrowserCdpConnectEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpAutofillTriggerCommandDataMethod enum. +func (e BrowserCdpAutofillTriggerCommandDataMethod) Valid() bool { switch e { - case CdpConnect: + case BrowserCdpAutofillTriggerCommandDataMethodAutofillTrigger: return true default: return false } } -// Defines values for BrowserCdpDisconnectEventCategory. +// Defines values for BrowserCdpBrowserCancelDownloadCommandDataMethod. const ( - BrowserCdpDisconnectEventCategoryConnection BrowserCdpDisconnectEventCategory = "connection" + BrowserCancelDownload BrowserCdpBrowserCancelDownloadCommandDataMethod = "Browser.cancelDownload" ) -// Valid indicates whether the value is a known member of the BrowserCdpDisconnectEventCategory enum. -func (e BrowserCdpDisconnectEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpBrowserCancelDownloadCommandDataMethod enum. +func (e BrowserCdpBrowserCancelDownloadCommandDataMethod) Valid() bool { switch e { - case BrowserCdpDisconnectEventCategoryConnection: + case BrowserCancelDownload: return true default: return false } } -// Defines values for BrowserCdpDisconnectEventType. +// Defines values for BrowserCdpBrowserCloseCommandDataMethod. const ( - CdpDisconnect BrowserCdpDisconnectEventType = "cdp_disconnect" + BrowserClose BrowserCdpBrowserCloseCommandDataMethod = "Browser.close" ) -// Valid indicates whether the value is a known member of the BrowserCdpDisconnectEventType enum. -func (e BrowserCdpDisconnectEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpBrowserCloseCommandDataMethod enum. +func (e BrowserCdpBrowserCloseCommandDataMethod) Valid() bool { switch e { - case CdpDisconnect: + case BrowserClose: return true default: return false } } -// Defines values for BrowserCdpDisconnectEventDataReason. +// Defines values for BrowserCdpBrowserSetContentsSizeCommandDataMethod. const ( - ClientClose BrowserCdpDisconnectEventDataReason = "client_close" - ContextCancelled BrowserCdpDisconnectEventDataReason = "context_cancelled" - UpstreamChanged BrowserCdpDisconnectEventDataReason = "upstream_changed" - UpstreamError BrowserCdpDisconnectEventDataReason = "upstream_error" + BrowserSetContentsSize BrowserCdpBrowserSetContentsSizeCommandDataMethod = "Browser.setContentsSize" ) -// Valid indicates whether the value is a known member of the BrowserCdpDisconnectEventDataReason enum. -func (e BrowserCdpDisconnectEventDataReason) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpBrowserSetContentsSizeCommandDataMethod enum. +func (e BrowserCdpBrowserSetContentsSizeCommandDataMethod) Valid() bool { switch e { - case ClientClose: - return true - case ContextCancelled: - return true - case UpstreamChanged: - return true - case UpstreamError: + case BrowserSetContentsSize: return true default: return false } } -// Defines values for BrowserConsoleErrorEventCategory. +// Defines values for BrowserCdpBrowserSetWindowBoundsCommandDataMethod. const ( - BrowserConsoleErrorEventCategoryConsole BrowserConsoleErrorEventCategory = "console" + BrowserSetWindowBounds BrowserCdpBrowserSetWindowBoundsCommandDataMethod = "Browser.setWindowBounds" ) -// Valid indicates whether the value is a known member of the BrowserConsoleErrorEventCategory enum. -func (e BrowserConsoleErrorEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpBrowserSetWindowBoundsCommandDataMethod enum. +func (e BrowserCdpBrowserSetWindowBoundsCommandDataMethod) Valid() bool { switch e { - case BrowserConsoleErrorEventCategoryConsole: + case BrowserSetWindowBounds: return true default: return false } } -// Defines values for BrowserConsoleErrorEventType. +// Defines values for BrowserCdpCommandEventCategory. const ( - ConsoleError BrowserConsoleErrorEventType = "console_error" + BrowserCdpCommandEventCategoryControl BrowserCdpCommandEventCategory = "control" ) -// Valid indicates whether the value is a known member of the BrowserConsoleErrorEventType enum. -func (e BrowserConsoleErrorEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpCommandEventCategory enum. +func (e BrowserCdpCommandEventCategory) Valid() bool { switch e { - case ConsoleError: + case BrowserCdpCommandEventCategoryControl: return true default: return false } } -// Defines values for BrowserConsoleLogEventCategory. +// Defines values for BrowserCdpCommandEventType. const ( - BrowserConsoleLogEventCategoryConsole BrowserConsoleLogEventCategory = "console" + CdpCommand BrowserCdpCommandEventType = "cdp_command" ) -// Valid indicates whether the value is a known member of the BrowserConsoleLogEventCategory enum. -func (e BrowserConsoleLogEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpCommandEventType enum. +func (e BrowserCdpCommandEventType) Valid() bool { switch e { - case BrowserConsoleLogEventCategoryConsole: + case CdpCommand: return true default: return false } } -// Defines values for BrowserConsoleLogEventType. +// Defines values for BrowserCdpCommandMethod. const ( - ConsoleLog BrowserConsoleLogEventType = "console_log" + BrowserCdpCommandMethodAutofillTrigger BrowserCdpCommandMethod = "Autofill.trigger" + BrowserCdpCommandMethodBrowserCancelDownload BrowserCdpCommandMethod = "Browser.cancelDownload" + BrowserCdpCommandMethodBrowserClose BrowserCdpCommandMethod = "Browser.close" + BrowserCdpCommandMethodBrowserSetContentsSize BrowserCdpCommandMethod = "Browser.setContentsSize" + BrowserCdpCommandMethodBrowserSetWindowBounds BrowserCdpCommandMethod = "Browser.setWindowBounds" + BrowserCdpCommandMethodDOMFocus BrowserCdpCommandMethod = "DOM.focus" + BrowserCdpCommandMethodDOMScrollIntoViewIfNeeded BrowserCdpCommandMethod = "DOM.scrollIntoViewIfNeeded" + BrowserCdpCommandMethodDOMSetFileInputFiles BrowserCdpCommandMethod = "DOM.setFileInputFiles" + BrowserCdpCommandMethodInputCancelDragging BrowserCdpCommandMethod = "Input.cancelDragging" + BrowserCdpCommandMethodInputDispatchDragEvent BrowserCdpCommandMethod = "Input.dispatchDragEvent" + BrowserCdpCommandMethodInputDispatchKeyEvent BrowserCdpCommandMethod = "Input.dispatchKeyEvent" + BrowserCdpCommandMethodInputDispatchMouseEvent BrowserCdpCommandMethod = "Input.dispatchMouseEvent" + BrowserCdpCommandMethodInputDispatchTouchEvent BrowserCdpCommandMethod = "Input.dispatchTouchEvent" + BrowserCdpCommandMethodInputEmulateTouchFromMouseEvent BrowserCdpCommandMethod = "Input.emulateTouchFromMouseEvent" + BrowserCdpCommandMethodInputImeSetComposition BrowserCdpCommandMethod = "Input.imeSetComposition" + BrowserCdpCommandMethodInputInsertText BrowserCdpCommandMethod = "Input.insertText" + BrowserCdpCommandMethodInputSynthesizePinchGesture BrowserCdpCommandMethod = "Input.synthesizePinchGesture" + BrowserCdpCommandMethodInputSynthesizeScrollGesture BrowserCdpCommandMethod = "Input.synthesizeScrollGesture" + BrowserCdpCommandMethodInputSynthesizeTapGesture BrowserCdpCommandMethod = "Input.synthesizeTapGesture" + BrowserCdpCommandMethodPageBringToFront BrowserCdpCommandMethod = "Page.bringToFront" + BrowserCdpCommandMethodPageCaptureScreenshot BrowserCdpCommandMethod = "Page.captureScreenshot" + BrowserCdpCommandMethodPageCaptureSnapshot BrowserCdpCommandMethod = "Page.captureSnapshot" + BrowserCdpCommandMethodPageClose BrowserCdpCommandMethod = "Page.close" + BrowserCdpCommandMethodPageHandleJavaScriptDialog BrowserCdpCommandMethod = "Page.handleJavaScriptDialog" + BrowserCdpCommandMethodPageNavigate BrowserCdpCommandMethod = "Page.navigate" + BrowserCdpCommandMethodPageNavigateToHistoryEntry BrowserCdpCommandMethod = "Page.navigateToHistoryEntry" + BrowserCdpCommandMethodPagePrintToPDF BrowserCdpCommandMethod = "Page.printToPDF" + BrowserCdpCommandMethodPageReload BrowserCdpCommandMethod = "Page.reload" + BrowserCdpCommandMethodPageSetWebLifecycleState BrowserCdpCommandMethod = "Page.setWebLifecycleState" + BrowserCdpCommandMethodPageStartScreencast BrowserCdpCommandMethod = "Page.startScreencast" + BrowserCdpCommandMethodPageStopLoading BrowserCdpCommandMethod = "Page.stopLoading" + BrowserCdpCommandMethodPageStopScreencast BrowserCdpCommandMethod = "Page.stopScreencast" + BrowserCdpCommandMethodTargetActivateTarget BrowserCdpCommandMethod = "Target.activateTarget" + BrowserCdpCommandMethodTargetCloseTarget BrowserCdpCommandMethod = "Target.closeTarget" + BrowserCdpCommandMethodTargetCreateBrowserContext BrowserCdpCommandMethod = "Target.createBrowserContext" + BrowserCdpCommandMethodTargetCreateTarget BrowserCdpCommandMethod = "Target.createTarget" + BrowserCdpCommandMethodTargetDisposeBrowserContext BrowserCdpCommandMethod = "Target.disposeBrowserContext" + BrowserCdpCommandMethodTargetOpenDevTools BrowserCdpCommandMethod = "Target.openDevTools" ) -// Valid indicates whether the value is a known member of the BrowserConsoleLogEventType enum. -func (e BrowserConsoleLogEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpCommandMethod enum. +func (e BrowserCdpCommandMethod) Valid() bool { switch e { - case ConsoleLog: + case BrowserCdpCommandMethodAutofillTrigger: + return true + case BrowserCdpCommandMethodBrowserCancelDownload: + return true + case BrowserCdpCommandMethodBrowserClose: + return true + case BrowserCdpCommandMethodBrowserSetContentsSize: + return true + case BrowserCdpCommandMethodBrowserSetWindowBounds: + return true + case BrowserCdpCommandMethodDOMFocus: + return true + case BrowserCdpCommandMethodDOMScrollIntoViewIfNeeded: + return true + case BrowserCdpCommandMethodDOMSetFileInputFiles: + return true + case BrowserCdpCommandMethodInputCancelDragging: + return true + case BrowserCdpCommandMethodInputDispatchDragEvent: + return true + case BrowserCdpCommandMethodInputDispatchKeyEvent: + return true + case BrowserCdpCommandMethodInputDispatchMouseEvent: + return true + case BrowserCdpCommandMethodInputDispatchTouchEvent: + return true + case BrowserCdpCommandMethodInputEmulateTouchFromMouseEvent: + return true + case BrowserCdpCommandMethodInputImeSetComposition: + return true + case BrowserCdpCommandMethodInputInsertText: + return true + case BrowserCdpCommandMethodInputSynthesizePinchGesture: + return true + case BrowserCdpCommandMethodInputSynthesizeScrollGesture: + return true + case BrowserCdpCommandMethodInputSynthesizeTapGesture: + return true + case BrowserCdpCommandMethodPageBringToFront: + return true + case BrowserCdpCommandMethodPageCaptureScreenshot: + return true + case BrowserCdpCommandMethodPageCaptureSnapshot: + return true + case BrowserCdpCommandMethodPageClose: + return true + case BrowserCdpCommandMethodPageHandleJavaScriptDialog: + return true + case BrowserCdpCommandMethodPageNavigate: + return true + case BrowserCdpCommandMethodPageNavigateToHistoryEntry: + return true + case BrowserCdpCommandMethodPagePrintToPDF: + return true + case BrowserCdpCommandMethodPageReload: + return true + case BrowserCdpCommandMethodPageSetWebLifecycleState: + return true + case BrowserCdpCommandMethodPageStartScreencast: + return true + case BrowserCdpCommandMethodPageStopLoading: + return true + case BrowserCdpCommandMethodPageStopScreencast: + return true + case BrowserCdpCommandMethodTargetActivateTarget: + return true + case BrowserCdpCommandMethodTargetCloseTarget: + return true + case BrowserCdpCommandMethodTargetCreateBrowserContext: + return true + case BrowserCdpCommandMethodTargetCreateTarget: + return true + case BrowserCdpCommandMethodTargetDisposeBrowserContext: + return true + case BrowserCdpCommandMethodTargetOpenDevTools: return true default: return false } } -// Defines values for BrowserEventSourceKind. +// Defines values for BrowserCdpConnectEventCategory. const ( - Cdp BrowserEventSourceKind = "cdp" - Extension BrowserEventSourceKind = "extension" - KernelApi BrowserEventSourceKind = "kernel_api" - LocalProcess BrowserEventSourceKind = "local_process" + BrowserCdpConnectEventCategoryConnection BrowserCdpConnectEventCategory = "connection" ) -// Valid indicates whether the value is a known member of the BrowserEventSourceKind enum. -func (e BrowserEventSourceKind) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpConnectEventCategory enum. +func (e BrowserCdpConnectEventCategory) Valid() bool { switch e { - case Cdp: - return true - case Extension: - return true - case KernelApi: - return true - case LocalProcess: + case BrowserCdpConnectEventCategoryConnection: return true default: return false } } -// Defines values for BrowserInteractionClickEventCategory. +// Defines values for BrowserCdpConnectEventType. const ( - BrowserInteractionClickEventCategoryInteraction BrowserInteractionClickEventCategory = "interaction" + CdpConnect BrowserCdpConnectEventType = "cdp_connect" ) -// Valid indicates whether the value is a known member of the BrowserInteractionClickEventCategory enum. -func (e BrowserInteractionClickEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpConnectEventType enum. +func (e BrowserCdpConnectEventType) Valid() bool { switch e { - case BrowserInteractionClickEventCategoryInteraction: + case CdpConnect: return true default: return false } } -// Defines values for BrowserInteractionClickEventType. +// Defines values for BrowserCdpDisconnectEventCategory. const ( - InteractionClick BrowserInteractionClickEventType = "interaction_click" + BrowserCdpDisconnectEventCategoryConnection BrowserCdpDisconnectEventCategory = "connection" ) -// Valid indicates whether the value is a known member of the BrowserInteractionClickEventType enum. -func (e BrowserInteractionClickEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDisconnectEventCategory enum. +func (e BrowserCdpDisconnectEventCategory) Valid() bool { switch e { - case InteractionClick: + case BrowserCdpDisconnectEventCategoryConnection: return true default: return false } } -// Defines values for BrowserInteractionKeyEventCategory. +// Defines values for BrowserCdpDisconnectEventType. const ( - BrowserInteractionKeyEventCategoryInteraction BrowserInteractionKeyEventCategory = "interaction" + CdpDisconnect BrowserCdpDisconnectEventType = "cdp_disconnect" ) -// Valid indicates whether the value is a known member of the BrowserInteractionKeyEventCategory enum. -func (e BrowserInteractionKeyEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDisconnectEventType enum. +func (e BrowserCdpDisconnectEventType) Valid() bool { switch e { - case BrowserInteractionKeyEventCategoryInteraction: + case CdpDisconnect: return true default: return false } } -// Defines values for BrowserInteractionKeyEventType. +// Defines values for BrowserCdpDisconnectEventDataReason. const ( - InteractionKey BrowserInteractionKeyEventType = "interaction_key" + ClientClose BrowserCdpDisconnectEventDataReason = "client_close" + ContextCancelled BrowserCdpDisconnectEventDataReason = "context_cancelled" + UpstreamChanged BrowserCdpDisconnectEventDataReason = "upstream_changed" + UpstreamError BrowserCdpDisconnectEventDataReason = "upstream_error" ) -// Valid indicates whether the value is a known member of the BrowserInteractionKeyEventType enum. -func (e BrowserInteractionKeyEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDisconnectEventDataReason enum. +func (e BrowserCdpDisconnectEventDataReason) Valid() bool { switch e { - case InteractionKey: + case ClientClose: + return true + case ContextCancelled: + return true + case UpstreamChanged: + return true + case UpstreamError: return true default: return false } } -// Defines values for BrowserInteractionScrollSettledEventCategory. +// Defines values for BrowserCdpDomFocusCommandDataMethod. const ( - Interaction BrowserInteractionScrollSettledEventCategory = "interaction" + DOMFocus BrowserCdpDomFocusCommandDataMethod = "DOM.focus" ) -// Valid indicates whether the value is a known member of the BrowserInteractionScrollSettledEventCategory enum. -func (e BrowserInteractionScrollSettledEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDomFocusCommandDataMethod enum. +func (e BrowserCdpDomFocusCommandDataMethod) Valid() bool { switch e { - case Interaction: + case DOMFocus: return true default: return false } } -// Defines values for BrowserInteractionScrollSettledEventType. +// Defines values for BrowserCdpDomScrollIntoViewIfNeededCommandDataMethod. const ( - InteractionScrollSettled BrowserInteractionScrollSettledEventType = "interaction_scroll_settled" + DOMScrollIntoViewIfNeeded BrowserCdpDomScrollIntoViewIfNeededCommandDataMethod = "DOM.scrollIntoViewIfNeeded" ) -// Valid indicates whether the value is a known member of the BrowserInteractionScrollSettledEventType enum. -func (e BrowserInteractionScrollSettledEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDomScrollIntoViewIfNeededCommandDataMethod enum. +func (e BrowserCdpDomScrollIntoViewIfNeededCommandDataMethod) Valid() bool { switch e { - case InteractionScrollSettled: + case DOMScrollIntoViewIfNeeded: return true default: return false } } -// Defines values for BrowserLiveViewConnectEventCategory. +// Defines values for BrowserCdpDomSetFileInputFilesCommandDataMethod. const ( - BrowserLiveViewConnectEventCategoryConnection BrowserLiveViewConnectEventCategory = "connection" + DOMSetFileInputFiles BrowserCdpDomSetFileInputFilesCommandDataMethod = "DOM.setFileInputFiles" ) -// Valid indicates whether the value is a known member of the BrowserLiveViewConnectEventCategory enum. -func (e BrowserLiveViewConnectEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDomSetFileInputFilesCommandDataMethod enum. +func (e BrowserCdpDomSetFileInputFilesCommandDataMethod) Valid() bool { switch e { - case BrowserLiveViewConnectEventCategoryConnection: + case DOMSetFileInputFiles: return true default: return false } } -// Defines values for BrowserLiveViewConnectEventType. +// Defines values for BrowserCdpDragEventType. const ( - LiveViewConnect BrowserLiveViewConnectEventType = "live_view_connect" + BrowserCdpDragEventTypeDragCancel BrowserCdpDragEventType = "dragCancel" + BrowserCdpDragEventTypeDragEnter BrowserCdpDragEventType = "dragEnter" + BrowserCdpDragEventTypeDragOver BrowserCdpDragEventType = "dragOver" + BrowserCdpDragEventTypeDrop BrowserCdpDragEventType = "drop" + BrowserCdpDragEventTypeOther BrowserCdpDragEventType = "other" ) -// Valid indicates whether the value is a known member of the BrowserLiveViewConnectEventType enum. -func (e BrowserLiveViewConnectEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDragEventType enum. +func (e BrowserCdpDragEventType) Valid() bool { switch e { - case LiveViewConnect: + case BrowserCdpDragEventTypeDragCancel: + return true + case BrowserCdpDragEventTypeDragEnter: + return true + case BrowserCdpDragEventTypeDragOver: + return true + case BrowserCdpDragEventTypeDrop: + return true + case BrowserCdpDragEventTypeOther: return true default: return false } } -// Defines values for BrowserLiveViewDisconnectEventCategory. +// Defines values for BrowserCdpDragMimeCategory. const ( - BrowserLiveViewDisconnectEventCategoryConnection BrowserLiveViewDisconnectEventCategory = "connection" + BrowserCdpDragMimeCategoryApplication BrowserCdpDragMimeCategory = "application" + BrowserCdpDragMimeCategoryAudio BrowserCdpDragMimeCategory = "audio" + BrowserCdpDragMimeCategoryFont BrowserCdpDragMimeCategory = "font" + BrowserCdpDragMimeCategoryImage BrowserCdpDragMimeCategory = "image" + BrowserCdpDragMimeCategoryMessage BrowserCdpDragMimeCategory = "message" + BrowserCdpDragMimeCategoryModel BrowserCdpDragMimeCategory = "model" + BrowserCdpDragMimeCategoryMultipart BrowserCdpDragMimeCategory = "multipart" + BrowserCdpDragMimeCategoryOther BrowserCdpDragMimeCategory = "other" + BrowserCdpDragMimeCategoryText BrowserCdpDragMimeCategory = "text" + BrowserCdpDragMimeCategoryVideo BrowserCdpDragMimeCategory = "video" ) -// Valid indicates whether the value is a known member of the BrowserLiveViewDisconnectEventCategory enum. -func (e BrowserLiveViewDisconnectEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpDragMimeCategory enum. +func (e BrowserCdpDragMimeCategory) Valid() bool { switch e { - case BrowserLiveViewDisconnectEventCategoryConnection: + case BrowserCdpDragMimeCategoryApplication: + return true + case BrowserCdpDragMimeCategoryAudio: + return true + case BrowserCdpDragMimeCategoryFont: + return true + case BrowserCdpDragMimeCategoryImage: + return true + case BrowserCdpDragMimeCategoryMessage: + return true + case BrowserCdpDragMimeCategoryModel: + return true + case BrowserCdpDragMimeCategoryMultipart: + return true + case BrowserCdpDragMimeCategoryOther: + return true + case BrowserCdpDragMimeCategoryText: + return true + case BrowserCdpDragMimeCategoryVideo: return true default: return false } } -// Defines values for BrowserLiveViewDisconnectEventType. +// Defines values for BrowserCdpGestureSourceType. const ( - LiveViewDisconnect BrowserLiveViewDisconnectEventType = "live_view_disconnect" + BrowserCdpGestureSourceTypeDefault BrowserCdpGestureSourceType = "default" + BrowserCdpGestureSourceTypeMouse BrowserCdpGestureSourceType = "mouse" + BrowserCdpGestureSourceTypeOther BrowserCdpGestureSourceType = "other" + BrowserCdpGestureSourceTypeTouch BrowserCdpGestureSourceType = "touch" ) -// Valid indicates whether the value is a known member of the BrowserLiveViewDisconnectEventType enum. -func (e BrowserLiveViewDisconnectEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpGestureSourceType enum. +func (e BrowserCdpGestureSourceType) Valid() bool { switch e { - case LiveViewDisconnect: + case BrowserCdpGestureSourceTypeDefault: + return true + case BrowserCdpGestureSourceTypeMouse: + return true + case BrowserCdpGestureSourceTypeOther: + return true + case BrowserCdpGestureSourceTypeTouch: return true default: return false } } -// Defines values for BrowserMonitorDisconnectedEventCategory. +// Defines values for BrowserCdpInputCancelDraggingCommandDataMethod. const ( - BrowserMonitorDisconnectedEventCategoryMonitor BrowserMonitorDisconnectedEventCategory = "monitor" + InputCancelDragging BrowserCdpInputCancelDraggingCommandDataMethod = "Input.cancelDragging" ) -// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventCategory enum. -func (e BrowserMonitorDisconnectedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputCancelDraggingCommandDataMethod enum. +func (e BrowserCdpInputCancelDraggingCommandDataMethod) Valid() bool { switch e { - case BrowserMonitorDisconnectedEventCategoryMonitor: + case InputCancelDragging: return true default: return false } } -// Defines values for BrowserMonitorDisconnectedEventType. +// Defines values for BrowserCdpInputDispatchDragEventCommandDataMethod. const ( - MonitorDisconnected BrowserMonitorDisconnectedEventType = "monitor_disconnected" + InputDispatchDragEvent BrowserCdpInputDispatchDragEventCommandDataMethod = "Input.dispatchDragEvent" ) -// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventType enum. -func (e BrowserMonitorDisconnectedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputDispatchDragEventCommandDataMethod enum. +func (e BrowserCdpInputDispatchDragEventCommandDataMethod) Valid() bool { switch e { - case MonitorDisconnected: + case InputDispatchDragEvent: return true default: return false } } -// Defines values for BrowserMonitorDisconnectedEventDataReason. +// Defines values for BrowserCdpInputDispatchKeyEventCommandDataMethod. const ( - ChromeRestarted BrowserMonitorDisconnectedEventDataReason = "chrome_restarted" + InputDispatchKeyEvent BrowserCdpInputDispatchKeyEventCommandDataMethod = "Input.dispatchKeyEvent" ) -// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventDataReason enum. -func (e BrowserMonitorDisconnectedEventDataReason) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputDispatchKeyEventCommandDataMethod enum. +func (e BrowserCdpInputDispatchKeyEventCommandDataMethod) Valid() bool { switch e { - case ChromeRestarted: + case InputDispatchKeyEvent: return true default: return false } } -// Defines values for BrowserMonitorInitFailedEventCategory. +// Defines values for BrowserCdpInputDispatchMouseEventCommandDataMethod. const ( - BrowserMonitorInitFailedEventCategoryMonitor BrowserMonitorInitFailedEventCategory = "monitor" + InputDispatchMouseEvent BrowserCdpInputDispatchMouseEventCommandDataMethod = "Input.dispatchMouseEvent" ) -// Valid indicates whether the value is a known member of the BrowserMonitorInitFailedEventCategory enum. -func (e BrowserMonitorInitFailedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputDispatchMouseEventCommandDataMethod enum. +func (e BrowserCdpInputDispatchMouseEventCommandDataMethod) Valid() bool { switch e { - case BrowserMonitorInitFailedEventCategoryMonitor: + case InputDispatchMouseEvent: return true default: return false } } -// Defines values for BrowserMonitorInitFailedEventType. +// Defines values for BrowserCdpInputDispatchTouchEventCommandDataMethod. const ( - MonitorInitFailed BrowserMonitorInitFailedEventType = "monitor_init_failed" + InputDispatchTouchEvent BrowserCdpInputDispatchTouchEventCommandDataMethod = "Input.dispatchTouchEvent" ) -// Valid indicates whether the value is a known member of the BrowserMonitorInitFailedEventType enum. -func (e BrowserMonitorInitFailedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputDispatchTouchEventCommandDataMethod enum. +func (e BrowserCdpInputDispatchTouchEventCommandDataMethod) Valid() bool { switch e { - case MonitorInitFailed: + case InputDispatchTouchEvent: return true default: return false } } -// Defines values for BrowserMonitorReconnectFailedEventCategory. +// Defines values for BrowserCdpInputEmulateTouchFromMouseEventCommandDataMethod. const ( - BrowserMonitorReconnectFailedEventCategoryMonitor BrowserMonitorReconnectFailedEventCategory = "monitor" + InputEmulateTouchFromMouseEvent BrowserCdpInputEmulateTouchFromMouseEventCommandDataMethod = "Input.emulateTouchFromMouseEvent" ) -// Valid indicates whether the value is a known member of the BrowserMonitorReconnectFailedEventCategory enum. -func (e BrowserMonitorReconnectFailedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputEmulateTouchFromMouseEventCommandDataMethod enum. +func (e BrowserCdpInputEmulateTouchFromMouseEventCommandDataMethod) Valid() bool { switch e { - case BrowserMonitorReconnectFailedEventCategoryMonitor: + case InputEmulateTouchFromMouseEvent: return true default: return false } } -// Defines values for BrowserMonitorReconnectFailedEventType. +// Defines values for BrowserCdpInputImeSetCompositionCommandDataMethod. const ( - MonitorReconnectFailed BrowserMonitorReconnectFailedEventType = "monitor_reconnect_failed" + InputImeSetComposition BrowserCdpInputImeSetCompositionCommandDataMethod = "Input.imeSetComposition" ) -// Valid indicates whether the value is a known member of the BrowserMonitorReconnectFailedEventType enum. -func (e BrowserMonitorReconnectFailedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputImeSetCompositionCommandDataMethod enum. +func (e BrowserCdpInputImeSetCompositionCommandDataMethod) Valid() bool { switch e { - case MonitorReconnectFailed: + case InputImeSetComposition: return true default: return false } } -// Defines values for BrowserMonitorReconnectFailedEventDataReason. +// Defines values for BrowserCdpInputInsertTextCommandDataMethod. const ( - ReconnectExhausted BrowserMonitorReconnectFailedEventDataReason = "reconnect_exhausted" + InputInsertText BrowserCdpInputInsertTextCommandDataMethod = "Input.insertText" ) -// Valid indicates whether the value is a known member of the BrowserMonitorReconnectFailedEventDataReason enum. -func (e BrowserMonitorReconnectFailedEventDataReason) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputInsertTextCommandDataMethod enum. +func (e BrowserCdpInputInsertTextCommandDataMethod) Valid() bool { switch e { - case ReconnectExhausted: + case InputInsertText: return true default: return false } } -// Defines values for BrowserMonitorReconnectedEventCategory. +// Defines values for BrowserCdpInputSynthesizePinchGestureCommandDataMethod. const ( - BrowserMonitorReconnectedEventCategoryMonitor BrowserMonitorReconnectedEventCategory = "monitor" + InputSynthesizePinchGesture BrowserCdpInputSynthesizePinchGestureCommandDataMethod = "Input.synthesizePinchGesture" ) -// Valid indicates whether the value is a known member of the BrowserMonitorReconnectedEventCategory enum. -func (e BrowserMonitorReconnectedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputSynthesizePinchGestureCommandDataMethod enum. +func (e BrowserCdpInputSynthesizePinchGestureCommandDataMethod) Valid() bool { switch e { - case BrowserMonitorReconnectedEventCategoryMonitor: + case InputSynthesizePinchGesture: return true default: return false } } -// Defines values for BrowserMonitorReconnectedEventType. +// Defines values for BrowserCdpInputSynthesizeScrollGestureCommandDataMethod. const ( - MonitorReconnected BrowserMonitorReconnectedEventType = "monitor_reconnected" + InputSynthesizeScrollGesture BrowserCdpInputSynthesizeScrollGestureCommandDataMethod = "Input.synthesizeScrollGesture" ) -// Valid indicates whether the value is a known member of the BrowserMonitorReconnectedEventType enum. -func (e BrowserMonitorReconnectedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputSynthesizeScrollGestureCommandDataMethod enum. +func (e BrowserCdpInputSynthesizeScrollGestureCommandDataMethod) Valid() bool { switch e { - case MonitorReconnected: + case InputSynthesizeScrollGesture: return true default: return false } } -// Defines values for BrowserMonitorScreenshotEventCategory. +// Defines values for BrowserCdpInputSynthesizeTapGestureCommandDataMethod. const ( - Screenshot BrowserMonitorScreenshotEventCategory = "screenshot" + InputSynthesizeTapGesture BrowserCdpInputSynthesizeTapGestureCommandDataMethod = "Input.synthesizeTapGesture" ) -// Valid indicates whether the value is a known member of the BrowserMonitorScreenshotEventCategory enum. -func (e BrowserMonitorScreenshotEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpInputSynthesizeTapGestureCommandDataMethod enum. +func (e BrowserCdpInputSynthesizeTapGestureCommandDataMethod) Valid() bool { switch e { - case Screenshot: + case InputSynthesizeTapGesture: return true default: return false } } -// Defines values for BrowserMonitorScreenshotEventType. +// Defines values for BrowserCdpKeyEventType. const ( - MonitorScreenshot BrowserMonitorScreenshotEventType = "monitor_screenshot" + BrowserCdpKeyEventTypeChar BrowserCdpKeyEventType = "char" + BrowserCdpKeyEventTypeKeyDown BrowserCdpKeyEventType = "keyDown" + BrowserCdpKeyEventTypeKeyUp BrowserCdpKeyEventType = "keyUp" + BrowserCdpKeyEventTypeOther BrowserCdpKeyEventType = "other" + BrowserCdpKeyEventTypeRawKeyDown BrowserCdpKeyEventType = "rawKeyDown" ) -// Valid indicates whether the value is a known member of the BrowserMonitorScreenshotEventType enum. -func (e BrowserMonitorScreenshotEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpKeyEventType enum. +func (e BrowserCdpKeyEventType) Valid() bool { switch e { - case MonitorScreenshot: + case BrowserCdpKeyEventTypeChar: + return true + case BrowserCdpKeyEventTypeKeyDown: + return true + case BrowserCdpKeyEventTypeKeyUp: + return true + case BrowserCdpKeyEventTypeOther: + return true + case BrowserCdpKeyEventTypeRawKeyDown: return true default: return false } } -// Defines values for BrowserNetworkIdleEventCategory. +// Defines values for BrowserCdpMouseButton. const ( - BrowserNetworkIdleEventCategoryNetwork BrowserNetworkIdleEventCategory = "network" + BrowserCdpMouseButtonBack BrowserCdpMouseButton = "back" + BrowserCdpMouseButtonForward BrowserCdpMouseButton = "forward" + BrowserCdpMouseButtonLeft BrowserCdpMouseButton = "left" + BrowserCdpMouseButtonMiddle BrowserCdpMouseButton = "middle" + BrowserCdpMouseButtonNone BrowserCdpMouseButton = "none" + BrowserCdpMouseButtonOther BrowserCdpMouseButton = "other" + BrowserCdpMouseButtonRight BrowserCdpMouseButton = "right" ) -// Valid indicates whether the value is a known member of the BrowserNetworkIdleEventCategory enum. -func (e BrowserNetworkIdleEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpMouseButton enum. +func (e BrowserCdpMouseButton) Valid() bool { switch e { - case BrowserNetworkIdleEventCategoryNetwork: + case BrowserCdpMouseButtonBack: + return true + case BrowserCdpMouseButtonForward: + return true + case BrowserCdpMouseButtonLeft: + return true + case BrowserCdpMouseButtonMiddle: + return true + case BrowserCdpMouseButtonNone: + return true + case BrowserCdpMouseButtonOther: + return true + case BrowserCdpMouseButtonRight: return true default: return false } } -// Defines values for BrowserNetworkIdleEventType. +// Defines values for BrowserCdpMouseEventType. const ( - NetworkIdle BrowserNetworkIdleEventType = "network_idle" + BrowserCdpMouseEventTypeMouseMoved BrowserCdpMouseEventType = "mouseMoved" + BrowserCdpMouseEventTypeMousePressed BrowserCdpMouseEventType = "mousePressed" + BrowserCdpMouseEventTypeMouseReleased BrowserCdpMouseEventType = "mouseReleased" + BrowserCdpMouseEventTypeMouseWheel BrowserCdpMouseEventType = "mouseWheel" + BrowserCdpMouseEventTypeOther BrowserCdpMouseEventType = "other" ) -// Valid indicates whether the value is a known member of the BrowserNetworkIdleEventType enum. -func (e BrowserNetworkIdleEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpMouseEventType enum. +func (e BrowserCdpMouseEventType) Valid() bool { switch e { - case NetworkIdle: + case BrowserCdpMouseEventTypeMouseMoved: + return true + case BrowserCdpMouseEventTypeMousePressed: + return true + case BrowserCdpMouseEventTypeMouseReleased: + return true + case BrowserCdpMouseEventTypeMouseWheel: + return true + case BrowserCdpMouseEventTypeOther: return true default: return false } } -// Defines values for BrowserNetworkLoadingFailedEventCategory. +// Defines values for BrowserCdpPageBringToFrontCommandDataMethod. const ( - BrowserNetworkLoadingFailedEventCategoryNetwork BrowserNetworkLoadingFailedEventCategory = "network" + PageBringToFront BrowserCdpPageBringToFrontCommandDataMethod = "Page.bringToFront" ) -// Valid indicates whether the value is a known member of the BrowserNetworkLoadingFailedEventCategory enum. -func (e BrowserNetworkLoadingFailedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageBringToFrontCommandDataMethod enum. +func (e BrowserCdpPageBringToFrontCommandDataMethod) Valid() bool { switch e { - case BrowserNetworkLoadingFailedEventCategoryNetwork: + case PageBringToFront: return true default: return false } } -// Defines values for BrowserNetworkLoadingFailedEventType. +// Defines values for BrowserCdpPageCaptureScreenshotCommandDataMethod. const ( - NetworkLoadingFailed BrowserNetworkLoadingFailedEventType = "network_loading_failed" + PageCaptureScreenshot BrowserCdpPageCaptureScreenshotCommandDataMethod = "Page.captureScreenshot" ) -// Valid indicates whether the value is a known member of the BrowserNetworkLoadingFailedEventType enum. -func (e BrowserNetworkLoadingFailedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageCaptureScreenshotCommandDataMethod enum. +func (e BrowserCdpPageCaptureScreenshotCommandDataMethod) Valid() bool { switch e { - case NetworkLoadingFailed: + case PageCaptureScreenshot: return true default: return false } } -// Defines values for BrowserNetworkRequestEventCategory. +// Defines values for BrowserCdpPageCaptureSnapshotCommandDataMethod. const ( - BrowserNetworkRequestEventCategoryNetwork BrowserNetworkRequestEventCategory = "network" + PageCaptureSnapshot BrowserCdpPageCaptureSnapshotCommandDataMethod = "Page.captureSnapshot" ) -// Valid indicates whether the value is a known member of the BrowserNetworkRequestEventCategory enum. -func (e BrowserNetworkRequestEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageCaptureSnapshotCommandDataMethod enum. +func (e BrowserCdpPageCaptureSnapshotCommandDataMethod) Valid() bool { switch e { - case BrowserNetworkRequestEventCategoryNetwork: + case PageCaptureSnapshot: return true default: return false } } -// Defines values for BrowserNetworkRequestEventType. +// Defines values for BrowserCdpPageCloseCommandDataMethod. const ( - NetworkRequest BrowserNetworkRequestEventType = "network_request" + PageClose BrowserCdpPageCloseCommandDataMethod = "Page.close" ) -// Valid indicates whether the value is a known member of the BrowserNetworkRequestEventType enum. -func (e BrowserNetworkRequestEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageCloseCommandDataMethod enum. +func (e BrowserCdpPageCloseCommandDataMethod) Valid() bool { switch e { - case NetworkRequest: + case PageClose: return true default: return false } } -// Defines values for BrowserNetworkResponseEventCategory. +// Defines values for BrowserCdpPageHandleJavaScriptDialogCommandDataMethod. const ( - BrowserNetworkResponseEventCategoryNetwork BrowserNetworkResponseEventCategory = "network" + PageHandleJavaScriptDialog BrowserCdpPageHandleJavaScriptDialogCommandDataMethod = "Page.handleJavaScriptDialog" ) -// Valid indicates whether the value is a known member of the BrowserNetworkResponseEventCategory enum. -func (e BrowserNetworkResponseEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageHandleJavaScriptDialogCommandDataMethod enum. +func (e BrowserCdpPageHandleJavaScriptDialogCommandDataMethod) Valid() bool { switch e { - case BrowserNetworkResponseEventCategoryNetwork: + case PageHandleJavaScriptDialog: return true default: return false } } -// Defines values for BrowserNetworkResponseEventType. +// Defines values for BrowserCdpPageNavigateCommandDataMethod. const ( - NetworkResponse BrowserNetworkResponseEventType = "network_response" + PageNavigate BrowserCdpPageNavigateCommandDataMethod = "Page.navigate" ) -// Valid indicates whether the value is a known member of the BrowserNetworkResponseEventType enum. -func (e BrowserNetworkResponseEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageNavigateCommandDataMethod enum. +func (e BrowserCdpPageNavigateCommandDataMethod) Valid() bool { switch e { - case NetworkResponse: + case PageNavigate: return true default: return false } } -// Defines values for BrowserPageCrashedEventCategory. +// Defines values for BrowserCdpPageNavigateToHistoryEntryCommandDataMethod. const ( - BrowserPageCrashedEventCategoryPage BrowserPageCrashedEventCategory = "page" + PageNavigateToHistoryEntry BrowserCdpPageNavigateToHistoryEntryCommandDataMethod = "Page.navigateToHistoryEntry" ) -// Valid indicates whether the value is a known member of the BrowserPageCrashedEventCategory enum. -func (e BrowserPageCrashedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageNavigateToHistoryEntryCommandDataMethod enum. +func (e BrowserCdpPageNavigateToHistoryEntryCommandDataMethod) Valid() bool { switch e { - case BrowserPageCrashedEventCategoryPage: + case PageNavigateToHistoryEntry: return true default: return false } } -// Defines values for BrowserPageCrashedEventType. +// Defines values for BrowserCdpPagePrintToPdfCommandDataMethod. const ( - PageCrashed BrowserPageCrashedEventType = "page_crashed" + PagePrintToPDF BrowserCdpPagePrintToPdfCommandDataMethod = "Page.printToPDF" ) -// Valid indicates whether the value is a known member of the BrowserPageCrashedEventType enum. -func (e BrowserPageCrashedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPagePrintToPdfCommandDataMethod enum. +func (e BrowserCdpPagePrintToPdfCommandDataMethod) Valid() bool { switch e { - case PageCrashed: + case PagePrintToPDF: return true default: return false } } -// Defines values for BrowserPageDomContentLoadedEventCategory. +// Defines values for BrowserCdpPageReloadCommandDataMethod. const ( - BrowserPageDomContentLoadedEventCategoryPage BrowserPageDomContentLoadedEventCategory = "page" + PageReload BrowserCdpPageReloadCommandDataMethod = "Page.reload" ) -// Valid indicates whether the value is a known member of the BrowserPageDomContentLoadedEventCategory enum. -func (e BrowserPageDomContentLoadedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageReloadCommandDataMethod enum. +func (e BrowserCdpPageReloadCommandDataMethod) Valid() bool { switch e { - case BrowserPageDomContentLoadedEventCategoryPage: + case PageReload: return true default: return false } } -// Defines values for BrowserPageDomContentLoadedEventType. +// Defines values for BrowserCdpPageSetWebLifecycleStateCommandDataMethod. const ( - PageDomContentLoaded BrowserPageDomContentLoadedEventType = "page_dom_content_loaded" + PageSetWebLifecycleState BrowserCdpPageSetWebLifecycleStateCommandDataMethod = "Page.setWebLifecycleState" ) -// Valid indicates whether the value is a known member of the BrowserPageDomContentLoadedEventType enum. -func (e BrowserPageDomContentLoadedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageSetWebLifecycleStateCommandDataMethod enum. +func (e BrowserCdpPageSetWebLifecycleStateCommandDataMethod) Valid() bool { switch e { - case PageDomContentLoaded: + case PageSetWebLifecycleState: return true default: return false } } -// Defines values for BrowserPageLayoutSettledEventCategory. +// Defines values for BrowserCdpPageStartScreencastCommandDataMethod. const ( - BrowserPageLayoutSettledEventCategoryPage BrowserPageLayoutSettledEventCategory = "page" + PageStartScreencast BrowserCdpPageStartScreencastCommandDataMethod = "Page.startScreencast" ) -// Valid indicates whether the value is a known member of the BrowserPageLayoutSettledEventCategory enum. -func (e BrowserPageLayoutSettledEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageStartScreencastCommandDataMethod enum. +func (e BrowserCdpPageStartScreencastCommandDataMethod) Valid() bool { switch e { - case BrowserPageLayoutSettledEventCategoryPage: + case PageStartScreencast: return true default: return false } } -// Defines values for BrowserPageLayoutSettledEventType. +// Defines values for BrowserCdpPageStopLoadingCommandDataMethod. const ( - PageLayoutSettled BrowserPageLayoutSettledEventType = "page_layout_settled" + PageStopLoading BrowserCdpPageStopLoadingCommandDataMethod = "Page.stopLoading" ) -// Valid indicates whether the value is a known member of the BrowserPageLayoutSettledEventType enum. -func (e BrowserPageLayoutSettledEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageStopLoadingCommandDataMethod enum. +func (e BrowserCdpPageStopLoadingCommandDataMethod) Valid() bool { switch e { - case PageLayoutSettled: + case PageStopLoading: return true default: return false } } -// Defines values for BrowserPageLayoutShiftEventCategory. +// Defines values for BrowserCdpPageStopScreencastCommandDataMethod. const ( - BrowserPageLayoutShiftEventCategoryPage BrowserPageLayoutShiftEventCategory = "page" + PageStopScreencast BrowserCdpPageStopScreencastCommandDataMethod = "Page.stopScreencast" ) -// Valid indicates whether the value is a known member of the BrowserPageLayoutShiftEventCategory enum. -func (e BrowserPageLayoutShiftEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPageStopScreencastCommandDataMethod enum. +func (e BrowserCdpPageStopScreencastCommandDataMethod) Valid() bool { switch e { - case BrowserPageLayoutShiftEventCategoryPage: + case PageStopScreencast: return true default: return false } } -// Defines values for BrowserPageLayoutShiftEventType. +// Defines values for BrowserCdpPdfTransferMode. const ( - PageLayoutShift BrowserPageLayoutShiftEventType = "page_layout_shift" + BrowserCdpPdfTransferModeOther BrowserCdpPdfTransferMode = "other" + BrowserCdpPdfTransferModeReturnAsBase64 BrowserCdpPdfTransferMode = "ReturnAsBase64" + BrowserCdpPdfTransferModeReturnAsStream BrowserCdpPdfTransferMode = "ReturnAsStream" ) -// Valid indicates whether the value is a known member of the BrowserPageLayoutShiftEventType enum. -func (e BrowserPageLayoutShiftEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPdfTransferMode enum. +func (e BrowserCdpPdfTransferMode) Valid() bool { switch e { - case PageLayoutShift: + case BrowserCdpPdfTransferModeOther: + return true + case BrowserCdpPdfTransferModeReturnAsBase64: + return true + case BrowserCdpPdfTransferModeReturnAsStream: return true default: return false } } -// Defines values for BrowserPageLcpEventCategory. +// Defines values for BrowserCdpPointerType. const ( - BrowserPageLcpEventCategoryPage BrowserPageLcpEventCategory = "page" + BrowserCdpPointerTypeMouse BrowserCdpPointerType = "mouse" + BrowserCdpPointerTypeOther BrowserCdpPointerType = "other" + BrowserCdpPointerTypePen BrowserCdpPointerType = "pen" ) -// Valid indicates whether the value is a known member of the BrowserPageLcpEventCategory enum. -func (e BrowserPageLcpEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpPointerType enum. +func (e BrowserCdpPointerType) Valid() bool { switch e { - case BrowserPageLcpEventCategoryPage: + case BrowserCdpPointerTypeMouse: + return true + case BrowserCdpPointerTypeOther: + return true + case BrowserCdpPointerTypePen: return true default: return false } } -// Defines values for BrowserPageLcpEventType. +// Defines values for BrowserCdpReferrerPolicy. const ( - PageLcp BrowserPageLcpEventType = "page_lcp" + BrowserCdpReferrerPolicyNoReferrer BrowserCdpReferrerPolicy = "noReferrer" + BrowserCdpReferrerPolicyNoReferrerWhenDowngrade BrowserCdpReferrerPolicy = "noReferrerWhenDowngrade" + BrowserCdpReferrerPolicyOrigin BrowserCdpReferrerPolicy = "origin" + BrowserCdpReferrerPolicyOriginWhenCrossOrigin BrowserCdpReferrerPolicy = "originWhenCrossOrigin" + BrowserCdpReferrerPolicyOther BrowserCdpReferrerPolicy = "other" + BrowserCdpReferrerPolicySameOrigin BrowserCdpReferrerPolicy = "sameOrigin" + BrowserCdpReferrerPolicyStrictOrigin BrowserCdpReferrerPolicy = "strictOrigin" + BrowserCdpReferrerPolicyStrictOriginWhenCrossOrigin BrowserCdpReferrerPolicy = "strictOriginWhenCrossOrigin" + BrowserCdpReferrerPolicyUnsafeUrl BrowserCdpReferrerPolicy = "unsafeUrl" ) -// Valid indicates whether the value is a known member of the BrowserPageLcpEventType enum. -func (e BrowserPageLcpEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpReferrerPolicy enum. +func (e BrowserCdpReferrerPolicy) Valid() bool { switch e { - case PageLcp: + case BrowserCdpReferrerPolicyNoReferrer: + return true + case BrowserCdpReferrerPolicyNoReferrerWhenDowngrade: + return true + case BrowserCdpReferrerPolicyOrigin: + return true + case BrowserCdpReferrerPolicyOriginWhenCrossOrigin: + return true + case BrowserCdpReferrerPolicyOther: + return true + case BrowserCdpReferrerPolicySameOrigin: + return true + case BrowserCdpReferrerPolicyStrictOrigin: + return true + case BrowserCdpReferrerPolicyStrictOriginWhenCrossOrigin: + return true + case BrowserCdpReferrerPolicyUnsafeUrl: return true default: return false } } -// Defines values for BrowserPageLoadEventCategory. +// Defines values for BrowserCdpScreencastFormat. const ( - BrowserPageLoadEventCategoryPage BrowserPageLoadEventCategory = "page" + BrowserCdpScreencastFormatJpeg BrowserCdpScreencastFormat = "jpeg" + BrowserCdpScreencastFormatOther BrowserCdpScreencastFormat = "other" + BrowserCdpScreencastFormatPng BrowserCdpScreencastFormat = "png" ) -// Valid indicates whether the value is a known member of the BrowserPageLoadEventCategory enum. -func (e BrowserPageLoadEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpScreencastFormat enum. +func (e BrowserCdpScreencastFormat) Valid() bool { switch e { - case BrowserPageLoadEventCategoryPage: + case BrowserCdpScreencastFormatJpeg: + return true + case BrowserCdpScreencastFormatOther: + return true + case BrowserCdpScreencastFormatPng: return true default: return false } } -// Defines values for BrowserPageLoadEventType. +// Defines values for BrowserCdpScreenshotFormat. const ( - PageLoad BrowserPageLoadEventType = "page_load" + BrowserCdpScreenshotFormatJpeg BrowserCdpScreenshotFormat = "jpeg" + BrowserCdpScreenshotFormatOther BrowserCdpScreenshotFormat = "other" + BrowserCdpScreenshotFormatPng BrowserCdpScreenshotFormat = "png" + BrowserCdpScreenshotFormatWebp BrowserCdpScreenshotFormat = "webp" ) -// Valid indicates whether the value is a known member of the BrowserPageLoadEventType enum. -func (e BrowserPageLoadEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpScreenshotFormat enum. +func (e BrowserCdpScreenshotFormat) Valid() bool { switch e { - case PageLoad: + case BrowserCdpScreenshotFormatJpeg: + return true + case BrowserCdpScreenshotFormatOther: + return true + case BrowserCdpScreenshotFormatPng: + return true + case BrowserCdpScreenshotFormatWebp: return true default: return false } } -// Defines values for BrowserPageNavigationEventCategory. +// Defines values for BrowserCdpSnapshotFormat. const ( - BrowserPageNavigationEventCategoryPage BrowserPageNavigationEventCategory = "page" + BrowserCdpSnapshotFormatMhtml BrowserCdpSnapshotFormat = "mhtml" + BrowserCdpSnapshotFormatOther BrowserCdpSnapshotFormat = "other" ) -// Valid indicates whether the value is a known member of the BrowserPageNavigationEventCategory enum. -func (e BrowserPageNavigationEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpSnapshotFormat enum. +func (e BrowserCdpSnapshotFormat) Valid() bool { switch e { - case BrowserPageNavigationEventCategoryPage: + case BrowserCdpSnapshotFormatMhtml: + return true + case BrowserCdpSnapshotFormatOther: return true default: return false } } -// Defines values for BrowserPageNavigationEventType. +// Defines values for BrowserCdpTargetActivateTargetCommandDataMethod. const ( - PageNavigation BrowserPageNavigationEventType = "page_navigation" + TargetActivateTarget BrowserCdpTargetActivateTargetCommandDataMethod = "Target.activateTarget" ) -// Valid indicates whether the value is a known member of the BrowserPageNavigationEventType enum. -func (e BrowserPageNavigationEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTargetActivateTargetCommandDataMethod enum. +func (e BrowserCdpTargetActivateTargetCommandDataMethod) Valid() bool { switch e { - case PageNavigation: + case TargetActivateTarget: return true default: return false } } -// Defines values for BrowserPageNavigationSettledEventCategory. +// Defines values for BrowserCdpTargetCloseTargetCommandDataMethod. const ( - BrowserPageNavigationSettledEventCategoryPage BrowserPageNavigationSettledEventCategory = "page" + TargetCloseTarget BrowserCdpTargetCloseTargetCommandDataMethod = "Target.closeTarget" ) -// Valid indicates whether the value is a known member of the BrowserPageNavigationSettledEventCategory enum. -func (e BrowserPageNavigationSettledEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTargetCloseTargetCommandDataMethod enum. +func (e BrowserCdpTargetCloseTargetCommandDataMethod) Valid() bool { switch e { - case BrowserPageNavigationSettledEventCategoryPage: + case TargetCloseTarget: return true default: return false } } -// Defines values for BrowserPageNavigationSettledEventType. +// Defines values for BrowserCdpTargetCreateBrowserContextCommandDataMethod. const ( - PageNavigationSettled BrowserPageNavigationSettledEventType = "page_navigation_settled" + TargetCreateBrowserContext BrowserCdpTargetCreateBrowserContextCommandDataMethod = "Target.createBrowserContext" ) -// Valid indicates whether the value is a known member of the BrowserPageNavigationSettledEventType enum. -func (e BrowserPageNavigationSettledEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTargetCreateBrowserContextCommandDataMethod enum. +func (e BrowserCdpTargetCreateBrowserContextCommandDataMethod) Valid() bool { switch e { - case PageNavigationSettled: + case TargetCreateBrowserContext: return true default: return false } } -// Defines values for BrowserPageTabOpenedEventCategory. +// Defines values for BrowserCdpTargetCreateTargetCommandDataMethod. const ( - Page BrowserPageTabOpenedEventCategory = "page" + TargetCreateTarget BrowserCdpTargetCreateTargetCommandDataMethod = "Target.createTarget" ) -// Valid indicates whether the value is a known member of the BrowserPageTabOpenedEventCategory enum. -func (e BrowserPageTabOpenedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTargetCreateTargetCommandDataMethod enum. +func (e BrowserCdpTargetCreateTargetCommandDataMethod) Valid() bool { switch e { - case Page: + case TargetCreateTarget: return true default: return false } } -// Defines values for BrowserPageTabOpenedEventType. +// Defines values for BrowserCdpTargetDisposeBrowserContextCommandDataMethod. const ( - PageTabOpened BrowserPageTabOpenedEventType = "page_tab_opened" + TargetDisposeBrowserContext BrowserCdpTargetDisposeBrowserContextCommandDataMethod = "Target.disposeBrowserContext" ) -// Valid indicates whether the value is a known member of the BrowserPageTabOpenedEventType enum. -func (e BrowserPageTabOpenedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTargetDisposeBrowserContextCommandDataMethod enum. +func (e BrowserCdpTargetDisposeBrowserContextCommandDataMethod) Valid() bool { switch e { - case PageTabOpened: + case TargetDisposeBrowserContext: return true default: return false } } -// Defines values for BrowserPlatformApiCallEventCategory. +// Defines values for BrowserCdpTargetOpenDevToolsCommandDataMethod. const ( - Platform BrowserPlatformApiCallEventCategory = "platform" + TargetOpenDevTools BrowserCdpTargetOpenDevToolsCommandDataMethod = "Target.openDevTools" ) -// Valid indicates whether the value is a known member of the BrowserPlatformApiCallEventCategory enum. -func (e BrowserPlatformApiCallEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTargetOpenDevToolsCommandDataMethod enum. +func (e BrowserCdpTargetOpenDevToolsCommandDataMethod) Valid() bool { switch e { - case Platform: + case TargetOpenDevTools: return true default: return false } } -// Defines values for BrowserPlatformApiCallEventType. +// Defines values for BrowserCdpTouchEventType. const ( - PlatformApiCall BrowserPlatformApiCallEventType = "platform_api_call" + BrowserCdpTouchEventTypeOther BrowserCdpTouchEventType = "other" + BrowserCdpTouchEventTypeTouchCancel BrowserCdpTouchEventType = "touchCancel" + BrowserCdpTouchEventTypeTouchEnd BrowserCdpTouchEventType = "touchEnd" + BrowserCdpTouchEventTypeTouchMove BrowserCdpTouchEventType = "touchMove" + BrowserCdpTouchEventTypeTouchStart BrowserCdpTouchEventType = "touchStart" ) -// Valid indicates whether the value is a known member of the BrowserPlatformApiCallEventType enum. -func (e BrowserPlatformApiCallEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTouchEventType enum. +func (e BrowserCdpTouchEventType) Valid() bool { switch e { - case PlatformApiCall: + case BrowserCdpTouchEventTypeOther: + return true + case BrowserCdpTouchEventTypeTouchCancel: + return true + case BrowserCdpTouchEventTypeTouchEnd: + return true + case BrowserCdpTouchEventTypeTouchMove: + return true + case BrowserCdpTouchEventTypeTouchStart: return true default: return false } } -// Defines values for BrowserProxyErrorEventCategory. +// Defines values for BrowserCdpTransitionType. const ( - Network BrowserProxyErrorEventCategory = "network" + BrowserCdpTransitionTypeAddressBar BrowserCdpTransitionType = "address_bar" + BrowserCdpTransitionTypeAutoBookmark BrowserCdpTransitionType = "auto_bookmark" + BrowserCdpTransitionTypeAutoSubframe BrowserCdpTransitionType = "auto_subframe" + BrowserCdpTransitionTypeAutoToplevel BrowserCdpTransitionType = "auto_toplevel" + BrowserCdpTransitionTypeFormSubmit BrowserCdpTransitionType = "form_submit" + BrowserCdpTransitionTypeGenerated BrowserCdpTransitionType = "generated" + BrowserCdpTransitionTypeKeyword BrowserCdpTransitionType = "keyword" + BrowserCdpTransitionTypeKeywordGenerated BrowserCdpTransitionType = "keyword_generated" + BrowserCdpTransitionTypeLink BrowserCdpTransitionType = "link" + BrowserCdpTransitionTypeManualSubframe BrowserCdpTransitionType = "manual_subframe" + BrowserCdpTransitionTypeOther BrowserCdpTransitionType = "other" + BrowserCdpTransitionTypeReload BrowserCdpTransitionType = "reload" + BrowserCdpTransitionTypeTyped BrowserCdpTransitionType = "typed" ) -// Valid indicates whether the value is a known member of the BrowserProxyErrorEventCategory enum. -func (e BrowserProxyErrorEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpTransitionType enum. +func (e BrowserCdpTransitionType) Valid() bool { switch e { - case Network: + case BrowserCdpTransitionTypeAddressBar: + return true + case BrowserCdpTransitionTypeAutoBookmark: + return true + case BrowserCdpTransitionTypeAutoSubframe: + return true + case BrowserCdpTransitionTypeAutoToplevel: + return true + case BrowserCdpTransitionTypeFormSubmit: + return true + case BrowserCdpTransitionTypeGenerated: + return true + case BrowserCdpTransitionTypeKeyword: + return true + case BrowserCdpTransitionTypeKeywordGenerated: + return true + case BrowserCdpTransitionTypeLink: + return true + case BrowserCdpTransitionTypeManualSubframe: + return true + case BrowserCdpTransitionTypeOther: + return true + case BrowserCdpTransitionTypeReload: + return true + case BrowserCdpTransitionTypeTyped: return true default: return false } } -// Defines values for BrowserProxyErrorEventType. +// Defines values for BrowserCdpWebLifecycleState. const ( - ProxyError BrowserProxyErrorEventType = "proxy_error" + BrowserCdpWebLifecycleStateActive BrowserCdpWebLifecycleState = "active" + BrowserCdpWebLifecycleStateFrozen BrowserCdpWebLifecycleState = "frozen" + BrowserCdpWebLifecycleStateOther BrowserCdpWebLifecycleState = "other" ) -// Valid indicates whether the value is a known member of the BrowserProxyErrorEventType enum. -func (e BrowserProxyErrorEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpWebLifecycleState enum. +func (e BrowserCdpWebLifecycleState) Valid() bool { switch e { - case ProxyError: + case BrowserCdpWebLifecycleStateActive: + return true + case BrowserCdpWebLifecycleStateFrozen: + return true + case BrowserCdpWebLifecycleStateOther: return true default: return false } } -// Defines values for BrowserProxyErrorEventDataCode. +// Defines values for BrowserCdpWindowState. const ( - DestinationBlocked BrowserProxyErrorEventDataCode = "destination_blocked" - ProviderBlacklisted BrowserProxyErrorEventDataCode = "provider_blacklisted" - ProviderUnreachable BrowserProxyErrorEventDataCode = "provider_unreachable" - ProxyUnavailable BrowserProxyErrorEventDataCode = "proxy_unavailable" - UpstreamConnectFailed BrowserProxyErrorEventDataCode = "upstream_connect_failed" - UpstreamDnsFailure BrowserProxyErrorEventDataCode = "upstream_dns_failure" - UpstreamTimeout BrowserProxyErrorEventDataCode = "upstream_timeout" + BrowserCdpWindowStateFullscreen BrowserCdpWindowState = "fullscreen" + BrowserCdpWindowStateMaximized BrowserCdpWindowState = "maximized" + BrowserCdpWindowStateMinimized BrowserCdpWindowState = "minimized" + BrowserCdpWindowStateNormal BrowserCdpWindowState = "normal" + BrowserCdpWindowStateOther BrowserCdpWindowState = "other" ) -// Valid indicates whether the value is a known member of the BrowserProxyErrorEventDataCode enum. -func (e BrowserProxyErrorEventDataCode) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserCdpWindowState enum. +func (e BrowserCdpWindowState) Valid() bool { switch e { - case DestinationBlocked: - return true - case ProviderBlacklisted: - return true - case ProviderUnreachable: + case BrowserCdpWindowStateFullscreen: return true - case ProxyUnavailable: + case BrowserCdpWindowStateMaximized: return true - case UpstreamConnectFailed: + case BrowserCdpWindowStateMinimized: return true - case UpstreamDnsFailure: + case BrowserCdpWindowStateNormal: return true - case UpstreamTimeout: + case BrowserCdpWindowStateOther: return true default: return false } } -// Defines values for BrowserServiceCrashedEventCategory. +// Defines values for BrowserConsoleErrorEventCategory. const ( - BrowserServiceCrashedEventCategorySystem BrowserServiceCrashedEventCategory = "system" + BrowserConsoleErrorEventCategoryConsole BrowserConsoleErrorEventCategory = "console" ) -// Valid indicates whether the value is a known member of the BrowserServiceCrashedEventCategory enum. -func (e BrowserServiceCrashedEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserConsoleErrorEventCategory enum. +func (e BrowserConsoleErrorEventCategory) Valid() bool { switch e { - case BrowserServiceCrashedEventCategorySystem: + case BrowserConsoleErrorEventCategoryConsole: return true default: return false } } -// Defines values for BrowserServiceCrashedEventType. +// Defines values for BrowserConsoleErrorEventType. const ( - ServiceCrashed BrowserServiceCrashedEventType = "service_crashed" + ConsoleError BrowserConsoleErrorEventType = "console_error" ) -// Valid indicates whether the value is a known member of the BrowserServiceCrashedEventType enum. -func (e BrowserServiceCrashedEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserConsoleErrorEventType enum. +func (e BrowserConsoleErrorEventType) Valid() bool { switch e { - case ServiceCrashed: + case ConsoleError: return true default: return false } } -// Defines values for BrowserServiceCrashedEventDataPhase. +// Defines values for BrowserConsoleLogEventCategory. const ( - BrowserServiceCrashedEventDataPhaseGaveUp BrowserServiceCrashedEventDataPhase = "gave_up" - BrowserServiceCrashedEventDataPhaseRunning BrowserServiceCrashedEventDataPhase = "running" - BrowserServiceCrashedEventDataPhaseStartup BrowserServiceCrashedEventDataPhase = "startup" + BrowserConsoleLogEventCategoryConsole BrowserConsoleLogEventCategory = "console" ) -// Valid indicates whether the value is a known member of the BrowserServiceCrashedEventDataPhase enum. -func (e BrowserServiceCrashedEventDataPhase) Valid() bool { - switch e { - case BrowserServiceCrashedEventDataPhaseGaveUp: - return true - case BrowserServiceCrashedEventDataPhaseRunning: - return true - case BrowserServiceCrashedEventDataPhaseStartup: +// Valid indicates whether the value is a known member of the BrowserConsoleLogEventCategory enum. +func (e BrowserConsoleLogEventCategory) Valid() bool { + switch e { + case BrowserConsoleLogEventCategoryConsole: return true default: return false } } -// Defines values for BrowserSystemOomKillEventCategory. +// Defines values for BrowserConsoleLogEventType. const ( - BrowserSystemOomKillEventCategorySystem BrowserSystemOomKillEventCategory = "system" + ConsoleLog BrowserConsoleLogEventType = "console_log" ) -// Valid indicates whether the value is a known member of the BrowserSystemOomKillEventCategory enum. -func (e BrowserSystemOomKillEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserConsoleLogEventType enum. +func (e BrowserConsoleLogEventType) Valid() bool { switch e { - case BrowserSystemOomKillEventCategorySystem: + case ConsoleLog: return true default: return false } } -// Defines values for BrowserSystemOomKillEventType. +// Defines values for BrowserEventSourceKind. const ( - SystemOomKill BrowserSystemOomKillEventType = "system_oom_kill" + Cdp BrowserEventSourceKind = "cdp" + Extension BrowserEventSourceKind = "extension" + KernelApi BrowserEventSourceKind = "kernel_api" + LocalProcess BrowserEventSourceKind = "local_process" ) -// Valid indicates whether the value is a known member of the BrowserSystemOomKillEventType enum. -func (e BrowserSystemOomKillEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserEventSourceKind enum. +func (e BrowserEventSourceKind) Valid() bool { switch e { - case SystemOomKill: + case Cdp: + return true + case Extension: + return true + case KernelApi: + return true + case LocalProcess: return true default: return false } } -// Defines values for BrowserSystemOomKillEventDataConstraint. +// Defines values for BrowserInteractionClickEventCategory. const ( - Cpuset BrowserSystemOomKillEventDataConstraint = "cpuset" - Memcg BrowserSystemOomKillEventDataConstraint = "memcg" - MemoryPolicy BrowserSystemOomKillEventDataConstraint = "memory_policy" - None BrowserSystemOomKillEventDataConstraint = "none" + BrowserInteractionClickEventCategoryInteraction BrowserInteractionClickEventCategory = "interaction" ) -// Valid indicates whether the value is a known member of the BrowserSystemOomKillEventDataConstraint enum. -func (e BrowserSystemOomKillEventDataConstraint) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserInteractionClickEventCategory enum. +func (e BrowserInteractionClickEventCategory) Valid() bool { switch e { - case Cpuset: - return true - case Memcg: - return true - case MemoryPolicy: - return true - case None: + case BrowserInteractionClickEventCategoryInteraction: return true default: return false } } -// Defines values for BrowserTargetType. +// Defines values for BrowserInteractionClickEventType. const ( - BrowserTargetTypeBackgroundPage BrowserTargetType = "background_page" - BrowserTargetTypeOther BrowserTargetType = "other" - BrowserTargetTypePage BrowserTargetType = "page" - BrowserTargetTypeServiceWorker BrowserTargetType = "service_worker" - BrowserTargetTypeSharedWorker BrowserTargetType = "shared_worker" + InteractionClick BrowserInteractionClickEventType = "interaction_click" ) -// Valid indicates whether the value is a known member of the BrowserTargetType enum. -func (e BrowserTargetType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserInteractionClickEventType enum. +func (e BrowserInteractionClickEventType) Valid() bool { switch e { - case BrowserTargetTypeBackgroundPage: - return true - case BrowserTargetTypeOther: - return true - case BrowserTargetTypePage: - return true - case BrowserTargetTypeServiceWorker: - return true - case BrowserTargetTypeSharedWorker: + case InteractionClick: return true default: return false } } -// Defines values for ChromiumConfigureErrorPhase. +// Defines values for BrowserInteractionKeyEventCategory. const ( - ConfigurePhase ChromiumConfigureErrorPhase = "configure_phase" - NavigatePhase ChromiumConfigureErrorPhase = "navigate_phase" + BrowserInteractionKeyEventCategoryInteraction BrowserInteractionKeyEventCategory = "interaction" ) -// Valid indicates whether the value is a known member of the ChromiumConfigureErrorPhase enum. -func (e ChromiumConfigureErrorPhase) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserInteractionKeyEventCategory enum. +func (e BrowserInteractionKeyEventCategory) Valid() bool { switch e { - case ConfigurePhase: - return true - case NavigatePhase: + case BrowserInteractionKeyEventCategoryInteraction: return true default: return false } } -// Defines values for ChromiumConfigureErrorStep. +// Defines values for BrowserInteractionKeyEventType. const ( - ChromePolicies ChromiumConfigureErrorStep = "chrome_policies" - ChromiumFlags ChromiumConfigureErrorStep = "chromium_flags" - Display ChromiumConfigureErrorStep = "display" - Extensions ChromiumConfigureErrorStep = "extensions" - Profile ChromiumConfigureErrorStep = "profile" - StartChromium ChromiumConfigureErrorStep = "start_chromium" - StopChromium ChromiumConfigureErrorStep = "stop_chromium" + InteractionKey BrowserInteractionKeyEventType = "interaction_key" ) -// Valid indicates whether the value is a known member of the ChromiumConfigureErrorStep enum. -func (e ChromiumConfigureErrorStep) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserInteractionKeyEventType enum. +func (e BrowserInteractionKeyEventType) Valid() bool { switch e { - case ChromePolicies: - return true - case ChromiumFlags: - return true - case Display: - return true - case Extensions: - return true - case Profile: - return true - case StartChromium: - return true - case StopChromium: + case InteractionKey: return true default: return false } } -// Defines values for ClickMouseRequestButton. +// Defines values for BrowserInteractionScrollSettledEventCategory. const ( - ClickMouseRequestButtonBack ClickMouseRequestButton = "back" - ClickMouseRequestButtonForward ClickMouseRequestButton = "forward" - ClickMouseRequestButtonLeft ClickMouseRequestButton = "left" - ClickMouseRequestButtonMiddle ClickMouseRequestButton = "middle" - ClickMouseRequestButtonRight ClickMouseRequestButton = "right" + BrowserInteractionScrollSettledEventCategoryInteraction BrowserInteractionScrollSettledEventCategory = "interaction" ) -// Valid indicates whether the value is a known member of the ClickMouseRequestButton enum. -func (e ClickMouseRequestButton) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserInteractionScrollSettledEventCategory enum. +func (e BrowserInteractionScrollSettledEventCategory) Valid() bool { switch e { - case ClickMouseRequestButtonBack: - return true - case ClickMouseRequestButtonForward: - return true - case ClickMouseRequestButtonLeft: - return true - case ClickMouseRequestButtonMiddle: - return true - case ClickMouseRequestButtonRight: + case BrowserInteractionScrollSettledEventCategoryInteraction: return true default: return false } } -// Defines values for ClickMouseRequestClickType. +// Defines values for BrowserInteractionScrollSettledEventType. const ( - Click ClickMouseRequestClickType = "click" - Down ClickMouseRequestClickType = "down" - Up ClickMouseRequestClickType = "up" + InteractionScrollSettled BrowserInteractionScrollSettledEventType = "interaction_scroll_settled" ) -// Valid indicates whether the value is a known member of the ClickMouseRequestClickType enum. -func (e ClickMouseRequestClickType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserInteractionScrollSettledEventType enum. +func (e BrowserInteractionScrollSettledEventType) Valid() bool { switch e { - case Click: - return true - case Down: - return true - case Up: + case InteractionScrollSettled: return true default: return false } } -// Defines values for ComputerActionType. +// Defines values for BrowserLiveViewConnectEventCategory. const ( - ClickMouse ComputerActionType = "click_mouse" - DragMouse ComputerActionType = "drag_mouse" - MoveMouse ComputerActionType = "move_mouse" - PressKey ComputerActionType = "press_key" - Scroll ComputerActionType = "scroll" - SetCursor ComputerActionType = "set_cursor" - Sleep ComputerActionType = "sleep" - TypeText ComputerActionType = "type_text" + BrowserLiveViewConnectEventCategoryConnection BrowserLiveViewConnectEventCategory = "connection" ) -// Valid indicates whether the value is a known member of the ComputerActionType enum. -func (e ComputerActionType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserLiveViewConnectEventCategory enum. +func (e BrowserLiveViewConnectEventCategory) Valid() bool { switch e { - case ClickMouse: - return true - case DragMouse: - return true - case MoveMouse: - return true - case PressKey: - return true - case Scroll: - return true - case SetCursor: - return true - case Sleep: - return true - case TypeText: + case BrowserLiveViewConnectEventCategoryConnection: return true default: return false } } -// Defines values for DragMouseRequestButton. +// Defines values for BrowserLiveViewConnectEventType. const ( - DragMouseRequestButtonLeft DragMouseRequestButton = "left" - DragMouseRequestButtonMiddle DragMouseRequestButton = "middle" - DragMouseRequestButtonRight DragMouseRequestButton = "right" + LiveViewConnect BrowserLiveViewConnectEventType = "live_view_connect" ) -// Valid indicates whether the value is a known member of the DragMouseRequestButton enum. -func (e DragMouseRequestButton) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserLiveViewConnectEventType enum. +func (e BrowserLiveViewConnectEventType) Valid() bool { switch e { - case DragMouseRequestButtonLeft: - return true - case DragMouseRequestButtonMiddle: - return true - case DragMouseRequestButtonRight: + case LiveViewConnect: return true default: return false } } -// Defines values for FileSystemEventType. +// Defines values for BrowserLiveViewDisconnectEventCategory. const ( - CREATE FileSystemEventType = "CREATE" - DELETE FileSystemEventType = "DELETE" - RENAME FileSystemEventType = "RENAME" - WRITE FileSystemEventType = "WRITE" + BrowserLiveViewDisconnectEventCategoryConnection BrowserLiveViewDisconnectEventCategory = "connection" ) -// Valid indicates whether the value is a known member of the FileSystemEventType enum. -func (e FileSystemEventType) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserLiveViewDisconnectEventCategory enum. +func (e BrowserLiveViewDisconnectEventCategory) Valid() bool { switch e { - case CREATE: - return true - case DELETE: - return true - case RENAME: - return true - case WRITE: + case BrowserLiveViewDisconnectEventCategoryConnection: return true default: return false } } -// Defines values for PatchDisplayRequestRefreshRate. +// Defines values for BrowserLiveViewDisconnectEventType. const ( - N10 PatchDisplayRequestRefreshRate = 10 - N25 PatchDisplayRequestRefreshRate = 25 - N30 PatchDisplayRequestRefreshRate = 30 - N60 PatchDisplayRequestRefreshRate = 60 + LiveViewDisconnect BrowserLiveViewDisconnectEventType = "live_view_disconnect" ) -// Valid indicates whether the value is a known member of the PatchDisplayRequestRefreshRate enum. -func (e PatchDisplayRequestRefreshRate) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserLiveViewDisconnectEventType enum. +func (e BrowserLiveViewDisconnectEventType) Valid() bool { switch e { - case N10: - return true - case N25: - return true - case N30: - return true - case N60: + case LiveViewDisconnect: return true default: return false } } -// Defines values for ProcessKillRequestSignal. +// Defines values for BrowserMonitorDisconnectedEventCategory. const ( - HUP ProcessKillRequestSignal = "HUP" - INT ProcessKillRequestSignal = "INT" - KILL ProcessKillRequestSignal = "KILL" - TERM ProcessKillRequestSignal = "TERM" + BrowserMonitorDisconnectedEventCategoryMonitor BrowserMonitorDisconnectedEventCategory = "monitor" ) -// Valid indicates whether the value is a known member of the ProcessKillRequestSignal enum. -func (e ProcessKillRequestSignal) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventCategory enum. +func (e BrowserMonitorDisconnectedEventCategory) Valid() bool { switch e { - case HUP: - return true - case INT: - return true - case KILL: - return true - case TERM: + case BrowserMonitorDisconnectedEventCategoryMonitor: return true default: return false } } -// Defines values for ProcessStatusState. +// Defines values for BrowserMonitorDisconnectedEventType. const ( - ProcessStatusStateExited ProcessStatusState = "exited" - ProcessStatusStateRunning ProcessStatusState = "running" + MonitorDisconnected BrowserMonitorDisconnectedEventType = "monitor_disconnected" ) -// Valid indicates whether the value is a known member of the ProcessStatusState enum. -func (e ProcessStatusState) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventType enum. +func (e BrowserMonitorDisconnectedEventType) Valid() bool { switch e { - case ProcessStatusStateExited: - return true - case ProcessStatusStateRunning: + case MonitorDisconnected: return true default: return false } } -// Defines values for ProcessStreamEventEvent. +// Defines values for BrowserMonitorDisconnectedEventDataReason. const ( - Exit ProcessStreamEventEvent = "exit" + ChromeRestarted BrowserMonitorDisconnectedEventDataReason = "chrome_restarted" ) -// Valid indicates whether the value is a known member of the ProcessStreamEventEvent enum. -func (e ProcessStreamEventEvent) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventDataReason enum. +func (e BrowserMonitorDisconnectedEventDataReason) Valid() bool { switch e { - case Exit: + case ChromeRestarted: return true default: return false } } -// Defines values for ProcessStreamEventStream. +// Defines values for BrowserMonitorInitFailedEventCategory. const ( - Stderr ProcessStreamEventStream = "stderr" - Stdout ProcessStreamEventStream = "stdout" + BrowserMonitorInitFailedEventCategoryMonitor BrowserMonitorInitFailedEventCategory = "monitor" ) -// Valid indicates whether the value is a known member of the ProcessStreamEventStream enum. -func (e ProcessStreamEventStream) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorInitFailedEventCategory enum. +func (e BrowserMonitorInitFailedEventCategory) Valid() bool { switch e { - case Stderr: - return true - case Stdout: + case BrowserMonitorInitFailedEventCategoryMonitor: return true default: return false } } -// Defines values for PublishEventRequestCategory. +// Defines values for BrowserMonitorInitFailedEventType. const ( - PublishEventRequestCategoryCaptcha PublishEventRequestCategory = "captcha" - PublishEventRequestCategoryConnection PublishEventRequestCategory = "connection" - PublishEventRequestCategoryConsole PublishEventRequestCategory = "console" - PublishEventRequestCategoryControl PublishEventRequestCategory = "control" - PublishEventRequestCategoryInteraction PublishEventRequestCategory = "interaction" - PublishEventRequestCategoryMonitor PublishEventRequestCategory = "monitor" - PublishEventRequestCategoryNetwork PublishEventRequestCategory = "network" - PublishEventRequestCategoryPage PublishEventRequestCategory = "page" - PublishEventRequestCategoryPlatform PublishEventRequestCategory = "platform" - PublishEventRequestCategoryScreenshot PublishEventRequestCategory = "screenshot" - PublishEventRequestCategorySystem PublishEventRequestCategory = "system" + MonitorInitFailed BrowserMonitorInitFailedEventType = "monitor_init_failed" ) -// Valid indicates whether the value is a known member of the PublishEventRequestCategory enum. -func (e PublishEventRequestCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorInitFailedEventType enum. +func (e BrowserMonitorInitFailedEventType) Valid() bool { switch e { - case PublishEventRequestCategoryCaptcha: - return true - case PublishEventRequestCategoryConnection: - return true - case PublishEventRequestCategoryConsole: - return true - case PublishEventRequestCategoryControl: - return true - case PublishEventRequestCategoryInteraction: - return true - case PublishEventRequestCategoryMonitor: - return true - case PublishEventRequestCategoryNetwork: - return true - case PublishEventRequestCategoryPage: - return true - case PublishEventRequestCategoryPlatform: - return true - case PublishEventRequestCategoryScreenshot: - return true - case PublishEventRequestCategorySystem: + case MonitorInitFailed: return true default: return false } } -// Defines values for TelemetryEventCategory. +// Defines values for BrowserMonitorReconnectFailedEventCategory. const ( - TelemetryEventCategoryCaptcha TelemetryEventCategory = "captcha" - TelemetryEventCategoryConnection TelemetryEventCategory = "connection" - TelemetryEventCategoryConsole TelemetryEventCategory = "console" - TelemetryEventCategoryControl TelemetryEventCategory = "control" - TelemetryEventCategoryInteraction TelemetryEventCategory = "interaction" - TelemetryEventCategoryMonitor TelemetryEventCategory = "monitor" - TelemetryEventCategoryNetwork TelemetryEventCategory = "network" - TelemetryEventCategoryPage TelemetryEventCategory = "page" - TelemetryEventCategoryPlatform TelemetryEventCategory = "platform" - TelemetryEventCategoryScreenshot TelemetryEventCategory = "screenshot" - TelemetryEventCategorySystem TelemetryEventCategory = "system" + BrowserMonitorReconnectFailedEventCategoryMonitor BrowserMonitorReconnectFailedEventCategory = "monitor" ) -// Valid indicates whether the value is a known member of the TelemetryEventCategory enum. -func (e TelemetryEventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorReconnectFailedEventCategory enum. +func (e BrowserMonitorReconnectFailedEventCategory) Valid() bool { switch e { - case TelemetryEventCategoryCaptcha: - return true - case TelemetryEventCategoryConnection: - return true - case TelemetryEventCategoryConsole: - return true - case TelemetryEventCategoryControl: - return true - case TelemetryEventCategoryInteraction: - return true - case TelemetryEventCategoryMonitor: - return true - case TelemetryEventCategoryNetwork: - return true - case TelemetryEventCategoryPage: - return true - case TelemetryEventCategoryPlatform: - return true - case TelemetryEventCategoryScreenshot: - return true - case TelemetryEventCategorySystem: + case BrowserMonitorReconnectFailedEventCategoryMonitor: return true default: return false } } -// Defines values for DownloadDirZstdParamsCompressionLevel. +// Defines values for BrowserMonitorReconnectFailedEventType. const ( - Best DownloadDirZstdParamsCompressionLevel = "best" - Better DownloadDirZstdParamsCompressionLevel = "better" - Default DownloadDirZstdParamsCompressionLevel = "default" - Fastest DownloadDirZstdParamsCompressionLevel = "fastest" + MonitorReconnectFailed BrowserMonitorReconnectFailedEventType = "monitor_reconnect_failed" ) -// Valid indicates whether the value is a known member of the DownloadDirZstdParamsCompressionLevel enum. -func (e DownloadDirZstdParamsCompressionLevel) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorReconnectFailedEventType enum. +func (e BrowserMonitorReconnectFailedEventType) Valid() bool { switch e { - case Best: - return true - case Better: - return true - case Default: - return true - case Fastest: + case MonitorReconnectFailed: return true default: return false } } -// Defines values for LogsStreamParamsSource. +// Defines values for BrowserMonitorReconnectFailedEventDataReason. const ( - Path LogsStreamParamsSource = "path" - Supervisor LogsStreamParamsSource = "supervisor" + ReconnectExhausted BrowserMonitorReconnectFailedEventDataReason = "reconnect_exhausted" ) -// Valid indicates whether the value is a known member of the LogsStreamParamsSource enum. -func (e LogsStreamParamsSource) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorReconnectFailedEventDataReason enum. +func (e BrowserMonitorReconnectFailedEventDataReason) Valid() bool { switch e { - case Path: - return true - case Supervisor: + case ReconnectExhausted: return true default: return false } } -// Defines values for StreamTelemetryEventsParamsReplay. +// Defines values for BrowserMonitorReconnectedEventCategory. const ( - All StreamTelemetryEventsParamsReplay = "all" + BrowserMonitorReconnectedEventCategoryMonitor BrowserMonitorReconnectedEventCategory = "monitor" ) -// Valid indicates whether the value is a known member of the StreamTelemetryEventsParamsReplay enum. -func (e StreamTelemetryEventsParamsReplay) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorReconnectedEventCategory enum. +func (e BrowserMonitorReconnectedEventCategory) Valid() bool { switch e { - case All: + case BrowserMonitorReconnectedEventCategoryMonitor: return true default: return false } } -// BatchComputerActionRequest A batch of computer actions to execute sequentially. -type BatchComputerActionRequest struct { - // Actions Ordered list of actions to execute. Execution stops on the first error. - Actions []ComputerAction `json:"actions"` -} +// Defines values for BrowserMonitorReconnectedEventType. +const ( + MonitorReconnected BrowserMonitorReconnectedEventType = "monitor_reconnected" +) -// BrowserApiCallEvent A call that drives the browser, handled by the kernel-images-api server: computer-control actions, Playwright code execution, screenshots and clipboard access. Calls that manage the VM instead emit `platform_api_call`. -type BrowserApiCallEvent struct { - Category BrowserApiCallEventCategory `json:"category"` +// Valid indicates whether the value is a known member of the BrowserMonitorReconnectedEventType enum. +func (e BrowserMonitorReconnectedEventType) Valid() bool { + switch e { + case MonitorReconnected: + return true + default: + return false + } +} - // Data Per-call payload for `api_call` events. - Data *BrowserApiCallEventData `json:"data,omitempty"` +// Defines values for BrowserMonitorScreenshotEventCategory. +const ( + Screenshot BrowserMonitorScreenshotEventCategory = "screenshot" +) - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` +// Valid indicates whether the value is a known member of the BrowserMonitorScreenshotEventCategory enum. +func (e BrowserMonitorScreenshotEventCategory) Valid() bool { + switch e { + case Screenshot: + return true + default: + return false + } +} - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` +// Defines values for BrowserMonitorScreenshotEventType. +const ( + MonitorScreenshot BrowserMonitorScreenshotEventType = "monitor_screenshot" +) - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserApiCallEventType `json:"type"` +// Valid indicates whether the value is a known member of the BrowserMonitorScreenshotEventType enum. +func (e BrowserMonitorScreenshotEventType) Valid() bool { + switch e { + case MonitorScreenshot: + return true + default: + return false + } } -// BrowserApiCallEventCategory defines model for BrowserApiCallEvent.Category. -type BrowserApiCallEventCategory string - -// BrowserApiCallEventType defines model for BrowserApiCallEvent.Type. -type BrowserApiCallEventType string +// Defines values for BrowserNetworkIdleEventCategory. +const ( + BrowserNetworkIdleEventCategoryNetwork BrowserNetworkIdleEventCategory = "network" +) -// BrowserApiCallEventData Per-call payload for `api_call` events. -type BrowserApiCallEventData struct { - // Code Source submitted to `executePlaywrightCode`, capped at 8192 bytes like every other captured string. A capped value is cut on a character boundary and ends in `...[truncated]`. Absent for every other operation. - Code *string `json:"code,omitempty"` +// Valid indicates whether the value is a known member of the BrowserNetworkIdleEventCategory enum. +func (e BrowserNetworkIdleEventCategory) Valid() bool { + switch e { + case BrowserNetworkIdleEventCategoryNetwork: + return true + default: + return false + } +} - // DurationMs Wall-clock duration of the handler in milliseconds. - DurationMs float32 `json:"duration_ms"` +// Defines values for BrowserNetworkIdleEventType. +const ( + NetworkIdle BrowserNetworkIdleEventType = "network_idle" +) - // OperationId Matched route's operation, named as the server names its handler (e.g. `TakeScreenshot`, `ExecutePlaywrightCode`). - OperationId string `json:"operation_id"` +// Valid indicates whether the value is a known member of the BrowserNetworkIdleEventType enum. +func (e BrowserNetworkIdleEventType) Valid() bool { + switch e { + case NetworkIdle: + return true + default: + return false + } +} + +// Defines values for BrowserNetworkLoadingFailedEventCategory. +const ( + BrowserNetworkLoadingFailedEventCategoryNetwork BrowserNetworkLoadingFailedEventCategory = "network" +) + +// Valid indicates whether the value is a known member of the BrowserNetworkLoadingFailedEventCategory enum. +func (e BrowserNetworkLoadingFailedEventCategory) Valid() bool { + switch e { + case BrowserNetworkLoadingFailedEventCategoryNetwork: + return true + default: + return false + } +} + +// Defines values for BrowserNetworkLoadingFailedEventType. +const ( + NetworkLoadingFailed BrowserNetworkLoadingFailedEventType = "network_loading_failed" +) + +// Valid indicates whether the value is a known member of the BrowserNetworkLoadingFailedEventType enum. +func (e BrowserNetworkLoadingFailedEventType) Valid() bool { + switch e { + case NetworkLoadingFailed: + return true + default: + return false + } +} + +// Defines values for BrowserNetworkRequestEventCategory. +const ( + BrowserNetworkRequestEventCategoryNetwork BrowserNetworkRequestEventCategory = "network" +) + +// Valid indicates whether the value is a known member of the BrowserNetworkRequestEventCategory enum. +func (e BrowserNetworkRequestEventCategory) Valid() bool { + switch e { + case BrowserNetworkRequestEventCategoryNetwork: + return true + default: + return false + } +} + +// Defines values for BrowserNetworkRequestEventType. +const ( + NetworkRequest BrowserNetworkRequestEventType = "network_request" +) + +// Valid indicates whether the value is a known member of the BrowserNetworkRequestEventType enum. +func (e BrowserNetworkRequestEventType) Valid() bool { + switch e { + case NetworkRequest: + return true + default: + return false + } +} + +// Defines values for BrowserNetworkResponseEventCategory. +const ( + BrowserNetworkResponseEventCategoryNetwork BrowserNetworkResponseEventCategory = "network" +) + +// Valid indicates whether the value is a known member of the BrowserNetworkResponseEventCategory enum. +func (e BrowserNetworkResponseEventCategory) Valid() bool { + switch e { + case BrowserNetworkResponseEventCategoryNetwork: + return true + default: + return false + } +} + +// Defines values for BrowserNetworkResponseEventType. +const ( + NetworkResponse BrowserNetworkResponseEventType = "network_response" +) + +// Valid indicates whether the value is a known member of the BrowserNetworkResponseEventType enum. +func (e BrowserNetworkResponseEventType) Valid() bool { + switch e { + case NetworkResponse: + return true + default: + return false + } +} + +// Defines values for BrowserPageCrashedEventCategory. +const ( + BrowserPageCrashedEventCategoryPage BrowserPageCrashedEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageCrashedEventCategory enum. +func (e BrowserPageCrashedEventCategory) Valid() bool { + switch e { + case BrowserPageCrashedEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageCrashedEventType. +const ( + PageCrashed BrowserPageCrashedEventType = "page_crashed" +) + +// Valid indicates whether the value is a known member of the BrowserPageCrashedEventType enum. +func (e BrowserPageCrashedEventType) Valid() bool { + switch e { + case PageCrashed: + return true + default: + return false + } +} + +// Defines values for BrowserPageDomContentLoadedEventCategory. +const ( + BrowserPageDomContentLoadedEventCategoryPage BrowserPageDomContentLoadedEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageDomContentLoadedEventCategory enum. +func (e BrowserPageDomContentLoadedEventCategory) Valid() bool { + switch e { + case BrowserPageDomContentLoadedEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageDomContentLoadedEventType. +const ( + PageDomContentLoaded BrowserPageDomContentLoadedEventType = "page_dom_content_loaded" +) + +// Valid indicates whether the value is a known member of the BrowserPageDomContentLoadedEventType enum. +func (e BrowserPageDomContentLoadedEventType) Valid() bool { + switch e { + case PageDomContentLoaded: + return true + default: + return false + } +} + +// Defines values for BrowserPageLayoutSettledEventCategory. +const ( + BrowserPageLayoutSettledEventCategoryPage BrowserPageLayoutSettledEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageLayoutSettledEventCategory enum. +func (e BrowserPageLayoutSettledEventCategory) Valid() bool { + switch e { + case BrowserPageLayoutSettledEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageLayoutSettledEventType. +const ( + PageLayoutSettled BrowserPageLayoutSettledEventType = "page_layout_settled" +) + +// Valid indicates whether the value is a known member of the BrowserPageLayoutSettledEventType enum. +func (e BrowserPageLayoutSettledEventType) Valid() bool { + switch e { + case PageLayoutSettled: + return true + default: + return false + } +} + +// Defines values for BrowserPageLayoutShiftEventCategory. +const ( + BrowserPageLayoutShiftEventCategoryPage BrowserPageLayoutShiftEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageLayoutShiftEventCategory enum. +func (e BrowserPageLayoutShiftEventCategory) Valid() bool { + switch e { + case BrowserPageLayoutShiftEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageLayoutShiftEventType. +const ( + PageLayoutShift BrowserPageLayoutShiftEventType = "page_layout_shift" +) + +// Valid indicates whether the value is a known member of the BrowserPageLayoutShiftEventType enum. +func (e BrowserPageLayoutShiftEventType) Valid() bool { + switch e { + case PageLayoutShift: + return true + default: + return false + } +} + +// Defines values for BrowserPageLcpEventCategory. +const ( + BrowserPageLcpEventCategoryPage BrowserPageLcpEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageLcpEventCategory enum. +func (e BrowserPageLcpEventCategory) Valid() bool { + switch e { + case BrowserPageLcpEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageLcpEventType. +const ( + PageLcp BrowserPageLcpEventType = "page_lcp" +) + +// Valid indicates whether the value is a known member of the BrowserPageLcpEventType enum. +func (e BrowserPageLcpEventType) Valid() bool { + switch e { + case PageLcp: + return true + default: + return false + } +} + +// Defines values for BrowserPageLoadEventCategory. +const ( + BrowserPageLoadEventCategoryPage BrowserPageLoadEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageLoadEventCategory enum. +func (e BrowserPageLoadEventCategory) Valid() bool { + switch e { + case BrowserPageLoadEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageLoadEventType. +const ( + PageLoad BrowserPageLoadEventType = "page_load" +) + +// Valid indicates whether the value is a known member of the BrowserPageLoadEventType enum. +func (e BrowserPageLoadEventType) Valid() bool { + switch e { + case PageLoad: + return true + default: + return false + } +} + +// Defines values for BrowserPageNavigationEventCategory. +const ( + BrowserPageNavigationEventCategoryPage BrowserPageNavigationEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageNavigationEventCategory enum. +func (e BrowserPageNavigationEventCategory) Valid() bool { + switch e { + case BrowserPageNavigationEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageNavigationEventType. +const ( + PageNavigation BrowserPageNavigationEventType = "page_navigation" +) + +// Valid indicates whether the value is a known member of the BrowserPageNavigationEventType enum. +func (e BrowserPageNavigationEventType) Valid() bool { + switch e { + case PageNavigation: + return true + default: + return false + } +} + +// Defines values for BrowserPageNavigationSettledEventCategory. +const ( + BrowserPageNavigationSettledEventCategoryPage BrowserPageNavigationSettledEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageNavigationSettledEventCategory enum. +func (e BrowserPageNavigationSettledEventCategory) Valid() bool { + switch e { + case BrowserPageNavigationSettledEventCategoryPage: + return true + default: + return false + } +} + +// Defines values for BrowserPageNavigationSettledEventType. +const ( + PageNavigationSettled BrowserPageNavigationSettledEventType = "page_navigation_settled" +) + +// Valid indicates whether the value is a known member of the BrowserPageNavigationSettledEventType enum. +func (e BrowserPageNavigationSettledEventType) Valid() bool { + switch e { + case PageNavigationSettled: + return true + default: + return false + } +} + +// Defines values for BrowserPageTabOpenedEventCategory. +const ( + Page BrowserPageTabOpenedEventCategory = "page" +) + +// Valid indicates whether the value is a known member of the BrowserPageTabOpenedEventCategory enum. +func (e BrowserPageTabOpenedEventCategory) Valid() bool { + switch e { + case Page: + return true + default: + return false + } +} + +// Defines values for BrowserPageTabOpenedEventType. +const ( + PageTabOpened BrowserPageTabOpenedEventType = "page_tab_opened" +) + +// Valid indicates whether the value is a known member of the BrowserPageTabOpenedEventType enum. +func (e BrowserPageTabOpenedEventType) Valid() bool { + switch e { + case PageTabOpened: + return true + default: + return false + } +} + +// Defines values for BrowserPlatformApiCallEventCategory. +const ( + Platform BrowserPlatformApiCallEventCategory = "platform" +) + +// Valid indicates whether the value is a known member of the BrowserPlatformApiCallEventCategory enum. +func (e BrowserPlatformApiCallEventCategory) Valid() bool { + switch e { + case Platform: + return true + default: + return false + } +} + +// Defines values for BrowserPlatformApiCallEventType. +const ( + PlatformApiCall BrowserPlatformApiCallEventType = "platform_api_call" +) + +// Valid indicates whether the value is a known member of the BrowserPlatformApiCallEventType enum. +func (e BrowserPlatformApiCallEventType) Valid() bool { + switch e { + case PlatformApiCall: + return true + default: + return false + } +} + +// Defines values for BrowserProxyErrorEventCategory. +const ( + Network BrowserProxyErrorEventCategory = "network" +) + +// Valid indicates whether the value is a known member of the BrowserProxyErrorEventCategory enum. +func (e BrowserProxyErrorEventCategory) Valid() bool { + switch e { + case Network: + return true + default: + return false + } +} + +// Defines values for BrowserProxyErrorEventType. +const ( + ProxyError BrowserProxyErrorEventType = "proxy_error" +) + +// Valid indicates whether the value is a known member of the BrowserProxyErrorEventType enum. +func (e BrowserProxyErrorEventType) Valid() bool { + switch e { + case ProxyError: + return true + default: + return false + } +} + +// Defines values for BrowserProxyErrorEventDataCode. +const ( + DestinationBlocked BrowserProxyErrorEventDataCode = "destination_blocked" + ProviderBlacklisted BrowserProxyErrorEventDataCode = "provider_blacklisted" + ProviderUnreachable BrowserProxyErrorEventDataCode = "provider_unreachable" + ProxyUnavailable BrowserProxyErrorEventDataCode = "proxy_unavailable" + UpstreamConnectFailed BrowserProxyErrorEventDataCode = "upstream_connect_failed" + UpstreamDnsFailure BrowserProxyErrorEventDataCode = "upstream_dns_failure" + UpstreamTimeout BrowserProxyErrorEventDataCode = "upstream_timeout" +) + +// Valid indicates whether the value is a known member of the BrowserProxyErrorEventDataCode enum. +func (e BrowserProxyErrorEventDataCode) Valid() bool { + switch e { + case DestinationBlocked: + return true + case ProviderBlacklisted: + return true + case ProviderUnreachable: + return true + case ProxyUnavailable: + return true + case UpstreamConnectFailed: + return true + case UpstreamDnsFailure: + return true + case UpstreamTimeout: + return true + default: + return false + } +} + +// Defines values for BrowserServiceCrashedEventCategory. +const ( + BrowserServiceCrashedEventCategorySystem BrowserServiceCrashedEventCategory = "system" +) + +// Valid indicates whether the value is a known member of the BrowserServiceCrashedEventCategory enum. +func (e BrowserServiceCrashedEventCategory) Valid() bool { + switch e { + case BrowserServiceCrashedEventCategorySystem: + return true + default: + return false + } +} + +// Defines values for BrowserServiceCrashedEventType. +const ( + ServiceCrashed BrowserServiceCrashedEventType = "service_crashed" +) + +// Valid indicates whether the value is a known member of the BrowserServiceCrashedEventType enum. +func (e BrowserServiceCrashedEventType) Valid() bool { + switch e { + case ServiceCrashed: + return true + default: + return false + } +} + +// Defines values for BrowserServiceCrashedEventDataPhase. +const ( + BrowserServiceCrashedEventDataPhaseGaveUp BrowserServiceCrashedEventDataPhase = "gave_up" + BrowserServiceCrashedEventDataPhaseRunning BrowserServiceCrashedEventDataPhase = "running" + BrowserServiceCrashedEventDataPhaseStartup BrowserServiceCrashedEventDataPhase = "startup" +) + +// Valid indicates whether the value is a known member of the BrowserServiceCrashedEventDataPhase enum. +func (e BrowserServiceCrashedEventDataPhase) Valid() bool { + switch e { + case BrowserServiceCrashedEventDataPhaseGaveUp: + return true + case BrowserServiceCrashedEventDataPhaseRunning: + return true + case BrowserServiceCrashedEventDataPhaseStartup: + return true + default: + return false + } +} + +// Defines values for BrowserSystemOomKillEventCategory. +const ( + BrowserSystemOomKillEventCategorySystem BrowserSystemOomKillEventCategory = "system" +) + +// Valid indicates whether the value is a known member of the BrowserSystemOomKillEventCategory enum. +func (e BrowserSystemOomKillEventCategory) Valid() bool { + switch e { + case BrowserSystemOomKillEventCategorySystem: + return true + default: + return false + } +} + +// Defines values for BrowserSystemOomKillEventType. +const ( + SystemOomKill BrowserSystemOomKillEventType = "system_oom_kill" +) + +// Valid indicates whether the value is a known member of the BrowserSystemOomKillEventType enum. +func (e BrowserSystemOomKillEventType) Valid() bool { + switch e { + case SystemOomKill: + return true + default: + return false + } +} + +// Defines values for BrowserSystemOomKillEventDataConstraint. +const ( + Cpuset BrowserSystemOomKillEventDataConstraint = "cpuset" + Memcg BrowserSystemOomKillEventDataConstraint = "memcg" + MemoryPolicy BrowserSystemOomKillEventDataConstraint = "memory_policy" + None BrowserSystemOomKillEventDataConstraint = "none" +) + +// Valid indicates whether the value is a known member of the BrowserSystemOomKillEventDataConstraint enum. +func (e BrowserSystemOomKillEventDataConstraint) Valid() bool { + switch e { + case Cpuset: + return true + case Memcg: + return true + case MemoryPolicy: + return true + case None: + return true + default: + return false + } +} + +// Defines values for BrowserTargetType. +const ( + BrowserTargetTypeBackgroundPage BrowserTargetType = "background_page" + BrowserTargetTypeOther BrowserTargetType = "other" + BrowserTargetTypePage BrowserTargetType = "page" + BrowserTargetTypeServiceWorker BrowserTargetType = "service_worker" + BrowserTargetTypeSharedWorker BrowserTargetType = "shared_worker" +) + +// Valid indicates whether the value is a known member of the BrowserTargetType enum. +func (e BrowserTargetType) Valid() bool { + switch e { + case BrowserTargetTypeBackgroundPage: + return true + case BrowserTargetTypeOther: + return true + case BrowserTargetTypePage: + return true + case BrowserTargetTypeServiceWorker: + return true + case BrowserTargetTypeSharedWorker: + return true + default: + return false + } +} + +// Defines values for ChromiumConfigureErrorPhase. +const ( + ConfigurePhase ChromiumConfigureErrorPhase = "configure_phase" + NavigatePhase ChromiumConfigureErrorPhase = "navigate_phase" +) + +// Valid indicates whether the value is a known member of the ChromiumConfigureErrorPhase enum. +func (e ChromiumConfigureErrorPhase) Valid() bool { + switch e { + case ConfigurePhase: + return true + case NavigatePhase: + return true + default: + return false + } +} + +// Defines values for ChromiumConfigureErrorStep. +const ( + ChromePolicies ChromiumConfigureErrorStep = "chrome_policies" + ChromiumFlags ChromiumConfigureErrorStep = "chromium_flags" + Display ChromiumConfigureErrorStep = "display" + Extensions ChromiumConfigureErrorStep = "extensions" + Profile ChromiumConfigureErrorStep = "profile" + StartChromium ChromiumConfigureErrorStep = "start_chromium" + StopChromium ChromiumConfigureErrorStep = "stop_chromium" +) + +// Valid indicates whether the value is a known member of the ChromiumConfigureErrorStep enum. +func (e ChromiumConfigureErrorStep) Valid() bool { + switch e { + case ChromePolicies: + return true + case ChromiumFlags: + return true + case Display: + return true + case Extensions: + return true + case Profile: + return true + case StartChromium: + return true + case StopChromium: + return true + default: + return false + } +} + +// Defines values for ClickMouseRequestButton. +const ( + ClickMouseRequestButtonBack ClickMouseRequestButton = "back" + ClickMouseRequestButtonForward ClickMouseRequestButton = "forward" + ClickMouseRequestButtonLeft ClickMouseRequestButton = "left" + ClickMouseRequestButtonMiddle ClickMouseRequestButton = "middle" + ClickMouseRequestButtonRight ClickMouseRequestButton = "right" +) + +// Valid indicates whether the value is a known member of the ClickMouseRequestButton enum. +func (e ClickMouseRequestButton) Valid() bool { + switch e { + case ClickMouseRequestButtonBack: + return true + case ClickMouseRequestButtonForward: + return true + case ClickMouseRequestButtonLeft: + return true + case ClickMouseRequestButtonMiddle: + return true + case ClickMouseRequestButtonRight: + return true + default: + return false + } +} + +// Defines values for ClickMouseRequestClickType. +const ( + Click ClickMouseRequestClickType = "click" + Down ClickMouseRequestClickType = "down" + Up ClickMouseRequestClickType = "up" +) + +// Valid indicates whether the value is a known member of the ClickMouseRequestClickType enum. +func (e ClickMouseRequestClickType) Valid() bool { + switch e { + case Click: + return true + case Down: + return true + case Up: + return true + default: + return false + } +} + +// Defines values for ComputerActionType. +const ( + ClickMouse ComputerActionType = "click_mouse" + DragMouse ComputerActionType = "drag_mouse" + MoveMouse ComputerActionType = "move_mouse" + PressKey ComputerActionType = "press_key" + Scroll ComputerActionType = "scroll" + SetCursor ComputerActionType = "set_cursor" + Sleep ComputerActionType = "sleep" + TypeText ComputerActionType = "type_text" +) + +// Valid indicates whether the value is a known member of the ComputerActionType enum. +func (e ComputerActionType) Valid() bool { + switch e { + case ClickMouse: + return true + case DragMouse: + return true + case MoveMouse: + return true + case PressKey: + return true + case Scroll: + return true + case SetCursor: + return true + case Sleep: + return true + case TypeText: + return true + default: + return false + } +} + +// Defines values for DragMouseRequestButton. +const ( + DragMouseRequestButtonLeft DragMouseRequestButton = "left" + DragMouseRequestButtonMiddle DragMouseRequestButton = "middle" + DragMouseRequestButtonRight DragMouseRequestButton = "right" +) + +// Valid indicates whether the value is a known member of the DragMouseRequestButton enum. +func (e DragMouseRequestButton) Valid() bool { + switch e { + case DragMouseRequestButtonLeft: + return true + case DragMouseRequestButtonMiddle: + return true + case DragMouseRequestButtonRight: + return true + default: + return false + } +} + +// Defines values for FileSystemEventType. +const ( + CREATE FileSystemEventType = "CREATE" + DELETE FileSystemEventType = "DELETE" + RENAME FileSystemEventType = "RENAME" + WRITE FileSystemEventType = "WRITE" +) + +// Valid indicates whether the value is a known member of the FileSystemEventType enum. +func (e FileSystemEventType) Valid() bool { + switch e { + case CREATE: + return true + case DELETE: + return true + case RENAME: + return true + case WRITE: + return true + default: + return false + } +} + +// Defines values for PatchDisplayRequestRefreshRate. +const ( + N10 PatchDisplayRequestRefreshRate = 10 + N25 PatchDisplayRequestRefreshRate = 25 + N30 PatchDisplayRequestRefreshRate = 30 + N60 PatchDisplayRequestRefreshRate = 60 +) + +// Valid indicates whether the value is a known member of the PatchDisplayRequestRefreshRate enum. +func (e PatchDisplayRequestRefreshRate) Valid() bool { + switch e { + case N10: + return true + case N25: + return true + case N30: + return true + case N60: + return true + default: + return false + } +} + +// Defines values for ProcessKillRequestSignal. +const ( + HUP ProcessKillRequestSignal = "HUP" + INT ProcessKillRequestSignal = "INT" + KILL ProcessKillRequestSignal = "KILL" + TERM ProcessKillRequestSignal = "TERM" +) + +// Valid indicates whether the value is a known member of the ProcessKillRequestSignal enum. +func (e ProcessKillRequestSignal) Valid() bool { + switch e { + case HUP: + return true + case INT: + return true + case KILL: + return true + case TERM: + return true + default: + return false + } +} + +// Defines values for ProcessStatusState. +const ( + ProcessStatusStateExited ProcessStatusState = "exited" + ProcessStatusStateRunning ProcessStatusState = "running" +) + +// Valid indicates whether the value is a known member of the ProcessStatusState enum. +func (e ProcessStatusState) Valid() bool { + switch e { + case ProcessStatusStateExited: + return true + case ProcessStatusStateRunning: + return true + default: + return false + } +} + +// Defines values for ProcessStreamEventEvent. +const ( + Exit ProcessStreamEventEvent = "exit" +) + +// Valid indicates whether the value is a known member of the ProcessStreamEventEvent enum. +func (e ProcessStreamEventEvent) Valid() bool { + switch e { + case Exit: + return true + default: + return false + } +} + +// Defines values for ProcessStreamEventStream. +const ( + Stderr ProcessStreamEventStream = "stderr" + Stdout ProcessStreamEventStream = "stdout" +) + +// Valid indicates whether the value is a known member of the ProcessStreamEventStream enum. +func (e ProcessStreamEventStream) Valid() bool { + switch e { + case Stderr: + return true + case Stdout: + return true + default: + return false + } +} + +// Defines values for PublishEventRequestCategory. +const ( + PublishEventRequestCategoryCaptcha PublishEventRequestCategory = "captcha" + PublishEventRequestCategoryConnection PublishEventRequestCategory = "connection" + PublishEventRequestCategoryConsole PublishEventRequestCategory = "console" + PublishEventRequestCategoryControl PublishEventRequestCategory = "control" + PublishEventRequestCategoryInteraction PublishEventRequestCategory = "interaction" + PublishEventRequestCategoryMonitor PublishEventRequestCategory = "monitor" + PublishEventRequestCategoryNetwork PublishEventRequestCategory = "network" + PublishEventRequestCategoryPage PublishEventRequestCategory = "page" + PublishEventRequestCategoryPlatform PublishEventRequestCategory = "platform" + PublishEventRequestCategoryScreenshot PublishEventRequestCategory = "screenshot" + PublishEventRequestCategorySystem PublishEventRequestCategory = "system" +) + +// Valid indicates whether the value is a known member of the PublishEventRequestCategory enum. +func (e PublishEventRequestCategory) Valid() bool { + switch e { + case PublishEventRequestCategoryCaptcha: + return true + case PublishEventRequestCategoryConnection: + return true + case PublishEventRequestCategoryConsole: + return true + case PublishEventRequestCategoryControl: + return true + case PublishEventRequestCategoryInteraction: + return true + case PublishEventRequestCategoryMonitor: + return true + case PublishEventRequestCategoryNetwork: + return true + case PublishEventRequestCategoryPage: + return true + case PublishEventRequestCategoryPlatform: + return true + case PublishEventRequestCategoryScreenshot: + return true + case PublishEventRequestCategorySystem: + return true + default: + return false + } +} + +// Defines values for TelemetryEventCategory. +const ( + TelemetryEventCategoryCaptcha TelemetryEventCategory = "captcha" + TelemetryEventCategoryConnection TelemetryEventCategory = "connection" + TelemetryEventCategoryConsole TelemetryEventCategory = "console" + TelemetryEventCategoryControl TelemetryEventCategory = "control" + TelemetryEventCategoryInteraction TelemetryEventCategory = "interaction" + TelemetryEventCategoryMonitor TelemetryEventCategory = "monitor" + TelemetryEventCategoryNetwork TelemetryEventCategory = "network" + TelemetryEventCategoryPage TelemetryEventCategory = "page" + TelemetryEventCategoryPlatform TelemetryEventCategory = "platform" + TelemetryEventCategoryScreenshot TelemetryEventCategory = "screenshot" + TelemetryEventCategorySystem TelemetryEventCategory = "system" +) + +// Valid indicates whether the value is a known member of the TelemetryEventCategory enum. +func (e TelemetryEventCategory) Valid() bool { + switch e { + case TelemetryEventCategoryCaptcha: + return true + case TelemetryEventCategoryConnection: + return true + case TelemetryEventCategoryConsole: + return true + case TelemetryEventCategoryControl: + return true + case TelemetryEventCategoryInteraction: + return true + case TelemetryEventCategoryMonitor: + return true + case TelemetryEventCategoryNetwork: + return true + case TelemetryEventCategoryPage: + return true + case TelemetryEventCategoryPlatform: + return true + case TelemetryEventCategoryScreenshot: + return true + case TelemetryEventCategorySystem: + return true + default: + return false + } +} + +// Defines values for DownloadDirZstdParamsCompressionLevel. +const ( + Best DownloadDirZstdParamsCompressionLevel = "best" + Better DownloadDirZstdParamsCompressionLevel = "better" + Default DownloadDirZstdParamsCompressionLevel = "default" + Fastest DownloadDirZstdParamsCompressionLevel = "fastest" +) + +// Valid indicates whether the value is a known member of the DownloadDirZstdParamsCompressionLevel enum. +func (e DownloadDirZstdParamsCompressionLevel) Valid() bool { + switch e { + case Best: + return true + case Better: + return true + case Default: + return true + case Fastest: + return true + default: + return false + } +} + +// Defines values for LogsStreamParamsSource. +const ( + Path LogsStreamParamsSource = "path" + Supervisor LogsStreamParamsSource = "supervisor" +) + +// Valid indicates whether the value is a known member of the LogsStreamParamsSource enum. +func (e LogsStreamParamsSource) Valid() bool { + switch e { + case Path: + return true + case Supervisor: + return true + default: + return false + } +} + +// Defines values for StreamTelemetryEventsParamsReplay. +const ( + All StreamTelemetryEventsParamsReplay = "all" +) + +// Valid indicates whether the value is a known member of the StreamTelemetryEventsParamsReplay enum. +func (e StreamTelemetryEventsParamsReplay) Valid() bool { + switch e { + case All: + return true + default: + return false + } +} + +// BatchComputerActionRequest A batch of computer actions to execute sequentially. +type BatchComputerActionRequest struct { + // Actions Ordered list of actions to execute. Execution stops on the first error. + Actions []ComputerAction `json:"actions"` +} + +// BrowserApiCallEvent A call that drives the browser, handled by the kernel-images-api server: computer-control actions, Playwright code execution, screenshots and clipboard access. Calls that manage the VM instead emit `platform_api_call`. +type BrowserApiCallEvent struct { + Category BrowserApiCallEventCategory `json:"category"` + + // Data Per-call payload for `api_call` events. + Data *BrowserApiCallEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserApiCallEventType `json:"type"` +} + +// BrowserApiCallEventCategory defines model for BrowserApiCallEvent.Category. +type BrowserApiCallEventCategory string + +// BrowserApiCallEventType defines model for BrowserApiCallEvent.Type. +type BrowserApiCallEventType string + +// BrowserApiCallEventData Per-call payload for `api_call` events. +type BrowserApiCallEventData struct { + // Code Source submitted to `executePlaywrightCode`, capped at 8192 bytes like every other captured string. A capped value is cut on a character boundary and ends in `...[truncated]`. Absent for every other operation. + Code *string `json:"code,omitempty"` + + // DurationMs Wall-clock duration of the handler in milliseconds. + DurationMs float32 `json:"duration_ms"` + + // OperationId Matched route's operation, named as the server names its handler (e.g. `TakeScreenshot`, `ExecutePlaywrightCode`). + OperationId string `json:"operation_id"` // RequestId Per-request identifier from the kernel-images-api request middleware. RequestId string `json:"request_id"` - // Status HTTP response status code. - Status int `json:"status"` + // Status HTTP response status code. + Status int `json:"status"` +} + +// BrowserCallStack CDP Runtime.StackTrace representing the JavaScript call stack at the time of an event. Fields use CDP naming conventions rather than snake_case to match the Chrome DevTools Protocol wire format. +type BrowserCallStack struct { + // CallFrames Ordered list of call frames, outermost first. + CallFrames []struct { + // ColumnNumber Zero-based column number within the line. + ColumnNumber int `json:"columnNumber"` + + // FunctionName JavaScript function name, or empty string for anonymous functions. + FunctionName string `json:"functionName"` + + // LineNumber Zero-based line number within the script. + LineNumber int `json:"lineNumber"` + + // ScriptId CDP script identifier. + ScriptId string `json:"scriptId"` + + // Url URL or name of the script file. + Url string `json:"url"` + } `json:"callFrames"` + + // Description Optional label for the stack trace (e.g. async cause). + Description *string `json:"description,omitempty"` + + // Parent Parent stack trace for async stacks. + Parent *BrowserCallStack `json:"parent,omitempty"` +} + +// BrowserCaptchaSolveResultEvent A captcha solve attempt reached a terminal outcome. +type BrowserCaptchaSolveResultEvent struct { + Category BrowserCaptchaSolveResultEventCategory `json:"category"` + + // Data Per-attempt payload for `captcha_solve_result` events. + Data *BrowserCaptchaSolveResultEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserCaptchaSolveResultEventType `json:"type"` +} + +// BrowserCaptchaSolveResultEventCategory defines model for BrowserCaptchaSolveResultEvent.Category. +type BrowserCaptchaSolveResultEventCategory string + +// BrowserCaptchaSolveResultEventType defines model for BrowserCaptchaSolveResultEvent.Type. +type BrowserCaptchaSolveResultEventType string + +// BrowserCaptchaSolveResultEventData Per-attempt payload for `captcha_solve_result` events. +type BrowserCaptchaSolveResultEventData struct { + // CaptchaType Captcha vendor family. Producers normalize provider-specific task names into this set: enterprise variants of recaptcha collapse into their version bucket (v2 / v3), and anything not covered (e.g. DataDome, MtCaptcha, plain OCR) is reported as `other`. + CaptchaType BrowserCaptchaSolveResultEventDataCaptchaType `json:"captcha_type"` + + // DurationMs Wall-clock duration from solve start to terminal outcome. + DurationMs float32 `json:"duration_ms"` + + // ErrorCode Solver-specific error code on failure (e.g. `ERROR_CAPTCHA_UNSOLVABLE`). Absent on success. + ErrorCode *string `json:"error_code,omitempty"` + + // Status Terminal outcome. `success`: solver returned a usable solution. `failure`: solver returned an error (see `error_code`). `timeout`: solver did not return within the caller's wait budget. `abandoned`: caller cancelled or the page navigated away mid-solve. + Status BrowserCaptchaSolveResultEventDataStatus `json:"status"` + + // TaskId Solver-assigned identifier. Opaque, useful for support cross-references. + TaskId *string `json:"task_id,omitempty"` + + // WebsiteHost Host of the page where the captcha was solved. + WebsiteHost *string `json:"website_host,omitempty"` + + // WebsitePath Path of the page where the captcha was solved. Query string excluded. + WebsitePath *string `json:"website_path,omitempty"` +} + +// BrowserCaptchaSolveResultEventDataCaptchaType Captcha vendor family. Producers normalize provider-specific task names into this set: enterprise variants of recaptcha collapse into their version bucket (v2 / v3), and anything not covered (e.g. DataDome, MtCaptcha, plain OCR) is reported as `other`. +type BrowserCaptchaSolveResultEventDataCaptchaType string + +// BrowserCaptchaSolveResultEventDataStatus Terminal outcome. `success`: solver returned a usable solution. `failure`: solver returned an error (see `error_code`). `timeout`: solver did not return within the caller's wait budget. `abandoned`: caller cancelled or the page navigated away mid-solve. +type BrowserCaptchaSolveResultEventDataStatus string + +// BrowserCdpAutofillMode Which kind of value autofill filled. Canonical values from devtools-protocol@2d019e73. +type BrowserCdpAutofillMode string + +// BrowserCdpAutofillTriggerCommandData Sanitized `Autofill.trigger` arguments. Canonical input: `Autofill.trigger` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpAutofillTriggerCommandData struct { + // AddressFieldCount Number of address fields the command filled. Their names and values are never captured. + AddressFieldCount *int `json:"address_field_count,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // FieldId Opaque backend node identifier of the field that was autofilled. + FieldId int `json:"field_id"` + + // FrameId Opaque frame identifier. Clipped to 128 characters; a longer value is not a real identifier. + FrameId *string `json:"frame_id,omitempty"` + Method BrowserCdpAutofillTriggerCommandDataMethod `json:"method"` + + // Mode What was filled: `card` or `address`. The values themselves are never captured. + Mode *BrowserCdpAutofillMode `json:"mode,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpAutofillTriggerCommandDataMethod defines model for BrowserCdpAutofillTriggerCommandData.Method. +type BrowserCdpAutofillTriggerCommandDataMethod string + +// BrowserCdpBrowserCancelDownloadCommandData Sanitized `Browser.cancelDownload` arguments. Canonical input: `Browser.cancelDownload` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpBrowserCancelDownloadCommandData struct { + // BrowserContextId Opaque browser context identifier. Clipped to 128 characters; a longer value is not a real identifier. + BrowserContextId *string `json:"browser_context_id,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // DownloadGuid Opaque identifier of the download that was cancelled. Clipped to 128 characters; a longer value is not a real identifier. + DownloadGuid string `json:"download_guid"` + Method BrowserCdpBrowserCancelDownloadCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpBrowserCancelDownloadCommandDataMethod defines model for BrowserCdpBrowserCancelDownloadCommandData.Method. +type BrowserCdpBrowserCancelDownloadCommandDataMethod string + +// BrowserCdpBrowserCloseCommandData Sanitized `Browser.close` arguments. Canonical input: `Browser.close` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpBrowserCloseCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpBrowserCloseCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpBrowserCloseCommandDataMethod defines model for BrowserCdpBrowserCloseCommandData.Method. +type BrowserCdpBrowserCloseCommandDataMethod string + +// BrowserCdpBrowserSetContentsSizeCommandData Sanitized `Browser.setContentsSize` arguments. Canonical input: `Browser.setContentsSize` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpBrowserSetContentsSizeCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // Height Contents height in DIP. + Height *int `json:"height,omitempty"` + Method BrowserCdpBrowserSetContentsSizeCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // Width Contents width in DIP. + Width *int `json:"width,omitempty"` + + // WindowId Browser window identifier. + WindowId int `json:"window_id"` +} + +// BrowserCdpBrowserSetContentsSizeCommandDataMethod defines model for BrowserCdpBrowserSetContentsSizeCommandData.Method. +type BrowserCdpBrowserSetContentsSizeCommandDataMethod string + +// BrowserCdpBrowserSetWindowBoundsCommandData Sanitized `Browser.setWindowBounds` arguments. Canonical input: `Browser.setWindowBounds` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpBrowserSetWindowBoundsCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // Height Window height in DIP. + Height *int `json:"height,omitempty"` + + // Left Window x position in screen coordinates. + Left *int `json:"left,omitempty"` + Method BrowserCdpBrowserSetWindowBoundsCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // Top Window y position in screen coordinates. + Top *int `json:"top,omitempty"` + + // Width Window width in DIP. + Width *int `json:"width,omitempty"` + + // WindowId Browser window identifier. + WindowId int `json:"window_id"` + + // WindowState Window state requested (`normal`, `minimized`, `maximized`, `fullscreen`). A value the protocol does not define is reported as `other`. + WindowState *BrowserCdpWindowState `json:"window_state,omitempty"` +} + +// BrowserCdpBrowserSetWindowBoundsCommandDataMethod defines model for BrowserCdpBrowserSetWindowBoundsCommandData.Method. +type BrowserCdpBrowserSetWindowBoundsCommandDataMethod string + +// BrowserCdpCommandEvent A browser-control command a client sent over the CDP WebSocket proxy: input gestures, navigation, dialog handling, file selection and screenshots. Configuration commands and the DOM/Runtime traffic a client library issues on the caller's behalf are not reported. +// One event per browser-control command that reached the browser. The command stream is not sampled, coalesced or reordered. An event is lost only when the method is excluded by telemetry configuration, when the command's arguments do not decode, or when classification cannot keep up. Exclusions are counted in `cdp_disconnect.telemetry_excluded`; the rest in `cdp_disconnect.telemetry_dropped`. +type BrowserCdpCommandEvent struct { + Category BrowserCdpCommandEventCategory `json:"category"` + + // Data Per-command payload for `cdp_command` events, discriminated by `method`. Each variant carries only the arguments approved for that command: values that could hold a secret — typed and composition text, URLs, referrers, scripts, templates, file paths, drag contents and autofill values — are replaced by a length, a count, a presence flag, an enum or a URL scheme and host. + Data BrowserCdpCommandEventData `json:"data"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserCdpCommandEventType `json:"type"` +} + +// BrowserCdpCommandEventCategory defines model for BrowserCdpCommandEvent.Category. +type BrowserCdpCommandEventCategory string + +// BrowserCdpCommandEventType defines model for BrowserCdpCommandEvent.Type. +type BrowserCdpCommandEventType string + +// BrowserCdpCommandEventData Per-command payload for `cdp_command` events, discriminated by `method`. Each variant carries only the arguments approved for that command: values that could hold a secret — typed and composition text, URLs, referrers, scripts, templates, file paths, drag contents and autofill values — are replaced by a length, a count, a presence flag, an enum or a URL scheme and host. +type BrowserCdpCommandEventData struct { + union json.RawMessage +} + +// BrowserCdpCommandMethod A browser-control CDP method the proxy reports. The set covers the commands an agent drives the browser with; configuration, DOM and Runtime bookkeeping, and Chrome-specific UI commands are outside it. Canonical definitions: devtools-protocol@2d019e73. +type BrowserCdpCommandMethod string + +// BrowserCdpConnectEvent An external client (e.g. customer SDK, Playwright, Puppeteer) connected to the CDP WebSocket proxy on this VM. +type BrowserCdpConnectEvent struct { + Category BrowserCdpConnectEventCategory `json:"category"` + + // Data Per-connection payload for `cdp_connect` events. + Data *BrowserCdpConnectEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserCdpConnectEventType `json:"type"` +} + +// BrowserCdpConnectEventCategory defines model for BrowserCdpConnectEvent.Category. +type BrowserCdpConnectEventCategory string + +// BrowserCdpConnectEventType defines model for BrowserCdpConnectEvent.Type. +type BrowserCdpConnectEventType string + +// BrowserCdpConnectEventData Per-connection payload for `cdp_connect` events. +type BrowserCdpConnectEventData struct { + // ConnectionId Identifies this CDP proxy connection, matching the `connection_id` on the `cdp_command` events that arrived on it. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` +} + +// BrowserCdpDisconnectEvent An external client disconnected from the CDP WebSocket proxy on this VM. Pair with the immediately preceding `cdp_connect` on the same stream. +type BrowserCdpDisconnectEvent struct { + Category BrowserCdpDisconnectEventCategory `json:"category"` + + // Data Per-disconnect payload for `cdp_disconnect` events. + Data *BrowserCdpDisconnectEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserCdpDisconnectEventType `json:"type"` +} + +// BrowserCdpDisconnectEventCategory defines model for BrowserCdpDisconnectEvent.Category. +type BrowserCdpDisconnectEventCategory string + +// BrowserCdpDisconnectEventType defines model for BrowserCdpDisconnectEvent.Type. +type BrowserCdpDisconnectEventType string + +// BrowserCdpDisconnectEventData Per-disconnect payload for `cdp_disconnect` events. +type BrowserCdpDisconnectEventData struct { + // ConnectionId Identifies this CDP proxy connection, matching the `connection_id` on the `cdp_command` events that arrived on it. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // DurationMs Wall-clock duration of the connection in milliseconds. + DurationMs float32 `json:"duration_ms"` + + // MessageCount Number of CDP messages relayed across the connection in either direction. + MessageCount int `json:"message_count"` + + // Reason Why the connection ended. `client_close`: the client initiated the close. `upstream_changed`: Chromium restarted mid-session and the proxy tore down so the client could reconnect against the new upstream. `upstream_error`: upstream dial or message pump errored. `context_cancelled`: the request context was cancelled (typically server shutdown). + Reason BrowserCdpDisconnectEventDataReason `json:"reason"` + + // TelemetryDropped Number of supported browser-control commands that were forwarded to the browser but never classified, because the queue was full or classification panicked. Every increment is a real lost command — unsupported and excluded methods are filtered before admission and never count toward this total. Telemetry loss only; every command was still relayed to the browser. Always present on images that report it; absent on images predating the field, which is not the same as zero. + TelemetryDropped *int `json:"telemetry_dropped,omitempty"` + + // TelemetryExcluded Number of forwarded client commands that produced no `cdp_command` event because their method is listed in `control.cdp.excluded_methods`. Configuration rather than loss, so it is counted apart from `telemetry_dropped`. + TelemetryExcluded *int `json:"telemetry_excluded,omitempty"` +} + +// BrowserCdpDisconnectEventDataReason Why the connection ended. `client_close`: the client initiated the close. `upstream_changed`: Chromium restarted mid-session and the proxy tore down so the client could reconnect against the new upstream. `upstream_error`: upstream dial or message pump errored. `context_cancelled`: the request context was cancelled (typically server shutdown). +type BrowserCdpDisconnectEventDataReason string + +// BrowserCdpDomFocusCommandData Sanitized `DOM.focus` arguments. Canonical input: `DOM.focus` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpDomFocusCommandData struct { + // BackendNodeId Opaque backend DOM node identifier the command targeted. + BackendNodeId *int `json:"backend_node_id,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpDomFocusCommandDataMethod `json:"method"` + + // NodeId Opaque DOM node identifier the command targeted. + NodeId *int `json:"node_id,omitempty"` + + // ObjectId Opaque Runtime remote object identifier the command targeted. Clipped to 128 characters; a longer value is not a real identifier. + ObjectId *string `json:"object_id,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpDomFocusCommandDataMethod defines model for BrowserCdpDomFocusCommandData.Method. +type BrowserCdpDomFocusCommandDataMethod string + +// BrowserCdpDomScrollIntoViewIfNeededCommandData Sanitized `DOM.scrollIntoViewIfNeeded` arguments. Canonical input: `DOM.scrollIntoViewIfNeeded` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpDomScrollIntoViewIfNeededCommandData struct { + // BackendNodeId Opaque backend DOM node identifier the command targeted. + BackendNodeId *int `json:"backend_node_id,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpDomScrollIntoViewIfNeededCommandDataMethod `json:"method"` + + // NodeId Opaque DOM node identifier the command targeted. + NodeId *int `json:"node_id,omitempty"` + + // ObjectId Opaque Runtime remote object identifier the command targeted. Clipped to 128 characters; a longer value is not a real identifier. + ObjectId *string `json:"object_id,omitempty"` + + // RectHeight Height of the rect the command scrolled to. + RectHeight *float64 `json:"rect_height,omitempty"` + + // RectWidth Width of the rect the command scrolled to. + RectWidth *float64 `json:"rect_width,omitempty"` + + // RectX X offset of the rect the command scrolled to, relative to the node. + RectX *float64 `json:"rect_x,omitempty"` + + // RectY Y offset of the rect the command scrolled to, relative to the node. + RectY *float64 `json:"rect_y,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpDomScrollIntoViewIfNeededCommandDataMethod defines model for BrowserCdpDomScrollIntoViewIfNeededCommandData.Method. +type BrowserCdpDomScrollIntoViewIfNeededCommandDataMethod string + +// BrowserCdpDomSetFileInputFilesCommandData Sanitized `DOM.setFileInputFiles` arguments. Canonical input: `DOM.setFileInputFiles` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpDomSetFileInputFilesCommandData struct { + // BackendNodeId Opaque backend DOM node identifier the command targeted. + BackendNodeId *int `json:"backend_node_id,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // FileCount Number of files handed to the input. File paths are never captured. + FileCount int `json:"file_count"` + Method BrowserCdpDomSetFileInputFilesCommandDataMethod `json:"method"` + + // NodeId Opaque DOM node identifier the command targeted. + NodeId *int `json:"node_id,omitempty"` + + // ObjectId Opaque Runtime remote object identifier the command targeted. Clipped to 128 characters; a longer value is not a real identifier. + ObjectId *string `json:"object_id,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpDomSetFileInputFilesCommandDataMethod defines model for BrowserCdpDomSetFileInputFilesCommandData.Method. +type BrowserCdpDomSetFileInputFilesCommandDataMethod string + +// BrowserCdpDragEventType Drag event phase. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpDragEventType string + +// BrowserCdpDragMimeCategory Top-level MIME category of a drag item, from the IANA registry rather than the protocol; a drag item's subtype names the file, so only the category is reported. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpDragMimeCategory string + +// BrowserCdpGestureSourceType Input source a synthesized gesture emulates. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpGestureSourceType string + +// BrowserCdpInputCancelDraggingCommandData Sanitized `Input.cancelDragging` arguments. Canonical input: `Input.cancelDragging` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputCancelDraggingCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpInputCancelDraggingCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpInputCancelDraggingCommandDataMethod defines model for BrowserCdpInputCancelDraggingCommandData.Method. +type BrowserCdpInputCancelDraggingCommandDataMethod string + +// BrowserCdpInputDispatchDragEventCommandData Sanitized `Input.dispatchDragEvent` arguments. Canonical input: `Input.dispatchDragEvent` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputDispatchDragEventCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // DragFileCount Number of files in the drag payload. File paths are never captured. + DragFileCount *int `json:"drag_file_count,omitempty"` + + // DragItemCount Number of items in the drag payload. Item contents are never captured. + DragItemCount *int `json:"drag_item_count,omitempty"` + + // DragMimeCategories Distinct top-level MIME categories of the drag items (e.g. `text`, `image`, `application`). Subtypes and contents are never captured. A value the protocol does not define is reported as `other`. + DragMimeCategories *[]BrowserCdpDragMimeCategory `json:"drag_mime_categories,omitempty"` + + // DragOperationsMask Bit field of allowed drag operations (1=copy, 2=link, 16=move). + DragOperationsMask *int `json:"drag_operations_mask,omitempty"` + + // EventType Drag event phase: `dragEnter`, `dragOver`, `drop` or `dragCancel`. A value the protocol does not define is reported as `other`. + EventType BrowserCdpDragEventType `json:"event_type"` + Method BrowserCdpInputDispatchDragEventCommandDataMethod `json:"method"` + + // Modifiers Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, 8=Shift). + Modifiers *int `json:"modifiers,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // X Viewport x coordinate in CSS pixels. + X *float64 `json:"x,omitempty"` + + // Y Viewport y coordinate in CSS pixels. + Y *float64 `json:"y,omitempty"` +} + +// BrowserCdpInputDispatchDragEventCommandDataMethod defines model for BrowserCdpInputDispatchDragEventCommandData.Method. +type BrowserCdpInputDispatchDragEventCommandDataMethod string + +// BrowserCdpInputDispatchKeyEventCommandData Sanitized `Input.dispatchKeyEvent` arguments. Canonical input: `Input.dispatchKeyEvent` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputDispatchKeyEventCommandData struct { + // AutoRepeat Whether the event was generated by key repeat. + AutoRepeat *bool `json:"auto_repeat,omitempty"` + + // CommandCount Number of editing commands (e.g. `selectAll`) carried by the event. + CommandCount *int `json:"command_count,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // EventType Key event phase: `keyDown`, `keyUp`, `rawKeyDown` or `char`. A value the protocol does not define is reported as `other`. + EventType BrowserCdpKeyEventType `json:"event_type"` + + // IsKeypad Whether the key is on the numeric keypad. + IsKeypad *bool `json:"is_keypad,omitempty"` + + // IsSystemKey Whether the event is a system key event. + IsSystemKey *bool `json:"is_system_key,omitempty"` + + // Location Keyboard location (1=left, 2=right, 3=numpad). + Location *int `json:"location,omitempty"` + Method BrowserCdpInputDispatchKeyEventCommandDataMethod `json:"method"` + + // Modifiers Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, 8=Shift). + Modifiers *int `json:"modifiers,omitempty"` + + // NamedKey Key that commands the page rather than typing into it (e.g. `Enter`, `Tab`, `ArrowDown`, `F5`). Keys that produce a character are never captured; those are counted by `text_length`. + NamedKey *string `json:"named_key,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TextLength Number of characters the command submitted. The text itself is never captured. + TextLength *int `json:"text_length,omitempty"` +} + +// BrowserCdpInputDispatchKeyEventCommandDataMethod defines model for BrowserCdpInputDispatchKeyEventCommandData.Method. +type BrowserCdpInputDispatchKeyEventCommandDataMethod string + +// BrowserCdpInputDispatchMouseEventCommandData Sanitized `Input.dispatchMouseEvent` arguments. Canonical input: `Input.dispatchMouseEvent` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputDispatchMouseEventCommandData struct { + // Button Button named by the command (`none`, `left`, `middle`, `right`, `back`, `forward`). A value the protocol does not define is reported as `other`. + Button *BrowserCdpMouseButton `json:"button,omitempty"` + + // Buttons Bit field of buttons held down. Non-zero on a `mouseMoved` means the move is a drag path. + Buttons *int `json:"buttons,omitempty"` + + // ClickCount Number of times the button was clicked (2 is a double click). + ClickCount *int `json:"click_count,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // DeltaX Horizontal scroll delta, for `mouseWheel`. + DeltaX *float64 `json:"delta_x,omitempty"` + + // DeltaY Vertical scroll delta, for `mouseWheel`. + DeltaY *float64 `json:"delta_y,omitempty"` + + // EventType Mouse event phase: `mousePressed`, `mouseReleased`, `mouseMoved` or `mouseWheel`. A value the protocol does not define is reported as `other`. + EventType BrowserCdpMouseEventType `json:"event_type"` + + // Force Normalized pressure, 0 to 1. + Force *float64 `json:"force,omitempty"` + Method BrowserCdpInputDispatchMouseEventCommandDataMethod `json:"method"` + + // Modifiers Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, 8=Shift). + Modifiers *int `json:"modifiers,omitempty"` + + // PointerType Pointer that generated the event (`mouse` or `pen`). A value the protocol does not define is reported as `other`. + PointerType *BrowserCdpPointerType `json:"pointer_type,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TangentialPressure Normalized tangential pressure, -1 to 1. + TangentialPressure *float64 `json:"tangential_pressure,omitempty"` + + // TiltX Pen tilt from the Y-Z plane, in degrees. + TiltX *float64 `json:"tilt_x,omitempty"` + + // TiltY Pen tilt from the X-Z plane, in degrees. + TiltY *float64 `json:"tilt_y,omitempty"` + + // Twist Pen clockwise rotation, in degrees. + Twist *int `json:"twist,omitempty"` + + // X Viewport x coordinate in CSS pixels. + X *float64 `json:"x,omitempty"` + + // Y Viewport y coordinate in CSS pixels. + Y *float64 `json:"y,omitempty"` +} + +// BrowserCdpInputDispatchMouseEventCommandDataMethod defines model for BrowserCdpInputDispatchMouseEventCommandData.Method. +type BrowserCdpInputDispatchMouseEventCommandDataMethod string + +// BrowserCdpInputDispatchTouchEventCommandData Sanitized `Input.dispatchTouchEvent` arguments. Canonical input: `Input.dispatchTouchEvent` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputDispatchTouchEventCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // EventType Touch event phase: `touchStart`, `touchEnd`, `touchMove` or `touchCancel`. A value the protocol does not define is reported as `other`. + EventType BrowserCdpTouchEventType `json:"event_type"` + + // Force Normalized pressure of the first touch point, 0 to 1. + Force *float64 `json:"force,omitempty"` + Method BrowserCdpInputDispatchTouchEventCommandDataMethod `json:"method"` + + // Modifiers Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, 8=Shift). + Modifiers *int `json:"modifiers,omitempty"` + + // RadiusX Horizontal radius of the first touch point. + RadiusX *float64 `json:"radius_x,omitempty"` + + // RadiusY Vertical radius of the first touch point. + RadiusY *float64 `json:"radius_y,omitempty"` + + // RotationAngle Rotation of the first touch point, in degrees. + RotationAngle *float64 `json:"rotation_angle,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TangentialPressure Normalized tangential pressure of the first touch point, -1 to 1. + TangentialPressure *float64 `json:"tangential_pressure,omitempty"` + + // TiltX Tilt of the first touch point from the Y-Z plane, in degrees. + TiltX *float64 `json:"tilt_x,omitempty"` + + // TiltY Tilt of the first touch point from the X-Z plane, in degrees. + TiltY *float64 `json:"tilt_y,omitempty"` + + // TouchPointCount Number of active touch points the command carried. + TouchPointCount int `json:"touch_point_count"` + + // Twist Clockwise rotation of the first touch point, in degrees. + Twist *int `json:"twist,omitempty"` + + // X Viewport x coordinate of the first touch point. Touch coordinates live inside `touchPoints`, so this is the primary point rather than a command-level argument. + X *float64 `json:"x,omitempty"` + + // Y Viewport y coordinate of the first touch point. + Y *float64 `json:"y,omitempty"` +} + +// BrowserCdpInputDispatchTouchEventCommandDataMethod defines model for BrowserCdpInputDispatchTouchEventCommandData.Method. +type BrowserCdpInputDispatchTouchEventCommandDataMethod string + +// BrowserCdpInputEmulateTouchFromMouseEventCommandData Sanitized `Input.emulateTouchFromMouseEvent` arguments. Canonical input: `Input.emulateTouchFromMouseEvent` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputEmulateTouchFromMouseEventCommandData struct { + // Button Button named by the command. A value the protocol does not define is reported as `other`. + Button *BrowserCdpMouseButton `json:"button,omitempty"` + + // ClickCount Number of times the button was clicked. + ClickCount *int `json:"click_count,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // DeltaX Horizontal scroll delta. + DeltaX *float64 `json:"delta_x,omitempty"` + + // DeltaY Vertical scroll delta. + DeltaY *float64 `json:"delta_y,omitempty"` + + // EventType Mouse event phase being emulated as touch. A value the protocol does not define is reported as `other`. + EventType BrowserCdpMouseEventType `json:"event_type"` + Method BrowserCdpInputEmulateTouchFromMouseEventCommandDataMethod `json:"method"` + + // Modifiers Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, 8=Shift). + Modifiers *int `json:"modifiers,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // X Viewport x coordinate in CSS pixels. + X *float64 `json:"x,omitempty"` + + // Y Viewport y coordinate in CSS pixels. + Y *float64 `json:"y,omitempty"` +} + +// BrowserCdpInputEmulateTouchFromMouseEventCommandDataMethod defines model for BrowserCdpInputEmulateTouchFromMouseEventCommandData.Method. +type BrowserCdpInputEmulateTouchFromMouseEventCommandDataMethod string + +// BrowserCdpInputImeSetCompositionCommandData Sanitized `Input.imeSetComposition` arguments. Canonical input: `Input.imeSetComposition` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputImeSetCompositionCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpInputImeSetCompositionCommandDataMethod `json:"method"` + + // ReplacementEnd Replacement range end offset. + ReplacementEnd *int `json:"replacement_end,omitempty"` + + // ReplacementStart Replacement range start offset. + ReplacementStart *int `json:"replacement_start,omitempty"` + + // SelectionEnd Selection end offset within the composition. + SelectionEnd *int `json:"selection_end,omitempty"` + + // SelectionStart Selection start offset within the composition. + SelectionStart *int `json:"selection_start,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TextLength Number of characters in the composition. The text itself is never captured. + TextLength int `json:"text_length"` +} + +// BrowserCdpInputImeSetCompositionCommandDataMethod defines model for BrowserCdpInputImeSetCompositionCommandData.Method. +type BrowserCdpInputImeSetCompositionCommandDataMethod string + +// BrowserCdpInputInsertTextCommandData Sanitized `Input.insertText` arguments. Canonical input: `Input.insertText` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputInsertTextCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpInputInsertTextCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TextLength Number of characters inserted. The text itself is never captured. + TextLength int `json:"text_length"` +} + +// BrowserCdpInputInsertTextCommandDataMethod defines model for BrowserCdpInputInsertTextCommandData.Method. +type BrowserCdpInputInsertTextCommandDataMethod string + +// BrowserCdpInputSynthesizePinchGestureCommandData Sanitized `Input.synthesizePinchGesture` arguments. Canonical input: `Input.synthesizePinchGesture` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputSynthesizePinchGestureCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // GestureSourceType Input source the synthesized gesture emulates. A value the protocol does not define is reported as `other`. + GestureSourceType *BrowserCdpGestureSourceType `json:"gesture_source_type,omitempty"` + Method BrowserCdpInputSynthesizePinchGestureCommandDataMethod `json:"method"` + + // RelativeSpeed Relative pointer speed, in pixels per second. + RelativeSpeed *int `json:"relative_speed,omitempty"` + + // ScaleFactor Relative scale of the pinch (>1 zooms in). + ScaleFactor *float64 `json:"scale_factor,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // X Viewport x coordinate in CSS pixels. + X *float64 `json:"x,omitempty"` + + // Y Viewport y coordinate in CSS pixels. + Y *float64 `json:"y,omitempty"` +} + +// BrowserCdpInputSynthesizePinchGestureCommandDataMethod defines model for BrowserCdpInputSynthesizePinchGestureCommandData.Method. +type BrowserCdpInputSynthesizePinchGestureCommandDataMethod string + +// BrowserCdpInputSynthesizeScrollGestureCommandData Sanitized `Input.synthesizeScrollGesture` arguments. Canonical input: `Input.synthesizeScrollGesture` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputSynthesizeScrollGestureCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // GestureSourceType Input source the synthesized gesture emulates. A value the protocol does not define is reported as `other`. + GestureSourceType *BrowserCdpGestureSourceType `json:"gesture_source_type,omitempty"` + Method BrowserCdpInputSynthesizeScrollGestureCommandDataMethod `json:"method"` + + // PreventFling Whether fling was suppressed. + PreventFling *bool `json:"prevent_fling,omitempty"` + + // RepeatCount Number of additional repeats of the scroll. + RepeatCount *int `json:"repeat_count,omitempty"` + + // RepeatDelayMs Delay between repeats, in milliseconds. + RepeatDelayMs *int `json:"repeat_delay_ms,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // Speed Swipe speed in pixels per second. + Speed *int `json:"speed,omitempty"` + + // X Viewport x coordinate in CSS pixels. + X *float64 `json:"x,omitempty"` + + // XDistance Horizontal scroll distance in CSS pixels; positive scrolls left. + XDistance *float64 `json:"x_distance,omitempty"` + + // XOverscroll Additional horizontal distance scrolled past the end. + XOverscroll *float64 `json:"x_overscroll,omitempty"` + + // Y Viewport y coordinate in CSS pixels. + Y *float64 `json:"y,omitempty"` + + // YDistance Vertical scroll distance in CSS pixels; positive scrolls up. + YDistance *float64 `json:"y_distance,omitempty"` + + // YOverscroll Additional vertical distance scrolled past the end. + YOverscroll *float64 `json:"y_overscroll,omitempty"` +} + +// BrowserCdpInputSynthesizeScrollGestureCommandDataMethod defines model for BrowserCdpInputSynthesizeScrollGestureCommandData.Method. +type BrowserCdpInputSynthesizeScrollGestureCommandDataMethod string + +// BrowserCdpInputSynthesizeTapGestureCommandData Sanitized `Input.synthesizeTapGesture` arguments. Canonical input: `Input.synthesizeTapGesture` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpInputSynthesizeTapGestureCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // Duration Duration between touchdown and touchup, in milliseconds. + Duration *int `json:"duration,omitempty"` + + // GestureSourceType Input source the synthesized gesture emulates. A value the protocol does not define is reported as `other`. + GestureSourceType *BrowserCdpGestureSourceType `json:"gesture_source_type,omitempty"` + Method BrowserCdpInputSynthesizeTapGestureCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TapCount Number of times to tap (2 is a double tap). + TapCount *int `json:"tap_count,omitempty"` + + // X Viewport x coordinate in CSS pixels. + X *float64 `json:"x,omitempty"` + + // Y Viewport y coordinate in CSS pixels. + Y *float64 `json:"y,omitempty"` +} + +// BrowserCdpInputSynthesizeTapGestureCommandDataMethod defines model for BrowserCdpInputSynthesizeTapGestureCommandData.Method. +type BrowserCdpInputSynthesizeTapGestureCommandDataMethod string + +// BrowserCdpKeyEventType Key event phase. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpKeyEventType string + +// BrowserCdpMouseButton Mouse button named by a command. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpMouseButton string + +// BrowserCdpMouseEventType Mouse event phase. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpMouseEventType string + +// BrowserCdpPageBringToFrontCommandData Sanitized `Page.bringToFront` arguments. Canonical input: `Page.bringToFront` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageBringToFrontCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpPageBringToFrontCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageBringToFrontCommandDataMethod defines model for BrowserCdpPageBringToFrontCommandData.Method. +type BrowserCdpPageBringToFrontCommandDataMethod string + +// BrowserCdpPageCaptureScreenshotCommandData Sanitized `Page.captureScreenshot` arguments. Canonical input: `Page.captureScreenshot` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageCaptureScreenshotCommandData struct { + // CaptureBeyondViewport Whether the capture extended past the viewport. + CaptureBeyondViewport *bool `json:"capture_beyond_viewport,omitempty"` + + // ClipHeight Clip region height in CSS pixels. + ClipHeight *float64 `json:"clip_height,omitempty"` + + // ClipScale Clip region page scale factor. + ClipScale *float64 `json:"clip_scale,omitempty"` + + // ClipWidth Clip region width in CSS pixels. + ClipWidth *float64 `json:"clip_width,omitempty"` + + // ClipX Clip region x offset in CSS pixels. + ClipX *float64 `json:"clip_x,omitempty"` + + // ClipY Clip region y offset in CSS pixels. + ClipY *float64 `json:"clip_y,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // Format Image format requested (`jpeg`, `png` or `webp`). A value the protocol does not define is reported as `other`. + Format *BrowserCdpScreenshotFormat `json:"format,omitempty"` + + // FromSurface Whether the capture was taken from the surface rather than the view. + FromSurface *bool `json:"from_surface,omitempty"` + Method BrowserCdpPageCaptureScreenshotCommandDataMethod `json:"method"` + + // OptimizeForSpeed Whether encoding favored speed over size. + OptimizeForSpeed *bool `json:"optimize_for_speed,omitempty"` + + // Quality Compression quality, 0 to 100, for lossy formats. + Quality *int `json:"quality,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageCaptureScreenshotCommandDataMethod defines model for BrowserCdpPageCaptureScreenshotCommandData.Method. +type BrowserCdpPageCaptureScreenshotCommandDataMethod string + +// BrowserCdpPageCaptureSnapshotCommandData Sanitized `Page.captureSnapshot` arguments. Canonical input: `Page.captureSnapshot` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageCaptureSnapshotCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // Format Snapshot format requested (`mhtml`). A value the protocol does not define is reported as `other`. + Format *BrowserCdpSnapshotFormat `json:"format,omitempty"` + Method BrowserCdpPageCaptureSnapshotCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageCaptureSnapshotCommandDataMethod defines model for BrowserCdpPageCaptureSnapshotCommandData.Method. +type BrowserCdpPageCaptureSnapshotCommandDataMethod string + +// BrowserCdpPageCloseCommandData Sanitized `Page.close` arguments. Canonical input: `Page.close` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageCloseCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpPageCloseCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageCloseCommandDataMethod defines model for BrowserCdpPageCloseCommandData.Method. +type BrowserCdpPageCloseCommandDataMethod string + +// BrowserCdpPageHandleJavaScriptDialogCommandData Sanitized `Page.handleJavaScriptDialog` arguments. Canonical input: `Page.handleJavaScriptDialog` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageHandleJavaScriptDialogCommandData struct { + // Accept Whether the dialog was accepted or dismissed. + Accept bool `json:"accept"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpPageHandleJavaScriptDialogCommandDataMethod `json:"method"` + + // PromptTextLength Number of characters entered into a prompt dialog. The text itself is never captured. + PromptTextLength *int `json:"prompt_text_length,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageHandleJavaScriptDialogCommandDataMethod defines model for BrowserCdpPageHandleJavaScriptDialogCommandData.Method. +type BrowserCdpPageHandleJavaScriptDialogCommandDataMethod string + +// BrowserCdpPageNavigateCommandData Sanitized `Page.navigate` arguments. Canonical input: `Page.navigate` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageNavigateCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // FrameId Opaque frame identifier. Clipped to 128 characters; a longer value is not a real identifier. + FrameId *string `json:"frame_id,omitempty"` + Method BrowserCdpPageNavigateCommandDataMethod `json:"method"` + + // ReferrerPolicy Referrer policy named by the command. A value the protocol does not define is reported as `other`. + ReferrerPolicy *BrowserCdpReferrerPolicy `json:"referrer_policy,omitempty"` + + // ReferrerPresent Whether the command carried a referrer. The referrer itself is never captured. + ReferrerPresent *bool `json:"referrer_present,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TransitionType Navigation reason reported by the caller (e.g. `link`, `typed`, `reload`). A value the protocol does not define is reported as `other`. + TransitionType *BrowserCdpTransitionType `json:"transition_type,omitempty"` + + // UrlScheme Scheme of the destination URL (e.g. `https`, `about`, `data`). The rest of the URL is never captured. + UrlScheme *string `json:"url_scheme,omitempty"` +} + +// BrowserCdpPageNavigateCommandDataMethod defines model for BrowserCdpPageNavigateCommandData.Method. +type BrowserCdpPageNavigateCommandDataMethod string + +// BrowserCdpPageNavigateToHistoryEntryCommandData Sanitized `Page.navigateToHistoryEntry` arguments. Canonical input: `Page.navigateToHistoryEntry` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageNavigateToHistoryEntryCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // EntryId History entry the command navigated to. + EntryId int `json:"entry_id"` + Method BrowserCdpPageNavigateToHistoryEntryCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageNavigateToHistoryEntryCommandDataMethod defines model for BrowserCdpPageNavigateToHistoryEntryCommandData.Method. +type BrowserCdpPageNavigateToHistoryEntryCommandDataMethod string + +// BrowserCdpPagePrintToPdfCommandData Sanitized `Page.printToPDF` arguments. Canonical input: `Page.printToPDF` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPagePrintToPdfCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // DisplayHeaderFooter Whether a header and footer were rendered. + DisplayHeaderFooter *bool `json:"display_header_footer,omitempty"` + + // FooterTemplatePresent Whether a footer template was supplied. The template itself is never captured. + FooterTemplatePresent *bool `json:"footer_template_present,omitempty"` + + // GenerateDocumentOutline Whether a document outline was embedded. + GenerateDocumentOutline *bool `json:"generate_document_outline,omitempty"` + + // GenerateTaggedPdf Whether a tagged (accessible) PDF was requested. + GenerateTaggedPdf *bool `json:"generate_tagged_pdf,omitempty"` + + // HeaderTemplatePresent Whether a header template was supplied. The template itself is never captured. + HeaderTemplatePresent *bool `json:"header_template_present,omitempty"` + + // Landscape Whether the page was laid out in landscape. + Landscape *bool `json:"landscape,omitempty"` + + // MarginBottom Bottom margin in inches. + MarginBottom *float64 `json:"margin_bottom,omitempty"` + + // MarginLeft Left margin in inches. + MarginLeft *float64 `json:"margin_left,omitempty"` + + // MarginRight Right margin in inches. + MarginRight *float64 `json:"margin_right,omitempty"` + + // MarginTop Top margin in inches. + MarginTop *float64 `json:"margin_top,omitempty"` + Method BrowserCdpPagePrintToPdfCommandDataMethod `json:"method"` + + // PageRangesPresent Whether a page range was supplied. + PageRangesPresent *bool `json:"page_ranges_present,omitempty"` + + // PaperHeight Paper height in inches. + PaperHeight *float64 `json:"paper_height,omitempty"` + + // PaperWidth Paper width in inches. + PaperWidth *float64 `json:"paper_width,omitempty"` + + // PreferCssPageSize Whether the CSS page size was preferred over the paper size. + PreferCssPageSize *bool `json:"prefer_css_page_size,omitempty"` + + // PrintBackground Whether background graphics were printed. + PrintBackground *bool `json:"print_background,omitempty"` + + // Scale Page render scale. + Scale *float64 `json:"scale,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TransferMode How the PDF was returned (`ReturnAsBase64` or `ReturnAsStream`). A value the protocol does not define is reported as `other`. + TransferMode *BrowserCdpPdfTransferMode `json:"transfer_mode,omitempty"` +} + +// BrowserCdpPagePrintToPdfCommandDataMethod defines model for BrowserCdpPagePrintToPdfCommandData.Method. +type BrowserCdpPagePrintToPdfCommandDataMethod string + +// BrowserCdpPageReloadCommandData Sanitized `Page.reload` arguments. Canonical input: `Page.reload` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageReloadCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // IgnoreCache Whether the reload bypassed the cache. + IgnoreCache *bool `json:"ignore_cache,omitempty"` + + // LoaderId Opaque document loader identifier. Clipped to 128 characters; a longer value is not a real identifier. + LoaderId *string `json:"loader_id,omitempty"` + Method BrowserCdpPageReloadCommandDataMethod `json:"method"` + + // ScriptLength Number of characters in the injected script. + ScriptLength *int `json:"script_length,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageReloadCommandDataMethod defines model for BrowserCdpPageReloadCommandData.Method. +type BrowserCdpPageReloadCommandDataMethod string + +// BrowserCdpPageSetWebLifecycleStateCommandData Sanitized `Page.setWebLifecycleState` arguments. Canonical input: `Page.setWebLifecycleState` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageSetWebLifecycleStateCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpPageSetWebLifecycleStateCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // State Lifecycle state applied (`frozen` or `active`). A value the protocol does not define is reported as `other`. + State BrowserCdpWebLifecycleState `json:"state"` +} + +// BrowserCdpPageSetWebLifecycleStateCommandDataMethod defines model for BrowserCdpPageSetWebLifecycleStateCommandData.Method. +type BrowserCdpPageSetWebLifecycleStateCommandDataMethod string + +// BrowserCdpPageStartScreencastCommandData Sanitized `Page.startScreencast` arguments. Canonical input: `Page.startScreencast` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageStartScreencastCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // EveryNthFrame Frame sampling interval. + EveryNthFrame *int `json:"every_nth_frame,omitempty"` + + // Format Frame format requested (`jpeg` or `png`). A value the protocol does not define is reported as `other`. + Format *BrowserCdpScreencastFormat `json:"format,omitempty"` + + // MaxHeight Maximum frame height in pixels. + MaxHeight *int `json:"max_height,omitempty"` + + // MaxWidth Maximum frame width in pixels. + MaxWidth *int `json:"max_width,omitempty"` + Method BrowserCdpPageStartScreencastCommandDataMethod `json:"method"` + + // Quality Compression quality, 0 to 100. + Quality *int `json:"quality,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageStartScreencastCommandDataMethod defines model for BrowserCdpPageStartScreencastCommandData.Method. +type BrowserCdpPageStartScreencastCommandDataMethod string + +// BrowserCdpPageStopLoadingCommandData Sanitized `Page.stopLoading` arguments. Canonical input: `Page.stopLoading` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageStopLoadingCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpPageStopLoadingCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageStopLoadingCommandDataMethod defines model for BrowserCdpPageStopLoadingCommandData.Method. +type BrowserCdpPageStopLoadingCommandDataMethod string + +// BrowserCdpPageStopScreencastCommandData Sanitized `Page.stopScreencast` arguments. Canonical input: `Page.stopScreencast` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpPageStopScreencastCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpPageStopScreencastCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpPageStopScreencastCommandDataMethod defines model for BrowserCdpPageStopScreencastCommandData.Method. +type BrowserCdpPageStopScreencastCommandDataMethod string + +// BrowserCdpPdfTransferMode How a generated PDF is returned. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpPdfTransferMode string + +// BrowserCdpPointerType Pointer that generated an input event. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpPointerType string + +// BrowserCdpReferrerPolicy Referrer policy named by a navigation. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpReferrerPolicy string + +// BrowserCdpScreencastFormat Frame format requested for a screencast. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpScreencastFormat string + +// BrowserCdpScreenshotFormat Image format requested for a screenshot. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpScreenshotFormat string + +// BrowserCdpSnapshotFormat Format requested for a page snapshot. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpSnapshotFormat string + +// BrowserCdpTargetActivateTargetCommandData Sanitized `Target.activateTarget` arguments. Canonical input: `Target.activateTarget` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpTargetActivateTargetCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpTargetActivateTargetCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TargetId Opaque target identifier. Clipped to 128 characters; a longer value is not a real identifier. + TargetId string `json:"target_id"` +} + +// BrowserCdpTargetActivateTargetCommandDataMethod defines model for BrowserCdpTargetActivateTargetCommandData.Method. +type BrowserCdpTargetActivateTargetCommandDataMethod string + +// BrowserCdpTargetCloseTargetCommandData Sanitized `Target.closeTarget` arguments. Canonical input: `Target.closeTarget` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpTargetCloseTargetCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpTargetCloseTargetCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TargetId Opaque target identifier. Clipped to 128 characters; a longer value is not a real identifier. + TargetId string `json:"target_id"` +} + +// BrowserCdpTargetCloseTargetCommandDataMethod defines model for BrowserCdpTargetCloseTargetCommandData.Method. +type BrowserCdpTargetCloseTargetCommandDataMethod string + +// BrowserCdpTargetCreateBrowserContextCommandData Sanitized `Target.createBrowserContext` arguments. Canonical input: `Target.createBrowserContext` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpTargetCreateBrowserContextCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // DisposeOnDetach Whether the context is disposed when the debugging session detaches. + DisposeOnDetach *bool `json:"dispose_on_detach,omitempty"` + Method BrowserCdpTargetCreateBrowserContextCommandDataMethod `json:"method"` + + // ProxyBypassListPresent Whether a proxy bypass list was configured. + ProxyBypassListPresent *bool `json:"proxy_bypass_list_present,omitempty"` + + // ProxyServerPresent Whether a proxy was configured. The proxy address is never captured. + ProxyServerPresent *bool `json:"proxy_server_present,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // UniversalNetworkAccessOriginCount Number of origins granted universal network access. The origins themselves are never captured. + UniversalNetworkAccessOriginCount *int `json:"universal_network_access_origin_count,omitempty"` +} + +// BrowserCdpTargetCreateBrowserContextCommandDataMethod defines model for BrowserCdpTargetCreateBrowserContextCommandData.Method. +type BrowserCdpTargetCreateBrowserContextCommandDataMethod string + +// BrowserCdpTargetCreateTargetCommandData Sanitized `Target.createTarget` arguments. Canonical input: `Target.createTarget` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpTargetCreateTargetCommandData struct { + // Background Whether the target was created in the background. + Background *bool `json:"background,omitempty"` + + // BrowserContextId Opaque browser context identifier. Clipped to 128 characters; a longer value is not a real identifier. + BrowserContextId *string `json:"browser_context_id,omitempty"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + + // EnableBeginFrameControl Whether BeginFrame control was enabled (headless only). + EnableBeginFrameControl *bool `json:"enable_begin_frame_control,omitempty"` + + // Focus Whether the new target was focused. + Focus *bool `json:"focus,omitempty"` + + // ForTab Whether a tab target rather than a page target was created. + ForTab *bool `json:"for_tab,omitempty"` + + // Height Window height in DIP. + Height *int `json:"height,omitempty"` + + // Hidden Whether the target was created hidden. + Hidden *bool `json:"hidden,omitempty"` + + // Left Window x position in screen coordinates. + Left *int `json:"left,omitempty"` + Method BrowserCdpTargetCreateTargetCommandDataMethod `json:"method"` + + // NewWindow Whether a new window was requested. + NewWindow *bool `json:"new_window,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // Top Window y position in screen coordinates. + Top *int `json:"top,omitempty"` + + // UrlScheme Scheme of the destination URL (e.g. `https`, `about`, `data`). The rest of the URL is never captured. + UrlScheme *string `json:"url_scheme,omitempty"` + + // Width Window width in DIP. + Width *int `json:"width,omitempty"` + + // WindowState Window state requested (`normal`, `minimized`, `maximized`, `fullscreen`). A value the protocol does not define is reported as `other`. + WindowState *BrowserCdpWindowState `json:"window_state,omitempty"` +} + +// BrowserCdpTargetCreateTargetCommandDataMethod defines model for BrowserCdpTargetCreateTargetCommandData.Method. +type BrowserCdpTargetCreateTargetCommandDataMethod string + +// BrowserCdpTargetDisposeBrowserContextCommandData Sanitized `Target.disposeBrowserContext` arguments. Canonical input: `Target.disposeBrowserContext` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpTargetDisposeBrowserContextCommandData struct { + // BrowserContextId Opaque browser context identifier. Clipped to 128 characters; a longer value is not a real identifier. + BrowserContextId string `json:"browser_context_id"` + + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpTargetDisposeBrowserContextCommandDataMethod `json:"method"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` +} + +// BrowserCdpTargetDisposeBrowserContextCommandDataMethod defines model for BrowserCdpTargetDisposeBrowserContextCommandData.Method. +type BrowserCdpTargetDisposeBrowserContextCommandDataMethod string + +// BrowserCdpTargetOpenDevToolsCommandData Sanitized `Target.openDevTools` arguments. Canonical input: `Target.openDevTools` in devtools-protocol@2d019e73, pinned at https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. Every argument of this command has a retained or redacted decision in lib/devtoolsproxy/testdata/cdp_arguments.yaml. +type BrowserCdpTargetOpenDevToolsCommandData struct { + // CommandId The command's JSON-RPC id, so the command can be joined to the result the browser returned for it. Absent when the client sent none. + CommandId *int64 `json:"command_id,omitempty"` + + // ConnectionId Identifies the CDP proxy connection the command arrived on, matching `cdp_connect` and `cdp_disconnect`. Two clients driving the same browser are told apart by this. + ConnectionId *string `json:"connection_id,omitempty"` + Method BrowserCdpTargetOpenDevToolsCommandDataMethod `json:"method"` + + // PanelId DevTools panel opened. Clipped to 128 characters; a longer value is not a real identifier. + PanelId *string `json:"panel_id,omitempty"` + + // SessionId CDP session identifier the command was addressed to. Absent for browser-level commands. Clipped to 128 characters. + SessionId *string `json:"session_id,omitempty"` + + // TargetId Opaque target identifier. Clipped to 128 characters; a longer value is not a real identifier. + TargetId string `json:"target_id"` +} + +// BrowserCdpTargetOpenDevToolsCommandDataMethod defines model for BrowserCdpTargetOpenDevToolsCommandData.Method. +type BrowserCdpTargetOpenDevToolsCommandDataMethod string + +// BrowserCdpTouchEventType Touch event phase. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpTouchEventType string + +// BrowserCdpTransitionType Navigation reason reported by the caller. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpTransitionType string + +// BrowserCdpWebLifecycleState Page lifecycle state applied. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpWebLifecycleState string + +// BrowserCdpWindowState Browser window state requested. Canonical values from devtools-protocol@2d019e73. `other` stands for a value outside that set, so a client cannot put an arbitrary string into the stream. +type BrowserCdpWindowState string + +// BrowserConsoleErrorEvent A browser console error or uncaught JavaScript exception event. Emitted from two distinct CDP sources with different data shapes. Runtime.consoleAPICalled (console.error calls) produces level, text, args, and stack_trace. Runtime.exceptionThrown (uncaught exceptions) produces text, line, column, source_url, and stack_trace. Fields not applicable to the source are absent. +type BrowserConsoleErrorEvent struct { + Category BrowserConsoleErrorEventCategory `json:"category"` + Data *BrowserConsoleErrorEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserConsoleErrorEventType `json:"type"` +} + +// BrowserConsoleErrorEventCategory defines model for BrowserConsoleErrorEvent.Category. +type BrowserConsoleErrorEventCategory string + +// BrowserConsoleErrorEventType defines model for BrowserConsoleErrorEvent.Type. +type BrowserConsoleErrorEventType string + +// BrowserConsoleErrorEventData defines model for BrowserConsoleErrorEventData. +type BrowserConsoleErrorEventData struct { + // Args All console arguments coerced to strings. Present only when sourced from Runtime.consoleAPICalled. + Args *[]string `json:"args,omitempty"` + + // Column Column number in the script where the exception was thrown. Present only when sourced from Runtime.exceptionThrown. + Column *int `json:"column,omitempty"` + + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // Level CDP console type value, always "error". Present only when sourced from Runtime.consoleAPICalled. + Level *string `json:"level,omitempty"` + + // Line Line number in the script where the exception was thrown. Present only when sourced from Runtime.exceptionThrown. + Line *int `json:"line,omitempty"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` + + // SourceUrl URL of the script file that threw the exception. Present only when sourced from Runtime.exceptionThrown. + SourceUrl *string `json:"source_url,omitempty"` + + // StackTrace CDP Runtime.StackTrace representing the JavaScript call stack at the time of an event. Fields use CDP naming conventions rather than snake_case to match the Chrome DevTools Protocol wire format. + StackTrace *BrowserCallStack `json:"stack_trace,omitempty"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Text Human-readable error text, as the browser console would display it. For console.error() calls, the first argument coerced to a string. For uncaught exceptions, the prefix and error message, e.g. "Uncaught Error: boom" or "Uncaught (in promise) TypeError: x is not a function". + Text string `json:"text"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` +} + +// BrowserConsoleLogEvent A browser console log event (console.log, console.info, console.warn, etc.). +type BrowserConsoleLogEvent struct { + Category BrowserConsoleLogEventCategory `json:"category"` + Data *BrowserConsoleLogEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserConsoleLogEventType `json:"type"` +} + +// BrowserConsoleLogEventCategory defines model for BrowserConsoleLogEvent.Category. +type BrowserConsoleLogEventCategory string + +// BrowserConsoleLogEventType defines model for BrowserConsoleLogEvent.Type. +type BrowserConsoleLogEventType string + +// BrowserConsoleLogEventData defines model for BrowserConsoleLogEventData. +type BrowserConsoleLogEventData struct { + // Args All console arguments coerced to strings. + Args *[]string `json:"args,omitempty"` + + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // Level CDP Runtime.consoleAPICalled type, passed through unfiltered from Chrome. `error` is routed to console_error events instead; all other CDP console types appear here. See CDP spec for the full enum. + Level string `json:"level"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` + + // StackTrace CDP Runtime.StackTrace representing the JavaScript call stack at the time of an event. Fields use CDP naming conventions rather than snake_case to match the Chrome DevTools Protocol wire format. + StackTrace *BrowserCallStack `json:"stack_trace,omitempty"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Text First console argument coerced to string. + Text string `json:"text"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` +} + +// BrowserEventContext Browser event context stamped by the browser monitor onto all CDP-sourced events. Identifies the target, frame, and navigation epoch in which the event occurred. +type BrowserEventContext struct { + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` +} + +// BrowserEventSource Provenance metadata identifying which producer emitted the event. +type BrowserEventSource struct { + // Event Producer-specific event name (e.g. `Runtime.consoleAPICalled` for CDP-sourced console events). + Event *string `json:"event,omitempty"` + + // Kind Event producer. `cdp`: Chrome DevTools Protocol events from the browser. `kernel_api`: Kernel API server (reserved for server-generated events). `extension`: injected Chrome extension. `local_process`: system process running alongside the browser. + Kind BrowserEventSourceKind `json:"kind"` + + // Metadata Producer-specific context (e.g. CDP target/session/frame IDs). + Metadata *map[string]string `json:"metadata,omitempty"` +} + +// BrowserEventSourceKind Event producer. `cdp`: Chrome DevTools Protocol events from the browser. `kernel_api`: Kernel API server (reserved for server-generated events). `extension`: injected Chrome extension. `local_process`: system process running alongside the browser. +type BrowserEventSourceKind string + +// BrowserHttpHeaders HTTP headers map forwarded as-is from CDP without normalization. Values are typically strings but may be any JSON type. +type BrowserHttpHeaders map[string]interface{} + +// BrowserInteractionClickEvent A browser user click event captured via injected page script. +type BrowserInteractionClickEvent struct { + Category BrowserInteractionClickEventCategory `json:"category"` + Data *BrowserInteractionClickEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserInteractionClickEventType `json:"type"` +} + +// BrowserInteractionClickEventCategory defines model for BrowserInteractionClickEvent.Category. +type BrowserInteractionClickEventCategory string + +// BrowserInteractionClickEventType defines model for BrowserInteractionClickEvent.Type. +type BrowserInteractionClickEventType string + +// BrowserInteractionClickEventData defines model for BrowserInteractionClickEventData. +type BrowserInteractionClickEventData struct { + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // Selector CSS selector path to the clicked element. + Selector string `json:"selector"` + + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` + + // Tag HTML tag name of the clicked element in uppercase (e.g. BUTTON, A, DIV). + Tag string `json:"tag"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Text Visible text content of the clicked element, trimmed. + Text *string `json:"text,omitempty"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` + + // X Viewport x-coordinate of the click in CSS pixels. + X int `json:"x"` + + // Y Viewport y-coordinate of the click in CSS pixels. + Y int `json:"y"` +} + +// BrowserInteractionKeyEvent A browser keyboard event captured via injected page script. +type BrowserInteractionKeyEvent struct { + Category BrowserInteractionKeyEventCategory `json:"category"` + Data *BrowserInteractionKeyEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserInteractionKeyEventType `json:"type"` +} + +// BrowserInteractionKeyEventCategory defines model for BrowserInteractionKeyEvent.Category. +type BrowserInteractionKeyEventCategory string + +// BrowserInteractionKeyEventType defines model for BrowserInteractionKeyEvent.Type. +type BrowserInteractionKeyEventType string + +// BrowserInteractionKeyEventData defines model for BrowserInteractionKeyEventData. +type BrowserInteractionKeyEventData struct { + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // Key Key value from the KeyboardEvent (e.g. Enter, Backspace, a). + Key string `json:"key"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // Selector CSS selector path to the element that had focus when the key was pressed. + Selector string `json:"selector"` + + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` + + // Tag HTML tag name of the focused element in uppercase (e.g. INPUT, TEXTAREA, DIV). + Tag string `json:"tag"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` +} + +// BrowserInteractionScrollSettledEvent A browser scroll settled event emitted after scroll position stops changing, captured via injected page script. +type BrowserInteractionScrollSettledEvent struct { + Category BrowserInteractionScrollSettledEventCategory `json:"category"` + Data *BrowserInteractionScrollSettledEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserInteractionScrollSettledEventType `json:"type"` +} + +// BrowserInteractionScrollSettledEventCategory defines model for BrowserInteractionScrollSettledEvent.Category. +type BrowserInteractionScrollSettledEventCategory string + +// BrowserInteractionScrollSettledEventType defines model for BrowserInteractionScrollSettledEvent.Type. +type BrowserInteractionScrollSettledEventType string + +// BrowserInteractionScrollSettledEventData defines model for BrowserInteractionScrollSettledEventData. +type BrowserInteractionScrollSettledEventData struct { + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // FromX Scroll x-position at the start of the scroll gesture in CSS pixels. + FromX int `json:"from_x"` + + // FromY Scroll y-position at the start of the scroll gesture in CSS pixels. + FromY int `json:"from_y"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetSelector CSS selector path to the scrolled element. + TargetSelector string `json:"target_selector"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // ToX Final scroll x-position after the gesture settled in CSS pixels. + ToX int `json:"to_x"` + + // ToY Final scroll y-position after the gesture settled in CSS pixels. + ToY int `json:"to_y"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` } -// BrowserCallStack CDP Runtime.StackTrace representing the JavaScript call stack at the time of an event. Fields use CDP naming conventions rather than snake_case to match the Chrome DevTools Protocol wire format. -type BrowserCallStack struct { - // CallFrames Ordered list of call frames, outermost first. - CallFrames []struct { - // ColumnNumber Zero-based column number within the line. - ColumnNumber int `json:"columnNumber"` +// BrowserLiveViewConnectEvent A live view client connected to the headful browser's WebRTC server (Neko). Headful only; not emitted for headless images. +type BrowserLiveViewConnectEvent struct { + Category BrowserLiveViewConnectEventCategory `json:"category"` - // FunctionName JavaScript function name, or empty string for anonymous functions. - FunctionName string `json:"functionName"` + // Data Per-session payload for `live_view_connect` events. + Data *BrowserLiveViewConnectEventData `json:"data,omitempty"` - // LineNumber Zero-based line number within the script. - LineNumber int `json:"lineNumber"` + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` - // ScriptId CDP script identifier. - ScriptId string `json:"scriptId"` + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` - // Url URL or name of the script file. - Url string `json:"url"` - } `json:"callFrames"` + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserLiveViewConnectEventType `json:"type"` +} - // Description Optional label for the stack trace (e.g. async cause). - Description *string `json:"description,omitempty"` +// BrowserLiveViewConnectEventCategory defines model for BrowserLiveViewConnectEvent.Category. +type BrowserLiveViewConnectEventCategory string - // Parent Parent stack trace for async stacks. - Parent *BrowserCallStack `json:"parent,omitempty"` +// BrowserLiveViewConnectEventType defines model for BrowserLiveViewConnectEvent.Type. +type BrowserLiveViewConnectEventType string + +// BrowserLiveViewConnectEventData Per-session payload for `live_view_connect` events. +type BrowserLiveViewConnectEventData struct { + // SessionId Live view session identifier. Stable across reconnects, so a transient network blip can emit two events with the same `session_id`. + SessionId string `json:"session_id"` } -// BrowserCaptchaSolveResultEvent A captcha solve attempt reached a terminal outcome. -type BrowserCaptchaSolveResultEvent struct { - Category BrowserCaptchaSolveResultEventCategory `json:"category"` +// BrowserLiveViewDisconnectEvent A live view client disconnected from the headful browser's WebRTC server (Neko). Pair with `live_view_connect` by `session_id`. +type BrowserLiveViewDisconnectEvent struct { + Category BrowserLiveViewDisconnectEventCategory `json:"category"` - // Data Per-attempt payload for `captcha_solve_result` events. - Data *BrowserCaptchaSolveResultEventData `json:"data,omitempty"` + // Data Per-session payload for `live_view_disconnect` events. + Data *BrowserLiveViewDisconnectEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -1769,48 +4689,59 @@ type BrowserCaptchaSolveResultEvent struct { // Ts Event timestamp in Unix microseconds. Ts int64 `json:"ts"` - Type BrowserCaptchaSolveResultEventType `json:"type"` + Type BrowserLiveViewDisconnectEventType `json:"type"` } -// BrowserCaptchaSolveResultEventCategory defines model for BrowserCaptchaSolveResultEvent.Category. -type BrowserCaptchaSolveResultEventCategory string - -// BrowserCaptchaSolveResultEventType defines model for BrowserCaptchaSolveResultEvent.Type. -type BrowserCaptchaSolveResultEventType string +// BrowserLiveViewDisconnectEventCategory defines model for BrowserLiveViewDisconnectEvent.Category. +type BrowserLiveViewDisconnectEventCategory string -// BrowserCaptchaSolveResultEventData Per-attempt payload for `captcha_solve_result` events. -type BrowserCaptchaSolveResultEventData struct { - // CaptchaType Captcha vendor family. Producers normalize provider-specific task names into this set: enterprise variants of recaptcha collapse into their version bucket (v2 / v3), and anything not covered (e.g. DataDome, MtCaptcha, plain OCR) is reported as `other`. - CaptchaType BrowserCaptchaSolveResultEventDataCaptchaType `json:"captcha_type"` +// BrowserLiveViewDisconnectEventType defines model for BrowserLiveViewDisconnectEvent.Type. +type BrowserLiveViewDisconnectEventType string - // DurationMs Wall-clock duration from solve start to terminal outcome. +// BrowserLiveViewDisconnectEventData Per-session payload for `live_view_disconnect` events. +type BrowserLiveViewDisconnectEventData struct { + // DurationMs Wall-clock duration of the connection in milliseconds. DurationMs float32 `json:"duration_ms"` - // ErrorCode Solver-specific error code on failure (e.g. `ERROR_CAPTCHA_UNSOLVABLE`). Absent on success. - ErrorCode *string `json:"error_code,omitempty"` + // SessionId Live view session identifier; matches the corresponding `live_view_connect` event. + SessionId string `json:"session_id"` +} - // Status Terminal outcome. `success`: solver returned a usable solution. `failure`: solver returned an error (see `error_code`). `timeout`: solver did not return within the caller's wait budget. `abandoned`: caller cancelled or the page navigated away mid-solve. - Status BrowserCaptchaSolveResultEventDataStatus `json:"status"` +// BrowserMonitorDisconnectedEvent The CDP connection to Chrome was lost. Telemetry events may be dropped until monitor_reconnected arrives. Treat any in-progress computed state (network_idle, page_layout_settled) as unreliable until then. +type BrowserMonitorDisconnectedEvent struct { + Category BrowserMonitorDisconnectedEventCategory `json:"category"` + Data *BrowserMonitorDisconnectedEventData `json:"data,omitempty"` - // TaskId Solver-assigned identifier. Opaque, useful for support cross-references. - TaskId *string `json:"task_id,omitempty"` + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` - // WebsiteHost Host of the page where the captcha was solved. - WebsiteHost *string `json:"website_host,omitempty"` + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` - // WebsitePath Path of the page where the captcha was solved. Query string excluded. - WebsitePath *string `json:"website_path,omitempty"` + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserMonitorDisconnectedEventType `json:"type"` } -// BrowserCaptchaSolveResultEventDataCaptchaType Captcha vendor family. Producers normalize provider-specific task names into this set: enterprise variants of recaptcha collapse into their version bucket (v2 / v3), and anything not covered (e.g. DataDome, MtCaptcha, plain OCR) is reported as `other`. -type BrowserCaptchaSolveResultEventDataCaptchaType string +// BrowserMonitorDisconnectedEventCategory defines model for BrowserMonitorDisconnectedEvent.Category. +type BrowserMonitorDisconnectedEventCategory string -// BrowserCaptchaSolveResultEventDataStatus Terminal outcome. `success`: solver returned a usable solution. `failure`: solver returned an error (see `error_code`). `timeout`: solver did not return within the caller's wait budget. `abandoned`: caller cancelled or the page navigated away mid-solve. -type BrowserCaptchaSolveResultEventDataStatus string +// BrowserMonitorDisconnectedEventType defines model for BrowserMonitorDisconnectedEvent.Type. +type BrowserMonitorDisconnectedEventType string -// BrowserCdpConnectEvent An external client (e.g. customer SDK, Playwright, Puppeteer) connected to the CDP WebSocket proxy on this VM. -type BrowserCdpConnectEvent struct { - Category BrowserCdpConnectEventCategory `json:"category"` +// BrowserMonitorDisconnectedEventData defines model for BrowserMonitorDisconnectedEventData. +type BrowserMonitorDisconnectedEventData struct { + // Reason Reason for the disconnection. chrome_restarted: Chrome process restarted. + Reason BrowserMonitorDisconnectedEventDataReason `json:"reason"` +} + +// BrowserMonitorDisconnectedEventDataReason Reason for the disconnection. chrome_restarted: Chrome process restarted. +type BrowserMonitorDisconnectedEventDataReason string + +// BrowserMonitorInitFailedEvent The CDP session could not be initialized. +type BrowserMonitorInitFailedEvent struct { + Category BrowserMonitorInitFailedEventCategory `json:"category"` + Data *BrowserMonitorInitFailedEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -1819,22 +4750,26 @@ type BrowserCdpConnectEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserCdpConnectEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserMonitorInitFailedEventType `json:"type"` } -// BrowserCdpConnectEventCategory defines model for BrowserCdpConnectEvent.Category. -type BrowserCdpConnectEventCategory string +// BrowserMonitorInitFailedEventCategory defines model for BrowserMonitorInitFailedEvent.Category. +type BrowserMonitorInitFailedEventCategory string -// BrowserCdpConnectEventType defines model for BrowserCdpConnectEvent.Type. -type BrowserCdpConnectEventType string +// BrowserMonitorInitFailedEventType defines model for BrowserMonitorInitFailedEvent.Type. +type BrowserMonitorInitFailedEventType string -// BrowserCdpDisconnectEvent An external client disconnected from the CDP WebSocket proxy on this VM. Pair with the immediately preceding `cdp_connect` on the same stream. -type BrowserCdpDisconnectEvent struct { - Category BrowserCdpDisconnectEventCategory `json:"category"` +// BrowserMonitorInitFailedEventData defines model for BrowserMonitorInitFailedEventData. +type BrowserMonitorInitFailedEventData struct { + // Step The CDP method or initialization step that failed (e.g. Target.setAutoAttach). + Step string `json:"step"` +} - // Data Per-disconnect payload for `cdp_disconnect` events. - Data *BrowserCdpDisconnectEventData `json:"data,omitempty"` +// BrowserMonitorReconnectFailedEvent The CDP connection to Chrome could not be re-established after exhausting all reconnection attempts. No further telemetry events will arrive on this session. +type BrowserMonitorReconnectFailedEvent struct { + Category BrowserMonitorReconnectFailedEventCategory `json:"category"` + Data *BrowserMonitorReconnectFailedEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -1843,35 +4778,109 @@ type BrowserCdpDisconnectEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserCdpDisconnectEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserMonitorReconnectFailedEventType `json:"type"` } -// BrowserCdpDisconnectEventCategory defines model for BrowserCdpDisconnectEvent.Category. -type BrowserCdpDisconnectEventCategory string +// BrowserMonitorReconnectFailedEventCategory defines model for BrowserMonitorReconnectFailedEvent.Category. +type BrowserMonitorReconnectFailedEventCategory string -// BrowserCdpDisconnectEventType defines model for BrowserCdpDisconnectEvent.Type. -type BrowserCdpDisconnectEventType string +// BrowserMonitorReconnectFailedEventType defines model for BrowserMonitorReconnectFailedEvent.Type. +type BrowserMonitorReconnectFailedEventType string -// BrowserCdpDisconnectEventData Per-disconnect payload for `cdp_disconnect` events. -type BrowserCdpDisconnectEventData struct { - // DurationMs Wall-clock duration of the connection in milliseconds. - DurationMs float32 `json:"duration_ms"` +// BrowserMonitorReconnectFailedEventData defines model for BrowserMonitorReconnectFailedEventData. +type BrowserMonitorReconnectFailedEventData struct { + // Reason Reason for the reconnection failure. reconnect_exhausted: all retry attempts were used up without successfully restoring the CDP connection. + Reason BrowserMonitorReconnectFailedEventDataReason `json:"reason"` +} + +// BrowserMonitorReconnectFailedEventDataReason Reason for the reconnection failure. reconnect_exhausted: all retry attempts were used up without successfully restoring the CDP connection. +type BrowserMonitorReconnectFailedEventDataReason string + +// BrowserMonitorReconnectedEvent The CDP connection to Chrome was successfully re-established after a disconnection. Events emitted during the gap are lost. Computed state is reset, so navigation and network tracking restart fresh from this point. +type BrowserMonitorReconnectedEvent struct { + Category BrowserMonitorReconnectedEventCategory `json:"category"` + Data *BrowserMonitorReconnectedEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserMonitorReconnectedEventType `json:"type"` +} + +// BrowserMonitorReconnectedEventCategory defines model for BrowserMonitorReconnectedEvent.Category. +type BrowserMonitorReconnectedEventCategory string + +// BrowserMonitorReconnectedEventType defines model for BrowserMonitorReconnectedEvent.Type. +type BrowserMonitorReconnectedEventType string + +// BrowserMonitorReconnectedEventData defines model for BrowserMonitorReconnectedEventData. +type BrowserMonitorReconnectedEventData struct { + // ReconnectDurationMs Wall-clock time in milliseconds taken to reconnect after the disconnection. + ReconnectDurationMs int64 `json:"reconnect_duration_ms"` +} + +// BrowserMonitorScreenshotEvent A periodic screenshot of the browser viewport. +type BrowserMonitorScreenshotEvent struct { + Category BrowserMonitorScreenshotEventCategory `json:"category"` + Data *BrowserMonitorScreenshotEventData `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` + + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` + + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserMonitorScreenshotEventType `json:"type"` +} + +// BrowserMonitorScreenshotEventCategory defines model for BrowserMonitorScreenshotEvent.Category. +type BrowserMonitorScreenshotEventCategory string + +// BrowserMonitorScreenshotEventType defines model for BrowserMonitorScreenshotEvent.Type. +type BrowserMonitorScreenshotEventType string + +// BrowserMonitorScreenshotEventData defines model for BrowserMonitorScreenshotEventData. +type BrowserMonitorScreenshotEventData struct { + // Png Base64-encoded PNG screenshot of the browser viewport. + Png []byte `json:"png"` +} + +// BrowserNetworkIdleEvent A browser network idle event emitted after a 500ms quiet period with no in-flight HTTP requests. +type BrowserNetworkIdleEvent struct { + Category BrowserNetworkIdleEventCategory `json:"category"` + + // Data Browser event context stamped by the browser monitor onto all CDP-sourced events. Identifies the target, frame, and navigation epoch in which the event occurred. + Data *BrowserEventContext `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source BrowserEventSource `json:"source"` - // MessageCount Number of CDP messages relayed across the connection in either direction. - MessageCount int `json:"message_count"` + // Truncated True if the data field was truncated due to size limits. + Truncated *bool `json:"truncated,omitempty"` - // Reason Why the connection ended. `client_close`: the client initiated the close. `upstream_changed`: Chromium restarted mid-session and the proxy tore down so the client could reconnect against the new upstream. `upstream_error`: upstream dial or message pump errored. `context_cancelled`: the request context was cancelled (typically server shutdown). - Reason BrowserCdpDisconnectEventDataReason `json:"reason"` + // Ts Event timestamp in Unix microseconds. + Ts int64 `json:"ts"` + Type BrowserNetworkIdleEventType `json:"type"` } -// BrowserCdpDisconnectEventDataReason Why the connection ended. `client_close`: the client initiated the close. `upstream_changed`: Chromium restarted mid-session and the proxy tore down so the client could reconnect against the new upstream. `upstream_error`: upstream dial or message pump errored. `context_cancelled`: the request context was cancelled (typically server shutdown). -type BrowserCdpDisconnectEventDataReason string +// BrowserNetworkIdleEventCategory defines model for BrowserNetworkIdleEvent.Category. +type BrowserNetworkIdleEventCategory string -// BrowserConsoleErrorEvent A browser console error or uncaught JavaScript exception event. Emitted from two distinct CDP sources with different data shapes. Runtime.consoleAPICalled (console.error calls) produces level, text, args, and stack_trace. Runtime.exceptionThrown (uncaught exceptions) produces text, line, column, source_url, and stack_trace. Fields not applicable to the source are absent. -type BrowserConsoleErrorEvent struct { - Category BrowserConsoleErrorEventCategory `json:"category"` - Data *BrowserConsoleErrorEventData `json:"data,omitempty"` +// BrowserNetworkIdleEventType defines model for BrowserNetworkIdleEvent.Type. +type BrowserNetworkIdleEventType string + +// BrowserNetworkLoadingFailedEvent A browser network loading failed event. If the request was already in flight when CDP attached (no prior `network_request` was emitted for it), `url`, `frame_id`, `loader_id`, and `resource_type` are absent; `BrowserEventContext` is partially populated in that case. +type BrowserNetworkLoadingFailedEvent struct { + Category BrowserNetworkLoadingFailedEventCategory `json:"category"` + Data *BrowserNetworkLoadingFailedEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -1880,47 +4889,41 @@ type BrowserConsoleErrorEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserConsoleErrorEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserNetworkLoadingFailedEventType `json:"type"` } -// BrowserConsoleErrorEventCategory defines model for BrowserConsoleErrorEvent.Category. -type BrowserConsoleErrorEventCategory string +// BrowserNetworkLoadingFailedEventCategory defines model for BrowserNetworkLoadingFailedEvent.Category. +type BrowserNetworkLoadingFailedEventCategory string -// BrowserConsoleErrorEventType defines model for BrowserConsoleErrorEvent.Type. -type BrowserConsoleErrorEventType string +// BrowserNetworkLoadingFailedEventType defines model for BrowserNetworkLoadingFailedEvent.Type. +type BrowserNetworkLoadingFailedEventType string -// BrowserConsoleErrorEventData defines model for BrowserConsoleErrorEventData. -type BrowserConsoleErrorEventData struct { - // Args All console arguments coerced to strings. Present only when sourced from Runtime.consoleAPICalled. - Args *[]string `json:"args,omitempty"` +// BrowserNetworkLoadingFailedEventData defines model for BrowserNetworkLoadingFailedEventData. +type BrowserNetworkLoadingFailedEventData struct { + // Canceled True if the request was canceled by the browser or page script. + Canceled bool `json:"canceled"` - // Column Column number in the script where the exception was thrown. Present only when sourced from Runtime.exceptionThrown. - Column *int `json:"column,omitempty"` + // ErrorText Network error description (e.g. net::ERR_CONNECTION_REFUSED). + ErrorText string `json:"error_text"` // FrameId CDP frame identifier within the target. FrameId *string `json:"frame_id,omitempty"` - // Level CDP console type value, always "error". Present only when sourced from Runtime.consoleAPICalled. - Level *string `json:"level,omitempty"` - - // Line Line number in the script where the exception was thrown. Present only when sourced from Runtime.exceptionThrown. - Line *int `json:"line,omitempty"` - // LoaderId CDP document loader identifier, reset on each navigation. LoaderId *string `json:"loader_id,omitempty"` // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. NavSeq int64 `json:"nav_seq"` - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` + // RequestId CDP request identifier matching the originating network_request event. + RequestId string `json:"request_id"` - // SourceUrl URL of the script file that threw the exception. Present only when sourced from Runtime.exceptionThrown. - SourceUrl *string `json:"source_url,omitempty"` + // ResourceType CDP Network.ResourceType for the request, passed through as-is from Chrome. Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. + ResourceType *string `json:"resource_type,omitempty"` - // StackTrace CDP Runtime.StackTrace representing the JavaScript call stack at the time of an event. Fields use CDP naming conventions rather than snake_case to match the Chrome DevTools Protocol wire format. - StackTrace *BrowserCallStack `json:"stack_trace,omitempty"` + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` // TargetId Browser target identifier (stable across navigations within a tab). TargetId string `json:"target_id"` @@ -1928,17 +4931,14 @@ type BrowserConsoleErrorEventData struct { // TargetType CDP target type of the page that produced the event. TargetType BrowserTargetType `json:"target_type"` - // Text Human-readable error text, as the browser console would display it. For console.error() calls, the first argument coerced to a string. For uncaught exceptions, the prefix and error message, e.g. "Uncaught Error: boom" or "Uncaught (in promise) TypeError: x is not a function". - Text string `json:"text"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. Url *string `json:"url,omitempty"` } -// BrowserConsoleLogEvent A browser console log event (console.log, console.info, console.warn, etc.). -type BrowserConsoleLogEvent struct { - Category BrowserConsoleLogEventCategory `json:"category"` - Data *BrowserConsoleLogEventData `json:"data,omitempty"` +// BrowserNetworkRequestEvent A browser network request sent event. +type BrowserNetworkRequestEvent struct { + Category BrowserNetworkRequestEventCategory `json:"category"` + Data *BrowserNetworkRequestEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -1947,62 +4947,53 @@ type BrowserConsoleLogEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserConsoleLogEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserNetworkRequestEventType `json:"type"` } -// BrowserConsoleLogEventCategory defines model for BrowserConsoleLogEvent.Category. -type BrowserConsoleLogEventCategory string +// BrowserNetworkRequestEventCategory defines model for BrowserNetworkRequestEvent.Category. +type BrowserNetworkRequestEventCategory string -// BrowserConsoleLogEventType defines model for BrowserConsoleLogEvent.Type. -type BrowserConsoleLogEventType string +// BrowserNetworkRequestEventType defines model for BrowserNetworkRequestEvent.Type. +type BrowserNetworkRequestEventType string -// BrowserConsoleLogEventData defines model for BrowserConsoleLogEventData. -type BrowserConsoleLogEventData struct { - // Args All console arguments coerced to strings. - Args *[]string `json:"args,omitempty"` +// BrowserNetworkRequestEventData defines model for BrowserNetworkRequestEventData. +type BrowserNetworkRequestEventData struct { + // DocumentUrl URL of the document that initiated the request. + DocumentUrl string `json:"document_url"` // FrameId CDP frame identifier within the target. FrameId *string `json:"frame_id,omitempty"` - // Level CDP Runtime.consoleAPICalled type, passed through unfiltered from Chrome. `error` is routed to console_error events instead; all other CDP console types appear here. See CDP spec for the full enum. - Level string `json:"level"` + // Headers Request headers. + Headers BrowserHttpHeaders `json:"headers"` + + // InitiatorType CDP Initiator.type indicating what caused the request, passed through as-is from Chrome. Known values include script, parser, preload, and other. + InitiatorType string `json:"initiator_type"` + + // IsRedirect True if this request is the result of a redirect. + IsRedirect *bool `json:"is_redirect,omitempty"` // LoaderId CDP document loader identifier, reset on each navigation. LoaderId *string `json:"loader_id,omitempty"` + // Method HTTP method as sent on the wire (e.g. GET, POST). + Method string `json:"method"` + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. NavSeq int64 `json:"nav_seq"` - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` - - // StackTrace CDP Runtime.StackTrace representing the JavaScript call stack at the time of an event. Fields use CDP naming conventions rather than snake_case to match the Chrome DevTools Protocol wire format. - StackTrace *BrowserCallStack `json:"stack_trace,omitempty"` - - // TargetId Browser target identifier (stable across navigations within a tab). - TargetId string `json:"target_id"` - - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` - - // Text First console argument coerced to string. - Text string `json:"text"` - - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` -} + // PostData Request body for POST/PUT requests, if available. + PostData *string `json:"post_data,omitempty"` -// BrowserEventContext Browser event context stamped by the browser monitor onto all CDP-sourced events. Identifies the target, frame, and navigation epoch in which the event occurred. -type BrowserEventContext struct { - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` + // RedirectUrl Original URL before the redirect, present when is_redirect is true. + RedirectUrl *string `json:"redirect_url,omitempty"` - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` + // RequestId CDP request identifier, unique within the session. + RequestId string `json:"request_id"` - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` + // ResourceType CDP Network.ResourceType for the request, passed through as-is from Chrome. Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. + ResourceType *string `json:"resource_type,omitempty"` // SessionId CDP session identifier for the target connection. SessionId string `json:"session_id"` @@ -2017,28 +5008,10 @@ type BrowserEventContext struct { Url *string `json:"url,omitempty"` } -// BrowserEventSource Provenance metadata identifying which producer emitted the event. -type BrowserEventSource struct { - // Event Producer-specific event name (e.g. `Runtime.consoleAPICalled` for CDP-sourced console events). - Event *string `json:"event,omitempty"` - - // Kind Event producer. `cdp`: Chrome DevTools Protocol events from the browser. `kernel_api`: Kernel API server (reserved for server-generated events). `extension`: injected Chrome extension. `local_process`: system process running alongside the browser. - Kind BrowserEventSourceKind `json:"kind"` - - // Metadata Producer-specific context (e.g. CDP target/session/frame IDs). - Metadata *map[string]string `json:"metadata,omitempty"` -} - -// BrowserEventSourceKind Event producer. `cdp`: Chrome DevTools Protocol events from the browser. `kernel_api`: Kernel API server (reserved for server-generated events). `extension`: injected Chrome extension. `local_process`: system process running alongside the browser. -type BrowserEventSourceKind string - -// BrowserHttpHeaders HTTP headers map forwarded as-is from CDP without normalization. Values are typically strings but may be any JSON type. -type BrowserHttpHeaders map[string]interface{} - -// BrowserInteractionClickEvent A browser user click event captured via injected page script. -type BrowserInteractionClickEvent struct { - Category BrowserInteractionClickEventCategory `json:"category"` - Data *BrowserInteractionClickEventData `json:"data,omitempty"` +// BrowserNetworkResponseEvent A browser network response received event. Fired after the response body is fully received, not when headers arrive. +type BrowserNetworkResponseEvent struct { + Category BrowserNetworkResponseEventCategory `json:"category"` + Data *BrowserNetworkResponseEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2047,35 +5020,53 @@ type BrowserInteractionClickEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserInteractionClickEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserNetworkResponseEventType `json:"type"` } -// BrowserInteractionClickEventCategory defines model for BrowserInteractionClickEvent.Category. -type BrowserInteractionClickEventCategory string +// BrowserNetworkResponseEventCategory defines model for BrowserNetworkResponseEvent.Category. +type BrowserNetworkResponseEventCategory string -// BrowserInteractionClickEventType defines model for BrowserInteractionClickEvent.Type. -type BrowserInteractionClickEventType string +// BrowserNetworkResponseEventType defines model for BrowserNetworkResponseEvent.Type. +type BrowserNetworkResponseEventType string + +// BrowserNetworkResponseEventData defines model for BrowserNetworkResponseEventData. +type BrowserNetworkResponseEventData struct { + // Body Truncated response body, present only for text MIME types. + Body *string `json:"body,omitempty"` -// BrowserInteractionClickEventData defines model for BrowserInteractionClickEventData. -type BrowserInteractionClickEventData struct { // FrameId CDP frame identifier within the target. FrameId *string `json:"frame_id,omitempty"` + // Headers Response headers. + Headers BrowserHttpHeaders `json:"headers"` + // LoaderId CDP document loader identifier, reset on each navigation. LoaderId *string `json:"loader_id,omitempty"` + // Method HTTP method of the original request. + Method string `json:"method"` + + // MimeType MIME type of the response (e.g. text/html, application/json). + MimeType *string `json:"mime_type,omitempty"` + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. NavSeq int64 `json:"nav_seq"` - // Selector CSS selector path to the clicked element. - Selector string `json:"selector"` + // RequestId CDP request identifier matching the originating network_request event. + RequestId string `json:"request_id"` + + // ResourceType CDP Network.ResourceType for the request, passed through as-is from Chrome. Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. + ResourceType *string `json:"resource_type,omitempty"` // SessionId CDP session identifier for the target connection. SessionId string `json:"session_id"` - // Tag HTML tag name of the clicked element in uppercase (e.g. BUTTON, A, DIV). - Tag string `json:"tag"` + // Status HTTP response status code. + Status int `json:"status"` + + // StatusText HTTP response status text (e.g. OK, Not Found). + StatusText *string `json:"status_text,omitempty"` // TargetId Browser target identifier (stable across navigations within a tab). TargetId string `json:"target_id"` @@ -2083,23 +5074,14 @@ type BrowserInteractionClickEventData struct { // TargetType CDP target type of the page that produced the event. TargetType BrowserTargetType `json:"target_type"` - // Text Visible text content of the clicked element, trimmed. - Text *string `json:"text,omitempty"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. Url *string `json:"url,omitempty"` - - // X Viewport x-coordinate of the click in CSS pixels. - X int `json:"x"` - - // Y Viewport y-coordinate of the click in CSS pixels. - Y int `json:"y"` } -// BrowserInteractionKeyEvent A browser keyboard event captured via injected page script. -type BrowserInteractionKeyEvent struct { - Category BrowserInteractionKeyEventCategory `json:"category"` - Data *BrowserInteractionKeyEventData `json:"data,omitempty"` +// BrowserPageCrashedEvent A page's renderer process crashed (an "Aw, Snap!" failure) while the browser process itself stayed alive. Reported on the crashed page's session, with the session and target ids on `source.metadata`. Captured only while the `page` category is enabled. +type BrowserPageCrashedEvent struct { + Category BrowserPageCrashedEventCategory `json:"category"` + Data *BrowserPageCrashedEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2108,53 +5090,32 @@ type BrowserInteractionKeyEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserInteractionKeyEventType `json:"type"` -} - -// BrowserInteractionKeyEventCategory defines model for BrowserInteractionKeyEvent.Category. -type BrowserInteractionKeyEventCategory string - -// BrowserInteractionKeyEventType defines model for BrowserInteractionKeyEvent.Type. -type BrowserInteractionKeyEventType string - -// BrowserInteractionKeyEventData defines model for BrowserInteractionKeyEventData. -type BrowserInteractionKeyEventData struct { - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` - - // Key Key value from the KeyboardEvent (e.g. Enter, Backspace, a). - Key string `json:"key"` - - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` - - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` - - // Selector CSS selector path to the element that had focus when the key was pressed. - Selector string `json:"selector"` + Ts int64 `json:"ts"` + Type BrowserPageCrashedEventType `json:"type"` +} - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` +// BrowserPageCrashedEventCategory defines model for BrowserPageCrashedEvent.Category. +type BrowserPageCrashedEventCategory string - // Tag HTML tag name of the focused element in uppercase (e.g. INPUT, TEXTAREA, DIV). - Tag string `json:"tag"` +// BrowserPageCrashedEventType defines model for BrowserPageCrashedEvent.Type. +type BrowserPageCrashedEventType string - // TargetId Browser target identifier (stable across navigations within a tab). +// BrowserPageCrashedEventData defines model for BrowserPageCrashedEventData. +type BrowserPageCrashedEventData struct { + // TargetId CDP target identifier of the crashed page. TargetId string `json:"target_id"` // TargetType CDP target type of the page that produced the event. TargetType BrowserTargetType `json:"target_type"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` + // Url URL the page was on when its renderer process crashed. + Url string `json:"url"` } -// BrowserInteractionScrollSettledEvent A browser scroll settled event emitted after scroll position stops changing, captured via injected page script. -type BrowserInteractionScrollSettledEvent struct { - Category BrowserInteractionScrollSettledEventCategory `json:"category"` - Data *BrowserInteractionScrollSettledEventData `json:"data,omitempty"` +// BrowserPageDomContentLoadedEvent A browser DOMContentLoaded event (CDP Page.domContentEventFired). +type BrowserPageDomContentLoadedEvent struct { + Category BrowserPageDomContentLoadedEventCategory `json:"category"` + Data *BrowserPageDomContentLoadedEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2163,27 +5124,24 @@ type BrowserInteractionScrollSettledEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserInteractionScrollSettledEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageDomContentLoadedEventType `json:"type"` } -// BrowserInteractionScrollSettledEventCategory defines model for BrowserInteractionScrollSettledEvent.Category. -type BrowserInteractionScrollSettledEventCategory string +// BrowserPageDomContentLoadedEventCategory defines model for BrowserPageDomContentLoadedEvent.Category. +type BrowserPageDomContentLoadedEventCategory string -// BrowserInteractionScrollSettledEventType defines model for BrowserInteractionScrollSettledEvent.Type. -type BrowserInteractionScrollSettledEventType string +// BrowserPageDomContentLoadedEventType defines model for BrowserPageDomContentLoadedEvent.Type. +type BrowserPageDomContentLoadedEventType string + +// BrowserPageDomContentLoadedEventData defines model for BrowserPageDomContentLoadedEventData. +type BrowserPageDomContentLoadedEventData struct { + // CdpTimestamp Chrome monotonic clock value in seconds at which DOMContentLoaded fired, relative to browser process start (not Unix epoch). Use `ts` for wall-clock time. + CdpTimestamp float32 `json:"cdp_timestamp"` -// BrowserInteractionScrollSettledEventData defines model for BrowserInteractionScrollSettledEventData. -type BrowserInteractionScrollSettledEventData struct { // FrameId CDP frame identifier within the target. FrameId *string `json:"frame_id,omitempty"` - // FromX Scroll x-position at the start of the scroll gesture in CSS pixels. - FromX int `json:"from_x"` - - // FromY Scroll y-position at the start of the scroll gesture in CSS pixels. - FromY int `json:"from_y"` - // LoaderId CDP document loader identifier, reset on each navigation. LoaderId *string `json:"loader_id,omitempty"` @@ -2196,28 +5154,19 @@ type BrowserInteractionScrollSettledEventData struct { // TargetId Browser target identifier (stable across navigations within a tab). TargetId string `json:"target_id"` - // TargetSelector CSS selector path to the scrolled element. - TargetSelector string `json:"target_selector"` - // TargetType CDP target type of the page that produced the event. TargetType BrowserTargetType `json:"target_type"` - // ToX Final scroll x-position after the gesture settled in CSS pixels. - ToX int `json:"to_x"` - - // ToY Final scroll y-position after the gesture settled in CSS pixels. - ToY int `json:"to_y"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. Url *string `json:"url,omitempty"` } -// BrowserLiveViewConnectEvent A live view client connected to the headful browser's WebRTC server (Neko). Headful only; not emitted for headless images. -type BrowserLiveViewConnectEvent struct { - Category BrowserLiveViewConnectEventCategory `json:"category"` +// BrowserPageLayoutSettledEvent A browser layout settled event emitted 1 second after page load with no intervening layout shifts, indicating visual stability. Each layout shift resets the 1-second timer. +type BrowserPageLayoutSettledEvent struct { + Category BrowserPageLayoutSettledEventCategory `json:"category"` - // Data Per-session payload for `live_view_connect` events. - Data *BrowserLiveViewConnectEventData `json:"data,omitempty"` + // Data Browser event context stamped by the browser monitor onto all CDP-sourced events. Identifies the target, frame, and navigation epoch in which the event occurred. + Data *BrowserEventContext `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2226,28 +5175,20 @@ type BrowserLiveViewConnectEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserLiveViewConnectEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageLayoutSettledEventType `json:"type"` } -// BrowserLiveViewConnectEventCategory defines model for BrowserLiveViewConnectEvent.Category. -type BrowserLiveViewConnectEventCategory string - -// BrowserLiveViewConnectEventType defines model for BrowserLiveViewConnectEvent.Type. -type BrowserLiveViewConnectEventType string - -// BrowserLiveViewConnectEventData Per-session payload for `live_view_connect` events. -type BrowserLiveViewConnectEventData struct { - // SessionId Live view session identifier. Stable across reconnects, so a transient network blip can emit two events with the same `session_id`. - SessionId string `json:"session_id"` -} +// BrowserPageLayoutSettledEventCategory defines model for BrowserPageLayoutSettledEvent.Category. +type BrowserPageLayoutSettledEventCategory string -// BrowserLiveViewDisconnectEvent A live view client disconnected from the headful browser's WebRTC server (Neko). Pair with `live_view_connect` by `session_id`. -type BrowserLiveViewDisconnectEvent struct { - Category BrowserLiveViewDisconnectEventCategory `json:"category"` +// BrowserPageLayoutSettledEventType defines model for BrowserPageLayoutSettledEvent.Type. +type BrowserPageLayoutSettledEventType string - // Data Per-session payload for `live_view_disconnect` events. - Data *BrowserLiveViewDisconnectEventData `json:"data,omitempty"` +// BrowserPageLayoutShiftEvent A browser cumulative layout shift (CLS) event from the Performance Timeline API. +type BrowserPageLayoutShiftEvent struct { + Category BrowserPageLayoutShiftEventCategory `json:"category"` + Data *BrowserPageLayoutShiftEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2256,29 +5197,62 @@ type BrowserLiveViewDisconnectEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserLiveViewDisconnectEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageLayoutShiftEventType `json:"type"` } -// BrowserLiveViewDisconnectEventCategory defines model for BrowserLiveViewDisconnectEvent.Category. -type BrowserLiveViewDisconnectEventCategory string +// BrowserPageLayoutShiftEventCategory defines model for BrowserPageLayoutShiftEvent.Category. +type BrowserPageLayoutShiftEventCategory string -// BrowserLiveViewDisconnectEventType defines model for BrowserLiveViewDisconnectEvent.Type. -type BrowserLiveViewDisconnectEventType string +// BrowserPageLayoutShiftEventType defines model for BrowserPageLayoutShiftEvent.Type. +type BrowserPageLayoutShiftEventType string -// BrowserLiveViewDisconnectEventData Per-session payload for `live_view_disconnect` events. -type BrowserLiveViewDisconnectEventData struct { - // DurationMs Wall-clock duration of the connection in milliseconds. - DurationMs float32 `json:"duration_ms"` +// BrowserPageLayoutShiftEventData defines model for BrowserPageLayoutShiftEventData. +type BrowserPageLayoutShiftEventData struct { + // Duration Duration of the layout shift entry in milliseconds (always 0 for layout shifts per spec). + Duration float32 `json:"duration"` - // SessionId Live view session identifier; matches the corresponding `live_view_connect` event. + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // LayoutShiftDetails PerformanceLayoutShift attributes from the Performance Timeline entry. + LayoutShiftDetails *struct { + // HadRecentInput True if the layout shift was preceded by user input within 500ms, excluding it from CLS. + HadRecentInput *bool `json:"had_recent_input,omitempty"` + + // Value Layout shift score for this entry (contribution to CLS). + Value *float32 `json:"value,omitempty"` + } `json:"layout_shift_details,omitempty"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // SessionId CDP session identifier for the target connection. SessionId string `json:"session_id"` + + // SourceFrameId CDP frame identifier of the frame where the layout shift occurred. + SourceFrameId string `json:"source_frame_id"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Time Performance Timeline timestamp of the layout shift in milliseconds. + Time float32 `json:"time"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` } -// BrowserMonitorDisconnectedEvent The CDP connection to Chrome was lost. Telemetry events may be dropped until monitor_reconnected arrives. Treat any in-progress computed state (network_idle, page_layout_settled) as unreliable until then. -type BrowserMonitorDisconnectedEvent struct { - Category BrowserMonitorDisconnectedEventCategory `json:"category"` - Data *BrowserMonitorDisconnectedEventData `json:"data,omitempty"` +// BrowserPageLcpEvent A browser Largest Contentful Paint (LCP) event from the Performance Timeline API. +type BrowserPageLcpEvent struct { + Category BrowserPageLcpEventCategory `json:"category"` + Data *BrowserPageLcpEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2287,88 +5261,71 @@ type BrowserMonitorDisconnectedEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserMonitorDisconnectedEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageLcpEventType `json:"type"` } -// BrowserMonitorDisconnectedEventCategory defines model for BrowserMonitorDisconnectedEvent.Category. -type BrowserMonitorDisconnectedEventCategory string - -// BrowserMonitorDisconnectedEventType defines model for BrowserMonitorDisconnectedEvent.Type. -type BrowserMonitorDisconnectedEventType string +// BrowserPageLcpEventCategory defines model for BrowserPageLcpEvent.Category. +type BrowserPageLcpEventCategory string -// BrowserMonitorDisconnectedEventData defines model for BrowserMonitorDisconnectedEventData. -type BrowserMonitorDisconnectedEventData struct { - // Reason Reason for the disconnection. chrome_restarted: Chrome process restarted. - Reason BrowserMonitorDisconnectedEventDataReason `json:"reason"` -} +// BrowserPageLcpEventType defines model for BrowserPageLcpEvent.Type. +type BrowserPageLcpEventType string -// BrowserMonitorDisconnectedEventDataReason Reason for the disconnection. chrome_restarted: Chrome process restarted. -type BrowserMonitorDisconnectedEventDataReason string +// BrowserPageLcpEventData defines model for BrowserPageLcpEventData. +type BrowserPageLcpEventData struct { + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` -// BrowserMonitorInitFailedEvent The CDP session could not be initialized. -type BrowserMonitorInitFailedEvent struct { - Category BrowserMonitorInitFailedEventCategory `json:"category"` - Data *BrowserMonitorInitFailedEventData `json:"data,omitempty"` + // LcpDetails LargestContentfulPaint attributes from the Performance Timeline entry. + LcpDetails *struct { + // ElementId id attribute of the LCP element, if present. + ElementId *string `json:"element_id,omitempty"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // LoadTime Load time of the LCP element in milliseconds. + LoadTime *float32 `json:"load_time,omitempty"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // NodeId CDP DOM node identifier of the LCP element. + NodeId *int `json:"node_id,omitempty"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserMonitorInitFailedEventType `json:"type"` -} + // RenderTime Render time of the LCP element in milliseconds; 0 for cross-origin images without Timing-Allow-Origin. + RenderTime *float32 `json:"render_time,omitempty"` -// BrowserMonitorInitFailedEventCategory defines model for BrowserMonitorInitFailedEvent.Category. -type BrowserMonitorInitFailedEventCategory string + // Size Visible area of the LCP element in pixels squared. + Size *float32 `json:"size,omitempty"` -// BrowserMonitorInitFailedEventType defines model for BrowserMonitorInitFailedEvent.Type. -type BrowserMonitorInitFailedEventType string + // Url URL of the LCP element for image or video elements. + Url *string `json:"url,omitempty"` + } `json:"lcp_details,omitempty"` -// BrowserMonitorInitFailedEventData defines model for BrowserMonitorInitFailedEventData. -type BrowserMonitorInitFailedEventData struct { - // Step The CDP method or initialization step that failed (e.g. Target.setAutoAttach). - Step string `json:"step"` -} + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` -// BrowserMonitorReconnectFailedEvent The CDP connection to Chrome could not be re-established after exhausting all reconnection attempts. No further telemetry events will arrive on this session. -type BrowserMonitorReconnectFailedEvent struct { - Category BrowserMonitorReconnectFailedEventCategory `json:"category"` - Data *BrowserMonitorReconnectFailedEventData `json:"data,omitempty"` + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // SourceFrameId CDP frame identifier of the frame where the LCP element was rendered. + SourceFrameId string `json:"source_frame_id"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserMonitorReconnectFailedEventType `json:"type"` -} + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` -// BrowserMonitorReconnectFailedEventCategory defines model for BrowserMonitorReconnectFailedEvent.Category. -type BrowserMonitorReconnectFailedEventCategory string + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` -// BrowserMonitorReconnectFailedEventType defines model for BrowserMonitorReconnectFailedEvent.Type. -type BrowserMonitorReconnectFailedEventType string + // Time Performance Timeline timestamp of the LCP entry in milliseconds. + Time float32 `json:"time"` -// BrowserMonitorReconnectFailedEventData defines model for BrowserMonitorReconnectFailedEventData. -type BrowserMonitorReconnectFailedEventData struct { - // Reason Reason for the reconnection failure. reconnect_exhausted: all retry attempts were used up without successfully restoring the CDP connection. - Reason BrowserMonitorReconnectFailedEventDataReason `json:"reason"` + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` } -// BrowserMonitorReconnectFailedEventDataReason Reason for the reconnection failure. reconnect_exhausted: all retry attempts were used up without successfully restoring the CDP connection. -type BrowserMonitorReconnectFailedEventDataReason string - -// BrowserMonitorReconnectedEvent The CDP connection to Chrome was successfully re-established after a disconnection. Events emitted during the gap are lost. Computed state is reset, so navigation and network tracking restart fresh from this point. -type BrowserMonitorReconnectedEvent struct { - Category BrowserMonitorReconnectedEventCategory `json:"category"` - Data *BrowserMonitorReconnectedEventData `json:"data,omitempty"` +// BrowserPageLoadEvent A browser page load event (CDP Page.loadEventFired). +type BrowserPageLoadEvent struct { + Category BrowserPageLoadEventCategory `json:"category"` + Data *BrowserPageLoadEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2377,26 +5334,47 @@ type BrowserMonitorReconnectedEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserMonitorReconnectedEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageLoadEventType `json:"type"` } -// BrowserMonitorReconnectedEventCategory defines model for BrowserMonitorReconnectedEvent.Category. -type BrowserMonitorReconnectedEventCategory string +// BrowserPageLoadEventCategory defines model for BrowserPageLoadEvent.Category. +type BrowserPageLoadEventCategory string -// BrowserMonitorReconnectedEventType defines model for BrowserMonitorReconnectedEvent.Type. -type BrowserMonitorReconnectedEventType string +// BrowserPageLoadEventType defines model for BrowserPageLoadEvent.Type. +type BrowserPageLoadEventType string -// BrowserMonitorReconnectedEventData defines model for BrowserMonitorReconnectedEventData. -type BrowserMonitorReconnectedEventData struct { - // ReconnectDurationMs Wall-clock time in milliseconds taken to reconnect after the disconnection. - ReconnectDurationMs int64 `json:"reconnect_duration_ms"` +// BrowserPageLoadEventData defines model for BrowserPageLoadEventData. +type BrowserPageLoadEventData struct { + // CdpTimestamp Chrome monotonic clock value in seconds at which the load event fired, relative to browser process start (not Unix epoch). Use `ts` for wall-clock time. + CdpTimestamp float32 `json:"cdp_timestamp"` + + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,omitempty"` + + // LoaderId CDP document loader identifier, reset on each navigation. + LoaderId *string `json:"loader_id,omitempty"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // SessionId CDP session identifier for the target connection. + SessionId string `json:"session_id"` + + // TargetId Browser target identifier (stable across navigations within a tab). + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. + Url *string `json:"url,omitempty"` } -// BrowserMonitorScreenshotEvent A periodic screenshot of the browser viewport. -type BrowserMonitorScreenshotEvent struct { - Category BrowserMonitorScreenshotEventCategory `json:"category"` - Data *BrowserMonitorScreenshotEventData `json:"data,omitempty"` +// BrowserPageNavigationEvent A browser page navigation started event (CDP Page.frameNavigated). Carries nav context fields inline but not nav_seq, as this event resets the navigation epoch. +type BrowserPageNavigationEvent struct { + Category BrowserPageNavigationEventCategory `json:"category"` + Data *BrowserPageNavigationEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2405,25 +5383,43 @@ type BrowserMonitorScreenshotEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserMonitorScreenshotEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageNavigationEventType `json:"type"` } -// BrowserMonitorScreenshotEventCategory defines model for BrowserMonitorScreenshotEvent.Category. -type BrowserMonitorScreenshotEventCategory string +// BrowserPageNavigationEventCategory defines model for BrowserPageNavigationEvent.Category. +type BrowserPageNavigationEventCategory string -// BrowserMonitorScreenshotEventType defines model for BrowserMonitorScreenshotEvent.Type. -type BrowserMonitorScreenshotEventType string +// BrowserPageNavigationEventType defines model for BrowserPageNavigationEvent.Type. +type BrowserPageNavigationEventType string -// BrowserMonitorScreenshotEventData defines model for BrowserMonitorScreenshotEventData. -type BrowserMonitorScreenshotEventData struct { - // Png Base64-encoded PNG screenshot of the browser viewport. - Png []byte `json:"png"` +// BrowserPageNavigationEventData defines model for BrowserPageNavigationEventData. +type BrowserPageNavigationEventData struct { + // FrameId CDP frame identifier of the navigated frame. + FrameId string `json:"frame_id"` + + // LoaderId New CDP document loader identifier assigned for this navigation. + LoaderId string `json:"loader_id"` + + // ParentFrameId Parent frame identifier for subframe navigations; absent for top-level navigations. + ParentFrameId *string `json:"parent_frame_id,omitempty"` + + // SessionId CDP session identifier. + SessionId string `json:"session_id"` + + // TargetId Browser target identifier. + TargetId string `json:"target_id"` + + // TargetType CDP target type of the page that produced the event. + TargetType BrowserTargetType `json:"target_type"` + + // Url URL navigated to. + Url string `json:"url"` } -// BrowserNetworkIdleEvent A browser network idle event emitted after a 500ms quiet period with no in-flight HTTP requests. -type BrowserNetworkIdleEvent struct { - Category BrowserNetworkIdleEventCategory `json:"category"` +// BrowserPageNavigationSettledEvent Emitted when page_dom_content_loaded and page_layout_settled have both fired for the same navigation, indicating the page is loaded and visually stable. Independent of network_idle; a single pending request does not block it. +type BrowserPageNavigationSettledEvent struct { + Category BrowserPageNavigationSettledEventCategory `json:"category"` // Data Browser event context stamped by the browser monitor onto all CDP-sourced events. Identifies the target, frame, and navigation epoch in which the event occurred. Data *BrowserEventContext `json:"data,omitempty"` @@ -2435,20 +5431,20 @@ type BrowserNetworkIdleEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserNetworkIdleEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageNavigationSettledEventType `json:"type"` } -// BrowserNetworkIdleEventCategory defines model for BrowserNetworkIdleEvent.Category. -type BrowserNetworkIdleEventCategory string +// BrowserPageNavigationSettledEventCategory defines model for BrowserPageNavigationSettledEvent.Category. +type BrowserPageNavigationSettledEventCategory string -// BrowserNetworkIdleEventType defines model for BrowserNetworkIdleEvent.Type. -type BrowserNetworkIdleEventType string +// BrowserPageNavigationSettledEventType defines model for BrowserPageNavigationSettledEvent.Type. +type BrowserPageNavigationSettledEventType string -// BrowserNetworkLoadingFailedEvent A browser network loading failed event. If the request was already in flight when CDP attached (no prior `network_request` was emitted for it), `url`, `frame_id`, `loader_id`, and `resource_type` are absent; `BrowserEventContext` is partially populated in that case. -type BrowserNetworkLoadingFailedEvent struct { - Category BrowserNetworkLoadingFailedEventCategory `json:"category"` - Data *BrowserNetworkLoadingFailedEventData `json:"data,omitempty"` +// BrowserPageTabOpenedEvent A new browser tab or target was opened (CDP Target.attachedToTarget for page targets). Fires before a CDP session is attached to the new target, so `session_id`, `frame_id`, `loader_id`, and `nav_seq` are absent; this event does not compose `BrowserEventContext`. Consumers reading context fields generically should treat it as a special case. +type BrowserPageTabOpenedEvent struct { + Category BrowserPageTabOpenedEventCategory `json:"category"` + Data *BrowserPageTabOpenedEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2457,56 +5453,40 @@ type BrowserNetworkLoadingFailedEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserNetworkLoadingFailedEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPageTabOpenedEventType `json:"type"` } -// BrowserNetworkLoadingFailedEventCategory defines model for BrowserNetworkLoadingFailedEvent.Category. -type BrowserNetworkLoadingFailedEventCategory string - -// BrowserNetworkLoadingFailedEventType defines model for BrowserNetworkLoadingFailedEvent.Type. -type BrowserNetworkLoadingFailedEventType string - -// BrowserNetworkLoadingFailedEventData defines model for BrowserNetworkLoadingFailedEventData. -type BrowserNetworkLoadingFailedEventData struct { - // Canceled True if the request was canceled by the browser or page script. - Canceled bool `json:"canceled"` - - // ErrorText Network error description (e.g. net::ERR_CONNECTION_REFUSED). - ErrorText string `json:"error_text"` - - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` - - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` - - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` - - // RequestId CDP request identifier matching the originating network_request event. - RequestId string `json:"request_id"` +// BrowserPageTabOpenedEventCategory defines model for BrowserPageTabOpenedEvent.Category. +type BrowserPageTabOpenedEventCategory string - // ResourceType CDP Network.ResourceType for the request, passed through as-is from Chrome. Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. - ResourceType *string `json:"resource_type,omitempty"` +// BrowserPageTabOpenedEventType defines model for BrowserPageTabOpenedEvent.Type. +type BrowserPageTabOpenedEventType string - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` +// BrowserPageTabOpenedEventData defines model for BrowserPageTabOpenedEventData. +type BrowserPageTabOpenedEventData struct { + // OpenerId Target identifier of the tab that opened this one, if any. + OpenerId *string `json:"opener_id,omitempty"` - // TargetId Browser target identifier (stable across navigations within a tab). + // TargetId CDP target identifier for the newly opened tab. TargetId string `json:"target_id"` // TargetType CDP target type of the page that produced the event. TargetType BrowserTargetType `json:"target_type"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` + // Title Initial page title of the new tab. + Title *string `json:"title,omitempty"` + + // Url Initial URL of the new tab. + Url string `json:"url"` } -// BrowserNetworkRequestEvent A browser network request sent event. -type BrowserNetworkRequestEvent struct { - Category BrowserNetworkRequestEventCategory `json:"category"` - Data *BrowserNetworkRequestEventData `json:"data,omitempty"` +// BrowserPlatformApiCallEvent A call that manages the browser VM rather than driving the browser, handled by the kernel-images-api server: recording lifecycle, filesystem and process management, telemetry and browser configuration. These are mostly platform-induced (e.g. profile save, replay capture) rather than agent actions. +type BrowserPlatformApiCallEvent struct { + Category BrowserPlatformApiCallEventCategory `json:"category"` + + // Data Per-call payload for `platform_api_call` events. Metadata only: a platform call carries no submitted content, so there is no `code` field as there is on `api_call`. + Data *BrowserPlatformApiCallEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2515,72 +5495,36 @@ type BrowserNetworkRequestEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserNetworkRequestEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserPlatformApiCallEventType `json:"type"` } -// BrowserNetworkRequestEventCategory defines model for BrowserNetworkRequestEvent.Category. -type BrowserNetworkRequestEventCategory string - -// BrowserNetworkRequestEventType defines model for BrowserNetworkRequestEvent.Type. -type BrowserNetworkRequestEventType string - -// BrowserNetworkRequestEventData defines model for BrowserNetworkRequestEventData. -type BrowserNetworkRequestEventData struct { - // DocumentUrl URL of the document that initiated the request. - DocumentUrl string `json:"document_url"` - - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` - - // Headers Request headers. - Headers BrowserHttpHeaders `json:"headers"` - - // InitiatorType CDP Initiator.type indicating what caused the request, passed through as-is from Chrome. Known values include script, parser, preload, and other. - InitiatorType string `json:"initiator_type"` - - // IsRedirect True if this request is the result of a redirect. - IsRedirect *bool `json:"is_redirect,omitempty"` - - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` - - // Method HTTP method as sent on the wire (e.g. GET, POST). - Method string `json:"method"` +// BrowserPlatformApiCallEventCategory defines model for BrowserPlatformApiCallEvent.Category. +type BrowserPlatformApiCallEventCategory string - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` +// BrowserPlatformApiCallEventType defines model for BrowserPlatformApiCallEvent.Type. +type BrowserPlatformApiCallEventType string - // PostData Request body for POST/PUT requests, if available. - PostData *string `json:"post_data,omitempty"` +// BrowserPlatformApiCallEventData Per-call payload for `platform_api_call` events. Metadata only: a platform call carries no submitted content, so there is no `code` field as there is on `api_call`. +type BrowserPlatformApiCallEventData struct { + // DurationMs Wall-clock duration of the handler in milliseconds. + DurationMs float32 `json:"duration_ms"` - // RedirectUrl Original URL before the redirect, present when is_redirect is true. - RedirectUrl *string `json:"redirect_url,omitempty"` + // OperationId Matched route's operation, named as the server names its handler (e.g. `ProcessExec`, `StartRecording`). + OperationId string `json:"operation_id"` - // RequestId CDP request identifier, unique within the session. + // RequestId Per-request identifier from the kernel-images-api request middleware. RequestId string `json:"request_id"` - // ResourceType CDP Network.ResourceType for the request, passed through as-is from Chrome. Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. - ResourceType *string `json:"resource_type,omitempty"` - - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` - - // TargetId Browser target identifier (stable across navigations within a tab). - TargetId string `json:"target_id"` - - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` - - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` + // Status HTTP response status code. + Status int `json:"status"` } -// BrowserNetworkResponseEvent A browser network response received event. Fired after the response body is fully received, not when headers arrive. -type BrowserNetworkResponseEvent struct { - Category BrowserNetworkResponseEventCategory `json:"category"` - Data *BrowserNetworkResponseEventData `json:"data,omitempty"` - +// BrowserProxyErrorEvent A branded proxy-layer failure observed by the browser. Emitted when the metro egress host-proxy serves a branded 5xx error page whose response carries the `X-Kernel-Proxy-Error` header. Low-volume and carries a typed code. Its value is per-session and per-URL attribution for sessions that already capture the network stream: proxy failures are only observable while the CDP network collector is running, so this is an opt-in refinement of the raw network events rather than a default-on alerting signal. +type BrowserProxyErrorEvent struct { + Category BrowserProxyErrorEventCategory `json:"category"` + Data *BrowserProxyErrorEventData `json:"data,omitempty"` + // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2588,54 +5532,45 @@ type BrowserNetworkResponseEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserNetworkResponseEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserProxyErrorEventType `json:"type"` } -// BrowserNetworkResponseEventCategory defines model for BrowserNetworkResponseEvent.Category. -type BrowserNetworkResponseEventCategory string +// BrowserProxyErrorEventCategory defines model for BrowserProxyErrorEvent.Category. +type BrowserProxyErrorEventCategory string -// BrowserNetworkResponseEventType defines model for BrowserNetworkResponseEvent.Type. -type BrowserNetworkResponseEventType string +// BrowserProxyErrorEventType defines model for BrowserProxyErrorEvent.Type. +type BrowserProxyErrorEventType string -// BrowserNetworkResponseEventData defines model for BrowserNetworkResponseEventData. -type BrowserNetworkResponseEventData struct { - // Body Truncated response body, present only for text MIME types. - Body *string `json:"body,omitempty"` +// BrowserProxyErrorEventData defines model for BrowserProxyErrorEventData. +type BrowserProxyErrorEventData struct { + // Code Proxy-layer error code: the `X-Kernel-Proxy-Error` response header value from a branded 5xx error page served by the metro egress host-proxy. Values mirror what the proxy emits: destination_blocked, provider_blacklisted, provider_unreachable, proxy_unavailable, upstream_timeout, upstream_dns_failure, upstream_connect_failed. Unknown header values are dropped. + Code BrowserProxyErrorEventDataCode `json:"code"` // FrameId CDP frame identifier within the target. FrameId *string `json:"frame_id,omitempty"` - // Headers Response headers. - Headers BrowserHttpHeaders `json:"headers"` - // LoaderId CDP document loader identifier, reset on each navigation. LoaderId *string `json:"loader_id,omitempty"` - // Method HTTP method of the original request. - Method string `json:"method"` - - // MimeType MIME type of the response (e.g. text/html, application/json). - MimeType *string `json:"mime_type,omitempty"` + // Method HTTP method of the failed request, when known. + Method *string `json:"method,omitempty"` // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. NavSeq int64 `json:"nav_seq"` - // RequestId CDP request identifier matching the originating network_request event. + // RequestId CDP request identifier matching the originating request. RequestId string `json:"request_id"` - // ResourceType CDP Network.ResourceType for the request, passed through as-is from Chrome. Known values include Document, Fetch, XHR, Script, Stylesheet, Image, Media, Font, TextTrack, EventSource, WebSocket, Manifest, Prefetch, Other, and more. + // ResourceType CDP Network.ResourceType for the request, when known. ResourceType *string `json:"resource_type,omitempty"` // SessionId CDP session identifier for the target connection. SessionId string `json:"session_id"` - // Status HTTP response status code. + // Status HTTP response status of the branded error page (502). Status int `json:"status"` - // StatusText HTTP response status text (e.g. OK, Not Found). - StatusText *string `json:"status_text,omitempty"` - // TargetId Browser target identifier (stable across navigations within a tab). TargetId string `json:"target_id"` @@ -2646,10 +5581,15 @@ type BrowserNetworkResponseEventData struct { Url *string `json:"url,omitempty"` } -// BrowserPageCrashedEvent A page's renderer process crashed (an "Aw, Snap!" failure) while the browser process itself stayed alive. Reported on the crashed page's session, with the session and target ids on `source.metadata`. Captured only while the `page` category is enabled. -type BrowserPageCrashedEvent struct { - Category BrowserPageCrashedEventCategory `json:"category"` - Data *BrowserPageCrashedEventData `json:"data,omitempty"` +// BrowserProxyErrorEventDataCode Proxy-layer error code: the `X-Kernel-Proxy-Error` response header value from a branded 5xx error page served by the metro egress host-proxy. Values mirror what the proxy emits: destination_blocked, provider_blacklisted, provider_unreachable, proxy_unavailable, upstream_timeout, upstream_dns_failure, upstream_connect_failed. Unknown header values are dropped. +type BrowserProxyErrorEventDataCode string + +// BrowserServiceCrashedEvent A managed service exited unexpectedly. Intentional stops (e.g. operator-initiated shutdown) do not produce this event — only unexpected exits and terminal restart-give-up transitions do. +type BrowserServiceCrashedEvent struct { + Category BrowserServiceCrashedEventCategory `json:"category"` + + // Data Per-crash payload for `service_crashed` events. Exit code and signal are not exposed by the underlying process manager on this channel, so only the service identity, the lifecycle phase the crash occurred in, and (when available) the PID are reported. + Data *BrowserServiceCrashedEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2658,32 +5598,37 @@ type BrowserPageCrashedEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageCrashedEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserServiceCrashedEventType `json:"type"` } -// BrowserPageCrashedEventCategory defines model for BrowserPageCrashedEvent.Category. -type BrowserPageCrashedEventCategory string +// BrowserServiceCrashedEventCategory defines model for BrowserServiceCrashedEvent.Category. +type BrowserServiceCrashedEventCategory string -// BrowserPageCrashedEventType defines model for BrowserPageCrashedEvent.Type. -type BrowserPageCrashedEventType string +// BrowserServiceCrashedEventType defines model for BrowserServiceCrashedEvent.Type. +type BrowserServiceCrashedEventType string -// BrowserPageCrashedEventData defines model for BrowserPageCrashedEventData. -type BrowserPageCrashedEventData struct { - // TargetId CDP target identifier of the crashed page. - TargetId string `json:"target_id"` +// BrowserServiceCrashedEventData Per-crash payload for `service_crashed` events. Exit code and signal are not exposed by the underlying process manager on this channel, so only the service identity, the lifecycle phase the crash occurred in, and (when available) the PID are reported. +type BrowserServiceCrashedEventData struct { + // Phase Lifecycle phase the crash occurred in. `startup` means the process died before it ever reached a healthy running state. `running` means a previously healthy process died unexpectedly. `gave_up` means the process manager exhausted its restart attempts and stopped trying; no further `service_crashed` events will fire for this service until something restarts it. + Phase BrowserServiceCrashedEventDataPhase `json:"phase"` - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` + // Pid PID of the crashed process. Absent when the process manager gave up after exhausting restart attempts and is no longer tracking a live PID. + Pid *int `json:"pid,omitempty"` - // Url URL the page was on when its renderer process crashed. - Url string `json:"url"` + // ServiceName Program name of the crashed service (e.g. `chromium`, `mutter`, `kernel-images-api`). + ServiceName string `json:"service_name"` } -// BrowserPageDomContentLoadedEvent A browser DOMContentLoaded event (CDP Page.domContentEventFired). -type BrowserPageDomContentLoadedEvent struct { - Category BrowserPageDomContentLoadedEventCategory `json:"category"` - Data *BrowserPageDomContentLoadedEventData `json:"data,omitempty"` +// BrowserServiceCrashedEventDataPhase Lifecycle phase the crash occurred in. `startup` means the process died before it ever reached a healthy running state. `running` means a previously healthy process died unexpectedly. `gave_up` means the process manager exhausted its restart attempts and stopped trying; no further `service_crashed` events will fire for this service until something restarts it. +type BrowserServiceCrashedEventDataPhase string + +// BrowserSystemOomKillEvent The Linux kernel OOM-killer terminated a process inside the VM. Sourced from `/dev/kmsg`. Fires for any process killed by the kernel due to memory exhaustion, including Chrome renderer subprocesses that are not supervised. +type BrowserSystemOomKillEvent struct { + Category BrowserSystemOomKillEventCategory `json:"category"` + + // Data Per-kill payload for `system_oom_kill` events. + Data *BrowserSystemOomKillEventData `json:"data,omitempty"` // Source Provenance metadata identifying which producer emitted the event. Source BrowserEventSource `json:"source"` @@ -2692,1591 +5637,2265 @@ type BrowserPageDomContentLoadedEvent struct { Truncated *bool `json:"truncated,omitempty"` // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageDomContentLoadedEventType `json:"type"` + Ts int64 `json:"ts"` + Type BrowserSystemOomKillEventType `json:"type"` } -// BrowserPageDomContentLoadedEventCategory defines model for BrowserPageDomContentLoadedEvent.Category. -type BrowserPageDomContentLoadedEventCategory string +// BrowserSystemOomKillEventCategory defines model for BrowserSystemOomKillEvent.Category. +type BrowserSystemOomKillEventCategory string -// BrowserPageDomContentLoadedEventType defines model for BrowserPageDomContentLoadedEvent.Type. -type BrowserPageDomContentLoadedEventType string +// BrowserSystemOomKillEventType defines model for BrowserSystemOomKillEvent.Type. +type BrowserSystemOomKillEventType string -// BrowserPageDomContentLoadedEventData defines model for BrowserPageDomContentLoadedEventData. -type BrowserPageDomContentLoadedEventData struct { - // CdpTimestamp Chrome monotonic clock value in seconds at which DOMContentLoaded fired, relative to browser process start (not Unix epoch). Use `ts` for wall-clock time. - CdpTimestamp float32 `json:"cdp_timestamp"` +// BrowserSystemOomKillEventData Per-kill payload for `system_oom_kill` events. +type BrowserSystemOomKillEventData struct { + // Constraint Why the kernel decided to OOM-kill. `none` means global memory exhaustion; `memcg` means a cgroup memory limit was hit; `cpuset` / `memory_policy` are NUMA/policy-driven kills. Absent on kernels older than 5.0 which did not emit the structured `oom-kill:` line. + Constraint *BrowserSystemOomKillEventDataConstraint `json:"constraint,omitempty"` - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` + // MemFreeKb Free system memory in KiB at the time of the kill, derived from the `free:N` field in the kernel's Mem-Info dump. Assumes a 4 KiB page size. Does not include reclaimable caches, so a small value with a large `mem_total_kb` may still mean the system was not under hard pressure. Absent if the kernel did not emit a parseable Mem-Info section. + MemFreeKb *int `json:"mem_free_kb,omitempty"` - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` + // MemTotalKb Total system memory in KiB at the time of the kill, derived from the `N pages RAM` line in the kernel's Mem-Info dump. Assumes a 4 KiB page size. Absent if the kernel did not emit a parseable Mem-Info section. + MemTotalKb *int `json:"mem_total_kb,omitempty"` - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` + // Pid PID of the killed process. + Pid int `json:"pid"` - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` + // ProcessName Comm of the killed process as reported by the kernel (max 15 chars, truncated by the kernel). + ProcessName string `json:"process_name"` - // TargetId Browser target identifier (stable across navigations within a tab). - TargetId string `json:"target_id"` + // RssKb Resident set size of the killed process in KiB (sum of anon-rss, file-rss, and shmem-rss). This is the physical memory the process was using at the time of the kill. + RssKb int `json:"rss_kb"` - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` + // TopTasks Top processes by resident-set-size at the moment of the kill, sorted descending. Sourced from the kernel's `Tasks state` table. Empty if the kernel did not emit the table. Capped at 5 entries to bound payload size. + TopTasks *[]BrowserSystemOomKillTask `json:"top_tasks,omitempty"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` -} + // TriggerPid PID of the triggering process. Absent if the kernel did not emit the standard `CPU: N PID: N Comm:` header line. + TriggerPid *int `json:"trigger_pid,omitempty"` -// BrowserPageLayoutSettledEvent A browser layout settled event emitted 1 second after page load with no intervening layout shifts, indicating visual stability. Each layout shift resets the 1-second timer. -type BrowserPageLayoutSettledEvent struct { - Category BrowserPageLayoutSettledEventCategory `json:"category"` + // TriggerProcessName Comm of the process whose allocation request caused the kernel to invoke the OOM-killer. Often the same as `process_name` (the kernel killed the requester) but can differ when the kernel chose a different victim. Max 15 chars, truncated by the kernel. + TriggerProcessName *string `json:"trigger_process_name,omitempty"` +} - // Data Browser event context stamped by the browser monitor onto all CDP-sourced events. Identifies the target, frame, and navigation epoch in which the event occurred. - Data *BrowserEventContext `json:"data,omitempty"` +// BrowserSystemOomKillEventDataConstraint Why the kernel decided to OOM-kill. `none` means global memory exhaustion; `memcg` means a cgroup memory limit was hit; `cpuset` / `memory_policy` are NUMA/policy-driven kills. Absent on kernels older than 5.0 which did not emit the structured `oom-kill:` line. +type BrowserSystemOomKillEventDataConstraint string - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` +// BrowserSystemOomKillTask A single process entry from the kernel's `Tasks state` dump. +type BrowserSystemOomKillTask struct { + // Name Comm of the process (max 15 chars, truncated by the kernel). + Name string `json:"name"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // Pid PID of the process. + Pid int `json:"pid"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageLayoutSettledEventType `json:"type"` + // RssKb Resident set size in KiB at the moment of the kill. + RssKb int `json:"rss_kb"` } -// BrowserPageLayoutSettledEventCategory defines model for BrowserPageLayoutSettledEvent.Category. -type BrowserPageLayoutSettledEventCategory string +// BrowserTargetType CDP target type of the page that produced the event. +type BrowserTargetType string -// BrowserPageLayoutSettledEventType defines model for BrowserPageLayoutSettledEvent.Type. -type BrowserPageLayoutSettledEventType string +// BrowserTelemetryCategoriesConfig Per-category telemetry capture settings for browser events. +type BrowserTelemetryCategoriesConfig struct { + // Captcha Captcha solve attempt outcomes. + Captcha *BrowserTelemetryCategoryConfig `json:"captcha,omitempty"` -// BrowserPageLayoutShiftEvent A browser cumulative layout shift (CLS) event from the Performance Timeline API. -type BrowserPageLayoutShiftEvent struct { - Category BrowserPageLayoutShiftEventCategory `json:"category"` - Data *BrowserPageLayoutShiftEventData `json:"data,omitempty"` + // Connection Client attach/detach lifecycle for the CDP proxy and live view. + Connection *BrowserTelemetryCategoryConfig `json:"connection,omitempty"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // Console Console output (log, warn, error) and uncaught exceptions. + Console *BrowserTelemetryCategoryConfig `json:"console,omitempty"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // Control Agent-driven actions against the browser — computer-control calls, Playwright code execution, screenshots, clipboard access, and browser-control commands sent over the CDP proxy. + Control *BrowserTelemetryControlConfig `json:"control,omitempty"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageLayoutShiftEventType `json:"type"` + // Interaction User interaction events (clicks, keydowns, scroll). + Interaction *BrowserTelemetryCategoryConfig `json:"interaction,omitempty"` + + // Network HTTP request/response metadata. + Network *BrowserTelemetryCategoryConfig `json:"network,omitempty"` + + // Page Page lifecycle events (navigation, load, layout shifts, LCP). + Page *BrowserTelemetryCategoryConfig `json:"page,omitempty"` + + // Platform Calls that manage the VM rather than drive the browser (recording, filesystem, process, telemetry and browser configuration). Mostly platform-induced; off by default and opt-in. + Platform *BrowserTelemetryCategoryConfig `json:"platform,omitempty"` + + // Screenshot Periodic base64-encoded viewport screenshots. High volume; off by default and opt-in. + Screenshot *BrowserTelemetryCategoryConfig `json:"screenshot,omitempty"` + + // System Browser VM health, such as out-of-memory kills and managed-service crashes. + System *BrowserTelemetryCategoryConfig `json:"system,omitempty"` } -// BrowserPageLayoutShiftEventCategory defines model for BrowserPageLayoutShiftEvent.Category. -type BrowserPageLayoutShiftEventCategory string +// BrowserTelemetryCategoryConfig Configuration for a single telemetry category. +type BrowserTelemetryCategoryConfig struct { + // Enabled Whether this category is captured. In PUT requests selection is opt-in: omitting this field (or the whole category) leaves the category off, so a PUT captures exactly the categories set to true. In PATCH requests, omitting this field (or sending an empty object `{}`) is a no-op; the category retains its current state. To enable or disable a category via PATCH, you must send an explicit `true` or `false`. + Enabled *bool `json:"enabled,omitempty"` +} -// BrowserPageLayoutShiftEventType defines model for BrowserPageLayoutShiftEvent.Type. -type BrowserPageLayoutShiftEventType string +// BrowserTelemetryCdpControlConfig Settings for the `cdp_command` events the DevTools proxy reports. +type BrowserTelemetryCdpControlConfig struct { + // ExcludedMethods Methods to leave out of the `cdp_command` stream. Omit the list (or send an empty one) to report every supported method. Exclusion is a telemetry setting only: an excluded command is still relayed to the browser unchanged, it simply produces no event. Use it to drop the highest-volume methods — `Input.dispatchMouseEvent` during a humanized cursor path, or `Page.captureScreenshot` under a screencast — without turning the whole category off. + ExcludedMethods *[]BrowserCdpCommandMethod `json:"excluded_methods,omitempty"` +} -// BrowserPageLayoutShiftEventData defines model for BrowserPageLayoutShiftEventData. -type BrowserPageLayoutShiftEventData struct { - // Duration Duration of the layout shift entry in milliseconds (always 0 for layout shifts per spec). - Duration float32 `json:"duration"` +// BrowserTelemetryConfig Telemetry configuration for a browser. Selection is opt-in. Omit the browser key (or send an empty object) to capture the default set: lightweight operational signals (control, connection, system, captcha). Within `browser`, only the categories you set enabled: true are captured; anything omitted is off. The CDP categories (console, network, page, interaction), `screenshot` and `platform` are off by default and must be opted into. A `browser` config with nothing enabled clears the telemetry configuration. The `monitor` category (CDP collector health) is not configurable here; it flows automatically whenever a CDP category is captured. +type BrowserTelemetryConfig struct { + // Browser Per-category telemetry capture settings for browser events. + Browser *BrowserTelemetryCategoriesConfig `json:"browser,omitempty"` - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` + // Export Forwarding of captured telemetry to an external destination. Independent of what is captured: export is off unless explicitly enabled here, even when an export destination is configured. In a PUT (full replace) an omitted export block resets export to off, the same as omitted categories turn off; in a PATCH an omitted field leaves the current setting unchanged. + Export *BrowserTelemetryExportConfig `json:"export,omitempty"` +} + +// BrowserTelemetryControlConfig Configuration for the control category. Same `enabled` semantics as any other category, plus settings for the browser-control commands the CDP proxy reports. +type BrowserTelemetryControlConfig struct { + // Cdp Settings for the `cdp_command` events the DevTools proxy reports. + Cdp *BrowserTelemetryCdpControlConfig `json:"cdp,omitempty"` + + // Enabled Whether this category is captured. In PUT requests selection is opt-in: omitting this field (or the whole category) leaves the category off, so a PUT captures exactly the categories set to true. In PATCH requests, omitting this field (or sending an empty object `{}`) is a no-op; the category retains its current state. To enable or disable a category via PATCH, you must send an explicit `true` or `false`. + Enabled *bool `json:"enabled,omitempty"` +} + +// BrowserTelemetryExportConfig Forwarding of captured telemetry to an external destination. Independent of what is captured: export is off unless explicitly enabled here, even when an export destination is configured. In a PUT (full replace) an omitted export block resets export to off, the same as omitted categories turn off; in a PATCH an omitted field leaves the current setting unchanged. +type BrowserTelemetryExportConfig struct { + // Otlp OTLP/HTTP export settings. + Otlp *BrowserTelemetryOTLPExportConfig `json:"otlp,omitempty"` +} + +// BrowserTelemetryOTLPExportConfig OTLP/HTTP export settings. +type BrowserTelemetryOTLPExportConfig struct { + // Enabled Whether captured telemetry is forwarded to the configured OTLP destination. Off by default. Has no effect (export stays inactive) when no export destination is configured. + Enabled *bool `json:"enabled,omitempty"` +} + +// ChromiumConfigureError Failure from batched chromium configure — includes which phase failed. +type ChromiumConfigureError struct { + Message string `json:"message"` - // LayoutShiftDetails PerformanceLayoutShift attributes from the Performance Timeline entry. - LayoutShiftDetails *struct { - // HadRecentInput True if the layout shift was preceded by user input within 500ms, excluding it from CLS. - HadRecentInput *bool `json:"had_recent_input,omitempty"` + // Phase configure_phase maps to restart/filesystem/policy/extension/profile/display work; navigate_phase is retained for compatibility. + Phase ChromiumConfigureErrorPhase `json:"phase"` - // Value Layout shift score for this entry (contribution to CLS). - Value *float32 `json:"value,omitempty"` - } `json:"layout_shift_details,omitempty"` + // Step Optional configure step that failed. + Step *ChromiumConfigureErrorStep `json:"step,omitempty"` +} - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` +// ChromiumConfigureErrorPhase configure_phase maps to restart/filesystem/policy/extension/profile/display work; navigate_phase is retained for compatibility. +type ChromiumConfigureErrorPhase string - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` +// ChromiumConfigureErrorStep Optional configure step that failed. +type ChromiumConfigureErrorStep string - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` +// ClickMouseRequest defines model for ClickMouseRequest. +type ClickMouseRequest struct { + // Button Mouse button to interact with + Button *ClickMouseRequestButton `json:"button,omitempty"` - // SourceFrameId CDP frame identifier of the frame where the layout shift occurred. - SourceFrameId string `json:"source_frame_id"` + // ClickType Type of click action + ClickType *ClickMouseRequestClickType `json:"click_type,omitempty"` - // TargetId Browser target identifier (stable across navigations within a tab). - TargetId string `json:"target_id"` + // HoldKeys Modifier keys to hold during the click + HoldKeys *[]string `json:"hold_keys,omitempty"` - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` + // NumClicks Number of times to repeat the click + NumClicks *int `json:"num_clicks,omitempty"` - // Time Performance Timeline timestamp of the layout shift in milliseconds. - Time float32 `json:"time"` + // X X coordinate of the click position + X int `json:"x"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` + // Y Y coordinate of the click position + Y int `json:"y"` } -// BrowserPageLcpEvent A browser Largest Contentful Paint (LCP) event from the Performance Timeline API. -type BrowserPageLcpEvent struct { - Category BrowserPageLcpEventCategory `json:"category"` - Data *BrowserPageLcpEventData `json:"data,omitempty"` - - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` +// ClickMouseRequestButton Mouse button to interact with +type ClickMouseRequestButton string - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` +// ClickMouseRequestClickType Type of click action +type ClickMouseRequestClickType string - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageLcpEventType `json:"type"` +// ClipboardContent defines model for ClipboardContent. +type ClipboardContent struct { + // Text Current clipboard text content + Text string `json:"text"` } -// BrowserPageLcpEventCategory defines model for BrowserPageLcpEvent.Category. -type BrowserPageLcpEventCategory string +// ComputerAction A single computer action to execute as part of a batch. The `type` field selects which +// action to perform, and the corresponding field contains the action parameters. +// Exactly one action field matching the type must be provided. +type ComputerAction struct { + ClickMouse *ClickMouseRequest `json:"click_mouse,omitempty"` + DragMouse *DragMouseRequest `json:"drag_mouse,omitempty"` + MoveMouse *MoveMouseRequest `json:"move_mouse,omitempty"` + PressKey *PressKeyRequest `json:"press_key,omitempty"` + Scroll *ScrollRequest `json:"scroll,omitempty"` + SetCursor *SetCursorRequest `json:"set_cursor,omitempty"` -// BrowserPageLcpEventType defines model for BrowserPageLcpEvent.Type. -type BrowserPageLcpEventType string + // Sleep Pause execution for a specified duration. + Sleep *SleepAction `json:"sleep,omitempty"` -// BrowserPageLcpEventData defines model for BrowserPageLcpEventData. -type BrowserPageLcpEventData struct { - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` + // Type The type of action to perform. + Type ComputerActionType `json:"type"` + TypeText *TypeTextRequest `json:"type_text,omitempty"` +} - // LcpDetails LargestContentfulPaint attributes from the Performance Timeline entry. - LcpDetails *struct { - // ElementId id attribute of the LCP element, if present. - ElementId *string `json:"element_id,omitempty"` +// ComputerActionType The type of action to perform. +type ComputerActionType string - // LoadTime Load time of the LCP element in milliseconds. - LoadTime *float32 `json:"load_time,omitempty"` +// CreateDirectoryRequest defines model for CreateDirectoryRequest. +type CreateDirectoryRequest struct { + // Mode Optional directory mode (octal string, e.g. 755). Defaults to 755. + Mode *string `json:"mode,omitempty"` - // NodeId CDP DOM node identifier of the LCP element. - NodeId *int `json:"node_id,omitempty"` + // Path Absolute directory path to create. + Path string `json:"path"` +} - // RenderTime Render time of the LCP element in milliseconds; 0 for cross-origin images without Timing-Allow-Origin. - RenderTime *float32 `json:"render_time,omitempty"` +// DeletePathRequest defines model for DeletePathRequest. +type DeletePathRequest struct { + // Path Absolute path to delete. + Path string `json:"path"` +} - // Size Visible area of the LCP element in pixels squared. - Size *float32 `json:"size,omitempty"` +// DeleteRecordingRequest defines model for DeleteRecordingRequest. +type DeleteRecordingRequest struct { + // Id Identifier of the recording session to delete, as passed to /recording/start. Alphanumeric or hyphen. When omitted, the default recording session is deleted. + Id *string `json:"id,omitempty"` +} - // Url URL of the LCP element for image or video elements. - Url *string `json:"url,omitempty"` - } `json:"lcp_details,omitempty"` +// DisplayConfig defines model for DisplayConfig. +type DisplayConfig struct { + // Height Current display height in pixels + Height *int `json:"height,omitempty"` - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` + // RefreshRate Current display refresh rate in Hz (may be null if not detectable) + RefreshRate *int `json:"refresh_rate,omitempty"` - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` + // Width Current display width in pixels + Width *int `json:"width,omitempty"` +} - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` +// DragMouseRequest defines model for DragMouseRequest. +type DragMouseRequest struct { + // Button Mouse button to drag with + Button *DragMouseRequestButton `json:"button,omitempty"` - // SourceFrameId CDP frame identifier of the frame where the LCP element was rendered. - SourceFrameId string `json:"source_frame_id"` + // Delay Delay in milliseconds between button down and starting to move along the path. + Delay *int `json:"delay,omitempty"` - // TargetId Browser target identifier (stable across navigations within a tab). - TargetId string `json:"target_id"` + // DurationMs Target total duration in milliseconds for the entire drag movement when smooth=true. Omit for automatic timing based on total path length. + DurationMs *int `json:"duration_ms,omitempty"` - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` + // HoldKeys Modifier keys to hold during the drag + HoldKeys *[]string `json:"hold_keys,omitempty"` - // Time Performance Timeline timestamp of the LCP entry in milliseconds. - Time float32 `json:"time"` + // Path Ordered list of [x, y] coordinate pairs to move through while dragging. Must contain at least 2 points. + Path [][]int `json:"path"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` + // Smooth Use human-like Bezier curves between path waypoints instead of linear interpolation. When true, steps_per_segment and step_delay_ms are ignored. + Smooth *bool `json:"smooth,omitempty"` + + // StepDelayMs Delay in milliseconds between relative steps while dragging. Ignored when smooth=true. + StepDelayMs *int `json:"step_delay_ms,omitempty"` + + // StepsPerSegment Number of relative move steps per segment in the path. Ignored when smooth=true. Minimum 1. + StepsPerSegment *int `json:"steps_per_segment,omitempty"` } -// BrowserPageLoadEvent A browser page load event (CDP Page.loadEventFired). -type BrowserPageLoadEvent struct { - Category BrowserPageLoadEventCategory `json:"category"` - Data *BrowserPageLoadEventData `json:"data,omitempty"` +// DragMouseRequestButton Mouse button to drag with +type DragMouseRequestButton string - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` +// Error defines model for Error. +type Error struct { + Message string `json:"message"` +} - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` +// ExecutePlaywrightRequest Request to execute Playwright code +type ExecutePlaywrightRequest struct { + // Code TypeScript/JavaScript code to execute. The code has access to 'page', 'context', and 'browser' variables. + // Example: "await page.goto('https://example.com'); return await page.title();" + Code string `json:"code"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageLoadEventType `json:"type"` + // TimeoutSec Maximum execution time in seconds. Default is 60. + TimeoutSec *int `json:"timeout_sec,omitempty"` } -// BrowserPageLoadEventCategory defines model for BrowserPageLoadEvent.Category. -type BrowserPageLoadEventCategory string +// ExecutePlaywrightResult Result of Playwright code execution +type ExecutePlaywrightResult struct { + // Error Error message if execution failed + Error *string `json:"error,omitempty"` -// BrowserPageLoadEventType defines model for BrowserPageLoadEvent.Type. -type BrowserPageLoadEventType string + // Result The value returned by the code (if any) + Result interface{} `json:"result,omitempty"` -// BrowserPageLoadEventData defines model for BrowserPageLoadEventData. -type BrowserPageLoadEventData struct { - // CdpTimestamp Chrome monotonic clock value in seconds at which the load event fired, relative to browser process start (not Unix epoch). Use `ts` for wall-clock time. - CdpTimestamp float32 `json:"cdp_timestamp"` + // Stderr Standard error from the execution + Stderr *string `json:"stderr,omitempty"` - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` + // Stdout Standard output from the execution + Stdout *string `json:"stdout,omitempty"` - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` + // Success Whether the code executed successfully + Success bool `json:"success"` +} - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` +// FileInfo defines model for FileInfo. +type FileInfo struct { + // IsDir Whether the path is a directory. + IsDir bool `json:"is_dir"` - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` + // ModTime Last modification time. + ModTime time.Time `json:"mod_time"` - // TargetId Browser target identifier (stable across navigations within a tab). - TargetId string `json:"target_id"` + // Mode File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--"). + Mode string `json:"mode"` - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` + // Name Base name of the file or directory. + Name string `json:"name"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` + // Path Absolute path. + Path string `json:"path"` + + // SizeBytes Size in bytes. 0 for directories. + SizeBytes int `json:"size_bytes"` } -// BrowserPageNavigationEvent A browser page navigation started event (CDP Page.frameNavigated). Carries nav context fields inline but not nav_seq, as this event resets the navigation epoch. -type BrowserPageNavigationEvent struct { - Category BrowserPageNavigationEventCategory `json:"category"` - Data *BrowserPageNavigationEventData `json:"data,omitempty"` +// FileSystemEvent Filesystem change event. +type FileSystemEvent struct { + // IsDir Whether the affected path is a directory. + IsDir *bool `json:"is_dir,omitempty"` + + // Name Base name of the file or directory affected. + Name *string `json:"name,omitempty"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // Path Absolute path of the file or directory. + Path string `json:"path"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // Type Event type. + Type FileSystemEventType `json:"type"` +} - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageNavigationEventType `json:"type"` +// FileSystemEventType Event type. +type FileSystemEventType string + +// KnownBrowserTelemetryEvent Discriminated union of browser telemetry events emitted by the Kernel image. This is a structural taxonomy: any event on the telemetry stream whose `data` conforms to one of the variants below (selected by `type`) is a `KnownBrowserTelemetryEvent`, regardless of who published it. Caller-published events via POST /telemetry/events are not constrained to this union; see `TelemetryEvent` for the wire shape. Validation of caller payloads against this taxonomy is the consumer's responsibility. +type KnownBrowserTelemetryEvent struct { + union json.RawMessage } -// BrowserPageNavigationEventCategory defines model for BrowserPageNavigationEvent.Category. -type BrowserPageNavigationEventCategory string +// ListFiles Array of file or directory information entries. +type ListFiles = []FileInfo -// BrowserPageNavigationEventType defines model for BrowserPageNavigationEvent.Type. -type BrowserPageNavigationEventType string +// LogEvent A log entry from the application. +type LogEvent struct { + // Message Log message text. + Message string `json:"message"` -// BrowserPageNavigationEventData defines model for BrowserPageNavigationEventData. -type BrowserPageNavigationEventData struct { - // FrameId CDP frame identifier of the navigated frame. - FrameId string `json:"frame_id"` + // Timestamp Time the log entry was produced. + Timestamp time.Time `json:"timestamp"` +} - // LoaderId New CDP document loader identifier assigned for this navigation. - LoaderId string `json:"loader_id"` +// MarkRecordingRequest defines model for MarkRecordingRequest. +type MarkRecordingRequest struct { + // Id Identifier of the recording session to mark, as passed to /recording/start. Alphanumeric or hyphen. When omitted, the marker is added to the default recording session. + Id *string `json:"id,omitempty"` - // ParentFrameId Parent frame identifier for subframe navigations; absent for top-level navigations. - ParentFrameId *string `json:"parent_frame_id,omitempty"` + // Name Name of the marker, used as the MP4 chapter title. + Name string `json:"name"` +} - // SessionId CDP session identifier. - SessionId string `json:"session_id"` +// MarkRecordingResult defines model for MarkRecordingResult. +type MarkRecordingResult struct { + // Name Name of the recorded marker. + Name string `json:"name"` - // TargetId Browser target identifier. - TargetId string `json:"target_id"` + // OffsetMs Provisional offset of the marker from the recording start, in milliseconds, measured against the start time at mark time. The authoritative offset is the chapter start written at finalize. + OffsetMs int64 `json:"offset_ms"` +} - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` +// MousePositionResponse defines model for MousePositionResponse. +type MousePositionResponse struct { + // X X coordinate of the cursor + X int `json:"x"` - // Url URL navigated to. - Url string `json:"url"` + // Y Y coordinate of the cursor + Y int `json:"y"` } -// BrowserPageNavigationSettledEvent Emitted when page_dom_content_loaded and page_layout_settled have both fired for the same navigation, indicating the page is loaded and visually stable. Independent of network_idle; a single pending request does not block it. -type BrowserPageNavigationSettledEvent struct { - Category BrowserPageNavigationSettledEventCategory `json:"category"` +// MoveMouseRequest defines model for MoveMouseRequest. +type MoveMouseRequest struct { + // DurationMs Target total duration in milliseconds for the mouse movement when smooth=true. Omit for automatic timing based on distance. + DurationMs *int `json:"duration_ms,omitempty"` - // Data Browser event context stamped by the browser monitor onto all CDP-sourced events. Identifies the target, frame, and navigation epoch in which the event occurred. - Data *BrowserEventContext `json:"data,omitempty"` + // HoldKeys Modifier keys to hold during the move + HoldKeys *[]string `json:"hold_keys,omitempty"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // Smooth Use human-like Bezier curve path instead of instant mouse movement. + Smooth *bool `json:"smooth,omitempty"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // X X coordinate to move the cursor to + X int `json:"x"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageNavigationSettledEventType `json:"type"` + // Y Y coordinate to move the cursor to + Y int `json:"y"` } -// BrowserPageNavigationSettledEventCategory defines model for BrowserPageNavigationSettledEvent.Category. -type BrowserPageNavigationSettledEventCategory string - -// BrowserPageNavigationSettledEventType defines model for BrowserPageNavigationSettledEvent.Type. -type BrowserPageNavigationSettledEventType string +// MovePathRequest defines model for MovePathRequest. +type MovePathRequest struct { + // DestPath Absolute destination path. + DestPath string `json:"dest_path"` -// BrowserPageTabOpenedEvent A new browser tab or target was opened (CDP Target.attachedToTarget for page targets). Fires before a CDP session is attached to the new target, so `session_id`, `frame_id`, `loader_id`, and `nav_seq` are absent; this event does not compose `BrowserEventContext`. Consumers reading context fields generically should treat it as a special case. -type BrowserPageTabOpenedEvent struct { - Category BrowserPageTabOpenedEventCategory `json:"category"` - Data *BrowserPageTabOpenedEventData `json:"data,omitempty"` + // SrcPath Absolute source path. + SrcPath string `json:"src_path"` +} - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` +// OkResponse Generic OK response. +type OkResponse struct { + // Ok Indicates success. + Ok bool `json:"ok"` +} - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` +// PatchDisplayRequest defines model for PatchDisplayRequest. +type PatchDisplayRequest struct { + // Height Display height in pixels + Height *int `json:"height,omitempty"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPageTabOpenedEventType `json:"type"` -} + // RefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. + RefreshRate *PatchDisplayRequestRefreshRate `json:"refresh_rate,omitempty"` -// BrowserPageTabOpenedEventCategory defines model for BrowserPageTabOpenedEvent.Category. -type BrowserPageTabOpenedEventCategory string + // RequireIdle If true, refuse to resize when live view or recording/replay is active. + RequireIdle *bool `json:"require_idle,omitempty"` -// BrowserPageTabOpenedEventType defines model for BrowserPageTabOpenedEvent.Type. -type BrowserPageTabOpenedEventType string + // RestartChromium If true, restart Chromium after resolution change to ensure it adapts to new size. Default is false for headful, true for headless. + RestartChromium *bool `json:"restart_chromium,omitempty"` -// BrowserPageTabOpenedEventData defines model for BrowserPageTabOpenedEventData. -type BrowserPageTabOpenedEventData struct { - // OpenerId Target identifier of the tab that opened this one, if any. - OpenerId *string `json:"opener_id,omitempty"` + // Width Display width in pixels + Width *int `json:"width,omitempty"` +} - // TargetId CDP target identifier for the newly opened tab. - TargetId string `json:"target_id"` +// PatchDisplayRequestRefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. +type PatchDisplayRequestRefreshRate int - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` +// PressKeyRequest defines model for PressKeyRequest. +type PressKeyRequest struct { + // Duration Duration to hold the keys down in milliseconds. If omitted or 0, keys are tapped. + Duration *int `json:"duration,omitempty"` - // Title Initial page title of the new tab. - Title *string `json:"title,omitempty"` + // HoldKeys Optional modifier keys to hold during the key press sequence. + HoldKeys *[]string `json:"hold_keys,omitempty"` - // Url Initial URL of the new tab. - Url string `json:"url"` + // Keys List of key symbols to press. Each item should be a key symbol supported by xdotool + // (see X11 keysym definitions). Examples include "Return", "Shift", "Ctrl", "Alt", "F5". + // Items in this list could also be combinations, e.g. "Ctrl+t" or "Ctrl+Shift+Tab". + Keys []string `json:"keys"` } -// BrowserPlatformApiCallEvent A call that manages the browser VM rather than driving the browser, handled by the kernel-images-api server: recording lifecycle, filesystem and process management, telemetry and browser configuration. These are mostly platform-induced (e.g. profile save, replay capture) rather than agent actions. -type BrowserPlatformApiCallEvent struct { - Category BrowserPlatformApiCallEventCategory `json:"category"` +// ProcessExecRequest Request to execute a command synchronously. +type ProcessExecRequest struct { + // Args Command arguments. + Args *[]string `json:"args,omitempty"` - // Data Per-call payload for `platform_api_call` events. Metadata only: a platform call carries no submitted content, so there is no `code` field as there is on `api_call`. - Data *BrowserPlatformApiCallEventData `json:"data,omitempty"` + // AsRoot Run the process with root privileges. + AsRoot *bool `json:"as_root,omitempty"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // AsUser Run the process as this user. + AsUser *string `json:"as_user,omitempty"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // Command Executable or shell command to run. + Command string `json:"command"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserPlatformApiCallEventType `json:"type"` -} + // Cwd Working directory (absolute path) to run the command in. + Cwd *string `json:"cwd,omitempty"` -// BrowserPlatformApiCallEventCategory defines model for BrowserPlatformApiCallEvent.Category. -type BrowserPlatformApiCallEventCategory string + // Env Environment variables to set for the process. + Env *map[string]string `json:"env,omitempty"` -// BrowserPlatformApiCallEventType defines model for BrowserPlatformApiCallEvent.Type. -type BrowserPlatformApiCallEventType string + // TimeoutSec Maximum execution time in seconds. + TimeoutSec *int `json:"timeout_sec,omitempty"` +} -// BrowserPlatformApiCallEventData Per-call payload for `platform_api_call` events. Metadata only: a platform call carries no submitted content, so there is no `code` field as there is on `api_call`. -type BrowserPlatformApiCallEventData struct { - // DurationMs Wall-clock duration of the handler in milliseconds. - DurationMs float32 `json:"duration_ms"` +// ProcessExecResult Result of a synchronous command execution. +type ProcessExecResult struct { + // DurationMs Execution duration in milliseconds. + DurationMs *int `json:"duration_ms,omitempty"` - // OperationId Matched route's operation, named as the server names its handler (e.g. `ProcessExec`, `StartRecording`). - OperationId string `json:"operation_id"` + // ExitCode Process exit code. + ExitCode *int `json:"exit_code,omitempty"` - // RequestId Per-request identifier from the kernel-images-api request middleware. - RequestId string `json:"request_id"` + // StderrB64 Base64-encoded stderr buffer. + StderrB64 *string `json:"stderr_b64,omitempty"` - // Status HTTP response status code. - Status int `json:"status"` + // StdoutB64 Base64-encoded stdout buffer. + StdoutB64 *string `json:"stdout_b64,omitempty"` } -// BrowserProxyErrorEvent A branded proxy-layer failure observed by the browser. Emitted when the metro egress host-proxy serves a branded 5xx error page whose response carries the `X-Kernel-Proxy-Error` header. Low-volume and carries a typed code. Its value is per-session and per-URL attribution for sessions that already capture the network stream: proxy failures are only observable while the CDP network collector is running, so this is an opt-in refinement of the raw network events rather than a default-on alerting signal. -type BrowserProxyErrorEvent struct { - Category BrowserProxyErrorEventCategory `json:"category"` - Data *BrowserProxyErrorEventData `json:"data,omitempty"` +// ProcessKillRequest Signal to send to the process. +type ProcessKillRequest struct { + // Signal Signal to send. + Signal ProcessKillRequestSignal `json:"signal"` +} - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` +// ProcessKillRequestSignal Signal to send. +type ProcessKillRequestSignal string - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` +// ProcessResizeRequest Resize a PTY-backed process. +type ProcessResizeRequest struct { + // Cols New terminal columns. + Cols int `json:"cols"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserProxyErrorEventType `json:"type"` + // Rows New terminal rows. + Rows int `json:"rows"` } -// BrowserProxyErrorEventCategory defines model for BrowserProxyErrorEvent.Category. -type BrowserProxyErrorEventCategory string - -// BrowserProxyErrorEventType defines model for BrowserProxyErrorEvent.Type. -type BrowserProxyErrorEventType string +// ProcessSpawnRequest defines model for ProcessSpawnRequest. +type ProcessSpawnRequest struct { + // AllocateTty Allocate a pseudo-terminal (PTY) for the process to enable interactive shells. + AllocateTty *bool `json:"allocate_tty,omitempty"` -// BrowserProxyErrorEventData defines model for BrowserProxyErrorEventData. -type BrowserProxyErrorEventData struct { - // Code Proxy-layer error code: the `X-Kernel-Proxy-Error` response header value from a branded 5xx error page served by the metro egress host-proxy. Values mirror what the proxy emits: destination_blocked, provider_blacklisted, provider_unreachable, proxy_unavailable, upstream_timeout, upstream_dns_failure, upstream_connect_failed. Unknown header values are dropped. - Code BrowserProxyErrorEventDataCode `json:"code"` + // Args Command arguments. + Args *[]string `json:"args,omitempty"` - // FrameId CDP frame identifier within the target. - FrameId *string `json:"frame_id,omitempty"` + // AsRoot Run the process with root privileges. + AsRoot *bool `json:"as_root,omitempty"` - // LoaderId CDP document loader identifier, reset on each navigation. - LoaderId *string `json:"loader_id,omitempty"` + // AsUser Run the process as this user. + AsUser *string `json:"as_user,omitempty"` - // Method HTTP method of the failed request, when known. - Method *string `json:"method,omitempty"` + // Cols Initial terminal columns when allocate_tty is true. + Cols *int `json:"cols,omitempty"` - // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. - NavSeq int64 `json:"nav_seq"` + // Command Executable or shell command to run. + Command string `json:"command"` - // RequestId CDP request identifier matching the originating request. - RequestId string `json:"request_id"` + // Cwd Working directory (absolute path) to run the command in. + Cwd *string `json:"cwd,omitempty"` - // ResourceType CDP Network.ResourceType for the request, when known. - ResourceType *string `json:"resource_type,omitempty"` + // Env Environment variables to set for the process. + Env *map[string]string `json:"env,omitempty"` - // SessionId CDP session identifier for the target connection. - SessionId string `json:"session_id"` + // Rows Initial terminal rows when allocate_tty is true. + Rows *int `json:"rows,omitempty"` - // Status HTTP response status of the branded error page (502). - Status int `json:"status"` + // TimeoutSec Maximum execution time in seconds. + TimeoutSec *int `json:"timeout_sec,omitempty"` +} - // TargetId Browser target identifier (stable across navigations within a tab). - TargetId string `json:"target_id"` +// ProcessSpawnResult Information about a spawned process. +type ProcessSpawnResult struct { + // Pid OS process ID. + Pid *int `json:"pid,omitempty"` - // TargetType CDP target type of the page that produced the event. - TargetType BrowserTargetType `json:"target_type"` + // ProcessId Server-assigned identifier for the process. + ProcessId *openapi_types.UUID `json:"process_id,omitempty"` - // Url URL relevant to this event; page URL for navigation and page events, request URL for network events. - Url *string `json:"url,omitempty"` + // StartedAt Timestamp when the process started. + StartedAt *time.Time `json:"started_at,omitempty"` } -// BrowserProxyErrorEventDataCode Proxy-layer error code: the `X-Kernel-Proxy-Error` response header value from a branded 5xx error page served by the metro egress host-proxy. Values mirror what the proxy emits: destination_blocked, provider_blacklisted, provider_unreachable, proxy_unavailable, upstream_timeout, upstream_dns_failure, upstream_connect_failed. Unknown header values are dropped. -type BrowserProxyErrorEventDataCode string +// ProcessStatus Current status of a process. +type ProcessStatus struct { + // CpuPct Estimated CPU usage percentage. + CpuPct *float32 `json:"cpu_pct,omitempty"` -// BrowserServiceCrashedEvent A managed service exited unexpectedly. Intentional stops (e.g. operator-initiated shutdown) do not produce this event — only unexpected exits and terminal restart-give-up transitions do. -type BrowserServiceCrashedEvent struct { - Category BrowserServiceCrashedEventCategory `json:"category"` + // ExitCode Exit code if the process has exited. + ExitCode *int `json:"exit_code,omitempty"` - // Data Per-crash payload for `service_crashed` events. Exit code and signal are not exposed by the underlying process manager on this channel, so only the service identity, the lifecycle phase the crash occurred in, and (when available) the PID are reported. - Data *BrowserServiceCrashedEventData `json:"data,omitempty"` + // MemBytes Estimated resident memory usage in bytes. + MemBytes *int `json:"mem_bytes,omitempty"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // State Process state. + State *ProcessStatusState `json:"state,omitempty"` +} - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` +// ProcessStatusState Process state. +type ProcessStatusState string - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserServiceCrashedEventType `json:"type"` +// ProcessStdinRequest Data to write to the process standard input. +type ProcessStdinRequest struct { + // DataB64 Base64-encoded data to write. + DataB64 string `json:"data_b64"` } -// BrowserServiceCrashedEventCategory defines model for BrowserServiceCrashedEvent.Category. -type BrowserServiceCrashedEventCategory string +// ProcessStdinResult Result of writing to stdin. +type ProcessStdinResult struct { + // WrittenBytes Number of bytes written. + WrittenBytes *int `json:"written_bytes,omitempty"` +} -// BrowserServiceCrashedEventType defines model for BrowserServiceCrashedEvent.Type. -type BrowserServiceCrashedEventType string +// ProcessStreamEvent SSE payload representing process output or lifecycle events. +type ProcessStreamEvent struct { + // DataB64 Base64-encoded data from the process stream. + DataB64 *string `json:"data_b64,omitempty"` -// BrowserServiceCrashedEventData Per-crash payload for `service_crashed` events. Exit code and signal are not exposed by the underlying process manager on this channel, so only the service identity, the lifecycle phase the crash occurred in, and (when available) the PID are reported. -type BrowserServiceCrashedEventData struct { - // Phase Lifecycle phase the crash occurred in. `startup` means the process died before it ever reached a healthy running state. `running` means a previously healthy process died unexpectedly. `gave_up` means the process manager exhausted its restart attempts and stopped trying; no further `service_crashed` events will fire for this service until something restarts it. - Phase BrowserServiceCrashedEventDataPhase `json:"phase"` + // Event Lifecycle event type. + Event *ProcessStreamEventEvent `json:"event,omitempty"` - // Pid PID of the crashed process. Absent when the process manager gave up after exhausting restart attempts and is no longer tracking a live PID. - Pid *int `json:"pid,omitempty"` + // ExitCode Exit code when the event is "exit". + ExitCode *int `json:"exit_code,omitempty"` - // ServiceName Program name of the crashed service (e.g. `chromium`, `mutter`, `kernel-images-api`). - ServiceName string `json:"service_name"` + // Stream Source stream of the data chunk. + Stream *ProcessStreamEventStream `json:"stream,omitempty"` } -// BrowserServiceCrashedEventDataPhase Lifecycle phase the crash occurred in. `startup` means the process died before it ever reached a healthy running state. `running` means a previously healthy process died unexpectedly. `gave_up` means the process manager exhausted its restart attempts and stopped trying; no further `service_crashed` events will fire for this service until something restarts it. -type BrowserServiceCrashedEventDataPhase string +// ProcessStreamEventEvent Lifecycle event type. +type ProcessStreamEventEvent string -// BrowserSystemOomKillEvent The Linux kernel OOM-killer terminated a process inside the VM. Sourced from `/dev/kmsg`. Fires for any process killed by the kernel due to memory exhaustion, including Chrome renderer subprocesses that are not supervised. -type BrowserSystemOomKillEvent struct { - Category BrowserSystemOomKillEventCategory `json:"category"` +// ProcessStreamEventStream Source stream of the data chunk. +type ProcessStreamEventStream string - // Data Per-kill payload for `system_oom_kill` events. - Data *BrowserSystemOomKillEventData `json:"data,omitempty"` +// PublishEventRequest Request body for publishing an event into the telemetry stream. +type PublishEventRequest struct { + // Category Event category. Optional and advisory: for a known event `type` the server assigns the category authoritatively and ignores this field. It is only used for unknown custom types, where it is required. + Category *PublishEventRequestCategory `json:"category,omitempty"` - // Source Provenance metadata identifying which producer emitted the event. - Source BrowserEventSource `json:"source"` + // Data Telemetry event payload. + Data interface{} `json:"data,omitempty"` - // Truncated True if the data field was truncated due to size limits. - Truncated *bool `json:"truncated,omitempty"` + // Source Provenance metadata identifying which producer emitted the event. + Source *BrowserEventSource `json:"source,omitempty"` - // Ts Event timestamp in Unix microseconds. - Ts int64 `json:"ts"` - Type BrowserSystemOomKillEventType `json:"type"` + // Type Event type identifier. + Type string `json:"type"` } -// BrowserSystemOomKillEventCategory defines model for BrowserSystemOomKillEvent.Category. -type BrowserSystemOomKillEventCategory string - -// BrowserSystemOomKillEventType defines model for BrowserSystemOomKillEvent.Type. -type BrowserSystemOomKillEventType string - -// BrowserSystemOomKillEventData Per-kill payload for `system_oom_kill` events. -type BrowserSystemOomKillEventData struct { - // Constraint Why the kernel decided to OOM-kill. `none` means global memory exhaustion; `memcg` means a cgroup memory limit was hit; `cpuset` / `memory_policy` are NUMA/policy-driven kills. Absent on kernels older than 5.0 which did not emit the structured `oom-kill:` line. - Constraint *BrowserSystemOomKillEventDataConstraint `json:"constraint,omitempty"` - - // MemFreeKb Free system memory in KiB at the time of the kill, derived from the `free:N` field in the kernel's Mem-Info dump. Assumes a 4 KiB page size. Does not include reclaimable caches, so a small value with a large `mem_total_kb` may still mean the system was not under hard pressure. Absent if the kernel did not emit a parseable Mem-Info section. - MemFreeKb *int `json:"mem_free_kb,omitempty"` +// PublishEventRequestCategory Event category. Optional and advisory: for a known event `type` the server assigns the category authoritatively and ignores this field. It is only used for unknown custom types, where it is required. +type PublishEventRequestCategory string - // MemTotalKb Total system memory in KiB at the time of the kill, derived from the `N pages RAM` line in the kernel's Mem-Info dump. Assumes a 4 KiB page size. Absent if the kernel did not emit a parseable Mem-Info section. - MemTotalKb *int `json:"mem_total_kb,omitempty"` +// RecorderInfo defines model for RecorderInfo. +type RecorderInfo struct { + // FinishedAt Timestamp when recording finished + FinishedAt *time.Time `json:"finished_at,omitempty"` + Id string `json:"id"` + IsRecording bool `json:"isRecording"` - // Pid PID of the killed process. - Pid int `json:"pid"` + // StartedAt Timestamp when recording started + StartedAt *time.Time `json:"started_at,omitempty"` +} - // ProcessName Comm of the killed process as reported by the kernel (max 15 chars, truncated by the kernel). - ProcessName string `json:"process_name"` +// ScreenshotRegion defines model for ScreenshotRegion. +type ScreenshotRegion struct { + // Height Height of the region in pixels + Height int `json:"height"` - // RssKb Resident set size of the killed process in KiB (sum of anon-rss, file-rss, and shmem-rss). This is the physical memory the process was using at the time of the kill. - RssKb int `json:"rss_kb"` + // Width Width of the region in pixels + Width int `json:"width"` - // TopTasks Top processes by resident-set-size at the moment of the kill, sorted descending. Sourced from the kernel's `Tasks state` table. Empty if the kernel did not emit the table. Capped at 5 entries to bound payload size. - TopTasks *[]BrowserSystemOomKillTask `json:"top_tasks,omitempty"` + // X X coordinate of the region's top-left corner + X int `json:"x"` - // TriggerPid PID of the triggering process. Absent if the kernel did not emit the standard `CPU: N PID: N Comm:` header line. - TriggerPid *int `json:"trigger_pid,omitempty"` + // Y Y coordinate of the region's top-left corner + Y int `json:"y"` +} - // TriggerProcessName Comm of the process whose allocation request caused the kernel to invoke the OOM-killer. Often the same as `process_name` (the kernel killed the requester) but can differ when the kernel chose a different victim. Max 15 chars, truncated by the kernel. - TriggerProcessName *string `json:"trigger_process_name,omitempty"` +// ScreenshotRequest defines model for ScreenshotRequest. +type ScreenshotRequest struct { + Region *ScreenshotRegion `json:"region,omitempty"` } -// BrowserSystemOomKillEventDataConstraint Why the kernel decided to OOM-kill. `none` means global memory exhaustion; `memcg` means a cgroup memory limit was hit; `cpuset` / `memory_policy` are NUMA/policy-driven kills. Absent on kernels older than 5.0 which did not emit the structured `oom-kill:` line. -type BrowserSystemOomKillEventDataConstraint string +// ScrollRequest defines model for ScrollRequest. +type ScrollRequest struct { + // DeltaX Horizontal scroll amount. Positive scrolls right, negative scrolls left. + DeltaX *int `json:"delta_x,omitempty"` -// BrowserSystemOomKillTask A single process entry from the kernel's `Tasks state` dump. -type BrowserSystemOomKillTask struct { - // Name Comm of the process (max 15 chars, truncated by the kernel). - Name string `json:"name"` + // DeltaY Vertical scroll amount. Positive scrolls down, negative scrolls up. + DeltaY *int `json:"delta_y,omitempty"` - // Pid PID of the process. - Pid int `json:"pid"` + // HoldKeys Modifier keys to hold during the scroll + HoldKeys *[]string `json:"hold_keys,omitempty"` - // RssKb Resident set size in KiB at the moment of the kill. - RssKb int `json:"rss_kb"` + // X X coordinate at which to perform the scroll + X int `json:"x"` + + // Y Y coordinate at which to perform the scroll + Y int `json:"y"` } -// BrowserTargetType CDP target type of the page that produced the event. -type BrowserTargetType string +// SetCursorRequest defines model for SetCursorRequest. +type SetCursorRequest struct { + // Hidden Whether the cursor should be hidden + Hidden bool `json:"hidden"` +} -// BrowserTelemetryCategoriesConfig Per-category telemetry capture settings for browser events. -type BrowserTelemetryCategoriesConfig struct { - // Captcha Captcha solve attempt outcomes. - Captcha *BrowserTelemetryCategoryConfig `json:"captcha,omitempty"` +// SetFilePermissionsRequest defines model for SetFilePermissionsRequest. +type SetFilePermissionsRequest struct { + // Group New group name or GID. + Group *string `json:"group,omitempty"` - // Connection Client attach/detach lifecycle for the CDP proxy and live view. - Connection *BrowserTelemetryCategoryConfig `json:"connection,omitempty"` + // Mode File mode bits (octal string, e.g. 644). + Mode string `json:"mode"` - // Console Console output (log, warn, error) and uncaught exceptions. - Console *BrowserTelemetryCategoryConfig `json:"console,omitempty"` + // Owner New owner username or UID. + Owner *string `json:"owner,omitempty"` - // Control Agent-driven actions against the browser — computer-control calls, Playwright code execution, screenshots and clipboard access. - Control *BrowserTelemetryCategoryConfig `json:"control,omitempty"` + // Path Absolute path whose permissions are to be changed. + Path string `json:"path"` +} - // Interaction User interaction events (clicks, keydowns, scroll). - Interaction *BrowserTelemetryCategoryConfig `json:"interaction,omitempty"` +// SleepAction Pause execution for a specified duration. +type SleepAction struct { + // DurationMs Duration to sleep in milliseconds. + DurationMs int `json:"duration_ms"` +} - // Network HTTP request/response metadata. - Network *BrowserTelemetryCategoryConfig `json:"network,omitempty"` +// StartFsWatchRequest defines model for StartFsWatchRequest. +type StartFsWatchRequest struct { + // Path Directory to watch. + Path string `json:"path"` - // Page Page lifecycle events (navigation, load, layout shifts, LCP). - Page *BrowserTelemetryCategoryConfig `json:"page,omitempty"` + // Recursive Whether to watch recursively. + Recursive *bool `json:"recursive,omitempty"` +} - // Platform Calls that manage the VM rather than drive the browser (recording, filesystem, process, telemetry and browser configuration). Mostly platform-induced; off by default and opt-in. - Platform *BrowserTelemetryCategoryConfig `json:"platform,omitempty"` +// StartRecordingRequest defines model for StartRecordingRequest. +type StartRecordingRequest struct { + // Framerate Recording framerate in fps (overrides server default) + Framerate *int `json:"framerate,omitempty"` - // Screenshot Periodic base64-encoded viewport screenshots. High volume; off by default and opt-in. - Screenshot *BrowserTelemetryCategoryConfig `json:"screenshot,omitempty"` + // Id Optional identifier for this recording session, used to target it from the other /recording endpoints (stop, mark, download, delete) and allowing multiple concurrent recordings. Alphanumeric or hyphen. When omitted, the default recording session is started. + Id *string `json:"id,omitempty"` - // System Browser VM health, such as out-of-memory kills and managed-service crashes. - System *BrowserTelemetryCategoryConfig `json:"system,omitempty"` -} + // MaxDurationInSeconds Maximum recording duration in seconds (overrides server default) + MaxDurationInSeconds *int `json:"maxDurationInSeconds,omitempty"` -// BrowserTelemetryCategoryConfig Configuration for a single telemetry category. -type BrowserTelemetryCategoryConfig struct { - // Enabled Whether this category is captured. In PUT requests selection is opt-in: omitting this field (or the whole category) leaves the category off, so a PUT captures exactly the categories set to true. In PATCH requests, omitting this field (or sending an empty object `{}`) is a no-op; the category retains its current state. To enable or disable a category via PATCH, you must send an explicit `true` or `false`. - Enabled *bool `json:"enabled,omitempty"` + // MaxFileSizeInMB Maximum file size in MB (overrides server default) + MaxFileSizeInMB *int `json:"maxFileSizeInMB,omitempty"` + + // RecordAudio Capture audio alongside video. Requires the server to have an audio source and PulseAudio socket configured (the image sets both by default). When false the recording is video-only. + RecordAudio *bool `json:"recordAudio,omitempty"` } -// BrowserTelemetryConfig Telemetry configuration for a browser. Selection is opt-in. Omit the browser key (or send an empty object) to capture the default set: lightweight operational signals (control, connection, system, captcha). Within `browser`, only the categories you set enabled: true are captured; anything omitted is off. The CDP categories (console, network, page, interaction), `screenshot` and `platform` are off by default and must be opted into. A `browser` config with nothing enabled clears the telemetry configuration. The `monitor` category (CDP collector health) is not configurable here; it flows automatically whenever a CDP category is captured. -type BrowserTelemetryConfig struct { - // Browser Per-category telemetry capture settings for browser events. - Browser *BrowserTelemetryCategoriesConfig `json:"browser,omitempty"` +// StopRecordingRequest defines model for StopRecordingRequest. +type StopRecordingRequest struct { + // ForceStop Immediately stop without graceful shutdown. This may result in a corrupted video file. + ForceStop *bool `json:"forceStop,omitempty"` - // Export Forwarding of captured telemetry to an external destination. Independent of what is captured: export is off unless explicitly enabled here, even when an export destination is configured. In a PUT (full replace) an omitted export block resets export to off, the same as omitted categories turn off; in a PATCH an omitted field leaves the current setting unchanged. - Export *BrowserTelemetryExportConfig `json:"export,omitempty"` + // Id Identifier of the recording session to stop, as passed to /recording/start. Alphanumeric or hyphen. When omitted, the default recording session is stopped. + Id *string `json:"id,omitempty"` } -// BrowserTelemetryExportConfig Forwarding of captured telemetry to an external destination. Independent of what is captured: export is off unless explicitly enabled here, even when an export destination is configured. In a PUT (full replace) an omitted export block resets export to off, the same as omitted categories turn off; in a PATCH an omitted field leaves the current setting unchanged. -type BrowserTelemetryExportConfig struct { - // Otlp OTLP/HTTP export settings. - Otlp *BrowserTelemetryOTLPExportConfig `json:"otlp,omitempty"` -} +// TelemetryEnvelope The envelope assigned to a successfully published event. +type TelemetryEnvelope struct { + // Event A telemetry event. The wire-level event shape accepted by the publish endpoint and emitted on the SSE stream. Arbitrary `type` strings and `data` payloads are admitted. For browser events emitted by the Kernel image, `data` conforms to the per-type schema documented in the `Browser*Event` / `Browser*EventData` definitions, selected by `type`. + Event TelemetryEvent `json:"event"` -// BrowserTelemetryOTLPExportConfig OTLP/HTTP export settings. -type BrowserTelemetryOTLPExportConfig struct { - // Enabled Whether captured telemetry is forwarded to the configured OTLP destination. Off by default. Has no effect (export stays inactive) when no export destination is configured. - Enabled *bool `json:"enabled,omitempty"` + // Seq Process-monotonic sequence number assigned across the lifetime of the server. Use with Last-Event-ID to resume the SSE stream from this point. + Seq int64 `json:"seq"` } -// ChromiumConfigureError Failure from batched chromium configure — includes which phase failed. -type ChromiumConfigureError struct { - Message string `json:"message"` +// TelemetryEvent A telemetry event. The wire-level event shape accepted by the publish endpoint and emitted on the SSE stream. Arbitrary `type` strings and `data` payloads are admitted. For browser events emitted by the Kernel image, `data` conforms to the per-type schema documented in the `Browser*Event` / `Browser*EventData` definitions, selected by `type`. +type TelemetryEvent struct { + // Category Event category. + Category *TelemetryEventCategory `json:"category,omitempty"` - // Phase configure_phase maps to restart/filesystem/policy/extension/profile/display work; navigate_phase is retained for compatibility. - Phase ChromiumConfigureErrorPhase `json:"phase"` + // Data Arbitrary JSON payload. For browser events listed in `KnownBrowserTelemetryEvent`, the payload conforms to the corresponding `Browser*EventData` schema. + Data interface{} `json:"data,omitempty"` - // Step Optional configure step that failed. - Step *ChromiumConfigureErrorStep `json:"step,omitempty"` -} + // Source Provenance metadata identifying which producer emitted the event. + Source *BrowserEventSource `json:"source,omitempty"` -// ChromiumConfigureErrorPhase configure_phase maps to restart/filesystem/policy/extension/profile/display work; navigate_phase is retained for compatibility. -type ChromiumConfigureErrorPhase string + // Truncated Set by the server when the data field was truncated to fit the size limit. + Truncated *bool `json:"truncated,omitempty"` -// ChromiumConfigureErrorStep Optional configure step that failed. -type ChromiumConfigureErrorStep string + // Ts Unix timestamp in microseconds. Defaults to the current time when omitted. + Ts *int64 `json:"ts,omitempty"` -// ClickMouseRequest defines model for ClickMouseRequest. -type ClickMouseRequest struct { - // Button Mouse button to interact with - Button *ClickMouseRequestButton `json:"button,omitempty"` + // Type Event type identifier. + Type string `json:"type"` +} - // ClickType Type of click action - ClickType *ClickMouseRequestClickType `json:"click_type,omitempty"` +// TelemetryEventCategory Event category. +type TelemetryEventCategory string - // HoldKeys Modifier keys to hold during the click - HoldKeys *[]string `json:"hold_keys,omitempty"` +// TelemetryState Current telemetry configuration. +type TelemetryState struct { + // AppliedAt Wall-clock time at which the current configuration was applied. Omitted when telemetry is not configured. + AppliedAt *time.Time `json:"applied_at,omitempty"` - // NumClicks Number of times to repeat the click - NumClicks *int `json:"num_clicks,omitempty"` + // Config Telemetry configuration for a browser. Selection is opt-in. Omit the browser key (or send an empty object) to capture the default set: lightweight operational signals (control, connection, system, captcha). Within `browser`, only the categories you set enabled: true are captured; anything omitted is off. The CDP categories (console, network, page, interaction), `screenshot` and `platform` are off by default and must be opted into. A `browser` config with nothing enabled clears the telemetry configuration. The `monitor` category (CDP collector health) is not configurable here; it flows automatically whenever a CDP category is captured. + Config BrowserTelemetryConfig `json:"config"` - // X X coordinate of the click position - X int `json:"x"` + // DroppedEvents Cumulative number of buffered events a consumer missed because it fell behind the ring, summed across consumers and configuration changes. A rising count means the stream is being produced faster than it is being read; a steady one means nothing has been lost. Always present on images that report it; absent on an image predating the field, which is not the same as zero. + DroppedEvents *int64 `json:"dropped_events,omitempty"` - // Y Y coordinate of the click position - Y int `json:"y"` + // Seq Process-monotonic sequence number of the last published event. Does not reset across configuration changes. + Seq int64 `json:"seq"` } -// ClickMouseRequestButton Mouse button to interact with -type ClickMouseRequestButton string +// TypeTextRequest defines model for TypeTextRequest. +type TypeTextRequest struct { + // Delay Delay in milliseconds between keystrokes. Ignored when smooth is true. + Delay *int `json:"delay,omitempty"` -// ClickMouseRequestClickType Type of click action -type ClickMouseRequestClickType string + // Smooth Use human-like variable keystroke timing instead of a fixed delay. + // Defaults to true (same as moveMouse/dragMouse). Set to false for + // xdotool typing with an optional fixed delay between keys (delay=0 is instant). + // When true, text is typed in word-sized chunks with variable intra-word delays + // and natural inter-word pauses. The delay field is ignored when smooth is true. + Smooth *bool `json:"smooth,omitempty"` -// ClipboardContent defines model for ClipboardContent. -type ClipboardContent struct { - // Text Current clipboard text content + // Text Text to type on the host computer Text string `json:"text"` + + // TypoChance Per-character typo injection rate; mistakes are corrected with backspace. + // Default 0. Only applies when smooth is true (silently ignored when + // smooth is false). + TypoChance *float32 `json:"typo_chance,omitempty"` } -// ComputerAction A single computer action to execute as part of a batch. The `type` field selects which -// action to perform, and the corresponding field contains the action parameters. -// Exactly one action field matching the type must be provided. -type ComputerAction struct { - ClickMouse *ClickMouseRequest `json:"click_mouse,omitempty"` - DragMouse *DragMouseRequest `json:"drag_mouse,omitempty"` - MoveMouse *MoveMouseRequest `json:"move_mouse,omitempty"` - PressKey *PressKeyRequest `json:"press_key,omitempty"` - Scroll *ScrollRequest `json:"scroll,omitempty"` - SetCursor *SetCursorRequest `json:"set_cursor,omitempty"` +// WriteClipboardRequest defines model for WriteClipboardRequest. +type WriteClipboardRequest struct { + // Text Text to write to the system clipboard + Text string `json:"text"` +} - // Sleep Pause execution for a specified duration. - Sleep *SleepAction `json:"sleep,omitempty"` +// BadRequestError defines model for BadRequestError. +type BadRequestError = Error - // Type The type of action to perform. - Type ComputerActionType `json:"type"` - TypeText *TypeTextRequest `json:"type_text,omitempty"` -} +// ConflictError defines model for ConflictError. +type ConflictError = Error -// ComputerActionType The type of action to perform. -type ComputerActionType string +// InternalError defines model for InternalError. +type InternalError = Error -// CreateDirectoryRequest defines model for CreateDirectoryRequest. -type CreateDirectoryRequest struct { - // Mode Optional directory mode (octal string, e.g. 755). Defaults to 755. - Mode *string `json:"mode,omitempty"` +// NotFoundError defines model for NotFoundError. +type NotFoundError = Error - // Path Absolute directory path to create. - Path string `json:"path"` +// PatchChromiumFlagsJSONBody defines parameters for PatchChromiumFlags. +type PatchChromiumFlagsJSONBody struct { + // Flags Chromium flags to merge (e.g., ["--kiosk", "--disable-gpu"]) + Flags []string `json:"flags"` } -// DeletePathRequest defines model for DeletePathRequest. -type DeletePathRequest struct { - // Path Absolute path to delete. - Path string `json:"path"` -} +// PatchChromiumPoliciesJSONBody defines parameters for PatchChromiumPolicies. +type PatchChromiumPoliciesJSONBody map[string]interface{} -// DeleteRecordingRequest defines model for DeleteRecordingRequest. -type DeleteRecordingRequest struct { - // Id Identifier of the recording session to delete, as passed to /recording/start. Alphanumeric or hyphen. When omitted, the default recording session is deleted. - Id *string `json:"id,omitempty"` +// UploadExtensionsAndRestartMultipartBody defines parameters for UploadExtensionsAndRestart. +type UploadExtensionsAndRestartMultipartBody struct { + // Extensions List of extensions to upload and activate + Extensions []struct { + // Name Folder name to place the extension under /home/kernel/extensions/ + Name string `json:"name"` + + // ZipFile Zip archive containing an unpacked Chromium extension (must include manifest.json) + ZipFile openapi_types.File `json:"zip_file"` + } `json:"extensions"` } -// DisplayConfig defines model for DisplayConfig. -type DisplayConfig struct { - // Height Current display height in pixels - Height *int `json:"height,omitempty"` +// ChromiumConfigureMultipartBody defines parameters for ChromiumConfigure. +type ChromiumConfigureMultipartBody struct { + // ChromePolicies UTF-8 JSON policy override map — same semantics as PATCH /chromium/policies. + ChromePolicies *string `json:"chrome_policies,omitempty"` - // RefreshRate Current display refresh rate in Hz (may be null if not detectable) - RefreshRate *int `json:"refresh_rate,omitempty"` + // ChromiumFlags UTF-8 JSON object `{"flags":["--kiosk"]}` — same semantics as PATCH /chromium/flags. + ChromiumFlags *string `json:"chromium_flags,omitempty"` - // Width Current display width in pixels - Width *int `json:"width,omitempty"` -} + // Display UTF-8 JSON object matching `#/components/schemas/PatchDisplayRequest` (width/height/etc.). When combined with restart-triggering fields, the resize is applied while Chromium is stopped and Chromium is started once at the end. + Display *string `json:"display,omitempty"` -// DragMouseRequest defines model for DragMouseRequest. -type DragMouseRequest struct { - // Button Mouse button to drag with - Button *DragMouseRequestButton `json:"button,omitempty"` + // Extensions Extension zips paired with consecutive extensions.name fields (same as upload-extensions-and-restart). + Extensions *[]struct { + Name string `json:"name"` + ZipFile openapi_types.File `json:"zip_file"` + } `json:"extensions,omitempty"` - // Delay Delay in milliseconds between button down and starting to move along the path. - Delay *int `json:"delay,omitempty"` + // ProfileArchive tar.zst archive containing the desired `/home/kernel/user-data` profile contents. Prefer archives whose root entries are the profile files/directories themselves (for example `Default/Preferences`). Use `strip_components` only when uploading an archive that includes leading wrapper directories. + ProfileArchive *openapi_types.File `json:"profile_archive,omitempty"` - // DurationMs Target total duration in milliseconds for the entire drag movement when smooth=true. Omit for automatic timing based on total path length. - DurationMs *int `json:"duration_ms,omitempty"` + // StartUrl URL text to navigate after configure. Bare hosts are normalized to https://, length is capped at 2048 bytes, and Chrome decides which schemes are navigable. + StartUrl *string `json:"start_url,omitempty"` - // HoldKeys Modifier keys to hold during the drag - HoldKeys *[]string `json:"hold_keys,omitempty"` + // StripComponents Optional number of leading path components to strip from profile_archive entries (non-negative integer as text). + StripComponents *string `json:"strip_components,omitempty"` +} - // Path Ordered list of [x, y] coordinate pairs to move through while dragging. Must contain at least 2 points. - Path [][]int `json:"path"` +// DownloadDirZipParams defines parameters for DownloadDirZip. +type DownloadDirZipParams struct { + // Path Absolute directory path to archive and download. + Path string `form:"path" json:"path"` +} - // Smooth Use human-like Bezier curves between path waypoints instead of linear interpolation. When true, steps_per_segment and step_delay_ms are ignored. - Smooth *bool `json:"smooth,omitempty"` +// DownloadDirZstdParams defines parameters for DownloadDirZstd. +type DownloadDirZstdParams struct { + // Path Absolute directory path to archive and download. + Path string `form:"path" json:"path"` - // StepDelayMs Delay in milliseconds between relative steps while dragging. Ignored when smooth=true. - StepDelayMs *int `json:"step_delay_ms,omitempty"` + // CompressionLevel Compression level. Higher levels produce smaller archives but take longer. + // - fastest: ~zstd level 1, maximum speed (~300-500 MB/s) + // - default: ~zstd level 3, balanced speed/ratio (~150 MB/s) + // - better: ~zstd level 7, better ratio (~50-80 MB/s) + // - best: ~zstd level 11, best ratio (~20-40 MB/s) + CompressionLevel *DownloadDirZstdParamsCompressionLevel `form:"compression_level,omitempty" json:"compression_level,omitempty"` +} - // StepsPerSegment Number of relative move steps per segment in the path. Ignored when smooth=true. Minimum 1. - StepsPerSegment *int `json:"steps_per_segment,omitempty"` +// DownloadDirZstdParamsCompressionLevel defines parameters for DownloadDirZstd. +type DownloadDirZstdParamsCompressionLevel string + +// FileInfoParams defines parameters for FileInfo. +type FileInfoParams struct { + // Path Absolute path of the file or directory. + Path string `form:"path" json:"path"` +} + +// ListFilesParams defines parameters for ListFiles. +type ListFilesParams struct { + // Path Absolute directory path. + Path string `form:"path" json:"path"` } -// DragMouseRequestButton Mouse button to drag with -type DragMouseRequestButton string - -// Error defines model for Error. -type Error struct { - Message string `json:"message"` +// ReadFileParams defines parameters for ReadFile. +type ReadFileParams struct { + // Path Absolute file path to read. + Path string `form:"path" json:"path"` } -// ExecutePlaywrightRequest Request to execute Playwright code -type ExecutePlaywrightRequest struct { - // Code TypeScript/JavaScript code to execute. The code has access to 'page', 'context', and 'browser' variables. - // Example: "await page.goto('https://example.com'); return await page.title();" - Code string `json:"code"` +// UploadFilesMultipartBody defines parameters for UploadFiles. +type UploadFilesMultipartBody struct { + Files []struct { + // DestPath Absolute destination path to write the file. + DestPath string `json:"dest_path"` + File openapi_types.File `json:"file"` + } `json:"files"` +} - // TimeoutSec Maximum execution time in seconds. Default is 60. - TimeoutSec *int `json:"timeout_sec,omitempty"` +// UploadZipMultipartBody defines parameters for UploadZip. +type UploadZipMultipartBody struct { + // DestPath Absolute destination directory to extract the archive to. + DestPath string `json:"dest_path"` + ZipFile openapi_types.File `json:"zip_file"` } -// ExecutePlaywrightResult Result of Playwright code execution -type ExecutePlaywrightResult struct { - // Error Error message if execution failed - Error *string `json:"error,omitempty"` +// UploadZstdMultipartBody defines parameters for UploadZstd. +type UploadZstdMultipartBody struct { + // Archive The tar.zst archive file. + Archive openapi_types.File `json:"archive"` - // Result The value returned by the code (if any) - Result interface{} `json:"result,omitempty"` + // DestPath Absolute destination directory to extract the archive to. + DestPath string `json:"dest_path"` - // Stderr Standard error from the execution - Stderr *string `json:"stderr,omitempty"` + // StripComponents Number of leading path components to strip during extraction (like tar --strip-components). + StripComponents *int `json:"strip_components,omitempty"` +} - // Stdout Standard output from the execution - Stdout *string `json:"stdout,omitempty"` +// WriteFileParams defines parameters for WriteFile. +type WriteFileParams struct { + // Path Destination absolute file path. + Path string `form:"path" json:"path"` - // Success Whether the code executed successfully - Success bool `json:"success"` + // Mode Optional file mode (octal string, e.g. 644). Defaults to 644. + Mode *string `form:"mode,omitempty" json:"mode,omitempty"` } -// FileInfo defines model for FileInfo. -type FileInfo struct { - // IsDir Whether the path is a directory. - IsDir bool `json:"is_dir"` +// LogsStreamParams defines parameters for LogsStream. +type LogsStreamParams struct { + Source LogsStreamParamsSource `form:"source" json:"source"` + Follow *bool `form:"follow,omitempty" json:"follow,omitempty"` - // ModTime Last modification time. - ModTime time.Time `json:"mod_time"` + // Path only required if source is path + Path *string `form:"path,omitempty" json:"path,omitempty"` - // Mode File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--"). - Mode string `json:"mode"` + // SupervisorProcess only required if source is supervisor + SupervisorProcess *string `form:"supervisor_process,omitempty" json:"supervisor_process,omitempty"` +} - // Name Base name of the file or directory. - Name string `json:"name"` +// LogsStreamParamsSource defines parameters for LogsStream. +type LogsStreamParamsSource string - // Path Absolute path. - Path string `json:"path"` +// DownloadRecordingParams defines parameters for DownloadRecording. +type DownloadRecordingParams struct { + // Id Identifier of the recording session to download, as passed to /recording/start. When omitted, the default recording session is downloaded. + Id *string `form:"id,omitempty" json:"id,omitempty"` +} - // SizeBytes Size in bytes. 0 for directories. - SizeBytes int `json:"size_bytes"` +// StreamTelemetryEventsParams defines parameters for StreamTelemetryEvents. +type StreamTelemetryEventsParams struct { + // Replay Pass `all` to start from the oldest retained event. Ring buffer caps at 1024; older events are evicted and surface as a first `id` greater than 1. + Replay *StreamTelemetryEventsParamsReplay `form:"replay,omitempty" json:"replay,omitempty"` + + // LastEventID Resume after this sequence number. Omit or send 0 to start from the current position. Sequence numbers are process-monotonic, so any previous value resumes correctly from that point. Takes precedence over `replay` when both are present, so SSE auto-reconnect resumes cleanly instead of re-replaying history. + LastEventID *string `json:"Last-Event-ID,omitempty"` } -// FileSystemEvent Filesystem change event. -type FileSystemEvent struct { - // IsDir Whether the affected path is a directory. - IsDir *bool `json:"is_dir,omitempty"` +// StreamTelemetryEventsParamsReplay defines parameters for StreamTelemetryEvents. +type StreamTelemetryEventsParamsReplay string - // Name Base name of the file or directory affected. - Name *string `json:"name,omitempty"` +// PatchChromiumFlagsJSONRequestBody defines body for PatchChromiumFlags for application/json ContentType. +type PatchChromiumFlagsJSONRequestBody PatchChromiumFlagsJSONBody - // Path Absolute path of the file or directory. - Path string `json:"path"` +// PatchChromiumPoliciesJSONRequestBody defines body for PatchChromiumPolicies for application/json ContentType. +type PatchChromiumPoliciesJSONRequestBody PatchChromiumPoliciesJSONBody - // Type Event type. - Type FileSystemEventType `json:"type"` -} +// UploadExtensionsAndRestartMultipartRequestBody defines body for UploadExtensionsAndRestart for multipart/form-data ContentType. +type UploadExtensionsAndRestartMultipartRequestBody UploadExtensionsAndRestartMultipartBody -// FileSystemEventType Event type. -type FileSystemEventType string +// BatchComputerActionJSONRequestBody defines body for BatchComputerAction for application/json ContentType. +type BatchComputerActionJSONRequestBody = BatchComputerActionRequest -// KnownBrowserTelemetryEvent Discriminated union of browser telemetry events emitted by the Kernel image. This is a structural taxonomy: any event on the telemetry stream whose `data` conforms to one of the variants below (selected by `type`) is a `KnownBrowserTelemetryEvent`, regardless of who published it. Caller-published events via POST /telemetry/events are not constrained to this union; see `TelemetryEvent` for the wire shape. Validation of caller payloads against this taxonomy is the consumer's responsibility. -type KnownBrowserTelemetryEvent struct { - union json.RawMessage -} +// ClickMouseJSONRequestBody defines body for ClickMouse for application/json ContentType. +type ClickMouseJSONRequestBody = ClickMouseRequest -// ListFiles Array of file or directory information entries. -type ListFiles = []FileInfo +// WriteClipboardJSONRequestBody defines body for WriteClipboard for application/json ContentType. +type WriteClipboardJSONRequestBody = WriteClipboardRequest -// LogEvent A log entry from the application. -type LogEvent struct { - // Message Log message text. - Message string `json:"message"` +// SetCursorJSONRequestBody defines body for SetCursor for application/json ContentType. +type SetCursorJSONRequestBody = SetCursorRequest - // Timestamp Time the log entry was produced. - Timestamp time.Time `json:"timestamp"` -} +// DragMouseJSONRequestBody defines body for DragMouse for application/json ContentType. +type DragMouseJSONRequestBody = DragMouseRequest -// MarkRecordingRequest defines model for MarkRecordingRequest. -type MarkRecordingRequest struct { - // Id Identifier of the recording session to mark, as passed to /recording/start. Alphanumeric or hyphen. When omitted, the marker is added to the default recording session. - Id *string `json:"id,omitempty"` +// MoveMouseJSONRequestBody defines body for MoveMouse for application/json ContentType. +type MoveMouseJSONRequestBody = MoveMouseRequest - // Name Name of the marker, used as the MP4 chapter title. - Name string `json:"name"` -} +// PressKeyJSONRequestBody defines body for PressKey for application/json ContentType. +type PressKeyJSONRequestBody = PressKeyRequest -// MarkRecordingResult defines model for MarkRecordingResult. -type MarkRecordingResult struct { - // Name Name of the recorded marker. - Name string `json:"name"` +// TakeScreenshotJSONRequestBody defines body for TakeScreenshot for application/json ContentType. +type TakeScreenshotJSONRequestBody = ScreenshotRequest - // OffsetMs Provisional offset of the marker from the recording start, in milliseconds, measured against the start time at mark time. The authoritative offset is the chapter start written at finalize. - OffsetMs int64 `json:"offset_ms"` -} +// ScrollJSONRequestBody defines body for Scroll for application/json ContentType. +type ScrollJSONRequestBody = ScrollRequest -// MousePositionResponse defines model for MousePositionResponse. -type MousePositionResponse struct { - // X X coordinate of the cursor - X int `json:"x"` +// TypeTextJSONRequestBody defines body for TypeText for application/json ContentType. +type TypeTextJSONRequestBody = TypeTextRequest - // Y Y coordinate of the cursor - Y int `json:"y"` -} +// ChromiumConfigureMultipartRequestBody defines body for ChromiumConfigure for multipart/form-data ContentType. +type ChromiumConfigureMultipartRequestBody ChromiumConfigureMultipartBody -// MoveMouseRequest defines model for MoveMouseRequest. -type MoveMouseRequest struct { - // DurationMs Target total duration in milliseconds for the mouse movement when smooth=true. Omit for automatic timing based on distance. - DurationMs *int `json:"duration_ms,omitempty"` +// PatchDisplayJSONRequestBody defines body for PatchDisplay for application/json ContentType. +type PatchDisplayJSONRequestBody = PatchDisplayRequest - // HoldKeys Modifier keys to hold during the move - HoldKeys *[]string `json:"hold_keys,omitempty"` +// CreateDirectoryJSONRequestBody defines body for CreateDirectory for application/json ContentType. +type CreateDirectoryJSONRequestBody = CreateDirectoryRequest - // Smooth Use human-like Bezier curve path instead of instant mouse movement. - Smooth *bool `json:"smooth,omitempty"` +// DeleteDirectoryJSONRequestBody defines body for DeleteDirectory for application/json ContentType. +type DeleteDirectoryJSONRequestBody = DeletePathRequest - // X X coordinate to move the cursor to - X int `json:"x"` +// DeleteFileJSONRequestBody defines body for DeleteFile for application/json ContentType. +type DeleteFileJSONRequestBody = DeletePathRequest - // Y Y coordinate to move the cursor to - Y int `json:"y"` -} +// MovePathJSONRequestBody defines body for MovePath for application/json ContentType. +type MovePathJSONRequestBody = MovePathRequest -// MovePathRequest defines model for MovePathRequest. -type MovePathRequest struct { - // DestPath Absolute destination path. - DestPath string `json:"dest_path"` +// SetFilePermissionsJSONRequestBody defines body for SetFilePermissions for application/json ContentType. +type SetFilePermissionsJSONRequestBody = SetFilePermissionsRequest - // SrcPath Absolute source path. - SrcPath string `json:"src_path"` -} +// UploadFilesMultipartRequestBody defines body for UploadFiles for multipart/form-data ContentType. +type UploadFilesMultipartRequestBody UploadFilesMultipartBody -// OkResponse Generic OK response. -type OkResponse struct { - // Ok Indicates success. - Ok bool `json:"ok"` -} +// UploadZipMultipartRequestBody defines body for UploadZip for multipart/form-data ContentType. +type UploadZipMultipartRequestBody UploadZipMultipartBody -// PatchDisplayRequest defines model for PatchDisplayRequest. -type PatchDisplayRequest struct { - // Height Display height in pixels - Height *int `json:"height,omitempty"` +// UploadZstdMultipartRequestBody defines body for UploadZstd for multipart/form-data ContentType. +type UploadZstdMultipartRequestBody UploadZstdMultipartBody - // RefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. - RefreshRate *PatchDisplayRequestRefreshRate `json:"refresh_rate,omitempty"` +// StartFsWatchJSONRequestBody defines body for StartFsWatch for application/json ContentType. +type StartFsWatchJSONRequestBody = StartFsWatchRequest - // RequireIdle If true, refuse to resize when live view or recording/replay is active. - RequireIdle *bool `json:"require_idle,omitempty"` +// ExecutePlaywrightCodeJSONRequestBody defines body for ExecutePlaywrightCode for application/json ContentType. +type ExecutePlaywrightCodeJSONRequestBody = ExecutePlaywrightRequest - // RestartChromium If true, restart Chromium after resolution change to ensure it adapts to new size. Default is false for headful, true for headless. - RestartChromium *bool `json:"restart_chromium,omitempty"` +// ProcessExecJSONRequestBody defines body for ProcessExec for application/json ContentType. +type ProcessExecJSONRequestBody = ProcessExecRequest - // Width Display width in pixels - Width *int `json:"width,omitempty"` -} +// ProcessSpawnJSONRequestBody defines body for ProcessSpawn for application/json ContentType. +type ProcessSpawnJSONRequestBody = ProcessSpawnRequest -// PatchDisplayRequestRefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. -type PatchDisplayRequestRefreshRate int +// ProcessKillJSONRequestBody defines body for ProcessKill for application/json ContentType. +type ProcessKillJSONRequestBody = ProcessKillRequest -// PressKeyRequest defines model for PressKeyRequest. -type PressKeyRequest struct { - // Duration Duration to hold the keys down in milliseconds. If omitted or 0, keys are tapped. - Duration *int `json:"duration,omitempty"` +// ProcessResizeJSONRequestBody defines body for ProcessResize for application/json ContentType. +type ProcessResizeJSONRequestBody = ProcessResizeRequest - // HoldKeys Optional modifier keys to hold during the key press sequence. - HoldKeys *[]string `json:"hold_keys,omitempty"` +// ProcessStdinJSONRequestBody defines body for ProcessStdin for application/json ContentType. +type ProcessStdinJSONRequestBody = ProcessStdinRequest - // Keys List of key symbols to press. Each item should be a key symbol supported by xdotool - // (see X11 keysym definitions). Examples include "Return", "Shift", "Ctrl", "Alt", "F5". - // Items in this list could also be combinations, e.g. "Ctrl+t" or "Ctrl+Shift+Tab". - Keys []string `json:"keys"` -} +// DeleteRecordingJSONRequestBody defines body for DeleteRecording for application/json ContentType. +type DeleteRecordingJSONRequestBody = DeleteRecordingRequest -// ProcessExecRequest Request to execute a command synchronously. -type ProcessExecRequest struct { - // Args Command arguments. - Args *[]string `json:"args,omitempty"` +// MarkRecordingJSONRequestBody defines body for MarkRecording for application/json ContentType. +type MarkRecordingJSONRequestBody = MarkRecordingRequest - // AsRoot Run the process with root privileges. - AsRoot *bool `json:"as_root,omitempty"` +// StartRecordingJSONRequestBody defines body for StartRecording for application/json ContentType. +type StartRecordingJSONRequestBody = StartRecordingRequest - // AsUser Run the process as this user. - AsUser *string `json:"as_user,omitempty"` +// StopRecordingJSONRequestBody defines body for StopRecording for application/json ContentType. +type StopRecordingJSONRequestBody = StopRecordingRequest - // Command Executable or shell command to run. - Command string `json:"command"` +// PatchTelemetryJSONRequestBody defines body for PatchTelemetry for application/json ContentType. +type PatchTelemetryJSONRequestBody = BrowserTelemetryConfig - // Cwd Working directory (absolute path) to run the command in. - Cwd *string `json:"cwd,omitempty"` +// PutTelemetryJSONRequestBody defines body for PutTelemetry for application/json ContentType. +type PutTelemetryJSONRequestBody = BrowserTelemetryConfig - // Env Environment variables to set for the process. - Env *map[string]string `json:"env,omitempty"` +// PublishTelemetryEventJSONRequestBody defines body for PublishTelemetryEvent for application/json ContentType. +type PublishTelemetryEventJSONRequestBody = PublishEventRequest - // TimeoutSec Maximum execution time in seconds. - TimeoutSec *int `json:"timeout_sec,omitempty"` +// AsBrowserCdpInputDispatchMouseEventCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputDispatchMouseEventCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputDispatchMouseEventCommandData() (BrowserCdpInputDispatchMouseEventCommandData, error) { + var body BrowserCdpInputDispatchMouseEventCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// ProcessExecResult Result of a synchronous command execution. -type ProcessExecResult struct { - // DurationMs Execution duration in milliseconds. - DurationMs *int `json:"duration_ms,omitempty"` - - // ExitCode Process exit code. - ExitCode *int `json:"exit_code,omitempty"` +// FromBrowserCdpInputDispatchMouseEventCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputDispatchMouseEventCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputDispatchMouseEventCommandData(v BrowserCdpInputDispatchMouseEventCommandData) error { + v.Method = "Input.dispatchMouseEvent" + b, err := json.Marshal(v) + t.union = b + return err +} - // StderrB64 Base64-encoded stderr buffer. - StderrB64 *string `json:"stderr_b64,omitempty"` +// MergeBrowserCdpInputDispatchMouseEventCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputDispatchMouseEventCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputDispatchMouseEventCommandData(v BrowserCdpInputDispatchMouseEventCommandData) error { + v.Method = "Input.dispatchMouseEvent" + b, err := json.Marshal(v) + if err != nil { + return err + } - // StdoutB64 Base64-encoded stdout buffer. - StdoutB64 *string `json:"stdout_b64,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ProcessKillRequest Signal to send to the process. -type ProcessKillRequest struct { - // Signal Signal to send. - Signal ProcessKillRequestSignal `json:"signal"` +// AsBrowserCdpInputDispatchKeyEventCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputDispatchKeyEventCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputDispatchKeyEventCommandData() (BrowserCdpInputDispatchKeyEventCommandData, error) { + var body BrowserCdpInputDispatchKeyEventCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// ProcessKillRequestSignal Signal to send. -type ProcessKillRequestSignal string +// FromBrowserCdpInputDispatchKeyEventCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputDispatchKeyEventCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputDispatchKeyEventCommandData(v BrowserCdpInputDispatchKeyEventCommandData) error { + v.Method = "Input.dispatchKeyEvent" + b, err := json.Marshal(v) + t.union = b + return err +} -// ProcessResizeRequest Resize a PTY-backed process. -type ProcessResizeRequest struct { - // Cols New terminal columns. - Cols int `json:"cols"` +// MergeBrowserCdpInputDispatchKeyEventCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputDispatchKeyEventCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputDispatchKeyEventCommandData(v BrowserCdpInputDispatchKeyEventCommandData) error { + v.Method = "Input.dispatchKeyEvent" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Rows New terminal rows. - Rows int `json:"rows"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ProcessSpawnRequest defines model for ProcessSpawnRequest. -type ProcessSpawnRequest struct { - // AllocateTty Allocate a pseudo-terminal (PTY) for the process to enable interactive shells. - AllocateTty *bool `json:"allocate_tty,omitempty"` - - // Args Command arguments. - Args *[]string `json:"args,omitempty"` +// AsBrowserCdpInputInsertTextCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputInsertTextCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputInsertTextCommandData() (BrowserCdpInputInsertTextCommandData, error) { + var body BrowserCdpInputInsertTextCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // AsRoot Run the process with root privileges. - AsRoot *bool `json:"as_root,omitempty"` +// FromBrowserCdpInputInsertTextCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputInsertTextCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputInsertTextCommandData(v BrowserCdpInputInsertTextCommandData) error { + v.Method = "Input.insertText" + b, err := json.Marshal(v) + t.union = b + return err +} - // AsUser Run the process as this user. - AsUser *string `json:"as_user,omitempty"` +// MergeBrowserCdpInputInsertTextCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputInsertTextCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputInsertTextCommandData(v BrowserCdpInputInsertTextCommandData) error { + v.Method = "Input.insertText" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Cols Initial terminal columns when allocate_tty is true. - Cols *int `json:"cols,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Command Executable or shell command to run. - Command string `json:"command"` +// AsBrowserCdpInputImeSetCompositionCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputImeSetCompositionCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputImeSetCompositionCommandData() (BrowserCdpInputImeSetCompositionCommandData, error) { + var body BrowserCdpInputImeSetCompositionCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Cwd Working directory (absolute path) to run the command in. - Cwd *string `json:"cwd,omitempty"` +// FromBrowserCdpInputImeSetCompositionCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputImeSetCompositionCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputImeSetCompositionCommandData(v BrowserCdpInputImeSetCompositionCommandData) error { + v.Method = "Input.imeSetComposition" + b, err := json.Marshal(v) + t.union = b + return err +} - // Env Environment variables to set for the process. - Env *map[string]string `json:"env,omitempty"` +// MergeBrowserCdpInputImeSetCompositionCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputImeSetCompositionCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputImeSetCompositionCommandData(v BrowserCdpInputImeSetCompositionCommandData) error { + v.Method = "Input.imeSetComposition" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Rows Initial terminal rows when allocate_tty is true. - Rows *int `json:"rows,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // TimeoutSec Maximum execution time in seconds. - TimeoutSec *int `json:"timeout_sec,omitempty"` +// AsBrowserCdpInputDispatchTouchEventCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputDispatchTouchEventCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputDispatchTouchEventCommandData() (BrowserCdpInputDispatchTouchEventCommandData, error) { + var body BrowserCdpInputDispatchTouchEventCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// ProcessSpawnResult Information about a spawned process. -type ProcessSpawnResult struct { - // Pid OS process ID. - Pid *int `json:"pid,omitempty"` +// FromBrowserCdpInputDispatchTouchEventCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputDispatchTouchEventCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputDispatchTouchEventCommandData(v BrowserCdpInputDispatchTouchEventCommandData) error { + v.Method = "Input.dispatchTouchEvent" + b, err := json.Marshal(v) + t.union = b + return err +} - // ProcessId Server-assigned identifier for the process. - ProcessId *openapi_types.UUID `json:"process_id,omitempty"` +// MergeBrowserCdpInputDispatchTouchEventCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputDispatchTouchEventCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputDispatchTouchEventCommandData(v BrowserCdpInputDispatchTouchEventCommandData) error { + v.Method = "Input.dispatchTouchEvent" + b, err := json.Marshal(v) + if err != nil { + return err + } - // StartedAt Timestamp when the process started. - StartedAt *time.Time `json:"started_at,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ProcessStatus Current status of a process. -type ProcessStatus struct { - // CpuPct Estimated CPU usage percentage. - CpuPct *float32 `json:"cpu_pct,omitempty"` +// AsBrowserCdpInputDispatchDragEventCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputDispatchDragEventCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputDispatchDragEventCommandData() (BrowserCdpInputDispatchDragEventCommandData, error) { + var body BrowserCdpInputDispatchDragEventCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // ExitCode Exit code if the process has exited. - ExitCode *int `json:"exit_code,omitempty"` +// FromBrowserCdpInputDispatchDragEventCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputDispatchDragEventCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputDispatchDragEventCommandData(v BrowserCdpInputDispatchDragEventCommandData) error { + v.Method = "Input.dispatchDragEvent" + b, err := json.Marshal(v) + t.union = b + return err +} - // MemBytes Estimated resident memory usage in bytes. - MemBytes *int `json:"mem_bytes,omitempty"` +// MergeBrowserCdpInputDispatchDragEventCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputDispatchDragEventCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputDispatchDragEventCommandData(v BrowserCdpInputDispatchDragEventCommandData) error { + v.Method = "Input.dispatchDragEvent" + b, err := json.Marshal(v) + if err != nil { + return err + } - // State Process state. - State *ProcessStatusState `json:"state,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ProcessStatusState Process state. -type ProcessStatusState string - -// ProcessStdinRequest Data to write to the process standard input. -type ProcessStdinRequest struct { - // DataB64 Base64-encoded data to write. - DataB64 string `json:"data_b64"` +// AsBrowserCdpInputCancelDraggingCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputCancelDraggingCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputCancelDraggingCommandData() (BrowserCdpInputCancelDraggingCommandData, error) { + var body BrowserCdpInputCancelDraggingCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// ProcessStdinResult Result of writing to stdin. -type ProcessStdinResult struct { - // WrittenBytes Number of bytes written. - WrittenBytes *int `json:"written_bytes,omitempty"` +// FromBrowserCdpInputCancelDraggingCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputCancelDraggingCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputCancelDraggingCommandData(v BrowserCdpInputCancelDraggingCommandData) error { + v.Method = "Input.cancelDragging" + b, err := json.Marshal(v) + t.union = b + return err } -// ProcessStreamEvent SSE payload representing process output or lifecycle events. -type ProcessStreamEvent struct { - // DataB64 Base64-encoded data from the process stream. - DataB64 *string `json:"data_b64,omitempty"` +// MergeBrowserCdpInputCancelDraggingCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputCancelDraggingCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputCancelDraggingCommandData(v BrowserCdpInputCancelDraggingCommandData) error { + v.Method = "Input.cancelDragging" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Event Lifecycle event type. - Event *ProcessStreamEventEvent `json:"event,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // ExitCode Exit code when the event is "exit". - ExitCode *int `json:"exit_code,omitempty"` +// AsBrowserCdpInputEmulateTouchFromMouseEventCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputEmulateTouchFromMouseEventCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputEmulateTouchFromMouseEventCommandData() (BrowserCdpInputEmulateTouchFromMouseEventCommandData, error) { + var body BrowserCdpInputEmulateTouchFromMouseEventCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Stream Source stream of the data chunk. - Stream *ProcessStreamEventStream `json:"stream,omitempty"` +// FromBrowserCdpInputEmulateTouchFromMouseEventCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputEmulateTouchFromMouseEventCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputEmulateTouchFromMouseEventCommandData(v BrowserCdpInputEmulateTouchFromMouseEventCommandData) error { + v.Method = "Input.emulateTouchFromMouseEvent" + b, err := json.Marshal(v) + t.union = b + return err } -// ProcessStreamEventEvent Lifecycle event type. -type ProcessStreamEventEvent string +// MergeBrowserCdpInputEmulateTouchFromMouseEventCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputEmulateTouchFromMouseEventCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputEmulateTouchFromMouseEventCommandData(v BrowserCdpInputEmulateTouchFromMouseEventCommandData) error { + v.Method = "Input.emulateTouchFromMouseEvent" + b, err := json.Marshal(v) + if err != nil { + return err + } -// ProcessStreamEventStream Source stream of the data chunk. -type ProcessStreamEventStream string + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PublishEventRequest Request body for publishing an event into the telemetry stream. -type PublishEventRequest struct { - // Category Event category. Optional and advisory: for a known event `type` the server assigns the category authoritatively and ignores this field. It is only used for unknown custom types, where it is required. - Category *PublishEventRequestCategory `json:"category,omitempty"` +// AsBrowserCdpInputSynthesizePinchGestureCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputSynthesizePinchGestureCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputSynthesizePinchGestureCommandData() (BrowserCdpInputSynthesizePinchGestureCommandData, error) { + var body BrowserCdpInputSynthesizePinchGestureCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Data Telemetry event payload. - Data interface{} `json:"data,omitempty"` +// FromBrowserCdpInputSynthesizePinchGestureCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputSynthesizePinchGestureCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputSynthesizePinchGestureCommandData(v BrowserCdpInputSynthesizePinchGestureCommandData) error { + v.Method = "Input.synthesizePinchGesture" + b, err := json.Marshal(v) + t.union = b + return err +} - // Source Provenance metadata identifying which producer emitted the event. - Source *BrowserEventSource `json:"source,omitempty"` +// MergeBrowserCdpInputSynthesizePinchGestureCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputSynthesizePinchGestureCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputSynthesizePinchGestureCommandData(v BrowserCdpInputSynthesizePinchGestureCommandData) error { + v.Method = "Input.synthesizePinchGesture" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Type Event type identifier. - Type string `json:"type"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsBrowserCdpInputSynthesizeScrollGestureCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputSynthesizeScrollGestureCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputSynthesizeScrollGestureCommandData() (BrowserCdpInputSynthesizeScrollGestureCommandData, error) { + var body BrowserCdpInputSynthesizeScrollGestureCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// PublishEventRequestCategory Event category. Optional and advisory: for a known event `type` the server assigns the category authoritatively and ignores this field. It is only used for unknown custom types, where it is required. -type PublishEventRequestCategory string +// FromBrowserCdpInputSynthesizeScrollGestureCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputSynthesizeScrollGestureCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputSynthesizeScrollGestureCommandData(v BrowserCdpInputSynthesizeScrollGestureCommandData) error { + v.Method = "Input.synthesizeScrollGesture" + b, err := json.Marshal(v) + t.union = b + return err +} -// RecorderInfo defines model for RecorderInfo. -type RecorderInfo struct { - // FinishedAt Timestamp when recording finished - FinishedAt *time.Time `json:"finished_at,omitempty"` - Id string `json:"id"` - IsRecording bool `json:"isRecording"` +// MergeBrowserCdpInputSynthesizeScrollGestureCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputSynthesizeScrollGestureCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputSynthesizeScrollGestureCommandData(v BrowserCdpInputSynthesizeScrollGestureCommandData) error { + v.Method = "Input.synthesizeScrollGesture" + b, err := json.Marshal(v) + if err != nil { + return err + } - // StartedAt Timestamp when recording started - StartedAt *time.Time `json:"started_at,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ScreenshotRegion defines model for ScreenshotRegion. -type ScreenshotRegion struct { - // Height Height of the region in pixels - Height int `json:"height"` +// AsBrowserCdpInputSynthesizeTapGestureCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpInputSynthesizeTapGestureCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpInputSynthesizeTapGestureCommandData() (BrowserCdpInputSynthesizeTapGestureCommandData, error) { + var body BrowserCdpInputSynthesizeTapGestureCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Width Width of the region in pixels - Width int `json:"width"` +// FromBrowserCdpInputSynthesizeTapGestureCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpInputSynthesizeTapGestureCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpInputSynthesizeTapGestureCommandData(v BrowserCdpInputSynthesizeTapGestureCommandData) error { + v.Method = "Input.synthesizeTapGesture" + b, err := json.Marshal(v) + t.union = b + return err +} - // X X coordinate of the region's top-left corner - X int `json:"x"` +// MergeBrowserCdpInputSynthesizeTapGestureCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpInputSynthesizeTapGestureCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpInputSynthesizeTapGestureCommandData(v BrowserCdpInputSynthesizeTapGestureCommandData) error { + v.Method = "Input.synthesizeTapGesture" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Y Y coordinate of the region's top-left corner - Y int `json:"y"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ScreenshotRequest defines model for ScreenshotRequest. -type ScreenshotRequest struct { - Region *ScreenshotRegion `json:"region,omitempty"` +// AsBrowserCdpDomSetFileInputFilesCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpDomSetFileInputFilesCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpDomSetFileInputFilesCommandData() (BrowserCdpDomSetFileInputFilesCommandData, error) { + var body BrowserCdpDomSetFileInputFilesCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// ScrollRequest defines model for ScrollRequest. -type ScrollRequest struct { - // DeltaX Horizontal scroll amount. Positive scrolls right, negative scrolls left. - DeltaX *int `json:"delta_x,omitempty"` - - // DeltaY Vertical scroll amount. Positive scrolls down, negative scrolls up. - DeltaY *int `json:"delta_y,omitempty"` - - // HoldKeys Modifier keys to hold during the scroll - HoldKeys *[]string `json:"hold_keys,omitempty"` +// FromBrowserCdpDomSetFileInputFilesCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpDomSetFileInputFilesCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpDomSetFileInputFilesCommandData(v BrowserCdpDomSetFileInputFilesCommandData) error { + v.Method = "DOM.setFileInputFiles" + b, err := json.Marshal(v) + t.union = b + return err +} - // X X coordinate at which to perform the scroll - X int `json:"x"` +// MergeBrowserCdpDomSetFileInputFilesCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpDomSetFileInputFilesCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpDomSetFileInputFilesCommandData(v BrowserCdpDomSetFileInputFilesCommandData) error { + v.Method = "DOM.setFileInputFiles" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Y Y coordinate at which to perform the scroll - Y int `json:"y"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// SetCursorRequest defines model for SetCursorRequest. -type SetCursorRequest struct { - // Hidden Whether the cursor should be hidden - Hidden bool `json:"hidden"` +// AsBrowserCdpDomFocusCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpDomFocusCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpDomFocusCommandData() (BrowserCdpDomFocusCommandData, error) { + var body BrowserCdpDomFocusCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// SetFilePermissionsRequest defines model for SetFilePermissionsRequest. -type SetFilePermissionsRequest struct { - // Group New group name or GID. - Group *string `json:"group,omitempty"` +// FromBrowserCdpDomFocusCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpDomFocusCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpDomFocusCommandData(v BrowserCdpDomFocusCommandData) error { + v.Method = "DOM.focus" + b, err := json.Marshal(v) + t.union = b + return err +} - // Mode File mode bits (octal string, e.g. 644). - Mode string `json:"mode"` +// MergeBrowserCdpDomFocusCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpDomFocusCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpDomFocusCommandData(v BrowserCdpDomFocusCommandData) error { + v.Method = "DOM.focus" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Owner New owner username or UID. - Owner *string `json:"owner,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Path Absolute path whose permissions are to be changed. - Path string `json:"path"` +// AsBrowserCdpDomScrollIntoViewIfNeededCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpDomScrollIntoViewIfNeededCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpDomScrollIntoViewIfNeededCommandData() (BrowserCdpDomScrollIntoViewIfNeededCommandData, error) { + var body BrowserCdpDomScrollIntoViewIfNeededCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// SleepAction Pause execution for a specified duration. -type SleepAction struct { - // DurationMs Duration to sleep in milliseconds. - DurationMs int `json:"duration_ms"` +// FromBrowserCdpDomScrollIntoViewIfNeededCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpDomScrollIntoViewIfNeededCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpDomScrollIntoViewIfNeededCommandData(v BrowserCdpDomScrollIntoViewIfNeededCommandData) error { + v.Method = "DOM.scrollIntoViewIfNeeded" + b, err := json.Marshal(v) + t.union = b + return err } -// StartFsWatchRequest defines model for StartFsWatchRequest. -type StartFsWatchRequest struct { - // Path Directory to watch. - Path string `json:"path"` +// MergeBrowserCdpDomScrollIntoViewIfNeededCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpDomScrollIntoViewIfNeededCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpDomScrollIntoViewIfNeededCommandData(v BrowserCdpDomScrollIntoViewIfNeededCommandData) error { + v.Method = "DOM.scrollIntoViewIfNeeded" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Recursive Whether to watch recursively. - Recursive *bool `json:"recursive,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// StartRecordingRequest defines model for StartRecordingRequest. -type StartRecordingRequest struct { - // Framerate Recording framerate in fps (overrides server default) - Framerate *int `json:"framerate,omitempty"` - - // Id Optional identifier for this recording session, used to target it from the other /recording endpoints (stop, mark, download, delete) and allowing multiple concurrent recordings. Alphanumeric or hyphen. When omitted, the default recording session is started. - Id *string `json:"id,omitempty"` +// AsBrowserCdpPageBringToFrontCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageBringToFrontCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageBringToFrontCommandData() (BrowserCdpPageBringToFrontCommandData, error) { + var body BrowserCdpPageBringToFrontCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // MaxDurationInSeconds Maximum recording duration in seconds (overrides server default) - MaxDurationInSeconds *int `json:"maxDurationInSeconds,omitempty"` +// FromBrowserCdpPageBringToFrontCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageBringToFrontCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageBringToFrontCommandData(v BrowserCdpPageBringToFrontCommandData) error { + v.Method = "Page.bringToFront" + b, err := json.Marshal(v) + t.union = b + return err +} - // MaxFileSizeInMB Maximum file size in MB (overrides server default) - MaxFileSizeInMB *int `json:"maxFileSizeInMB,omitempty"` +// MergeBrowserCdpPageBringToFrontCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageBringToFrontCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageBringToFrontCommandData(v BrowserCdpPageBringToFrontCommandData) error { + v.Method = "Page.bringToFront" + b, err := json.Marshal(v) + if err != nil { + return err + } - // RecordAudio Capture audio alongside video. Requires the server to have an audio source and PulseAudio socket configured (the image sets both by default). When false the recording is video-only. - RecordAudio *bool `json:"recordAudio,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// StopRecordingRequest defines model for StopRecordingRequest. -type StopRecordingRequest struct { - // ForceStop Immediately stop without graceful shutdown. This may result in a corrupted video file. - ForceStop *bool `json:"forceStop,omitempty"` +// AsBrowserCdpPageCaptureScreenshotCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageCaptureScreenshotCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageCaptureScreenshotCommandData() (BrowserCdpPageCaptureScreenshotCommandData, error) { + var body BrowserCdpPageCaptureScreenshotCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Id Identifier of the recording session to stop, as passed to /recording/start. Alphanumeric or hyphen. When omitted, the default recording session is stopped. - Id *string `json:"id,omitempty"` +// FromBrowserCdpPageCaptureScreenshotCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageCaptureScreenshotCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageCaptureScreenshotCommandData(v BrowserCdpPageCaptureScreenshotCommandData) error { + v.Method = "Page.captureScreenshot" + b, err := json.Marshal(v) + t.union = b + return err } -// TelemetryEnvelope The envelope assigned to a successfully published event. -type TelemetryEnvelope struct { - // Event A telemetry event. The wire-level event shape accepted by the publish endpoint and emitted on the SSE stream. Arbitrary `type` strings and `data` payloads are admitted. For browser events emitted by the Kernel image, `data` conforms to the per-type schema documented in the `Browser*Event` / `Browser*EventData` definitions, selected by `type`. - Event TelemetryEvent `json:"event"` +// MergeBrowserCdpPageCaptureScreenshotCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageCaptureScreenshotCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageCaptureScreenshotCommandData(v BrowserCdpPageCaptureScreenshotCommandData) error { + v.Method = "Page.captureScreenshot" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Seq Process-monotonic sequence number assigned across the lifetime of the server. Use with Last-Event-ID to resume the SSE stream from this point. - Seq int64 `json:"seq"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// TelemetryEvent A telemetry event. The wire-level event shape accepted by the publish endpoint and emitted on the SSE stream. Arbitrary `type` strings and `data` payloads are admitted. For browser events emitted by the Kernel image, `data` conforms to the per-type schema documented in the `Browser*Event` / `Browser*EventData` definitions, selected by `type`. -type TelemetryEvent struct { - // Category Event category. - Category *TelemetryEventCategory `json:"category,omitempty"` +// AsBrowserCdpPageCaptureSnapshotCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageCaptureSnapshotCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageCaptureSnapshotCommandData() (BrowserCdpPageCaptureSnapshotCommandData, error) { + var body BrowserCdpPageCaptureSnapshotCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Data Arbitrary JSON payload. For browser events listed in `KnownBrowserTelemetryEvent`, the payload conforms to the corresponding `Browser*EventData` schema. - Data interface{} `json:"data,omitempty"` +// FromBrowserCdpPageCaptureSnapshotCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageCaptureSnapshotCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageCaptureSnapshotCommandData(v BrowserCdpPageCaptureSnapshotCommandData) error { + v.Method = "Page.captureSnapshot" + b, err := json.Marshal(v) + t.union = b + return err +} - // Source Provenance metadata identifying which producer emitted the event. - Source *BrowserEventSource `json:"source,omitempty"` +// MergeBrowserCdpPageCaptureSnapshotCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageCaptureSnapshotCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageCaptureSnapshotCommandData(v BrowserCdpPageCaptureSnapshotCommandData) error { + v.Method = "Page.captureSnapshot" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Truncated Set by the server when the data field was truncated to fit the size limit. - Truncated *bool `json:"truncated,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Ts Unix timestamp in microseconds. Defaults to the current time when omitted. - Ts *int64 `json:"ts,omitempty"` +// AsBrowserCdpPageHandleJavaScriptDialogCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageHandleJavaScriptDialogCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageHandleJavaScriptDialogCommandData() (BrowserCdpPageHandleJavaScriptDialogCommandData, error) { + var body BrowserCdpPageHandleJavaScriptDialogCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Type Event type identifier. - Type string `json:"type"` +// FromBrowserCdpPageHandleJavaScriptDialogCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageHandleJavaScriptDialogCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageHandleJavaScriptDialogCommandData(v BrowserCdpPageHandleJavaScriptDialogCommandData) error { + v.Method = "Page.handleJavaScriptDialog" + b, err := json.Marshal(v) + t.union = b + return err } -// TelemetryEventCategory Event category. -type TelemetryEventCategory string +// MergeBrowserCdpPageHandleJavaScriptDialogCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageHandleJavaScriptDialogCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageHandleJavaScriptDialogCommandData(v BrowserCdpPageHandleJavaScriptDialogCommandData) error { + v.Method = "Page.handleJavaScriptDialog" + b, err := json.Marshal(v) + if err != nil { + return err + } -// TelemetryState Current telemetry configuration. -type TelemetryState struct { - // AppliedAt Wall-clock time at which the current configuration was applied. Omitted when telemetry is not configured. - AppliedAt *time.Time `json:"applied_at,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Config Telemetry configuration for a browser. Selection is opt-in. Omit the browser key (or send an empty object) to capture the default set: lightweight operational signals (control, connection, system, captcha). Within `browser`, only the categories you set enabled: true are captured; anything omitted is off. The CDP categories (console, network, page, interaction), `screenshot` and `platform` are off by default and must be opted into. A `browser` config with nothing enabled clears the telemetry configuration. The `monitor` category (CDP collector health) is not configurable here; it flows automatically whenever a CDP category is captured. - Config BrowserTelemetryConfig `json:"config"` +// AsBrowserCdpPageNavigateCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageNavigateCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageNavigateCommandData() (BrowserCdpPageNavigateCommandData, error) { + var body BrowserCdpPageNavigateCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Seq Process-monotonic sequence number of the last published event. Does not reset across configuration changes. - Seq int64 `json:"seq"` +// FromBrowserCdpPageNavigateCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageNavigateCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageNavigateCommandData(v BrowserCdpPageNavigateCommandData) error { + v.Method = "Page.navigate" + b, err := json.Marshal(v) + t.union = b + return err } -// TypeTextRequest defines model for TypeTextRequest. -type TypeTextRequest struct { - // Delay Delay in milliseconds between keystrokes. Ignored when smooth is true. - Delay *int `json:"delay,omitempty"` +// MergeBrowserCdpPageNavigateCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageNavigateCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageNavigateCommandData(v BrowserCdpPageNavigateCommandData) error { + v.Method = "Page.navigate" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Smooth Use human-like variable keystroke timing instead of a fixed delay. - // Defaults to true (same as moveMouse/dragMouse). Set to false for - // xdotool typing with an optional fixed delay between keys (delay=0 is instant). - // When true, text is typed in word-sized chunks with variable intra-word delays - // and natural inter-word pauses. The delay field is ignored when smooth is true. - Smooth *bool `json:"smooth,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsBrowserCdpPageNavigateToHistoryEntryCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageNavigateToHistoryEntryCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageNavigateToHistoryEntryCommandData() (BrowserCdpPageNavigateToHistoryEntryCommandData, error) { + var body BrowserCdpPageNavigateToHistoryEntryCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Text Text to type on the host computer - Text string `json:"text"` +// FromBrowserCdpPageNavigateToHistoryEntryCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageNavigateToHistoryEntryCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageNavigateToHistoryEntryCommandData(v BrowserCdpPageNavigateToHistoryEntryCommandData) error { + v.Method = "Page.navigateToHistoryEntry" + b, err := json.Marshal(v) + t.union = b + return err +} - // TypoChance Per-character typo injection rate; mistakes are corrected with backspace. - // Default 0. Only applies when smooth is true (silently ignored when - // smooth is false). - TypoChance *float32 `json:"typo_chance,omitempty"` +// MergeBrowserCdpPageNavigateToHistoryEntryCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageNavigateToHistoryEntryCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageNavigateToHistoryEntryCommandData(v BrowserCdpPageNavigateToHistoryEntryCommandData) error { + v.Method = "Page.navigateToHistoryEntry" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// WriteClipboardRequest defines model for WriteClipboardRequest. -type WriteClipboardRequest struct { - // Text Text to write to the system clipboard - Text string `json:"text"` +// AsBrowserCdpPageReloadCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageReloadCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageReloadCommandData() (BrowserCdpPageReloadCommandData, error) { + var body BrowserCdpPageReloadCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// BadRequestError defines model for BadRequestError. -type BadRequestError = Error +// FromBrowserCdpPageReloadCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageReloadCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageReloadCommandData(v BrowserCdpPageReloadCommandData) error { + v.Method = "Page.reload" + b, err := json.Marshal(v) + t.union = b + return err +} -// ConflictError defines model for ConflictError. -type ConflictError = Error +// MergeBrowserCdpPageReloadCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageReloadCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageReloadCommandData(v BrowserCdpPageReloadCommandData) error { + v.Method = "Page.reload" + b, err := json.Marshal(v) + if err != nil { + return err + } -// InternalError defines model for InternalError. -type InternalError = Error + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// NotFoundError defines model for NotFoundError. -type NotFoundError = Error +// AsBrowserCdpPagePrintToPdfCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPagePrintToPdfCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPagePrintToPdfCommandData() (BrowserCdpPagePrintToPdfCommandData, error) { + var body BrowserCdpPagePrintToPdfCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// PatchChromiumFlagsJSONBody defines parameters for PatchChromiumFlags. -type PatchChromiumFlagsJSONBody struct { - // Flags Chromium flags to merge (e.g., ["--kiosk", "--disable-gpu"]) - Flags []string `json:"flags"` +// FromBrowserCdpPagePrintToPdfCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPagePrintToPdfCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPagePrintToPdfCommandData(v BrowserCdpPagePrintToPdfCommandData) error { + v.Method = "Page.printToPDF" + b, err := json.Marshal(v) + t.union = b + return err } -// PatchChromiumPoliciesJSONBody defines parameters for PatchChromiumPolicies. -type PatchChromiumPoliciesJSONBody map[string]interface{} +// MergeBrowserCdpPagePrintToPdfCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPagePrintToPdfCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPagePrintToPdfCommandData(v BrowserCdpPagePrintToPdfCommandData) error { + v.Method = "Page.printToPDF" + b, err := json.Marshal(v) + if err != nil { + return err + } -// UploadExtensionsAndRestartMultipartBody defines parameters for UploadExtensionsAndRestart. -type UploadExtensionsAndRestartMultipartBody struct { - // Extensions List of extensions to upload and activate - Extensions []struct { - // Name Folder name to place the extension under /home/kernel/extensions/ - Name string `json:"name"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // ZipFile Zip archive containing an unpacked Chromium extension (must include manifest.json) - ZipFile openapi_types.File `json:"zip_file"` - } `json:"extensions"` +// AsBrowserCdpPageStartScreencastCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageStartScreencastCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageStartScreencastCommandData() (BrowserCdpPageStartScreencastCommandData, error) { + var body BrowserCdpPageStartScreencastCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// ChromiumConfigureMultipartBody defines parameters for ChromiumConfigure. -type ChromiumConfigureMultipartBody struct { - // ChromePolicies UTF-8 JSON policy override map — same semantics as PATCH /chromium/policies. - ChromePolicies *string `json:"chrome_policies,omitempty"` +// FromBrowserCdpPageStartScreencastCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageStartScreencastCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageStartScreencastCommandData(v BrowserCdpPageStartScreencastCommandData) error { + v.Method = "Page.startScreencast" + b, err := json.Marshal(v) + t.union = b + return err +} - // ChromiumFlags UTF-8 JSON object `{"flags":["--kiosk"]}` — same semantics as PATCH /chromium/flags. - ChromiumFlags *string `json:"chromium_flags,omitempty"` +// MergeBrowserCdpPageStartScreencastCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageStartScreencastCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageStartScreencastCommandData(v BrowserCdpPageStartScreencastCommandData) error { + v.Method = "Page.startScreencast" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Display UTF-8 JSON object matching `#/components/schemas/PatchDisplayRequest` (width/height/etc.). When combined with restart-triggering fields, the resize is applied while Chromium is stopped and Chromium is started once at the end. - Display *string `json:"display,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // Extensions Extension zips paired with consecutive extensions.name fields (same as upload-extensions-and-restart). - Extensions *[]struct { - Name string `json:"name"` - ZipFile openapi_types.File `json:"zip_file"` - } `json:"extensions,omitempty"` +// AsBrowserCdpPageStopScreencastCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageStopScreencastCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageStopScreencastCommandData() (BrowserCdpPageStopScreencastCommandData, error) { + var body BrowserCdpPageStopScreencastCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // ProfileArchive tar.zst archive containing the desired `/home/kernel/user-data` profile contents. Prefer archives whose root entries are the profile files/directories themselves (for example `Default/Preferences`). Use `strip_components` only when uploading an archive that includes leading wrapper directories. - ProfileArchive *openapi_types.File `json:"profile_archive,omitempty"` +// FromBrowserCdpPageStopScreencastCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageStopScreencastCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageStopScreencastCommandData(v BrowserCdpPageStopScreencastCommandData) error { + v.Method = "Page.stopScreencast" + b, err := json.Marshal(v) + t.union = b + return err +} - // StartUrl URL text to navigate after configure. Bare hosts are normalized to https://, length is capped at 2048 bytes, and Chrome decides which schemes are navigable. - StartUrl *string `json:"start_url,omitempty"` +// MergeBrowserCdpPageStopScreencastCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageStopScreencastCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageStopScreencastCommandData(v BrowserCdpPageStopScreencastCommandData) error { + v.Method = "Page.stopScreencast" + b, err := json.Marshal(v) + if err != nil { + return err + } - // StripComponents Optional number of leading path components to strip from profile_archive entries (non-negative integer as text). - StripComponents *string `json:"strip_components,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// DownloadDirZipParams defines parameters for DownloadDirZip. -type DownloadDirZipParams struct { - // Path Absolute directory path to archive and download. - Path string `form:"path" json:"path"` +// AsBrowserCdpPageStopLoadingCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageStopLoadingCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageStopLoadingCommandData() (BrowserCdpPageStopLoadingCommandData, error) { + var body BrowserCdpPageStopLoadingCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// DownloadDirZstdParams defines parameters for DownloadDirZstd. -type DownloadDirZstdParams struct { - // Path Absolute directory path to archive and download. - Path string `form:"path" json:"path"` - - // CompressionLevel Compression level. Higher levels produce smaller archives but take longer. - // - fastest: ~zstd level 1, maximum speed (~300-500 MB/s) - // - default: ~zstd level 3, balanced speed/ratio (~150 MB/s) - // - better: ~zstd level 7, better ratio (~50-80 MB/s) - // - best: ~zstd level 11, best ratio (~20-40 MB/s) - CompressionLevel *DownloadDirZstdParamsCompressionLevel `form:"compression_level,omitempty" json:"compression_level,omitempty"` +// FromBrowserCdpPageStopLoadingCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageStopLoadingCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageStopLoadingCommandData(v BrowserCdpPageStopLoadingCommandData) error { + v.Method = "Page.stopLoading" + b, err := json.Marshal(v) + t.union = b + return err } -// DownloadDirZstdParamsCompressionLevel defines parameters for DownloadDirZstd. -type DownloadDirZstdParamsCompressionLevel string +// MergeBrowserCdpPageStopLoadingCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageStopLoadingCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageStopLoadingCommandData(v BrowserCdpPageStopLoadingCommandData) error { + v.Method = "Page.stopLoading" + b, err := json.Marshal(v) + if err != nil { + return err + } -// FileInfoParams defines parameters for FileInfo. -type FileInfoParams struct { - // Path Absolute path of the file or directory. - Path string `form:"path" json:"path"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// ListFilesParams defines parameters for ListFiles. -type ListFilesParams struct { - // Path Absolute directory path. - Path string `form:"path" json:"path"` +// AsBrowserCdpPageCloseCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageCloseCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageCloseCommandData() (BrowserCdpPageCloseCommandData, error) { + var body BrowserCdpPageCloseCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// ReadFileParams defines parameters for ReadFile. -type ReadFileParams struct { - // Path Absolute file path to read. - Path string `form:"path" json:"path"` +// FromBrowserCdpPageCloseCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageCloseCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageCloseCommandData(v BrowserCdpPageCloseCommandData) error { + v.Method = "Page.close" + b, err := json.Marshal(v) + t.union = b + return err } -// UploadFilesMultipartBody defines parameters for UploadFiles. -type UploadFilesMultipartBody struct { - Files []struct { - // DestPath Absolute destination path to write the file. - DestPath string `json:"dest_path"` - File openapi_types.File `json:"file"` - } `json:"files"` +// MergeBrowserCdpPageCloseCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageCloseCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageCloseCommandData(v BrowserCdpPageCloseCommandData) error { + v.Method = "Page.close" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// UploadZipMultipartBody defines parameters for UploadZip. -type UploadZipMultipartBody struct { - // DestPath Absolute destination directory to extract the archive to. - DestPath string `json:"dest_path"` - ZipFile openapi_types.File `json:"zip_file"` +// AsBrowserCdpPageSetWebLifecycleStateCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpPageSetWebLifecycleStateCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpPageSetWebLifecycleStateCommandData() (BrowserCdpPageSetWebLifecycleStateCommandData, error) { + var body BrowserCdpPageSetWebLifecycleStateCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// UploadZstdMultipartBody defines parameters for UploadZstd. -type UploadZstdMultipartBody struct { - // Archive The tar.zst archive file. - Archive openapi_types.File `json:"archive"` +// FromBrowserCdpPageSetWebLifecycleStateCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpPageSetWebLifecycleStateCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpPageSetWebLifecycleStateCommandData(v BrowserCdpPageSetWebLifecycleStateCommandData) error { + v.Method = "Page.setWebLifecycleState" + b, err := json.Marshal(v) + t.union = b + return err +} - // DestPath Absolute destination directory to extract the archive to. - DestPath string `json:"dest_path"` +// MergeBrowserCdpPageSetWebLifecycleStateCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpPageSetWebLifecycleStateCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpPageSetWebLifecycleStateCommandData(v BrowserCdpPageSetWebLifecycleStateCommandData) error { + v.Method = "Page.setWebLifecycleState" + b, err := json.Marshal(v) + if err != nil { + return err + } - // StripComponents Number of leading path components to strip during extraction (like tar --strip-components). - StripComponents *int `json:"strip_components,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// WriteFileParams defines parameters for WriteFile. -type WriteFileParams struct { - // Path Destination absolute file path. - Path string `form:"path" json:"path"` +// AsBrowserCdpTargetActivateTargetCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpTargetActivateTargetCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpTargetActivateTargetCommandData() (BrowserCdpTargetActivateTargetCommandData, error) { + var body BrowserCdpTargetActivateTargetCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // Mode Optional file mode (octal string, e.g. 644). Defaults to 644. - Mode *string `form:"mode,omitempty" json:"mode,omitempty"` +// FromBrowserCdpTargetActivateTargetCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpTargetActivateTargetCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpTargetActivateTargetCommandData(v BrowserCdpTargetActivateTargetCommandData) error { + v.Method = "Target.activateTarget" + b, err := json.Marshal(v) + t.union = b + return err } -// LogsStreamParams defines parameters for LogsStream. -type LogsStreamParams struct { - Source LogsStreamParamsSource `form:"source" json:"source"` - Follow *bool `form:"follow,omitempty" json:"follow,omitempty"` +// MergeBrowserCdpTargetActivateTargetCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpTargetActivateTargetCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpTargetActivateTargetCommandData(v BrowserCdpTargetActivateTargetCommandData) error { + v.Method = "Target.activateTarget" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Path only required if source is path - Path *string `form:"path,omitempty" json:"path,omitempty"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} - // SupervisorProcess only required if source is supervisor - SupervisorProcess *string `form:"supervisor_process,omitempty" json:"supervisor_process,omitempty"` +// AsBrowserCdpTargetCloseTargetCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpTargetCloseTargetCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpTargetCloseTargetCommandData() (BrowserCdpTargetCloseTargetCommandData, error) { + var body BrowserCdpTargetCloseTargetCommandData + err := json.Unmarshal(t.union, &body) + return body, err } -// LogsStreamParamsSource defines parameters for LogsStream. -type LogsStreamParamsSource string +// FromBrowserCdpTargetCloseTargetCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpTargetCloseTargetCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpTargetCloseTargetCommandData(v BrowserCdpTargetCloseTargetCommandData) error { + v.Method = "Target.closeTarget" + b, err := json.Marshal(v) + t.union = b + return err +} -// DownloadRecordingParams defines parameters for DownloadRecording. -type DownloadRecordingParams struct { - // Id Identifier of the recording session to download, as passed to /recording/start. When omitted, the default recording session is downloaded. - Id *string `form:"id,omitempty" json:"id,omitempty"` +// MergeBrowserCdpTargetCloseTargetCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpTargetCloseTargetCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpTargetCloseTargetCommandData(v BrowserCdpTargetCloseTargetCommandData) error { + v.Method = "Target.closeTarget" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// StreamTelemetryEventsParams defines parameters for StreamTelemetryEvents. -type StreamTelemetryEventsParams struct { - // Replay Pass `all` to start from the oldest retained event. Ring buffer caps at 1024; older events are evicted and surface as a first `id` greater than 1. - Replay *StreamTelemetryEventsParamsReplay `form:"replay,omitempty" json:"replay,omitempty"` +// AsBrowserCdpTargetCreateTargetCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpTargetCreateTargetCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpTargetCreateTargetCommandData() (BrowserCdpTargetCreateTargetCommandData, error) { + var body BrowserCdpTargetCreateTargetCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} - // LastEventID Resume after this sequence number. Omit or send 0 to start from the current position. Sequence numbers are process-monotonic, so any previous value resumes correctly from that point. Takes precedence over `replay` when both are present, so SSE auto-reconnect resumes cleanly instead of re-replaying history. - LastEventID *string `json:"Last-Event-ID,omitempty"` +// FromBrowserCdpTargetCreateTargetCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpTargetCreateTargetCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpTargetCreateTargetCommandData(v BrowserCdpTargetCreateTargetCommandData) error { + v.Method = "Target.createTarget" + b, err := json.Marshal(v) + t.union = b + return err } -// StreamTelemetryEventsParamsReplay defines parameters for StreamTelemetryEvents. -type StreamTelemetryEventsParamsReplay string +// MergeBrowserCdpTargetCreateTargetCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpTargetCreateTargetCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpTargetCreateTargetCommandData(v BrowserCdpTargetCreateTargetCommandData) error { + v.Method = "Target.createTarget" + b, err := json.Marshal(v) + if err != nil { + return err + } -// PatchChromiumFlagsJSONRequestBody defines body for PatchChromiumFlags for application/json ContentType. -type PatchChromiumFlagsJSONRequestBody PatchChromiumFlagsJSONBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PatchChromiumPoliciesJSONRequestBody defines body for PatchChromiumPolicies for application/json ContentType. -type PatchChromiumPoliciesJSONRequestBody PatchChromiumPoliciesJSONBody +// AsBrowserCdpTargetCreateBrowserContextCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpTargetCreateBrowserContextCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpTargetCreateBrowserContextCommandData() (BrowserCdpTargetCreateBrowserContextCommandData, error) { + var body BrowserCdpTargetCreateBrowserContextCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// UploadExtensionsAndRestartMultipartRequestBody defines body for UploadExtensionsAndRestart for multipart/form-data ContentType. -type UploadExtensionsAndRestartMultipartRequestBody UploadExtensionsAndRestartMultipartBody +// FromBrowserCdpTargetCreateBrowserContextCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpTargetCreateBrowserContextCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpTargetCreateBrowserContextCommandData(v BrowserCdpTargetCreateBrowserContextCommandData) error { + v.Method = "Target.createBrowserContext" + b, err := json.Marshal(v) + t.union = b + return err +} -// BatchComputerActionJSONRequestBody defines body for BatchComputerAction for application/json ContentType. -type BatchComputerActionJSONRequestBody = BatchComputerActionRequest +// MergeBrowserCdpTargetCreateBrowserContextCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpTargetCreateBrowserContextCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpTargetCreateBrowserContextCommandData(v BrowserCdpTargetCreateBrowserContextCommandData) error { + v.Method = "Target.createBrowserContext" + b, err := json.Marshal(v) + if err != nil { + return err + } -// ClickMouseJSONRequestBody defines body for ClickMouse for application/json ContentType. -type ClickMouseJSONRequestBody = ClickMouseRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// WriteClipboardJSONRequestBody defines body for WriteClipboard for application/json ContentType. -type WriteClipboardJSONRequestBody = WriteClipboardRequest +// AsBrowserCdpTargetDisposeBrowserContextCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpTargetDisposeBrowserContextCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpTargetDisposeBrowserContextCommandData() (BrowserCdpTargetDisposeBrowserContextCommandData, error) { + var body BrowserCdpTargetDisposeBrowserContextCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// SetCursorJSONRequestBody defines body for SetCursor for application/json ContentType. -type SetCursorJSONRequestBody = SetCursorRequest +// FromBrowserCdpTargetDisposeBrowserContextCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpTargetDisposeBrowserContextCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpTargetDisposeBrowserContextCommandData(v BrowserCdpTargetDisposeBrowserContextCommandData) error { + v.Method = "Target.disposeBrowserContext" + b, err := json.Marshal(v) + t.union = b + return err +} -// DragMouseJSONRequestBody defines body for DragMouse for application/json ContentType. -type DragMouseJSONRequestBody = DragMouseRequest +// MergeBrowserCdpTargetDisposeBrowserContextCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpTargetDisposeBrowserContextCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpTargetDisposeBrowserContextCommandData(v BrowserCdpTargetDisposeBrowserContextCommandData) error { + v.Method = "Target.disposeBrowserContext" + b, err := json.Marshal(v) + if err != nil { + return err + } -// MoveMouseJSONRequestBody defines body for MoveMouse for application/json ContentType. -type MoveMouseJSONRequestBody = MoveMouseRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PressKeyJSONRequestBody defines body for PressKey for application/json ContentType. -type PressKeyJSONRequestBody = PressKeyRequest +// AsBrowserCdpTargetOpenDevToolsCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpTargetOpenDevToolsCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpTargetOpenDevToolsCommandData() (BrowserCdpTargetOpenDevToolsCommandData, error) { + var body BrowserCdpTargetOpenDevToolsCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// TakeScreenshotJSONRequestBody defines body for TakeScreenshot for application/json ContentType. -type TakeScreenshotJSONRequestBody = ScreenshotRequest +// FromBrowserCdpTargetOpenDevToolsCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpTargetOpenDevToolsCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpTargetOpenDevToolsCommandData(v BrowserCdpTargetOpenDevToolsCommandData) error { + v.Method = "Target.openDevTools" + b, err := json.Marshal(v) + t.union = b + return err +} -// ScrollJSONRequestBody defines body for Scroll for application/json ContentType. -type ScrollJSONRequestBody = ScrollRequest +// MergeBrowserCdpTargetOpenDevToolsCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpTargetOpenDevToolsCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpTargetOpenDevToolsCommandData(v BrowserCdpTargetOpenDevToolsCommandData) error { + v.Method = "Target.openDevTools" + b, err := json.Marshal(v) + if err != nil { + return err + } -// TypeTextJSONRequestBody defines body for TypeText for application/json ContentType. -type TypeTextJSONRequestBody = TypeTextRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// ChromiumConfigureMultipartRequestBody defines body for ChromiumConfigure for multipart/form-data ContentType. -type ChromiumConfigureMultipartRequestBody ChromiumConfigureMultipartBody +// AsBrowserCdpBrowserCancelDownloadCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpBrowserCancelDownloadCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpBrowserCancelDownloadCommandData() (BrowserCdpBrowserCancelDownloadCommandData, error) { + var body BrowserCdpBrowserCancelDownloadCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// PatchDisplayJSONRequestBody defines body for PatchDisplay for application/json ContentType. -type PatchDisplayJSONRequestBody = PatchDisplayRequest +// FromBrowserCdpBrowserCancelDownloadCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpBrowserCancelDownloadCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpBrowserCancelDownloadCommandData(v BrowserCdpBrowserCancelDownloadCommandData) error { + v.Method = "Browser.cancelDownload" + b, err := json.Marshal(v) + t.union = b + return err +} -// CreateDirectoryJSONRequestBody defines body for CreateDirectory for application/json ContentType. -type CreateDirectoryJSONRequestBody = CreateDirectoryRequest +// MergeBrowserCdpBrowserCancelDownloadCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpBrowserCancelDownloadCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpBrowserCancelDownloadCommandData(v BrowserCdpBrowserCancelDownloadCommandData) error { + v.Method = "Browser.cancelDownload" + b, err := json.Marshal(v) + if err != nil { + return err + } -// DeleteDirectoryJSONRequestBody defines body for DeleteDirectory for application/json ContentType. -type DeleteDirectoryJSONRequestBody = DeletePathRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// DeleteFileJSONRequestBody defines body for DeleteFile for application/json ContentType. -type DeleteFileJSONRequestBody = DeletePathRequest +// AsBrowserCdpBrowserCloseCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpBrowserCloseCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpBrowserCloseCommandData() (BrowserCdpBrowserCloseCommandData, error) { + var body BrowserCdpBrowserCloseCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// MovePathJSONRequestBody defines body for MovePath for application/json ContentType. -type MovePathJSONRequestBody = MovePathRequest +// FromBrowserCdpBrowserCloseCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpBrowserCloseCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpBrowserCloseCommandData(v BrowserCdpBrowserCloseCommandData) error { + v.Method = "Browser.close" + b, err := json.Marshal(v) + t.union = b + return err +} -// SetFilePermissionsJSONRequestBody defines body for SetFilePermissions for application/json ContentType. -type SetFilePermissionsJSONRequestBody = SetFilePermissionsRequest +// MergeBrowserCdpBrowserCloseCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpBrowserCloseCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpBrowserCloseCommandData(v BrowserCdpBrowserCloseCommandData) error { + v.Method = "Browser.close" + b, err := json.Marshal(v) + if err != nil { + return err + } -// UploadFilesMultipartRequestBody defines body for UploadFiles for multipart/form-data ContentType. -type UploadFilesMultipartRequestBody UploadFilesMultipartBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// UploadZipMultipartRequestBody defines body for UploadZip for multipart/form-data ContentType. -type UploadZipMultipartRequestBody UploadZipMultipartBody +// AsBrowserCdpBrowserSetWindowBoundsCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpBrowserSetWindowBoundsCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpBrowserSetWindowBoundsCommandData() (BrowserCdpBrowserSetWindowBoundsCommandData, error) { + var body BrowserCdpBrowserSetWindowBoundsCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// UploadZstdMultipartRequestBody defines body for UploadZstd for multipart/form-data ContentType. -type UploadZstdMultipartRequestBody UploadZstdMultipartBody +// FromBrowserCdpBrowserSetWindowBoundsCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpBrowserSetWindowBoundsCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpBrowserSetWindowBoundsCommandData(v BrowserCdpBrowserSetWindowBoundsCommandData) error { + v.Method = "Browser.setWindowBounds" + b, err := json.Marshal(v) + t.union = b + return err +} -// StartFsWatchJSONRequestBody defines body for StartFsWatch for application/json ContentType. -type StartFsWatchJSONRequestBody = StartFsWatchRequest +// MergeBrowserCdpBrowserSetWindowBoundsCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpBrowserSetWindowBoundsCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpBrowserSetWindowBoundsCommandData(v BrowserCdpBrowserSetWindowBoundsCommandData) error { + v.Method = "Browser.setWindowBounds" + b, err := json.Marshal(v) + if err != nil { + return err + } -// ExecutePlaywrightCodeJSONRequestBody defines body for ExecutePlaywrightCode for application/json ContentType. -type ExecutePlaywrightCodeJSONRequestBody = ExecutePlaywrightRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// ProcessExecJSONRequestBody defines body for ProcessExec for application/json ContentType. -type ProcessExecJSONRequestBody = ProcessExecRequest +// AsBrowserCdpBrowserSetContentsSizeCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpBrowserSetContentsSizeCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpBrowserSetContentsSizeCommandData() (BrowserCdpBrowserSetContentsSizeCommandData, error) { + var body BrowserCdpBrowserSetContentsSizeCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// ProcessSpawnJSONRequestBody defines body for ProcessSpawn for application/json ContentType. -type ProcessSpawnJSONRequestBody = ProcessSpawnRequest +// FromBrowserCdpBrowserSetContentsSizeCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpBrowserSetContentsSizeCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpBrowserSetContentsSizeCommandData(v BrowserCdpBrowserSetContentsSizeCommandData) error { + v.Method = "Browser.setContentsSize" + b, err := json.Marshal(v) + t.union = b + return err +} -// ProcessKillJSONRequestBody defines body for ProcessKill for application/json ContentType. -type ProcessKillJSONRequestBody = ProcessKillRequest +// MergeBrowserCdpBrowserSetContentsSizeCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpBrowserSetContentsSizeCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpBrowserSetContentsSizeCommandData(v BrowserCdpBrowserSetContentsSizeCommandData) error { + v.Method = "Browser.setContentsSize" + b, err := json.Marshal(v) + if err != nil { + return err + } -// ProcessResizeJSONRequestBody defines body for ProcessResize for application/json ContentType. -type ProcessResizeJSONRequestBody = ProcessResizeRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// ProcessStdinJSONRequestBody defines body for ProcessStdin for application/json ContentType. -type ProcessStdinJSONRequestBody = ProcessStdinRequest +// AsBrowserCdpAutofillTriggerCommandData returns the union data inside the BrowserCdpCommandEventData as a BrowserCdpAutofillTriggerCommandData +func (t BrowserCdpCommandEventData) AsBrowserCdpAutofillTriggerCommandData() (BrowserCdpAutofillTriggerCommandData, error) { + var body BrowserCdpAutofillTriggerCommandData + err := json.Unmarshal(t.union, &body) + return body, err +} -// DeleteRecordingJSONRequestBody defines body for DeleteRecording for application/json ContentType. -type DeleteRecordingJSONRequestBody = DeleteRecordingRequest +// FromBrowserCdpAutofillTriggerCommandData overwrites any union data inside the BrowserCdpCommandEventData as the provided BrowserCdpAutofillTriggerCommandData +func (t *BrowserCdpCommandEventData) FromBrowserCdpAutofillTriggerCommandData(v BrowserCdpAutofillTriggerCommandData) error { + v.Method = "Autofill.trigger" + b, err := json.Marshal(v) + t.union = b + return err +} -// MarkRecordingJSONRequestBody defines body for MarkRecording for application/json ContentType. -type MarkRecordingJSONRequestBody = MarkRecordingRequest +// MergeBrowserCdpAutofillTriggerCommandData performs a merge with any union data inside the BrowserCdpCommandEventData, using the provided BrowserCdpAutofillTriggerCommandData +func (t *BrowserCdpCommandEventData) MergeBrowserCdpAutofillTriggerCommandData(v BrowserCdpAutofillTriggerCommandData) error { + v.Method = "Autofill.trigger" + b, err := json.Marshal(v) + if err != nil { + return err + } -// StartRecordingJSONRequestBody defines body for StartRecording for application/json ContentType. -type StartRecordingJSONRequestBody = StartRecordingRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// StopRecordingJSONRequestBody defines body for StopRecording for application/json ContentType. -type StopRecordingJSONRequestBody = StopRecordingRequest +func (t BrowserCdpCommandEventData) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"method"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} -// PatchTelemetryJSONRequestBody defines body for PatchTelemetry for application/json ContentType. -type PatchTelemetryJSONRequestBody = BrowserTelemetryConfig +func (t BrowserCdpCommandEventData) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "Autofill.trigger": + return t.AsBrowserCdpAutofillTriggerCommandData() + case "Browser.cancelDownload": + return t.AsBrowserCdpBrowserCancelDownloadCommandData() + case "Browser.close": + return t.AsBrowserCdpBrowserCloseCommandData() + case "Browser.setContentsSize": + return t.AsBrowserCdpBrowserSetContentsSizeCommandData() + case "Browser.setWindowBounds": + return t.AsBrowserCdpBrowserSetWindowBoundsCommandData() + case "DOM.focus": + return t.AsBrowserCdpDomFocusCommandData() + case "DOM.scrollIntoViewIfNeeded": + return t.AsBrowserCdpDomScrollIntoViewIfNeededCommandData() + case "DOM.setFileInputFiles": + return t.AsBrowserCdpDomSetFileInputFilesCommandData() + case "Input.cancelDragging": + return t.AsBrowserCdpInputCancelDraggingCommandData() + case "Input.dispatchDragEvent": + return t.AsBrowserCdpInputDispatchDragEventCommandData() + case "Input.dispatchKeyEvent": + return t.AsBrowserCdpInputDispatchKeyEventCommandData() + case "Input.dispatchMouseEvent": + return t.AsBrowserCdpInputDispatchMouseEventCommandData() + case "Input.dispatchTouchEvent": + return t.AsBrowserCdpInputDispatchTouchEventCommandData() + case "Input.emulateTouchFromMouseEvent": + return t.AsBrowserCdpInputEmulateTouchFromMouseEventCommandData() + case "Input.imeSetComposition": + return t.AsBrowserCdpInputImeSetCompositionCommandData() + case "Input.insertText": + return t.AsBrowserCdpInputInsertTextCommandData() + case "Input.synthesizePinchGesture": + return t.AsBrowserCdpInputSynthesizePinchGestureCommandData() + case "Input.synthesizeScrollGesture": + return t.AsBrowserCdpInputSynthesizeScrollGestureCommandData() + case "Input.synthesizeTapGesture": + return t.AsBrowserCdpInputSynthesizeTapGestureCommandData() + case "Page.bringToFront": + return t.AsBrowserCdpPageBringToFrontCommandData() + case "Page.captureScreenshot": + return t.AsBrowserCdpPageCaptureScreenshotCommandData() + case "Page.captureSnapshot": + return t.AsBrowserCdpPageCaptureSnapshotCommandData() + case "Page.close": + return t.AsBrowserCdpPageCloseCommandData() + case "Page.handleJavaScriptDialog": + return t.AsBrowserCdpPageHandleJavaScriptDialogCommandData() + case "Page.navigate": + return t.AsBrowserCdpPageNavigateCommandData() + case "Page.navigateToHistoryEntry": + return t.AsBrowserCdpPageNavigateToHistoryEntryCommandData() + case "Page.printToPDF": + return t.AsBrowserCdpPagePrintToPdfCommandData() + case "Page.reload": + return t.AsBrowserCdpPageReloadCommandData() + case "Page.setWebLifecycleState": + return t.AsBrowserCdpPageSetWebLifecycleStateCommandData() + case "Page.startScreencast": + return t.AsBrowserCdpPageStartScreencastCommandData() + case "Page.stopLoading": + return t.AsBrowserCdpPageStopLoadingCommandData() + case "Page.stopScreencast": + return t.AsBrowserCdpPageStopScreencastCommandData() + case "Target.activateTarget": + return t.AsBrowserCdpTargetActivateTargetCommandData() + case "Target.closeTarget": + return t.AsBrowserCdpTargetCloseTargetCommandData() + case "Target.createBrowserContext": + return t.AsBrowserCdpTargetCreateBrowserContextCommandData() + case "Target.createTarget": + return t.AsBrowserCdpTargetCreateTargetCommandData() + case "Target.disposeBrowserContext": + return t.AsBrowserCdpTargetDisposeBrowserContextCommandData() + case "Target.openDevTools": + return t.AsBrowserCdpTargetOpenDevToolsCommandData() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} -// PutTelemetryJSONRequestBody defines body for PutTelemetry for application/json ContentType. -type PutTelemetryJSONRequestBody = BrowserTelemetryConfig +func (t BrowserCdpCommandEventData) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} -// PublishTelemetryEventJSONRequestBody defines body for PublishTelemetryEvent for application/json ContentType. -type PublishTelemetryEventJSONRequestBody = PublishEventRequest +func (t *BrowserCdpCommandEventData) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} // AsBrowserConsoleLogEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserConsoleLogEvent func (t KnownBrowserTelemetryEvent) AsBrowserConsoleLogEvent() (BrowserConsoleLogEvent, error) { @@ -5006,6 +8625,34 @@ func (t *KnownBrowserTelemetryEvent) MergeBrowserPlatformApiCallEvent(v BrowserP return err } +// AsBrowserCdpCommandEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserCdpCommandEvent +func (t KnownBrowserTelemetryEvent) AsBrowserCdpCommandEvent() (BrowserCdpCommandEvent, error) { + var body BrowserCdpCommandEvent + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBrowserCdpCommandEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserCdpCommandEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserCdpCommandEvent(v BrowserCdpCommandEvent) error { + v.Type = "cdp_command" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBrowserCdpCommandEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserCdpCommandEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserCdpCommandEvent(v BrowserCdpCommandEvent) error { + v.Type = "cdp_command" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + // AsBrowserCdpConnectEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserCdpConnectEvent func (t KnownBrowserTelemetryEvent) AsBrowserCdpConnectEvent() (BrowserCdpConnectEvent, error) { var body BrowserCdpConnectEvent @@ -5220,6 +8867,8 @@ func (t KnownBrowserTelemetryEvent) ValueByDiscriminator() (interface{}, error) return t.AsBrowserApiCallEvent() case "captcha_solve_result": return t.AsBrowserCaptchaSolveResultEvent() + case "cdp_command": + return t.AsBrowserCdpCommandEvent() case "cdp_connect": return t.AsBrowserCdpConnectEvent() case "cdp_disconnect": @@ -19526,400 +23175,544 @@ func (sh *strictHandler) StreamTelemetryEvents(w http.ResponseWriter, r *http.Re // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "ry09lhxgu4D/+PH/DwAA//84RyEXjewBAA==", + "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", + "Tq09Bg+wfIH/+vV/AgAA///8WZ2COTsDAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/server/lib/telemetry/telemetry.go b/server/lib/telemetry/telemetry.go index de41f880..c155ed71 100644 --- a/server/lib/telemetry/telemetry.go +++ b/server/lib/telemetry/telemetry.go @@ -1,7 +1,9 @@ package telemetry import ( + "sort" "sync" + "sync/atomic" "time" "github.com/kernel/kernel-images/server/lib/events" @@ -18,6 +20,10 @@ type TelemetryConfig struct { // ExportOTLP forwards captured events to the configured OTLP endpoint. // Off by default and independent of what is captured. ExportOTLP bool + // ExcludedCdpMethods leaves the named browser-control methods out of the + // cdp_command stream. Empty reports every supported method. Telemetry only: + // an excluded command still reaches the browser. + ExcludedCdpMethods []oapi.BrowserCdpCommandMethod } // TelemetrySession manages a telemetry session against a shared EventStream. @@ -36,6 +42,15 @@ type TelemetrySession struct { categories map[oapi.TelemetryEventCategory]struct{} exportOTLP bool appliedAt time.Time + excludedCdp map[string]struct{} + // active mirrors "a session is running with these categories" for callers + // on a hot path, who must decide whether to do any work at all before they + // reach Publish and its mutex. nil means no session. Written under mu; + // the pointed-to set is never mutated after it is stored. + active atomic.Pointer[map[oapi.TelemetryEventCategory]struct{}] + // excludedCdpActive mirrors excludedCdp for the same reason. Never nil once + // stored, and the pointed-to set is never mutated after it is stored. + excludedCdpActive atomic.Pointer[map[string]struct{}] } func NewTelemetrySession(es *events.EventStream) *TelemetrySession { @@ -45,6 +60,19 @@ func NewTelemetrySession(es *events.EventStream) *TelemetrySession { return &TelemetrySession{es: es, categories: categorySet(nil)} } +// setActiveLocked republishes the lock-free view of the session state. +// Requires s.mu to be held. +func (s *TelemetrySession) setActiveLocked() { + if s.id == "" { + s.active.Store(nil) + return + } + cats := s.categories + s.active.Store(&cats) + excluded := s.excludedCdp + s.excludedCdpActive.Store(&excluded) +} + // categorySet builds the active filter set from the configured categories. An // empty config falls back to the default set. Monitor is included whenever any // CDP category is present, since collector-health rides along with CDP data. @@ -62,6 +90,19 @@ func categorySet(cats []oapi.TelemetryEventCategory) map[oapi.TelemetryEventCate return set } +// excludedSet builds the cdp_command exclusion set. nil when nothing is +// excluded, which is the common case and the cheapest lookup. +func excludedSet(methods []oapi.BrowserCdpCommandMethod) map[string]struct{} { + if len(methods) == 0 { + return nil + } + set := make(map[string]struct{}, len(methods)) + for _, m := range methods { + set[string(m)] = struct{}{} + } + return set +} + // Start begins a new telemetry session with the given ID and config. Sequence // numbers are process-monotonic and do not reset between sessions; a // Last-Event-ID from any previous session is valid for resuming the SSE stream. @@ -73,6 +114,8 @@ func (s *TelemetrySession) Start(telemetrySessionID string, cfg TelemetryConfig) s.appliedAt = time.Now() s.categories = categorySet(cfg.Categories) s.exportOTLP = cfg.ExportOTLP + s.excludedCdp = excludedSet(cfg.ExcludedCdpMethods) + s.setActiveLocked() } // publishLocked stamps telemetry_session_id into ev.Source.Metadata and forwards to the bus. @@ -117,6 +160,16 @@ func (s *TelemetrySession) ID() string { return s.id } +// RecordDropped notes that a consumer found a gap of n envelopes in the stream. +func (s *TelemetrySession) RecordDropped(n uint64) { + s.es.RecordDropped(n) +} + +// DroppedEvents returns the cumulative gap count across consumers. +func (s *TelemetrySession) DroppedEvents() uint64 { + return s.es.DroppedEvents() +} + // Seq returns the sequence number of the last published event. func (s *TelemetrySession) Seq() uint64 { return s.es.Seq() @@ -138,7 +191,12 @@ func (s *TelemetrySession) Config() TelemetryConfig { for c := range s.categories { cats = append(cats, c) } - return TelemetryConfig{Categories: cats, ExportOTLP: s.exportOTLP} + excluded := make([]oapi.BrowserCdpCommandMethod, 0, len(s.excludedCdp)) + for m := range s.excludedCdp { + excluded = append(excluded, oapi.BrowserCdpCommandMethod(m)) + } + sort.Slice(excluded, func(i, j int) bool { return excluded[i] < excluded[j] }) + return TelemetryConfig{Categories: cats, ExportOTLP: s.exportOTLP, ExcludedCdpMethods: excluded} } // AppliedAt returns when the current configuration was applied, or the zero @@ -155,25 +213,36 @@ func (s *TelemetrySession) UpdateConfig(cfg TelemetryConfig) { defer s.mu.Unlock() s.categories = categorySet(cfg.Categories) s.exportOTLP = cfg.ExportOTLP + s.excludedCdp = excludedSet(cfg.ExcludedCdpMethods) + s.setActiveLocked() } // CategoryEnabled reports whether events in category c are currently captured. -// It returns false when no session is active. +// It returns false when no session is active. Lock-free, so a caller on the +// CDP forwarding path can check it per frame; Publish re-checks under mu. func (s *TelemetrySession) CategoryEnabled(c oapi.TelemetryEventCategory) bool { - s.mu.Lock() - defer s.mu.Unlock() - if s.id == "" { + cats := s.active.Load() + if cats == nil { return false } - _, ok := s.categories[c] + _, ok := (*cats)[c] return ok } +// ExcludedCdpMethods returns the methods left out of the cdp_command stream. +// Lock-free for the same reason CategoryEnabled is; the returned set is +// read-only and may be nil. +func (s *TelemetrySession) ExcludedCdpMethods() map[string]struct{} { + excluded := s.excludedCdpActive.Load() + if excluded == nil { + return nil + } + return *excluded +} + // Active reports whether a telemetry session is currently running. func (s *TelemetrySession) Active() bool { - s.mu.Lock() - defer s.mu.Unlock() - return s.id != "" + return s.active.Load() != nil } // Stop ends the current telemetry session. The ring buffer is left intact so @@ -186,4 +255,5 @@ func (s *TelemetrySession) Stop() { // The session is over, so export is off; keep Config() authoritative for the // desired export state after a clear. s.exportOTLP = false + s.setActiveLocked() } diff --git a/server/lib/wsproxy/wsproxy.go b/server/lib/wsproxy/wsproxy.go index 8a7e5940..65ad81f4 100644 --- a/server/lib/wsproxy/wsproxy.go +++ b/server/lib/wsproxy/wsproxy.go @@ -5,6 +5,7 @@ import ( "log/slog" "net/http" "sync" + "time" "github.com/coder/websocket" "github.com/kernel/kernel-images/server/lib/wsdrain" @@ -22,6 +23,13 @@ type Conn interface { // It returns the (possibly modified) message bytes to forward. type MessageTransform func(direction string, mt websocket.MessageType, msg []byte) []byte +// Observer is called after a message has been successfully written to the +// other side, with ts set to the time that write completed (Unix +// microseconds). It runs on the pump goroutine, so anything it does delays the +// next message: hand work to a worker rather than doing it here. msg is not +// retained by the pump after the call, so an observer may take ownership. +type Observer func(direction string, mt websocket.MessageType, msg []byte, ts int64) + // ProxyOptions configures the proxy accept/dial behavior and optional message // transformation. Zero values are valid and use sensible defaults. type ProxyOptions struct { @@ -29,6 +37,7 @@ type ProxyOptions struct { DialOptions *websocket.DialOptions Logger *slog.Logger Transform MessageTransform + Observe Observer // Registry, when set, tracks the accepted client connection so it is // closed with a Going Away frame on server shutdown. Registry *wsdrain.Registry @@ -54,8 +63,10 @@ const ( // Pump bidirectionally copies messages between client and upstream until // either side errors or ctx is cancelled, then calls onClose with the cause. // If transform is non-nil it is called for every message; the returned bytes -// are forwarded to the other side. -func Pump(ctx context.Context, client, upstream Conn, onClose func(cause PumpExitCause), logger *slog.Logger, transform MessageTransform) { +// are forwarded to the other side. If observe is non-nil it is called for +// every message that was forwarded successfully, so a message whose write +// failed is never observed. +func Pump(ctx context.Context, client, upstream Conn, onClose func(cause PumpExitCause), logger *slog.Logger, transform MessageTransform, observe Observer) { causeChan := make(chan PumpExitCause, 2) go func() { @@ -74,6 +85,9 @@ func Pump(ctx context.Context, client, upstream Conn, onClose func(cause PumpExi causeChan <- PumpExitUpstream return } + if observe != nil { + observe("->", mt, msg, time.Now().UnixMicro()) + } } }() @@ -93,6 +107,9 @@ func Pump(ctx context.Context, client, upstream Conn, onClose func(cause PumpExi causeChan <- PumpExitClient return } + if observe != nil { + observe("<-", mt, msg, time.Now().UnixMicro()) + } } }() @@ -146,5 +163,5 @@ func Proxy(w http.ResponseWriter, r *http.Request, upstreamURL string, opts Prox }) } - Pump(r.Context(), clientConn, upstreamConn, cleanup, logger, opts.Transform) + Pump(r.Context(), clientConn, upstreamConn, cleanup, logger, opts.Transform, opts.Observe) } diff --git a/server/lib/wsproxy/wsproxy_test.go b/server/lib/wsproxy/wsproxy_test.go new file mode 100644 index 00000000..f6ed2edf --- /dev/null +++ b/server/lib/wsproxy/wsproxy_test.go @@ -0,0 +1,139 @@ +package wsproxy + +import ( + "context" + "errors" + "io" + "log/slog" + "sync" + "testing" + "time" + + "github.com/coder/websocket" +) + +// fakeConn plays back a fixed script of reads and records what was written. +type fakeConn struct { + mu sync.Mutex + reads [][]byte + readErr error + written [][]byte + writeErr error + // idle, when set, parks Read once the script is exhausted instead of + // returning EOF, so one side does not end the pump before the other side + // has worked through its script. + idle chan struct{} +} + +func (c *fakeConn) Read(ctx context.Context) (websocket.MessageType, []byte, error) { + c.mu.Lock() + defer c.mu.Unlock() + if len(c.reads) == 0 { + idle, err := c.idle, c.readErr + c.mu.Unlock() + defer c.mu.Lock() + if idle != nil { + select { + case <-idle: + case <-ctx.Done(): + } + } + if err != nil { + return 0, nil, err + } + return 0, nil, io.EOF + } + msg := c.reads[0] + c.reads = c.reads[1:] + return websocket.MessageText, msg, nil +} + +func (c *fakeConn) Write(ctx context.Context, typ websocket.MessageType, p []byte) error { + c.mu.Lock() + defer c.mu.Unlock() + if c.writeErr != nil { + return c.writeErr + } + c.written = append(c.written, append([]byte(nil), p...)) + return nil +} + +func (c *fakeConn) Close(websocket.StatusCode, string) error { return nil } + +func (c *fakeConn) writes() [][]byte { + c.mu.Lock() + defer c.mu.Unlock() + out := make([][]byte, len(c.written)) + copy(out, c.written) + return out +} + +func silent() *slog.Logger { + return slog.New(slog.NewTextHandler(io.Discard, nil)) +} + +// An observer must never see a message the other side did not accept, or a +// reader would conclude the browser was told something it never was. +func TestPumpDoesNotObserveMessagesWhoseWriteFailed(t *testing.T) { + idle := make(chan struct{}) + defer close(idle) + client := &fakeConn{reads: [][]byte{[]byte("a"), []byte("b")}, idle: idle} + upstream := &fakeConn{writeErr: errors.New("upstream gone"), idle: idle} + + var observed [][]byte + var mu sync.Mutex + observe := func(direction string, mt websocket.MessageType, msg []byte, ts int64) { + mu.Lock() + defer mu.Unlock() + observed = append(observed, msg) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + Pump(ctx, client, upstream, func(PumpExitCause) {}, silent(), nil, observe) + + mu.Lock() + defer mu.Unlock() + if len(observed) != 0 { + t.Fatalf("observed %d messages whose write failed, want 0", len(observed)) + } +} + +// Observation runs after the forward, so the bytes an observer sees are the +// bytes the other side got, in the order it got them. +func TestPumpObservesForwardedMessagesInOrder(t *testing.T) { + idle := make(chan struct{}) + defer close(idle) + frames := [][]byte{[]byte("one"), []byte("two"), []byte("three")} + client := &fakeConn{reads: frames} + upstream := &fakeConn{idle: idle} + + var observed []string + var mu sync.Mutex + observe := func(direction string, mt websocket.MessageType, msg []byte, ts int64) { + if direction != "->" { + return + } + mu.Lock() + defer mu.Unlock() + observed = append(observed, string(msg)) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + Pump(ctx, client, upstream, func(PumpExitCause) {}, silent(), nil, observe) + + mu.Lock() + defer mu.Unlock() + if got := len(observed); got != len(frames) { + t.Fatalf("observed %d messages, want %d", got, len(frames)) + } + for i, want := range []string{"one", "two", "three"} { + if observed[i] != want { + t.Fatalf("observed[%d] = %q, want %q", i, observed[i], want) + } + } + if got := len(upstream.writes()); got != len(frames) { + t.Fatalf("forwarded %d messages, want %d", got, len(frames)) + } +} diff --git a/server/openapi.yaml b/server/openapi.yaml index cac97d23..3f186d4f 100644 --- a/server/openapi.yaml +++ b/server/openapi.yaml @@ -1723,8 +1723,8 @@ components: $ref: "#/components/schemas/BrowserTelemetryCategoryConfig" description: HTTP request/response metadata. control: - $ref: "#/components/schemas/BrowserTelemetryCategoryConfig" - description: Agent-driven actions against the browser — computer-control calls, Playwright code execution, screenshots and clipboard access. + $ref: "#/components/schemas/BrowserTelemetryControlConfig" + description: Agent-driven actions against the browser — computer-control calls, Playwright code execution, screenshots, clipboard access, and browser-control commands sent over the CDP proxy. platform: $ref: "#/components/schemas/BrowserTelemetryCategoryConfig" description: Calls that manage the VM rather than drive the browser (recording, filesystem, process, telemetry and browser configuration). Mostly platform-induced; off by default and opt-in. @@ -1755,6 +1755,42 @@ components: category retains its current state. To enable or disable a category via PATCH, you must send an explicit `true` or `false`. additionalProperties: false + BrowserTelemetryControlConfig: + type: object + description: > + Configuration for the control category. Same `enabled` semantics as any + other category, plus settings for the browser-control commands the CDP + proxy reports. + properties: + enabled: + type: boolean + description: > + Whether this category is captured. In PUT requests selection is opt-in: + omitting this field (or the whole category) leaves the category off, so + a PUT captures exactly the categories set to true. In PATCH requests, + omitting this field (or sending an empty object `{}`) is a no-op; the + category retains its current state. To enable or disable a category via + PATCH, you must send an explicit `true` or `false`. + cdp: + $ref: "#/components/schemas/BrowserTelemetryCdpControlConfig" + additionalProperties: false + BrowserTelemetryCdpControlConfig: + type: object + description: Settings for the `cdp_command` events the DevTools proxy reports. + properties: + excluded_methods: + type: array + description: > + Methods to leave out of the `cdp_command` stream. Omit the list (or + send an empty one) to report every supported method. Exclusion is a + telemetry setting only: an excluded command is still relayed to the + browser unchanged, it simply produces no event. Use it to drop the + highest-volume methods — `Input.dispatchMouseEvent` during a + humanized cursor path, or `Page.captureScreenshot` under a + screencast — without turning the whole category off. + items: + $ref: "#/components/schemas/BrowserCdpCommandMethod" + additionalProperties: false BrowserCallStack: type: object description: > @@ -2885,156 +2921,2723 @@ components: truncated: type: boolean description: True if the data field was truncated due to size limits. - BrowserCdpConnectEvent: + BrowserCdpMouseEventType: + type: string + description: > + Mouse event phase. Canonical values from devtools-protocol@2d019e73. + `other` stands for a value outside that set, so a client cannot put + an arbitrary string into the stream. + enum: + - mousePressed + - mouseReleased + - mouseMoved + - mouseWheel + - other + BrowserCdpKeyEventType: + type: string + description: > + Key event phase. Canonical values from devtools-protocol@2d019e73. + `other` stands for a value outside that set, so a client cannot put + an arbitrary string into the stream. + enum: + - keyDown + - keyUp + - rawKeyDown + - char + - other + BrowserCdpTouchEventType: + type: string + description: > + Touch event phase. Canonical values from devtools-protocol@2d019e73. + `other` stands for a value outside that set, so a client cannot put + an arbitrary string into the stream. + enum: + - touchStart + - touchEnd + - touchMove + - touchCancel + - other + BrowserCdpDragEventType: + type: string + description: > + Drag event phase. Canonical values from devtools-protocol@2d019e73. + `other` stands for a value outside that set, so a client cannot put + an arbitrary string into the stream. + enum: + - dragEnter + - dragOver + - drop + - dragCancel + - other + BrowserCdpMouseButton: + type: string + description: > + Mouse button named by a command. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - none + - left + - middle + - right + - back + - forward + - other + BrowserCdpPointerType: + type: string + description: > + Pointer that generated an input event. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - mouse + - pen + - other + BrowserCdpGestureSourceType: + type: string + description: > + Input source a synthesized gesture emulates. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - default + - touch + - mouse + - other + BrowserCdpScreenshotFormat: + type: string + description: > + Image format requested for a screenshot. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - jpeg + - png + - webp + - other + BrowserCdpSnapshotFormat: + type: string + description: > + Format requested for a page snapshot. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - mhtml + - other + BrowserCdpScreencastFormat: + type: string + description: > + Frame format requested for a screencast. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - jpeg + - png + - other + BrowserCdpWebLifecycleState: + type: string + description: > + Page lifecycle state applied. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - frozen + - active + - other + BrowserCdpPdfTransferMode: + type: string + description: > + How a generated PDF is returned. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - ReturnAsBase64 + - ReturnAsStream + - other + BrowserCdpWindowState: + type: string + description: > + Browser window state requested. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - normal + - minimized + - maximized + - fullscreen + - other + BrowserCdpTransitionType: + type: string + description: > + Navigation reason reported by the caller. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - link + - typed + - address_bar + - auto_bookmark + - auto_subframe + - manual_subframe + - generated + - auto_toplevel + - form_submit + - reload + - keyword + - keyword_generated + - other + BrowserCdpReferrerPolicy: + type: string + description: > + Referrer policy named by a navigation. Canonical values from + devtools-protocol@2d019e73. `other` stands for a value outside that + set, so a client cannot put an arbitrary string into the stream. + enum: + - noReferrer + - noReferrerWhenDowngrade + - origin + - originWhenCrossOrigin + - sameOrigin + - strictOrigin + - strictOriginWhenCrossOrigin + - unsafeUrl + - other + BrowserCdpAutofillMode: + type: string + description: > + Which kind of value autofill filled. Canonical values from + devtools-protocol@2d019e73. + enum: + - card + - address + BrowserCdpDragMimeCategory: + type: string + description: > + Top-level MIME category of a drag item, from the IANA registry + rather than the protocol; a drag item's subtype names the file, so + only the category is reported. `other` stands for a value outside + that set, so a client cannot put an arbitrary string into the + stream. + enum: + - text + - image + - audio + - video + - application + - font + - model + - multipart + - message + - other + BrowserCdpCommandMethod: + type: string + description: > + A browser-control CDP method the proxy reports. The set covers the + commands an agent drives the browser with; configuration, DOM and + Runtime bookkeeping, and Chrome-specific UI commands are outside it. + Canonical definitions: devtools-protocol@2d019e73. + enum: + - Input.dispatchMouseEvent + - Input.dispatchKeyEvent + - Input.insertText + - Input.imeSetComposition + - Input.dispatchTouchEvent + - Input.dispatchDragEvent + - Input.cancelDragging + - Input.emulateTouchFromMouseEvent + - Input.synthesizePinchGesture + - Input.synthesizeScrollGesture + - Input.synthesizeTapGesture + - DOM.setFileInputFiles + - DOM.focus + - DOM.scrollIntoViewIfNeeded + - Page.bringToFront + - Page.captureScreenshot + - Page.captureSnapshot + - Page.handleJavaScriptDialog + - Page.navigate + - Page.navigateToHistoryEntry + - Page.reload + - Page.printToPDF + - Page.startScreencast + - Page.stopScreencast + - Page.stopLoading + - Page.close + - Page.setWebLifecycleState + - Target.activateTarget + - Target.closeTarget + - Target.createTarget + - Target.createBrowserContext + - Target.disposeBrowserContext + - Target.openDevTools + - Browser.cancelDownload + - Browser.close + - Browser.setWindowBounds + - Browser.setContentsSize + - Autofill.trigger + BrowserCdpInputDispatchMouseEventCommandData: type: object - description: An external client (e.g. customer SDK, Playwright, Puppeteer) connected to the CDP WebSocket proxy on this VM. - required: [ts, type, category, source] + description: > + Sanitized `Input.dispatchMouseEvent` arguments. Canonical input: + `Input.dispatchMouseEvent` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, event_type] properties: - ts: + method: + type: string + const: Input.dispatchMouseEvent + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: type: integer format: int64 - description: Event timestamp in Unix microseconds. - type: - type: string - const: cdp_connect - category: + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: type: string - const: connection - source: - $ref: "#/components/schemas/BrowserEventSource" - truncated: - type: boolean - description: True if the data field was truncated due to size limits. - BrowserCdpDisconnectEventData: - type: object - description: Per-disconnect payload for `cdp_disconnect` events. - additionalProperties: false - required: [duration_ms, message_count, reason] - properties: - duration_ms: + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + event_type: + $ref: "#/components/schemas/BrowserCdpMouseEventType" + description: > + Mouse event phase: `mousePressed`, `mouseReleased`, `mouseMoved` + or `mouseWheel`. A value the protocol does not define is + reported as `other`. + x: type: number - description: Wall-clock duration of the connection in milliseconds. - message_count: + format: double + description: > + Viewport x coordinate in CSS pixels. + y: + type: number + format: double + description: > + Viewport y coordinate in CSS pixels. + modifiers: type: integer - description: Number of CDP messages relayed across the connection in either direction. - reason: - type: string description: > - Why the connection ended. `client_close`: the client initiated the close. - `upstream_changed`: Chromium restarted mid-session and the proxy tore down - so the client could reconnect against the new upstream. `upstream_error`: - upstream dial or message pump errored. `context_cancelled`: the request - context was cancelled (typically server shutdown). - enum: - - client_close - - upstream_changed - - upstream_error - - context_cancelled - BrowserCdpDisconnectEvent: + Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, + 8=Shift). + button: + $ref: "#/components/schemas/BrowserCdpMouseButton" + description: > + Button named by the command (`none`, `left`, `middle`, `right`, + `back`, `forward`). A value the protocol does not define is + reported as `other`. + buttons: + type: integer + description: > + Bit field of buttons held down. Non-zero on a `mouseMoved` means + the move is a drag path. + click_count: + type: integer + description: > + Number of times the button was clicked (2 is a double click). + delta_x: + type: number + format: double + description: > + Horizontal scroll delta, for `mouseWheel`. + delta_y: + type: number + format: double + description: > + Vertical scroll delta, for `mouseWheel`. + pointer_type: + $ref: "#/components/schemas/BrowserCdpPointerType" + description: > + Pointer that generated the event (`mouse` or `pen`). A value the + protocol does not define is reported as `other`. + force: + type: number + format: double + description: > + Normalized pressure, 0 to 1. + tangential_pressure: + type: number + format: double + description: > + Normalized tangential pressure, -1 to 1. + tilt_x: + type: number + format: double + description: > + Pen tilt from the Y-Z plane, in degrees. + tilt_y: + type: number + format: double + description: > + Pen tilt from the X-Z plane, in degrees. + twist: + type: integer + description: > + Pen clockwise rotation, in degrees. + BrowserCdpInputDispatchKeyEventCommandData: type: object - description: An external client disconnected from the CDP WebSocket proxy on this VM. Pair with the immediately preceding `cdp_connect` on the same stream. - required: [ts, type, category, source] + description: > + Sanitized `Input.dispatchKeyEvent` arguments. Canonical input: + `Input.dispatchKeyEvent` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, event_type] properties: - ts: + method: + type: string + const: Input.dispatchKeyEvent + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: type: integer format: int64 - description: Event timestamp in Unix microseconds. - type: + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: type: string - const: cdp_disconnect - category: + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + event_type: + $ref: "#/components/schemas/BrowserCdpKeyEventType" + description: > + Key event phase: `keyDown`, `keyUp`, `rawKeyDown` or `char`. A + value the protocol does not define is reported as `other`. + modifiers: + type: integer + description: > + Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, + 8=Shift). + text_length: + type: integer + description: > + Number of characters the command submitted. The text itself is + never captured. + named_key: type: string - const: connection - source: - $ref: "#/components/schemas/BrowserEventSource" - data: - $ref: "#/components/schemas/BrowserCdpDisconnectEventData" - truncated: + description: > + Key that commands the page rather than typing into it (e.g. + `Enter`, `Tab`, `ArrowDown`, `F5`). Keys that produce a + character are never captured; those are counted by + `text_length`. + location: + type: integer + description: > + Keyboard location (1=left, 2=right, 3=numpad). + auto_repeat: type: boolean - description: True if the data field was truncated due to size limits. - BrowserLiveViewConnectEventData: + description: > + Whether the event was generated by key repeat. + is_keypad: + type: boolean + description: > + Whether the key is on the numeric keypad. + is_system_key: + type: boolean + description: > + Whether the event is a system key event. + command_count: + type: integer + description: > + Number of editing commands (e.g. `selectAll`) carried by the + event. + BrowserCdpInputInsertTextCommandData: type: object - description: Per-session payload for `live_view_connect` events. + description: > + Sanitized `Input.insertText` arguments. Canonical input: + `Input.insertText` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. additionalProperties: false - required: [session_id] + required: [method, text_length] properties: + method: + type: string + const: Input.insertText session_id: type: string - description: Live view session identifier. Stable across reconnects, so a transient network blip can emit two events with the same `session_id`. - BrowserLiveViewConnectEvent: - type: object - description: A live view client connected to the headful browser's WebRTC server (Neko). Headful only; not emitted for headless images. - required: [ts, type, category, source] - properties: - ts: + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: type: integer format: int64 - description: Event timestamp in Unix microseconds. - type: - type: string - const: live_view_connect - category: + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: type: string - const: connection - source: - $ref: "#/components/schemas/BrowserEventSource" - data: - $ref: "#/components/schemas/BrowserLiveViewConnectEventData" - truncated: - type: boolean - description: True if the data field was truncated due to size limits. - BrowserLiveViewDisconnectEventData: + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + text_length: + type: integer + description: > + Number of characters inserted. The text itself is never + captured. + BrowserCdpInputImeSetCompositionCommandData: type: object - description: Per-session payload for `live_view_disconnect` events. + description: > + Sanitized `Input.imeSetComposition` arguments. Canonical input: + `Input.imeSetComposition` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. additionalProperties: false - required: [session_id, duration_ms] + required: [method, text_length] properties: + method: + type: string + const: Input.imeSetComposition session_id: type: string - description: Live view session identifier; matches the corresponding `live_view_connect` event. - duration_ms: - type: number - description: Wall-clock duration of the connection in milliseconds. - BrowserLiveViewDisconnectEvent: - type: object - description: A live view client disconnected from the headful browser's WebRTC server (Neko). Pair with `live_view_connect` by `session_id`. - required: [ts, type, category, source] - properties: - ts: + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: type: integer format: int64 - description: Event timestamp in Unix microseconds. - type: - type: string - const: live_view_disconnect - category: + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: type: string - const: connection - source: - $ref: "#/components/schemas/BrowserEventSource" - data: - $ref: "#/components/schemas/BrowserLiveViewDisconnectEventData" - truncated: - type: boolean - description: True if the data field was truncated due to size limits. - BrowserCaptchaSolveResultEventData: + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + text_length: + type: integer + description: > + Number of characters in the composition. The text itself is + never captured. + selection_start: + type: integer + description: > + Selection start offset within the composition. + selection_end: + type: integer + description: > + Selection end offset within the composition. + replacement_start: + type: integer + description: > + Replacement range start offset. + replacement_end: + type: integer + description: > + Replacement range end offset. + BrowserCdpInputDispatchTouchEventCommandData: type: object - description: Per-attempt payload for `captcha_solve_result` events. + description: > + Sanitized `Input.dispatchTouchEvent` arguments. Canonical input: + `Input.dispatchTouchEvent` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. additionalProperties: false - required: [captcha_type, status, duration_ms] + required: [method, event_type, touch_point_count] properties: - captcha_type: + method: + type: string + const: Input.dispatchTouchEvent + session_id: type: string + maxLength: 128 description: > - Captcha vendor family. Producers normalize provider-specific task - names into this set: enterprise variants of recaptcha collapse into - their version bucket (v2 / v3), and anything not covered (e.g. - DataDome, MtCaptcha, plain OCR) is reported as `other`. - enum: - - hcaptcha - - recaptcha_v2 - - recaptcha_v3 - - turnstile + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + event_type: + $ref: "#/components/schemas/BrowserCdpTouchEventType" + description: > + Touch event phase: `touchStart`, `touchEnd`, `touchMove` or + `touchCancel`. A value the protocol does not define is reported + as `other`. + touch_point_count: + type: integer + description: > + Number of active touch points the command carried. + x: + type: number + format: double + description: > + Viewport x coordinate of the first touch point. Touch + coordinates live inside `touchPoints`, so this is the primary + point rather than a command-level argument. + y: + type: number + format: double + description: > + Viewport y coordinate of the first touch point. + radius_x: + type: number + format: double + description: > + Horizontal radius of the first touch point. + radius_y: + type: number + format: double + description: > + Vertical radius of the first touch point. + rotation_angle: + type: number + format: double + description: > + Rotation of the first touch point, in degrees. + force: + type: number + format: double + description: > + Normalized pressure of the first touch point, 0 to 1. + tangential_pressure: + type: number + format: double + description: > + Normalized tangential pressure of the first touch point, -1 to + 1. + tilt_x: + type: number + format: double + description: > + Tilt of the first touch point from the Y-Z plane, in degrees. + tilt_y: + type: number + format: double + description: > + Tilt of the first touch point from the X-Z plane, in degrees. + twist: + type: integer + description: > + Clockwise rotation of the first touch point, in degrees. + modifiers: + type: integer + description: > + Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, + 8=Shift). + BrowserCdpInputDispatchDragEventCommandData: + type: object + description: > + Sanitized `Input.dispatchDragEvent` arguments. Canonical input: + `Input.dispatchDragEvent` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, event_type] + properties: + method: + type: string + const: Input.dispatchDragEvent + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + event_type: + $ref: "#/components/schemas/BrowserCdpDragEventType" + description: > + Drag event phase: `dragEnter`, `dragOver`, `drop` or + `dragCancel`. A value the protocol does not define is reported + as `other`. + x: + type: number + format: double + description: > + Viewport x coordinate in CSS pixels. + y: + type: number + format: double + description: > + Viewport y coordinate in CSS pixels. + modifiers: + type: integer + description: > + Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, + 8=Shift). + drag_item_count: + type: integer + description: > + Number of items in the drag payload. Item contents are never + captured. + drag_file_count: + type: integer + description: > + Number of files in the drag payload. File paths are never + captured. + drag_mime_categories: + type: array + items: + $ref: "#/components/schemas/BrowserCdpDragMimeCategory" + description: > + Distinct top-level MIME categories of the drag items (e.g. + `text`, `image`, `application`). Subtypes and contents are never + captured. A value the protocol does not define is reported as + `other`. + drag_operations_mask: + type: integer + description: > + Bit field of allowed drag operations (1=copy, 2=link, 16=move). + BrowserCdpInputCancelDraggingCommandData: + type: object + description: > + Sanitized `Input.cancelDragging` arguments. Canonical input: + `Input.cancelDragging` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Input.cancelDragging + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + BrowserCdpInputEmulateTouchFromMouseEventCommandData: + type: object + description: > + Sanitized `Input.emulateTouchFromMouseEvent` arguments. Canonical + input: `Input.emulateTouchFromMouseEvent` in + devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, event_type] + properties: + method: + type: string + const: Input.emulateTouchFromMouseEvent + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + event_type: + $ref: "#/components/schemas/BrowserCdpMouseEventType" + description: > + Mouse event phase being emulated as touch. A value the protocol + does not define is reported as `other`. + x: + type: number + format: double + description: > + Viewport x coordinate in CSS pixels. + y: + type: number + format: double + description: > + Viewport y coordinate in CSS pixels. + button: + $ref: "#/components/schemas/BrowserCdpMouseButton" + description: > + Button named by the command. A value the protocol does not + define is reported as `other`. + modifiers: + type: integer + description: > + Bit field of held modifier keys (1=Alt, 2=Ctrl, 4=Meta, + 8=Shift). + click_count: + type: integer + description: > + Number of times the button was clicked. + delta_x: + type: number + format: double + description: > + Horizontal scroll delta. + delta_y: + type: number + format: double + description: > + Vertical scroll delta. + BrowserCdpInputSynthesizePinchGestureCommandData: + type: object + description: > + Sanitized `Input.synthesizePinchGesture` arguments. Canonical input: + `Input.synthesizePinchGesture` in devtools-protocol@2d019e73, pinned + at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Input.synthesizePinchGesture + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + x: + type: number + format: double + description: > + Viewport x coordinate in CSS pixels. + y: + type: number + format: double + description: > + Viewport y coordinate in CSS pixels. + scale_factor: + type: number + format: double + description: > + Relative scale of the pinch (>1 zooms in). + relative_speed: + type: integer + description: > + Relative pointer speed, in pixels per second. + gesture_source_type: + $ref: "#/components/schemas/BrowserCdpGestureSourceType" + description: > + Input source the synthesized gesture emulates. A value the + protocol does not define is reported as `other`. + BrowserCdpInputSynthesizeScrollGestureCommandData: + type: object + description: > + Sanitized `Input.synthesizeScrollGesture` arguments. Canonical + input: `Input.synthesizeScrollGesture` in + devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Input.synthesizeScrollGesture + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + x: + type: number + format: double + description: > + Viewport x coordinate in CSS pixels. + y: + type: number + format: double + description: > + Viewport y coordinate in CSS pixels. + x_distance: + type: number + format: double + description: > + Horizontal scroll distance in CSS pixels; positive scrolls left. + y_distance: + type: number + format: double + description: > + Vertical scroll distance in CSS pixels; positive scrolls up. + x_overscroll: + type: number + format: double + description: > + Additional horizontal distance scrolled past the end. + y_overscroll: + type: number + format: double + description: > + Additional vertical distance scrolled past the end. + prevent_fling: + type: boolean + description: > + Whether fling was suppressed. + speed: + type: integer + description: > + Swipe speed in pixels per second. + gesture_source_type: + $ref: "#/components/schemas/BrowserCdpGestureSourceType" + description: > + Input source the synthesized gesture emulates. A value the + protocol does not define is reported as `other`. + repeat_count: + type: integer + description: > + Number of additional repeats of the scroll. + repeat_delay_ms: + type: integer + description: > + Delay between repeats, in milliseconds. + BrowserCdpInputSynthesizeTapGestureCommandData: + type: object + description: > + Sanitized `Input.synthesizeTapGesture` arguments. Canonical input: + `Input.synthesizeTapGesture` in devtools-protocol@2d019e73, pinned + at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Input.synthesizeTapGesture + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + x: + type: number + format: double + description: > + Viewport x coordinate in CSS pixels. + y: + type: number + format: double + description: > + Viewport y coordinate in CSS pixels. + duration: + type: integer + description: > + Duration between touchdown and touchup, in milliseconds. + tap_count: + type: integer + description: > + Number of times to tap (2 is a double tap). + gesture_source_type: + $ref: "#/components/schemas/BrowserCdpGestureSourceType" + description: > + Input source the synthesized gesture emulates. A value the + protocol does not define is reported as `other`. + BrowserCdpDomSetFileInputFilesCommandData: + type: object + description: > + Sanitized `DOM.setFileInputFiles` arguments. Canonical input: + `DOM.setFileInputFiles` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, file_count] + properties: + method: + type: string + const: DOM.setFileInputFiles + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + file_count: + type: integer + description: > + Number of files handed to the input. File paths are never + captured. + node_id: + type: integer + description: > + Opaque DOM node identifier the command targeted. + backend_node_id: + type: integer + description: > + Opaque backend DOM node identifier the command targeted. + object_id: + type: string + maxLength: 128 + description: > + Opaque Runtime remote object identifier the command targeted. + Clipped to 128 characters; a longer value is not a real + identifier. + BrowserCdpDomFocusCommandData: + type: object + description: > + Sanitized `DOM.focus` arguments. Canonical input: `DOM.focus` in + devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: DOM.focus + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + node_id: + type: integer + description: > + Opaque DOM node identifier the command targeted. + backend_node_id: + type: integer + description: > + Opaque backend DOM node identifier the command targeted. + object_id: + type: string + maxLength: 128 + description: > + Opaque Runtime remote object identifier the command targeted. + Clipped to 128 characters; a longer value is not a real + identifier. + BrowserCdpDomScrollIntoViewIfNeededCommandData: + type: object + description: > + Sanitized `DOM.scrollIntoViewIfNeeded` arguments. Canonical input: + `DOM.scrollIntoViewIfNeeded` in devtools-protocol@2d019e73, pinned + at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: DOM.scrollIntoViewIfNeeded + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + node_id: + type: integer + description: > + Opaque DOM node identifier the command targeted. + backend_node_id: + type: integer + description: > + Opaque backend DOM node identifier the command targeted. + object_id: + type: string + maxLength: 128 + description: > + Opaque Runtime remote object identifier the command targeted. + Clipped to 128 characters; a longer value is not a real + identifier. + rect_x: + type: number + format: double + description: > + X offset of the rect the command scrolled to, relative to the + node. + rect_y: + type: number + format: double + description: > + Y offset of the rect the command scrolled to, relative to the + node. + rect_width: + type: number + format: double + description: > + Width of the rect the command scrolled to. + rect_height: + type: number + format: double + description: > + Height of the rect the command scrolled to. + BrowserCdpPageBringToFrontCommandData: + type: object + description: > + Sanitized `Page.bringToFront` arguments. Canonical input: + `Page.bringToFront` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.bringToFront + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + BrowserCdpPageCaptureScreenshotCommandData: + type: object + description: > + Sanitized `Page.captureScreenshot` arguments. Canonical input: + `Page.captureScreenshot` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.captureScreenshot + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + format: + $ref: "#/components/schemas/BrowserCdpScreenshotFormat" + description: > + Image format requested (`jpeg`, `png` or `webp`). A value the + protocol does not define is reported as `other`. + quality: + type: integer + description: > + Compression quality, 0 to 100, for lossy formats. + from_surface: + type: boolean + description: > + Whether the capture was taken from the surface rather than the + view. + capture_beyond_viewport: + type: boolean + description: > + Whether the capture extended past the viewport. + optimize_for_speed: + type: boolean + description: > + Whether encoding favored speed over size. + clip_x: + type: number + format: double + description: > + Clip region x offset in CSS pixels. + clip_y: + type: number + format: double + description: > + Clip region y offset in CSS pixels. + clip_width: + type: number + format: double + description: > + Clip region width in CSS pixels. + clip_height: + type: number + format: double + description: > + Clip region height in CSS pixels. + clip_scale: + type: number + format: double + description: > + Clip region page scale factor. + BrowserCdpPageCaptureSnapshotCommandData: + type: object + description: > + Sanitized `Page.captureSnapshot` arguments. Canonical input: + `Page.captureSnapshot` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.captureSnapshot + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + format: + $ref: "#/components/schemas/BrowserCdpSnapshotFormat" + description: > + Snapshot format requested (`mhtml`). A value the protocol does + not define is reported as `other`. + BrowserCdpPageHandleJavaScriptDialogCommandData: + type: object + description: > + Sanitized `Page.handleJavaScriptDialog` arguments. Canonical input: + `Page.handleJavaScriptDialog` in devtools-protocol@2d019e73, pinned + at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, accept] + properties: + method: + type: string + const: Page.handleJavaScriptDialog + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + accept: + type: boolean + description: > + Whether the dialog was accepted or dismissed. + prompt_text_length: + type: integer + description: > + Number of characters entered into a prompt dialog. The text + itself is never captured. + BrowserCdpPageNavigateCommandData: + type: object + description: > + Sanitized `Page.navigate` arguments. Canonical input: + `Page.navigate` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.navigate + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + url_scheme: + type: string + maxLength: 32 + description: > + Scheme of the destination URL (e.g. `https`, `about`, `data`). + The rest of the URL is never captured. + transition_type: + $ref: "#/components/schemas/BrowserCdpTransitionType" + description: > + Navigation reason reported by the caller (e.g. `link`, `typed`, + `reload`). A value the protocol does not define is reported as + `other`. + referrer_present: + type: boolean + description: > + Whether the command carried a referrer. The referrer itself is + never captured. + referrer_policy: + $ref: "#/components/schemas/BrowserCdpReferrerPolicy" + description: > + Referrer policy named by the command. A value the protocol does + not define is reported as `other`. + frame_id: + type: string + maxLength: 128 + description: > + Opaque frame identifier. Clipped to 128 characters; a longer + value is not a real identifier. + BrowserCdpPageNavigateToHistoryEntryCommandData: + type: object + description: > + Sanitized `Page.navigateToHistoryEntry` arguments. Canonical input: + `Page.navigateToHistoryEntry` in devtools-protocol@2d019e73, pinned + at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, entry_id] + properties: + method: + type: string + const: Page.navigateToHistoryEntry + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + entry_id: + type: integer + description: > + History entry the command navigated to. + BrowserCdpPageReloadCommandData: + type: object + description: > + Sanitized `Page.reload` arguments. Canonical input: `Page.reload` in + devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.reload + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + ignore_cache: + type: boolean + description: > + Whether the reload bypassed the cache. + script_length: + type: integer + description: > + Number of characters in the injected script. + loader_id: + type: string + maxLength: 128 + description: > + Opaque document loader identifier. Clipped to 128 characters; a + longer value is not a real identifier. + BrowserCdpPagePrintToPdfCommandData: + type: object + description: > + Sanitized `Page.printToPDF` arguments. Canonical input: + `Page.printToPDF` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.printToPDF + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + landscape: + type: boolean + description: > + Whether the page was laid out in landscape. + scale: + type: number + format: double + description: > + Page render scale. + paper_width: + type: number + format: double + description: > + Paper width in inches. + paper_height: + type: number + format: double + description: > + Paper height in inches. + display_header_footer: + type: boolean + description: > + Whether a header and footer were rendered. + print_background: + type: boolean + description: > + Whether background graphics were printed. + margin_top: + type: number + format: double + description: > + Top margin in inches. + margin_bottom: + type: number + format: double + description: > + Bottom margin in inches. + margin_left: + type: number + format: double + description: > + Left margin in inches. + margin_right: + type: number + format: double + description: > + Right margin in inches. + prefer_css_page_size: + type: boolean + description: > + Whether the CSS page size was preferred over the paper size. + generate_tagged_pdf: + type: boolean + description: > + Whether a tagged (accessible) PDF was requested. + generate_document_outline: + type: boolean + description: > + Whether a document outline was embedded. + transfer_mode: + $ref: "#/components/schemas/BrowserCdpPdfTransferMode" + description: > + How the PDF was returned (`ReturnAsBase64` or `ReturnAsStream`). + A value the protocol does not define is reported as `other`. + page_ranges_present: + type: boolean + description: > + Whether a page range was supplied. + header_template_present: + type: boolean + description: > + Whether a header template was supplied. The template itself is + never captured. + footer_template_present: + type: boolean + description: > + Whether a footer template was supplied. The template itself is + never captured. + BrowserCdpPageStartScreencastCommandData: + type: object + description: > + Sanitized `Page.startScreencast` arguments. Canonical input: + `Page.startScreencast` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.startScreencast + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + format: + $ref: "#/components/schemas/BrowserCdpScreencastFormat" + description: > + Frame format requested (`jpeg` or `png`). A value the protocol + does not define is reported as `other`. + quality: + type: integer + description: > + Compression quality, 0 to 100. + max_width: + type: integer + description: > + Maximum frame width in pixels. + max_height: + type: integer + description: > + Maximum frame height in pixels. + every_nth_frame: + type: integer + description: > + Frame sampling interval. + BrowserCdpPageStopScreencastCommandData: + type: object + description: > + Sanitized `Page.stopScreencast` arguments. Canonical input: + `Page.stopScreencast` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.stopScreencast + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + BrowserCdpPageStopLoadingCommandData: + type: object + description: > + Sanitized `Page.stopLoading` arguments. Canonical input: + `Page.stopLoading` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.stopLoading + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + BrowserCdpPageCloseCommandData: + type: object + description: > + Sanitized `Page.close` arguments. Canonical input: `Page.close` in + devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Page.close + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + BrowserCdpPageSetWebLifecycleStateCommandData: + type: object + description: > + Sanitized `Page.setWebLifecycleState` arguments. Canonical input: + `Page.setWebLifecycleState` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, state] + properties: + method: + type: string + const: Page.setWebLifecycleState + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + state: + $ref: "#/components/schemas/BrowserCdpWebLifecycleState" + description: > + Lifecycle state applied (`frozen` or `active`). A value the + protocol does not define is reported as `other`. + BrowserCdpTargetActivateTargetCommandData: + type: object + description: > + Sanitized `Target.activateTarget` arguments. Canonical input: + `Target.activateTarget` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, target_id] + properties: + method: + type: string + const: Target.activateTarget + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + target_id: + type: string + maxLength: 128 + description: > + Opaque target identifier. Clipped to 128 characters; a longer + value is not a real identifier. + BrowserCdpTargetCloseTargetCommandData: + type: object + description: > + Sanitized `Target.closeTarget` arguments. Canonical input: + `Target.closeTarget` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, target_id] + properties: + method: + type: string + const: Target.closeTarget + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + target_id: + type: string + maxLength: 128 + description: > + Opaque target identifier. Clipped to 128 characters; a longer + value is not a real identifier. + BrowserCdpTargetCreateTargetCommandData: + type: object + description: > + Sanitized `Target.createTarget` arguments. Canonical input: + `Target.createTarget` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Target.createTarget + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + url_scheme: + type: string + maxLength: 32 + description: > + Scheme of the destination URL (e.g. `https`, `about`, `data`). + The rest of the URL is never captured. + left: + type: integer + description: > + Window x position in screen coordinates. + top: + type: integer + description: > + Window y position in screen coordinates. + width: + type: integer + description: > + Window width in DIP. + height: + type: integer + description: > + Window height in DIP. + window_state: + $ref: "#/components/schemas/BrowserCdpWindowState" + description: > + Window state requested (`normal`, `minimized`, `maximized`, + `fullscreen`). A value the protocol does not define is reported + as `other`. + browser_context_id: + type: string + maxLength: 128 + description: > + Opaque browser context identifier. Clipped to 128 characters; a + longer value is not a real identifier. + new_window: + type: boolean + description: > + Whether a new window was requested. + background: + type: boolean + description: > + Whether the target was created in the background. + for_tab: + type: boolean + description: > + Whether a tab target rather than a page target was created. + hidden: + type: boolean + description: > + Whether the target was created hidden. + enable_begin_frame_control: + type: boolean + description: > + Whether BeginFrame control was enabled (headless only). + focus: + type: boolean + description: > + Whether the new target was focused. + BrowserCdpTargetCreateBrowserContextCommandData: + type: object + description: > + Sanitized `Target.createBrowserContext` arguments. Canonical input: + `Target.createBrowserContext` in devtools-protocol@2d019e73, pinned + at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Target.createBrowserContext + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + dispose_on_detach: + type: boolean + description: > + Whether the context is disposed when the debugging session + detaches. + proxy_server_present: + type: boolean + description: > + Whether a proxy was configured. The proxy address is never + captured. + proxy_bypass_list_present: + type: boolean + description: > + Whether a proxy bypass list was configured. + universal_network_access_origin_count: + type: integer + description: > + Number of origins granted universal network access. The origins + themselves are never captured. + BrowserCdpTargetDisposeBrowserContextCommandData: + type: object + description: > + Sanitized `Target.disposeBrowserContext` arguments. Canonical input: + `Target.disposeBrowserContext` in devtools-protocol@2d019e73, pinned + at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, browser_context_id] + properties: + method: + type: string + const: Target.disposeBrowserContext + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + browser_context_id: + type: string + maxLength: 128 + description: > + Opaque browser context identifier. Clipped to 128 characters; a + longer value is not a real identifier. + BrowserCdpTargetOpenDevToolsCommandData: + type: object + description: > + Sanitized `Target.openDevTools` arguments. Canonical input: + `Target.openDevTools` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, target_id] + properties: + method: + type: string + const: Target.openDevTools + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + target_id: + type: string + maxLength: 128 + description: > + Opaque target identifier. Clipped to 128 characters; a longer + value is not a real identifier. + panel_id: + type: string + maxLength: 128 + description: > + DevTools panel opened. Clipped to 128 characters; a longer value + is not a real identifier. + BrowserCdpBrowserCancelDownloadCommandData: + type: object + description: > + Sanitized `Browser.cancelDownload` arguments. Canonical input: + `Browser.cancelDownload` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, download_guid] + properties: + method: + type: string + const: Browser.cancelDownload + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + download_guid: + type: string + maxLength: 128 + description: > + Opaque identifier of the download that was cancelled. Clipped to + 128 characters; a longer value is not a real identifier. + browser_context_id: + type: string + maxLength: 128 + description: > + Opaque browser context identifier. Clipped to 128 characters; a + longer value is not a real identifier. + BrowserCdpBrowserCloseCommandData: + type: object + description: > + Sanitized `Browser.close` arguments. Canonical input: + `Browser.close` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method] + properties: + method: + type: string + const: Browser.close + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + BrowserCdpBrowserSetWindowBoundsCommandData: + type: object + description: > + Sanitized `Browser.setWindowBounds` arguments. Canonical input: + `Browser.setWindowBounds` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, window_id] + properties: + method: + type: string + const: Browser.setWindowBounds + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + window_id: + type: integer + description: > + Browser window identifier. + left: + type: integer + description: > + Window x position in screen coordinates. + top: + type: integer + description: > + Window y position in screen coordinates. + width: + type: integer + description: > + Window width in DIP. + height: + type: integer + description: > + Window height in DIP. + window_state: + $ref: "#/components/schemas/BrowserCdpWindowState" + description: > + Window state requested (`normal`, `minimized`, `maximized`, + `fullscreen`). A value the protocol does not define is reported + as `other`. + BrowserCdpBrowserSetContentsSizeCommandData: + type: object + description: > + Sanitized `Browser.setContentsSize` arguments. Canonical input: + `Browser.setContentsSize` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, window_id] + properties: + method: + type: string + const: Browser.setContentsSize + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + window_id: + type: integer + description: > + Browser window identifier. + width: + type: integer + description: > + Contents width in DIP. + height: + type: integer + description: > + Contents height in DIP. + BrowserCdpAutofillTriggerCommandData: + type: object + description: > + Sanitized `Autofill.trigger` arguments. Canonical input: + `Autofill.trigger` in devtools-protocol@2d019e73, pinned at + https://github.com/ChromeDevTools/devtools-protocol/blob/2d019e73eb371d1d6985d26d395d78bd8f8a22ba/json/browser_protocol.json. + Every argument of this command has a retained or redacted decision + in lib/devtoolsproxy/testdata/cdp_arguments.yaml. + additionalProperties: false + required: [method, field_id] + properties: + method: + type: string + const: Autofill.trigger + session_id: + type: string + maxLength: 128 + description: > + CDP session identifier the command was addressed to. Absent for + browser-level commands. Clipped to 128 characters. + command_id: + type: integer + format: int64 + description: > + The command's JSON-RPC id, so the command can be joined to the + result the browser returned for it. Absent when the client sent + none. + connection_id: + type: string + maxLength: 128 + description: > + Identifies the CDP proxy connection the command arrived on, + matching `cdp_connect` and `cdp_disconnect`. Two clients driving + the same browser are told apart by this. + field_id: + type: integer + description: > + Opaque backend node identifier of the field that was autofilled. + frame_id: + type: string + maxLength: 128 + description: > + Opaque frame identifier. Clipped to 128 characters; a longer + value is not a real identifier. + mode: + $ref: "#/components/schemas/BrowserCdpAutofillMode" + description: > + What was filled: `card` or `address`. The values themselves are + never captured. + address_field_count: + type: integer + description: > + Number of address fields the command filled. Their names and + values are never captured. + BrowserCdpCommandEventData: + description: > + Per-command payload for `cdp_command` events, discriminated by + `method`. Each variant carries only the arguments approved for that + command: values that could hold a secret — typed and composition + text, URLs, referrers, scripts, templates, file paths, drag contents + and autofill values — are replaced by a length, a count, a presence + flag, an enum or a URL scheme and host. + oneOf: + - $ref: "#/components/schemas/BrowserCdpInputDispatchMouseEventCommandData" + - $ref: "#/components/schemas/BrowserCdpInputDispatchKeyEventCommandData" + - $ref: "#/components/schemas/BrowserCdpInputInsertTextCommandData" + - $ref: "#/components/schemas/BrowserCdpInputImeSetCompositionCommandData" + - $ref: "#/components/schemas/BrowserCdpInputDispatchTouchEventCommandData" + - $ref: "#/components/schemas/BrowserCdpInputDispatchDragEventCommandData" + - $ref: "#/components/schemas/BrowserCdpInputCancelDraggingCommandData" + - $ref: "#/components/schemas/BrowserCdpInputEmulateTouchFromMouseEventCommandData" + - $ref: "#/components/schemas/BrowserCdpInputSynthesizePinchGestureCommandData" + - $ref: "#/components/schemas/BrowserCdpInputSynthesizeScrollGestureCommandData" + - $ref: "#/components/schemas/BrowserCdpInputSynthesizeTapGestureCommandData" + - $ref: "#/components/schemas/BrowserCdpDomSetFileInputFilesCommandData" + - $ref: "#/components/schemas/BrowserCdpDomFocusCommandData" + - $ref: "#/components/schemas/BrowserCdpDomScrollIntoViewIfNeededCommandData" + - $ref: "#/components/schemas/BrowserCdpPageBringToFrontCommandData" + - $ref: "#/components/schemas/BrowserCdpPageCaptureScreenshotCommandData" + - $ref: "#/components/schemas/BrowserCdpPageCaptureSnapshotCommandData" + - $ref: "#/components/schemas/BrowserCdpPageHandleJavaScriptDialogCommandData" + - $ref: "#/components/schemas/BrowserCdpPageNavigateCommandData" + - $ref: "#/components/schemas/BrowserCdpPageNavigateToHistoryEntryCommandData" + - $ref: "#/components/schemas/BrowserCdpPageReloadCommandData" + - $ref: "#/components/schemas/BrowserCdpPagePrintToPdfCommandData" + - $ref: "#/components/schemas/BrowserCdpPageStartScreencastCommandData" + - $ref: "#/components/schemas/BrowserCdpPageStopScreencastCommandData" + - $ref: "#/components/schemas/BrowserCdpPageStopLoadingCommandData" + - $ref: "#/components/schemas/BrowserCdpPageCloseCommandData" + - $ref: "#/components/schemas/BrowserCdpPageSetWebLifecycleStateCommandData" + - $ref: "#/components/schemas/BrowserCdpTargetActivateTargetCommandData" + - $ref: "#/components/schemas/BrowserCdpTargetCloseTargetCommandData" + - $ref: "#/components/schemas/BrowserCdpTargetCreateTargetCommandData" + - $ref: "#/components/schemas/BrowserCdpTargetCreateBrowserContextCommandData" + - $ref: "#/components/schemas/BrowserCdpTargetDisposeBrowserContextCommandData" + - $ref: "#/components/schemas/BrowserCdpTargetOpenDevToolsCommandData" + - $ref: "#/components/schemas/BrowserCdpBrowserCancelDownloadCommandData" + - $ref: "#/components/schemas/BrowserCdpBrowserCloseCommandData" + - $ref: "#/components/schemas/BrowserCdpBrowserSetWindowBoundsCommandData" + - $ref: "#/components/schemas/BrowserCdpBrowserSetContentsSizeCommandData" + - $ref: "#/components/schemas/BrowserCdpAutofillTriggerCommandData" + discriminator: + propertyName: method + mapping: + Input.dispatchMouseEvent: "#/components/schemas/BrowserCdpInputDispatchMouseEventCommandData" + Input.dispatchKeyEvent: "#/components/schemas/BrowserCdpInputDispatchKeyEventCommandData" + Input.insertText: "#/components/schemas/BrowserCdpInputInsertTextCommandData" + Input.imeSetComposition: "#/components/schemas/BrowserCdpInputImeSetCompositionCommandData" + Input.dispatchTouchEvent: "#/components/schemas/BrowserCdpInputDispatchTouchEventCommandData" + Input.dispatchDragEvent: "#/components/schemas/BrowserCdpInputDispatchDragEventCommandData" + Input.cancelDragging: "#/components/schemas/BrowserCdpInputCancelDraggingCommandData" + Input.emulateTouchFromMouseEvent: "#/components/schemas/BrowserCdpInputEmulateTouchFromMouseEventCommandData" + Input.synthesizePinchGesture: "#/components/schemas/BrowserCdpInputSynthesizePinchGestureCommandData" + Input.synthesizeScrollGesture: "#/components/schemas/BrowserCdpInputSynthesizeScrollGestureCommandData" + Input.synthesizeTapGesture: "#/components/schemas/BrowserCdpInputSynthesizeTapGestureCommandData" + DOM.setFileInputFiles: "#/components/schemas/BrowserCdpDomSetFileInputFilesCommandData" + DOM.focus: "#/components/schemas/BrowserCdpDomFocusCommandData" + DOM.scrollIntoViewIfNeeded: "#/components/schemas/BrowserCdpDomScrollIntoViewIfNeededCommandData" + Page.bringToFront: "#/components/schemas/BrowserCdpPageBringToFrontCommandData" + Page.captureScreenshot: "#/components/schemas/BrowserCdpPageCaptureScreenshotCommandData" + Page.captureSnapshot: "#/components/schemas/BrowserCdpPageCaptureSnapshotCommandData" + Page.handleJavaScriptDialog: "#/components/schemas/BrowserCdpPageHandleJavaScriptDialogCommandData" + Page.navigate: "#/components/schemas/BrowserCdpPageNavigateCommandData" + Page.navigateToHistoryEntry: "#/components/schemas/BrowserCdpPageNavigateToHistoryEntryCommandData" + Page.reload: "#/components/schemas/BrowserCdpPageReloadCommandData" + Page.printToPDF: "#/components/schemas/BrowserCdpPagePrintToPdfCommandData" + Page.startScreencast: "#/components/schemas/BrowserCdpPageStartScreencastCommandData" + Page.stopScreencast: "#/components/schemas/BrowserCdpPageStopScreencastCommandData" + Page.stopLoading: "#/components/schemas/BrowserCdpPageStopLoadingCommandData" + Page.close: "#/components/schemas/BrowserCdpPageCloseCommandData" + Page.setWebLifecycleState: "#/components/schemas/BrowserCdpPageSetWebLifecycleStateCommandData" + Target.activateTarget: "#/components/schemas/BrowserCdpTargetActivateTargetCommandData" + Target.closeTarget: "#/components/schemas/BrowserCdpTargetCloseTargetCommandData" + Target.createTarget: "#/components/schemas/BrowserCdpTargetCreateTargetCommandData" + Target.createBrowserContext: "#/components/schemas/BrowserCdpTargetCreateBrowserContextCommandData" + Target.disposeBrowserContext: "#/components/schemas/BrowserCdpTargetDisposeBrowserContextCommandData" + Target.openDevTools: "#/components/schemas/BrowserCdpTargetOpenDevToolsCommandData" + Browser.cancelDownload: "#/components/schemas/BrowserCdpBrowserCancelDownloadCommandData" + Browser.close: "#/components/schemas/BrowserCdpBrowserCloseCommandData" + Browser.setWindowBounds: "#/components/schemas/BrowserCdpBrowserSetWindowBoundsCommandData" + Browser.setContentsSize: "#/components/schemas/BrowserCdpBrowserSetContentsSizeCommandData" + Autofill.trigger: "#/components/schemas/BrowserCdpAutofillTriggerCommandData" + BrowserCdpCommandEvent: + type: object + description: > + A browser-control command a client sent over the CDP WebSocket proxy: + input gestures, navigation, dialog handling, file selection and + screenshots. Configuration commands and the DOM/Runtime traffic a client + library issues on the caller's behalf are not reported. + + One event per browser-control command that reached the browser. The + command stream is not sampled, coalesced or reordered. An event is lost + only when the method is excluded by telemetry configuration, when the + command's arguments do not decode, or when classification cannot keep + up. Exclusions are counted in `cdp_disconnect.telemetry_excluded`; the + rest in `cdp_disconnect.telemetry_dropped`. + required: [ts, type, category, source, data] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: cdp_command + category: + type: string + const: control + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserCdpCommandEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserCdpConnectEventData: + type: object + description: Per-connection payload for `cdp_connect` events. + additionalProperties: false + properties: + connection_id: + type: string + maxLength: 128 + description: > + Identifies this CDP proxy connection, matching the `connection_id` + on the `cdp_command` events that arrived on it. Two clients driving + the same browser are told apart by this. + BrowserCdpConnectEvent: + type: object + description: An external client (e.g. customer SDK, Playwright, Puppeteer) connected to the CDP WebSocket proxy on this VM. + required: [ts, type, category, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: cdp_connect + category: + type: string + const: connection + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserCdpConnectEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserCdpDisconnectEventData: + type: object + description: Per-disconnect payload for `cdp_disconnect` events. + additionalProperties: false + required: [duration_ms, message_count, reason] + properties: + connection_id: + type: string + maxLength: 128 + description: > + Identifies this CDP proxy connection, matching the `connection_id` + on the `cdp_command` events that arrived on it. Two clients driving + the same browser are told apart by this. + duration_ms: + type: number + description: Wall-clock duration of the connection in milliseconds. + message_count: + type: integer + description: Number of CDP messages relayed across the connection in either direction. + telemetry_excluded: + type: integer + description: > + Number of forwarded client commands that produced no `cdp_command` + event because their method is listed in + `control.cdp.excluded_methods`. Configuration rather than loss, so + it is counted apart from `telemetry_dropped`. + telemetry_dropped: + type: integer + description: > + Number of supported browser-control commands that were forwarded + to the browser but never classified, because the queue was full + or classification panicked. Every increment is a real lost + command — unsupported and excluded methods are filtered before + admission and never count toward this total. Telemetry loss + only; every command was still relayed to the browser. Always + present on images that report it; absent on images predating the + field, which is not the same as zero. + reason: + type: string + description: > + Why the connection ended. `client_close`: the client initiated the close. + `upstream_changed`: Chromium restarted mid-session and the proxy tore down + so the client could reconnect against the new upstream. `upstream_error`: + upstream dial or message pump errored. `context_cancelled`: the request + context was cancelled (typically server shutdown). + enum: + - client_close + - upstream_changed + - upstream_error + - context_cancelled + BrowserCdpDisconnectEvent: + type: object + description: An external client disconnected from the CDP WebSocket proxy on this VM. Pair with the immediately preceding `cdp_connect` on the same stream. + required: [ts, type, category, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: cdp_disconnect + category: + type: string + const: connection + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserCdpDisconnectEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserLiveViewConnectEventData: + type: object + description: Per-session payload for `live_view_connect` events. + additionalProperties: false + required: [session_id] + properties: + session_id: + type: string + description: Live view session identifier. Stable across reconnects, so a transient network blip can emit two events with the same `session_id`. + BrowserLiveViewConnectEvent: + type: object + description: A live view client connected to the headful browser's WebRTC server (Neko). Headful only; not emitted for headless images. + required: [ts, type, category, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: live_view_connect + category: + type: string + const: connection + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserLiveViewConnectEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserLiveViewDisconnectEventData: + type: object + description: Per-session payload for `live_view_disconnect` events. + additionalProperties: false + required: [session_id, duration_ms] + properties: + session_id: + type: string + description: Live view session identifier; matches the corresponding `live_view_connect` event. + duration_ms: + type: number + description: Wall-clock duration of the connection in milliseconds. + BrowserLiveViewDisconnectEvent: + type: object + description: A live view client disconnected from the headful browser's WebRTC server (Neko). Pair with `live_view_connect` by `session_id`. + required: [ts, type, category, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: live_view_disconnect + category: + type: string + const: connection + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserLiveViewDisconnectEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserCaptchaSolveResultEventData: + type: object + description: Per-attempt payload for `captcha_solve_result` events. + additionalProperties: false + required: [captcha_type, status, duration_ms] + properties: + captcha_type: + type: string + description: > + Captcha vendor family. Producers normalize provider-specific task + names into this set: enterprise variants of recaptcha collapse into + their version bucket (v2 / v3), and anything not covered (e.g. + DataDome, MtCaptcha, plain OCR) is reported as `other`. + enum: + - hcaptcha + - recaptcha_v2 + - recaptcha_v3 + - turnstile - geetest - other status: @@ -3284,6 +5887,7 @@ components: - $ref: "#/components/schemas/BrowserMonitorInitFailedEvent" - $ref: "#/components/schemas/BrowserApiCallEvent" - $ref: "#/components/schemas/BrowserPlatformApiCallEvent" + - $ref: "#/components/schemas/BrowserCdpCommandEvent" - $ref: "#/components/schemas/BrowserCdpConnectEvent" - $ref: "#/components/schemas/BrowserCdpDisconnectEvent" - $ref: "#/components/schemas/BrowserLiveViewConnectEvent" @@ -3320,6 +5924,7 @@ components: monitor_init_failed: "#/components/schemas/BrowserMonitorInitFailedEvent" api_call: "#/components/schemas/BrowserApiCallEvent" platform_api_call: "#/components/schemas/BrowserPlatformApiCallEvent" + cdp_command: "#/components/schemas/BrowserCdpCommandEvent" cdp_connect: "#/components/schemas/BrowserCdpConnectEvent" cdp_disconnect: "#/components/schemas/BrowserCdpDisconnectEvent" live_view_connect: "#/components/schemas/BrowserLiveViewConnectEvent" @@ -3343,6 +5948,17 @@ components: Process-monotonic sequence number of the last published event. Does not reset across configuration changes. minimum: 0 + dropped_events: + type: integer + format: int64 + description: >- + Cumulative number of buffered events a consumer missed because it + fell behind the ring, summed across consumers and configuration + changes. A rising count means the stream is being produced faster + than it is being read; a steady one means nothing has been lost. + Always present on images that report it; absent on an image + predating the field, which is not the same as zero. + minimum: 0 applied_at: type: string format: date-time diff --git a/server/scripts/cdpmanifest/main.go b/server/scripts/cdpmanifest/main.go new file mode 100644 index 00000000..b7d6c6fa --- /dev/null +++ b/server/scripts/cdpmanifest/main.go @@ -0,0 +1,217 @@ +// Command cdpmanifest regenerates the pinned CDP argument snapshot that +// TestEveryCanonicalArgumentHasADecision checks the sanitizers against. +// +// The snapshot is what makes that check meaningful: comparing the sanitizers +// against this repo's own OpenAPI enum only compares two copies of the same +// list, and cannot notice a canonical argument nobody handled. The snapshot is +// derived from the protocol instead, so an argument added upstream shows up as +// a manifest gap the next time this runs. +// +// Usage: +// +// curl -sLo /tmp/browser_protocol.json \ +// https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol//json/browser_protocol.json +// go run ./scripts/cdpmanifest -protocol /tmp/browser_protocol.json -commit +// +// It rewrites testdata/cdp_protocol_pinned.json in place. Review the diff: a +// new argument there is a decision someone has to make in cdp_arguments.yaml. +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "sort" + "strings" +) + +// supportedMethods is the inventory the proxy reports. Kept here rather than +// imported so this script stays a standalone tool; the test asserts the two +// agree. +var supportedMethods = []string{ + "Autofill.trigger", + "Browser.cancelDownload", + "Browser.close", + "Browser.setContentsSize", + "Browser.setWindowBounds", + "DOM.focus", + "DOM.scrollIntoViewIfNeeded", + "DOM.setFileInputFiles", + "Input.cancelDragging", + "Input.dispatchDragEvent", + "Input.dispatchKeyEvent", + "Input.dispatchMouseEvent", + "Input.dispatchTouchEvent", + "Input.emulateTouchFromMouseEvent", + "Input.imeSetComposition", + "Input.insertText", + "Input.synthesizePinchGesture", + "Input.synthesizeScrollGesture", + "Input.synthesizeTapGesture", + "Page.bringToFront", + "Page.captureScreenshot", + "Page.captureSnapshot", + "Page.close", + "Page.handleJavaScriptDialog", + "Page.navigate", + "Page.navigateToHistoryEntry", + "Page.printToPDF", + "Page.reload", + "Page.setWebLifecycleState", + "Page.startScreencast", + "Page.stopLoading", + "Page.stopScreencast", + "Target.activateTarget", + "Target.closeTarget", + "Target.createBrowserContext", + "Target.createTarget", + "Target.disposeBrowserContext", + "Target.openDevTools", +} + +type protocol struct { + Domains []struct { + Domain string `json:"domain"` + Commands []struct { + Name string `json:"name"` + Parameters []property `json:"parameters"` + } `json:"commands"` + Types []struct { + ID string `json:"id"` + Type string `json:"type"` + Properties []property `json:"properties"` + } `json:"types"` + } `json:"domains"` +} + +type property struct { + Name string `json:"name"` + Type string `json:"type"` + Ref string `json:"$ref"` + Optional bool `json:"optional"` + Items *property `json:"items"` +} + +// Snapshot is the checked-in subset: every argument of every supported +// command, including the fields of the object types they carry. +type Snapshot struct { + Comment string `json:"_comment"` + Commit string `json:"commit"` + Permalink string `json:"permalink"` + Commands map[string][]string `json:"commands"` +} + +func main() { + protocolPath := flag.String("protocol", "", "path to the pinned browser_protocol.json") + commit := flag.String("commit", "", "the devtools-protocol commit it came from") + out := flag.String("out", "lib/devtoolsproxy/testdata/cdp_protocol_pinned.json", "snapshot to rewrite") + flag.Parse() + if *protocolPath == "" || *commit == "" { + fmt.Fprintln(os.Stderr, "both -protocol and -commit are required") + os.Exit(2) + } + + raw, err := os.ReadFile(*protocolPath) + if err != nil { + fmt.Fprintln(os.Stderr, "read protocol:", err) + os.Exit(1) + } + var proto protocol + if err := json.Unmarshal(raw, &proto); err != nil { + fmt.Fprintln(os.Stderr, "parse protocol:", err) + os.Exit(1) + } + + // Index object types so an argument that carries one expands into its + // fields: a touch point's pressure is as much an argument as its x. + types := map[string][]property{} + for _, dom := range proto.Domains { + for _, t := range dom.Types { + if t.Type == "object" { + types[dom.Domain+"."+t.ID] = t.Properties + types[t.ID] = t.Properties + } + } + } + + wanted := map[string]bool{} + for _, m := range supportedMethods { + wanted[m] = true + } + + commands := map[string][]string{} + for _, dom := range proto.Domains { + for _, c := range dom.Commands { + method := dom.Domain + "." + c.Name + if !wanted[method] { + continue + } + args := []string{} + for _, p := range c.Parameters { + args = append(args, expand(p, "", types)...) + } + sort.Strings(args) + commands[method] = args + } + } + for _, m := range supportedMethods { + if _, ok := commands[m]; !ok { + fmt.Fprintf(os.Stderr, "supported method %s is not in the protocol at %s\n", m, *commit) + os.Exit(1) + } + } + + snapshot := Snapshot{ + Comment: "Generated by scripts/cdpmanifest from the pinned protocol. " + + "Every argument here needs a retained or redacted decision in cdp_arguments.yaml.", + Commit: *commit, + Permalink: "https://github.com/ChromeDevTools/devtools-protocol/blob/" + *commit + "/json/browser_protocol.json", + Commands: commands, + } + body, err := json.MarshalIndent(snapshot, "", " ") + if err != nil { + fmt.Fprintln(os.Stderr, "encode snapshot:", err) + os.Exit(1) + } + if err := os.WriteFile(*out, append(body, '\n'), 0o644); err != nil { + fmt.Fprintln(os.Stderr, "write snapshot:", err) + os.Exit(1) + } + fmt.Printf("wrote %s: %d commands\n", *out, len(commands)) +} + +// expand names an argument, and the fields of any object type it carries, as +// dotted paths: touchPoints[].radiusX, bounds.left, data.items[].mimeType. +// One level of nesting is enough for every type these commands use. +func expand(p property, prefix string, types map[string][]property) []string { + name := p.Name + if prefix != "" { + name = prefix + "." + name + } + + ref, isArray := p.Ref, false + if p.Type == "array" && p.Items != nil { + ref, isArray = p.Items.Ref, true + } + fields, isObject := types[ref] + if ref == "" || !isObject { + return []string{name} + } + if isArray { + name += "[]" + } + if strings.Count(name, ".") >= 2 { + // Deep enough: the leaf is named, and nothing these commands carry + // needs a third level. + return []string{name} + } + out := make([]string, 0, len(fields)) + for _, f := range fields { + out = append(out, expand(f, name, types)...) + } + if len(out) == 0 { + return []string{name} + } + return out +}