diff --git a/server/Makefile b/server/Makefile index b3f7f90e..303ea419 100644 --- a/server/Makefile +++ b/server/Makefile @@ -16,7 +16,7 @@ $(RECORDING_DIR): # 3. go mod tidy to pull deps oapi-generate: pnpm i -g @apiture/openapi-down-convert - openapi-down-convert --input openapi.yaml --output openapi-3.0.yaml + openapi-down-convert --input openapi.yaml --output openapi-3.0.yaml --allOf go tool oapi-codegen -config ./oapi-codegen.yaml ./openapi-3.0.yaml @echo "Fixing oapi-codegen issue https://github.com/oapi-codegen/oapi-codegen/issues/1764..." go run ./scripts/oapi/patch_sse_methods -file ./lib/oapi/oapi.go -expected-replacements 4 diff --git a/server/cmd/api/api/api.go b/server/cmd/api/api/api.go index d28fedd9..5d49f2bc 100644 --- a/server/cmd/api/api/api.go +++ b/server/cmd/api/api/api.go @@ -10,9 +10,9 @@ import ( "sync" "time" - "github.com/kernel/kernel-images/server/lib/capturesession" "github.com/kernel/kernel-images/server/lib/cdpmonitor" "github.com/kernel/kernel-images/server/lib/devtoolsproxy" + "github.com/kernel/kernel-images/server/lib/telemetry" "github.com/kernel/kernel-images/server/lib/events" "github.com/kernel/kernel-images/server/lib/logger" "github.com/kernel/kernel-images/server/lib/nekoclient" @@ -81,13 +81,13 @@ type ApiService struct { // when multiple CDP fast-path resizes fire in quick succession. xvfbResizeMu sync.Mutex - // CDP event pipeline and cdpMonitor. - eventStream *events.EventStream - captureSession *capturesession.CaptureSession - cdpMonitor cdpMonitorController - monitorMu sync.Mutex - lifecycleCtx context.Context - lifecycleCancel context.CancelFunc + // Telemetry event pipeline and CDP monitor. + eventStream *events.EventStream + telemetrySession *telemetry.TelemetrySession + cdpMonitor cdpMonitorController + monitorMu sync.Mutex + lifecycleCtx context.Context + lifecycleCancel context.CancelFunc } var _ oapi.StrictServerInterface = (*ApiService)(nil) @@ -98,8 +98,8 @@ func New( upstreamMgr *devtoolsproxy.UpstreamManager, stz scaletozero.PinnedController, nekoAuthClient *nekoclient.AuthClient, - captureSession *capturesession.CaptureSession, - eventStream *events.EventStream, + telemetrySession *telemetry.TelemetrySession, + eventStream *events.EventStream, displayNum int, ) (*ApiService, error) { switch { @@ -111,18 +111,18 @@ func New( return nil, fmt.Errorf("upstreamMgr cannot be nil") case nekoAuthClient == nil: return nil, fmt.Errorf("nekoAuthClient cannot be nil") - case captureSession == nil: - return nil, fmt.Errorf("captureSession cannot be nil") + case telemetrySession == nil: + return nil, fmt.Errorf("telemetrySession cannot be nil") case eventStream == nil: return nil, fmt.Errorf("eventStream cannot be nil") } - mon := cdpmonitor.New(upstreamMgr, captureSession.Publish, displayNum, slog.Default()) + mon := cdpmonitor.New(upstreamMgr, telemetrySession.Publish, displayNum, slog.Default()) ctx, cancel := context.WithCancel(context.Background()) return &ApiService{ - recordManager: recordManager, - factory: factory, + recordManager: recordManager, + factory: factory, defaultRecorderID: "default", watches: make(map[string]*fsWatch), procs: make(map[string]*processHandle), @@ -131,7 +131,7 @@ func New( nekoAuthClient: nekoAuthClient, policy: &policy.Policy{}, eventStream: eventStream, - captureSession: captureSession, + telemetrySession: telemetrySession, cdpMonitor: mon, lifecycleCtx: ctx, lifecycleCancel: cancel, @@ -357,7 +357,7 @@ func (s *ApiService) Shutdown(ctx context.Context) error { s.monitorMu.Lock() s.lifecycleCancel() s.cdpMonitor.Stop() - s.captureSession.Stop() + s.telemetrySession.Stop() s.monitorMu.Unlock() return s.recordManager.StopAll(ctx) } diff --git a/server/cmd/api/api/api_test.go b/server/cmd/api/api/api_test.go index 02835ab1..72afd164 100644 --- a/server/cmd/api/api/api_test.go +++ b/server/cmd/api/api/api_test.go @@ -11,8 +11,8 @@ import ( "log/slog" - "github.com/kernel/kernel-images/server/lib/capturesession" "github.com/kernel/kernel-images/server/lib/devtoolsproxy" + "github.com/kernel/kernel-images/server/lib/telemetry" "github.com/kernel/kernel-images/server/lib/events" "github.com/kernel/kernel-images/server/lib/nekoclient" oapi "github.com/kernel/kernel-images/server/lib/oapi" @@ -305,20 +305,20 @@ func newMockNekoClient(t *testing.T) *nekoclient.AuthClient { return client } -func newCaptureSession(t *testing.T) (*capturesession.CaptureSession, *events.EventStream) { +func newTelemetrySession(t *testing.T) (*telemetry.TelemetrySession, *events.EventStream) { t.Helper() es, err := events.NewEventStream(events.EventStreamConfig{RingCapacity: 64}) if err != nil { t.Fatal(err) } - return capturesession.NewCaptureSession(es), es + return telemetry.NewTelemetrySession(es), es } -// newSvc constructs an ApiService with a fresh capture session and event stream. +// newSvc constructs an ApiService with a fresh telemetry session and event stream. func newSvc(t *testing.T, mgr recorder.RecordManager) (*ApiService, error) { t.Helper() - cs, es := newCaptureSession(t) - return New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), cs, es, 0) + ts, es := newTelemetrySession(t) + return New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), ts, es, 0) } func TestApiService_PatchChromiumFlags(t *testing.T) { diff --git a/server/cmd/api/api/capture_session.go b/server/cmd/api/api/capture_session.go deleted file mode 100644 index b69ed84a..00000000 --- a/server/cmd/api/api/capture_session.go +++ /dev/null @@ -1,147 +0,0 @@ -package api - -import ( - "context" - "fmt" - "sort" - - "github.com/nrednav/cuid2" - oapi "github.com/kernel/kernel-images/server/lib/oapi" - - "github.com/kernel/kernel-images/server/lib/capturesession" - "github.com/kernel/kernel-images/server/lib/events" - "github.com/kernel/kernel-images/server/lib/logger" -) - -// StartCaptureSession handles POST /events/capture_session. -// Returns 409 if a session is already active. -func (s *ApiService) StartCaptureSession(ctx context.Context, req oapi.StartCaptureSessionRequestObject) (oapi.StartCaptureSessionResponseObject, error) { - s.monitorMu.Lock() - defer s.monitorMu.Unlock() - - if s.captureSession.ID() != "" { - return oapi.StartCaptureSession409JSONResponse{ConflictErrorJSONResponse: oapi.ConflictErrorJSONResponse{Message: "a capture session is already active"}}, nil - } - - cfg, err := captureConfigFrom(req.Body) - if err != nil { - return oapi.StartCaptureSession400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: err.Error()}}, nil - } - - id := cuid2.Generate() - s.captureSession.Start(id, cfg) - - if err := s.cdpMonitor.Start(s.lifecycleCtx); err != nil { - // Roll back: clear the session so a retry can succeed. - s.captureSession.Stop() - logger.FromContext(ctx).Error("failed to start capture monitor", "err", err) - return oapi.StartCaptureSession500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to start capture"}}, nil - } - - return oapi.StartCaptureSession201JSONResponse(s.buildSessionResponse()), nil -} - -// GetCaptureSession handles GET /events/capture_session. -func (s *ApiService) GetCaptureSession(_ context.Context, _ oapi.GetCaptureSessionRequestObject) (oapi.GetCaptureSessionResponseObject, error) { - s.monitorMu.Lock() - defer s.monitorMu.Unlock() - - if s.captureSession.ID() == "" { - return oapi.GetCaptureSession404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "no active capture session"}}, nil - } - return oapi.GetCaptureSession200JSONResponse(s.buildSessionResponse()), nil -} - -// UpdateCaptureSession handles PATCH /events/capture_session. -func (s *ApiService) UpdateCaptureSession(_ context.Context, req oapi.UpdateCaptureSessionRequestObject) (oapi.UpdateCaptureSessionResponseObject, error) { - s.monitorMu.Lock() - defer s.monitorMu.Unlock() - - if s.captureSession.ID() == "" { - return oapi.UpdateCaptureSession404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "no active capture session"}}, nil - } - - if req.Body != nil && req.Body.Config != nil { - cfg, err := captureConfigFromOAPI(req.Body.Config) - if err != nil { - return oapi.UpdateCaptureSession400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: err.Error()}}, nil - } - s.captureSession.UpdateConfig(cfg) - } - - return oapi.UpdateCaptureSession200JSONResponse(s.buildSessionResponse()), nil -} - -// StopCaptureSession handles DELETE /events/capture_session. -// Stops the capture session and clears it so a new one can be started. -func (s *ApiService) StopCaptureSession(_ context.Context, _ oapi.StopCaptureSessionRequestObject) (oapi.StopCaptureSessionResponseObject, error) { - s.monitorMu.Lock() - defer s.monitorMu.Unlock() - - if s.captureSession.ID() == "" { - return oapi.StopCaptureSession404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "no active capture session"}}, nil - } - - s.cdpMonitor.Stop() - // Snapshot the final state before clearing the session ID so buildSessionResponse - // can still parse it. Force the status to Stopped because cdpMonitor.Stop may - // tear down asynchronously, leaving IsRunning briefly true. - resp := s.buildSessionResponse() - resp.Status = oapi.CaptureSessionStatusStopped - s.captureSession.Stop() - - return oapi.StopCaptureSession200JSONResponse(resp), nil -} - -// buildSessionResponse constructs the CaptureSession response from current state. -func (s *ApiService) buildSessionResponse() oapi.CaptureSession { - cfg := s.captureSession.Config() - - cats := make([]oapi.CaptureConfigCategories, len(cfg.Categories)) - for i, c := range cfg.Categories { - cats[i] = oapi.CaptureConfigCategories(c) - } - sort.Slice(cats, func(i, j int) bool { return cats[i] < cats[j] }) - - status := oapi.CaptureSessionStatusStopped - if s.cdpMonitor.IsRunning() { - status = oapi.CaptureSessionStatusRunning - } - - return oapi.CaptureSession{ - Id: s.captureSession.ID(), - Status: status, - Config: oapi.CaptureConfig{ - Categories: &cats, - }, - Seq: int64(s.captureSession.Seq()), - CreatedAt: s.captureSession.CreatedAt(), - } -} - -// captureConfigFrom converts the optional StartCaptureSessionRequest body -// into a capturesession.CaptureConfig. -func captureConfigFrom(body *oapi.StartCaptureSessionRequest) (capturesession.CaptureConfig, error) { - if body == nil { - return capturesession.CaptureConfig{}, nil - } - return captureConfigFromOAPI(body.Config) -} - -// captureConfigFromOAPI converts an oapi.CaptureConfig to capturesession.CaptureConfig. -func captureConfigFromOAPI(cfg *oapi.CaptureConfig) (capturesession.CaptureConfig, error) { - if cfg == nil || cfg.Categories == nil { - return capturesession.CaptureConfig{}, nil - } - out := capturesession.CaptureConfig{ - Categories: make([]events.EventCategory, 0, len(*cfg.Categories)), - } - for _, c := range *cfg.Categories { - cat := events.EventCategory(c) - if !events.ValidCategory(cat) { - return capturesession.CaptureConfig{}, fmt.Errorf("unknown category: %q", c) - } - out.Categories = append(out.Categories, cat) - } - return out, nil -} diff --git a/server/cmd/api/api/capture_session_test.go b/server/cmd/api/api/capture_session_test.go deleted file mode 100644 index 032cbb77..00000000 --- a/server/cmd/api/api/capture_session_test.go +++ /dev/null @@ -1,260 +0,0 @@ -package api - -import ( - "context" - "testing" - - 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/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestCaptureConfigFrom(t *testing.T) { - t.Run("nil body returns defaults", func(t *testing.T) { - cfg, err := captureConfigFrom(nil) - require.NoError(t, err) - assert.Empty(t, cfg.Categories) - }) - - t.Run("valid categories", func(t *testing.T) { - cats := []oapi.CaptureConfigCategories{oapi.CaptureConfigCategoriesConsole, oapi.CaptureConfigCategoriesNetwork} - body := &oapi.StartCaptureSessionRequest{ - Config: &oapi.CaptureConfig{Categories: &cats}, - } - cfg, err := captureConfigFrom(body) - require.NoError(t, err) - assert.Len(t, cfg.Categories, 2) - }) - - t.Run("invalid category returns error", func(t *testing.T) { - cats := []oapi.CaptureConfigCategories{"bogus"} - body := &oapi.StartCaptureSessionRequest{ - Config: &oapi.CaptureConfig{Categories: &cats}, - } - _, err := captureConfigFrom(body) - require.Error(t, err) - assert.Contains(t, err.Error(), "unknown category") - }) - - t.Run("nil config returns defaults", func(t *testing.T) { - body := &oapi.StartCaptureSessionRequest{} - cfg, err := captureConfigFrom(body) - require.NoError(t, err) - assert.Empty(t, cfg.Categories) - }) -} - -func TestStartCaptureSession(t *testing.T) { - ctx := context.Background() - - t.Run("success with no body", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - resp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - r201, ok := resp.(oapi.StartCaptureSession201JSONResponse) - require.True(t, ok) - assert.NotEmpty(t, r201.Id) - assert.NotZero(t, r201.CreatedAt) - // Status depends on cdpMonitor.IsRunning(); the stub monitor doesn't - // track state, so we only verify the field is populated. - assert.NotEmpty(t, r201.Status) - }) - - t.Run("success with config", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - cats := []oapi.CaptureConfigCategories{oapi.CaptureConfigCategoriesConsole} - resp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{ - Body: &oapi.StartCaptureSessionRequest{ - Config: &oapi.CaptureConfig{Categories: &cats}, - }, - }) - require.NoError(t, err) - r201, ok := resp.(oapi.StartCaptureSession201JSONResponse) - require.True(t, ok) - assert.NotEmpty(t, r201.Id) - }) - - t.Run("invalid category returns 400", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - cats := []oapi.CaptureConfigCategories{"badcat"} - resp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{ - Body: &oapi.StartCaptureSessionRequest{ - Config: &oapi.CaptureConfig{Categories: &cats}, - }, - }) - require.NoError(t, err) - assert.IsType(t, oapi.StartCaptureSession400JSONResponse{}, resp) - }) - - t.Run("duplicate returns 409", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - _, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - - resp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - assert.IsType(t, oapi.StartCaptureSession409JSONResponse{}, resp) - }) -} - -func TestGetCaptureSession(t *testing.T) { - ctx := context.Background() - - t.Run("no session returns 404", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - resp, err := svc.GetCaptureSession(ctx, oapi.GetCaptureSessionRequestObject{}) - require.NoError(t, err) - assert.IsType(t, oapi.GetCaptureSession404JSONResponse{}, resp) - }) - - t.Run("active session returns 200", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - startResp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - started := startResp.(oapi.StartCaptureSession201JSONResponse) - - resp, err := svc.GetCaptureSession(ctx, oapi.GetCaptureSessionRequestObject{}) - require.NoError(t, err) - r200, ok := resp.(oapi.GetCaptureSession200JSONResponse) - require.True(t, ok) - assert.Equal(t, started.Id, r200.Id) - assert.Equal(t, started.CreatedAt, r200.CreatedAt) - }) -} - -func TestUpdateCaptureSession(t *testing.T) { - ctx := context.Background() - - t.Run("no session returns 404", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - resp, err := svc.UpdateCaptureSession(ctx, oapi.UpdateCaptureSessionRequestObject{ - Body: &oapi.UpdateCaptureSessionRequest{}, - }) - require.NoError(t, err) - assert.IsType(t, oapi.UpdateCaptureSession404JSONResponse{}, resp) - }) - - t.Run("update config", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - _, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - - cats := []oapi.CaptureConfigCategories{oapi.CaptureConfigCategoriesConsole} - resp, err := svc.UpdateCaptureSession(ctx, oapi.UpdateCaptureSessionRequestObject{ - Body: &oapi.UpdateCaptureSessionRequest{ - Config: &oapi.CaptureConfig{Categories: &cats}, - }, - }) - require.NoError(t, err) - r200, ok := resp.(oapi.UpdateCaptureSession200JSONResponse) - require.True(t, ok) - require.NotNil(t, r200.Config.Categories) - assert.Len(t, *r200.Config.Categories, 1) - assert.Equal(t, oapi.CaptureConfigCategoriesConsole, (*r200.Config.Categories)[0]) - }) - - t.Run("empty body is no-op", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - startResp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - started := startResp.(oapi.StartCaptureSession201JSONResponse) - - resp, err := svc.UpdateCaptureSession(ctx, oapi.UpdateCaptureSessionRequestObject{ - Body: &oapi.UpdateCaptureSessionRequest{}, - }) - require.NoError(t, err) - r200, ok := resp.(oapi.UpdateCaptureSession200JSONResponse) - require.True(t, ok) - assert.Equal(t, started.Id, r200.Id) - }) - - t.Run("invalid category returns 400", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - _, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - - cats := []oapi.CaptureConfigCategories{"invalid"} - resp, err := svc.UpdateCaptureSession(ctx, oapi.UpdateCaptureSessionRequestObject{ - Body: &oapi.UpdateCaptureSessionRequest{ - Config: &oapi.CaptureConfig{Categories: &cats}, - }, - }) - require.NoError(t, err) - assert.IsType(t, oapi.UpdateCaptureSession400JSONResponse{}, resp) - }) -} - -func TestStopCaptureSession(t *testing.T) { - ctx := context.Background() - - t.Run("no session returns 404", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - resp, err := svc.StopCaptureSession(ctx, oapi.StopCaptureSessionRequestObject{}) - require.NoError(t, err) - assert.IsType(t, oapi.StopCaptureSession404JSONResponse{}, resp) - }) - - t.Run("stop active session", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - startResp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - started := startResp.(oapi.StartCaptureSession201JSONResponse) - - resp, err := svc.StopCaptureSession(ctx, oapi.StopCaptureSessionRequestObject{}) - require.NoError(t, err) - r200, ok := resp.(oapi.StopCaptureSession200JSONResponse) - require.True(t, ok) - assert.Equal(t, started.Id, r200.Id) - assert.Equal(t, oapi.CaptureSessionStatusStopped, r200.Status) - }) - - t.Run("start succeeds after stop", func(t *testing.T) { - svc := newTestService(t, newMockRecordManager()) - startResp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - started := startResp.(oapi.StartCaptureSession201JSONResponse) - - _, err = svc.StopCaptureSession(ctx, oapi.StopCaptureSessionRequestObject{}) - require.NoError(t, err) - - resp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) - require.NoError(t, err) - r201, ok := resp.(oapi.StartCaptureSession201JSONResponse) - require.True(t, ok) - assert.NotEqual(t, started.Id, r201.Id) - }) -} - -// newMockRecordManager returns a minimal record manager for tests that don't -// exercise recording. -func newMockRecordManager() *mockRecordManager { - return &mockRecordManager{} -} - -type mockRecordManager struct{} - -func (m *mockRecordManager) RegisterRecorder(_ context.Context, _ recorder.Recorder) error { return nil } -func (m *mockRecordManager) DeregisterRecorder(_ context.Context, _ recorder.Recorder) error { - return nil -} -func (m *mockRecordManager) GetRecorder(_ string) (recorder.Recorder, bool) { return nil, false } -func (m *mockRecordManager) ListActiveRecorders(_ context.Context) []recorder.Recorder { return nil } -func (m *mockRecordManager) StopAll(_ context.Context) error { return nil } - -// newTestService builds an ApiService with minimal dependencies for capture session tests. -func newTestService(t *testing.T, mgr recorder.RecordManager) *ApiService { - t.Helper() - cs, es := newCaptureSession(t) - svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), cs, es, 0) - require.NoError(t, err) - svc.cdpMonitor = &stubCdpMonitor{} - return svc -} - -type stubCdpMonitor struct{} - -func (s *stubCdpMonitor) Start(_ context.Context) error { return nil } -func (s *stubCdpMonitor) Stop() {} -func (s *stubCdpMonitor) IsRunning() bool { return false } diff --git a/server/cmd/api/api/display_test.go b/server/cmd/api/api/display_test.go index 1ecefa3c..3a9d919c 100644 --- a/server/cmd/api/api/display_test.go +++ b/server/cmd/api/api/display_test.go @@ -34,8 +34,8 @@ func testFFmpegFactory(t *testing.T, tempDir string) recorder.FFmpegRecorderFact func newTestServiceWithFactory(t *testing.T, mgr recorder.RecordManager, factory recorder.FFmpegRecorderFactory) *ApiService { t.Helper() - cs, es := newCaptureSession(t) - svc, err := New(mgr, factory, newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), cs, es, 0) + ts, es := newTelemetrySession(t) + svc, err := New(mgr, factory, newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), ts, es, 0) require.NoError(t, err) return svc } diff --git a/server/cmd/api/api/events.go b/server/cmd/api/api/events.go index c72ba756..28dd0355 100644 --- a/server/cmd/api/api/events.go +++ b/server/cmd/api/api/events.go @@ -15,60 +15,54 @@ import ( oapi "github.com/kernel/kernel-images/server/lib/oapi" ) -// PublishEvent handles POST /events/publish. -// Injects a caller-supplied event into the event bus. Returns 400 if the event +// PublishTelemetryEvent handles POST /telemetry/events. +// Injects a caller-supplied event into the telemetry stream. Returns 400 if the event // fails validation. -func (s *ApiService) PublishEvent(_ context.Context, req oapi.PublishEventRequestObject) (oapi.PublishEventResponseObject, error) { +func (s *ApiService) PublishTelemetryEvent(_ context.Context, req oapi.PublishTelemetryEventRequestObject) (oapi.PublishTelemetryEventResponseObject, error) { body := req.Body if body == nil || body.Type == "" { - return oapi.PublishEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "type is required"}}, nil + return oapi.PublishTelemetryEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "type is required"}}, nil } ev := events.Event{Type: body.Type} ev.Ts = time.Now().UnixMicro() if body.Category != nil { - cat := events.EventCategory(*body.Category) - if !events.ValidCategory(cat) { - return oapi.PublishEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid category"}}, nil + cat := oapi.TelemetryEventCategory(*body.Category) + if !cat.Valid() { + return oapi.PublishTelemetryEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid category"}}, nil } ev.Category = cat } else { - ev.Category = events.CategorySystem + ev.Category = events.System } if body.Source != nil { - if body.Source.Kind != nil { - if *body.Source.Kind == oapi.KernelApi { - return oapi.PublishEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "source.kind kernel_api is reserved for server-generated events"}}, nil - } - ev.Source.Kind = events.SourceKind(*body.Source.Kind) - } - if body.Source.Event != nil { - ev.Source.Event = *body.Source.Event - } - if body.Source.Metadata != nil { - ev.Source.Metadata = *body.Source.Metadata + if body.Source.Kind == oapi.KernelApi { + return oapi.PublishTelemetryEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "source.kind kernel_api is reserved for server-generated events"}}, nil } + ev.Source.Kind = oapi.BrowserEventSourceKind(body.Source.Kind) + ev.Source.Event = body.Source.Event + ev.Source.Metadata = body.Source.Metadata } if body.Data != nil { // re-marshal body.Data to normalize it into a canonical RawMessage byte slice. data, err := json.Marshal(body.Data) if err != nil { - return oapi.PublishEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid data"}}, nil + return oapi.PublishTelemetryEvent400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid data"}}, nil } ev.Data = json.RawMessage(data) } env := s.eventStream.Publish(events.Envelope{Event: ev}) - return publishEventOKResponse{env}, nil + return publishTelemetryEventOKResponse{env}, nil } -// StreamEvents handles GET /events/stream. -// Opens an SSE stream of envelopes from the event bus ring buffer. +// StreamTelemetryEvents handles GET /telemetry/stream. +// Opens an SSE stream of telemetry event envelopes from the telemetry stream ring buffer. // Supports reconnection via the Last-Event-ID header. Emits a keepalive comment // frame every 15 s when no event arrives. -func (s *ApiService) StreamEvents(ctx context.Context, req oapi.StreamEventsRequestObject) (oapi.StreamEventsResponseObject, error) { +func (s *ApiService) StreamTelemetryEvents(ctx context.Context, req oapi.StreamTelemetryEventsRequestObject) (oapi.StreamTelemetryEventsResponseObject, error) { // Default to the current seq so fresh connections only see new events. // Seqs are process-monotonic; a Last-Event-ID from any prior session resumes correctly. afterSeq := s.eventStream.Seq() @@ -114,15 +108,15 @@ func (s *ApiService) StreamEvents(ctx context.Context, req oapi.StreamEventsRequ } }() - headers := oapi.StreamEvents200ResponseHeaders{XSSEContentType: "application/json"} - return oapi.StreamEvents200TexteventStreamResponse{Body: pr, Headers: headers}, nil + headers := oapi.StreamTelemetryEvents200ResponseHeaders{XSSEContentType: "application/json"} + return oapi.StreamTelemetryEvents200TexteventStreamResponse{Body: pr, Headers: headers}, nil } -// publishEventOKResponse serializes events.Envelope directly so the response +// publishTelemetryEventOKResponse serializes events.Envelope directly so the response // is identical in shape to the SSE stream frames. -type publishEventOKResponse struct{ env events.Envelope } +type publishTelemetryEventOKResponse struct{ env events.Envelope } -func (r publishEventOKResponse) VisitPublishEventResponse(w http.ResponseWriter) error { +func (r publishTelemetryEventOKResponse) VisitPublishTelemetryEventResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) return json.NewEncoder(w).Encode(r.env) diff --git a/server/cmd/api/api/events_test.go b/server/cmd/api/api/events_test.go index b47cdffc..24129844 100644 --- a/server/cmd/api/api/events_test.go +++ b/server/cmd/api/api/events_test.go @@ -19,17 +19,17 @@ func TestEventLifecycle(t *testing.T) { ctx := context.Background() svc := newTestService(t, newMockRecordManager()) - // Start a capture session. - startResp, err := svc.StartCaptureSession(ctx, oapi.StartCaptureSessionRequestObject{}) + // Start a telemetry session. + startResp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) require.NoError(t, err) - require.IsType(t, oapi.StartCaptureSession201JSONResponse{}, startResp) + require.IsType(t, oapi.PutTelemetry201JSONResponse{}, startResp) // Open an SSE stream (5s budget covers the three 2s selects below). streamCtx, streamCancel := context.WithTimeout(ctx, 5*time.Second) defer streamCancel() - streamResp, err := svc.StreamEvents(streamCtx, oapi.StreamEventsRequestObject{}) + streamResp, err := svc.StreamTelemetryEvents(streamCtx, oapi.StreamTelemetryEventsRequestObject{}) require.NoError(t, err) - r200, ok := streamResp.(oapi.StreamEvents200TexteventStreamResponse) + r200, ok := streamResp.(oapi.StreamTelemetryEvents200TexteventStreamResponse) require.True(t, ok) // Drain SSE frames into a channel. @@ -55,11 +55,11 @@ func TestEventLifecycle(t *testing.T) { }() // Publish an event. - resp, err := svc.PublishEvent(ctx, oapi.PublishEventRequestObject{ + resp, err := svc.PublishTelemetryEvent(ctx, oapi.PublishTelemetryEventRequestObject{ Body: &oapi.PublishEventRequest{Type: "test.event"}, }) require.NoError(t, err) - r200pub, ok := resp.(publishEventOKResponse) + r200pub, ok := resp.(publishTelemetryEventOKResponse) require.True(t, ok, "expected 200 response") assert.Equal(t, "test.event", r200pub.env.Event.Type) assert.Greater(t, r200pub.env.Seq, uint64(0)) @@ -73,8 +73,18 @@ func TestEventLifecycle(t *testing.T) { t.Fatal("timed out waiting for test.event") } - // Stop the session. - stopResp, err := svc.StopCaptureSession(ctx, oapi.StopCaptureSessionRequestObject{}) + // Stop telemetry by disabling all categories. + f := false + stopResp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Page: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Interaction: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }, + }) require.NoError(t, err) - assert.IsType(t, oapi.StopCaptureSession200JSONResponse{}, stopResp) + assert.IsType(t, oapi.PatchTelemetry200JSONResponse{}, stopResp) } diff --git a/server/cmd/api/api/telemetry.go b/server/cmd/api/api/telemetry.go new file mode 100644 index 00000000..a4b4cfe4 --- /dev/null +++ b/server/cmd/api/api/telemetry.go @@ -0,0 +1,244 @@ +package api + +import ( + "context" + + "github.com/nrednav/cuid2" + oapi "github.com/kernel/kernel-images/server/lib/oapi" + + "github.com/kernel/kernel-images/server/lib/events" + "github.com/kernel/kernel-images/server/lib/logger" + "github.com/kernel/kernel-images/server/lib/telemetry" +) + +// GetTelemetry handles GET /telemetry. +// Returns the current telemetry configuration. Returns 404 if telemetry is not configured. +func (s *ApiService) GetTelemetry(_ context.Context, _ oapi.GetTelemetryRequestObject) (oapi.GetTelemetryResponseObject, error) { + s.monitorMu.Lock() + defer s.monitorMu.Unlock() + + if !s.telemetrySession.Active() { + return oapi.GetTelemetry404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "telemetry is not configured"}}, nil + } + return oapi.GetTelemetry200JSONResponse(s.buildTelemetryResponse()), nil +} + +// PutTelemetry handles PUT /telemetry. +// Sets the telemetry configuration. Returns 201 if not previously configured, 200 if it was. +// Setting all four categories to enabled:false clears the configuration (200). +func (s *ApiService) PutTelemetry(ctx context.Context, req oapi.PutTelemetryRequestObject) (oapi.PutTelemetryResponseObject, error) { + s.monitorMu.Lock() + defer s.monitorMu.Unlock() + + cfg, allDisabled, err := telemetryConfigFromOAPI(req.Body) + if err != nil { + return oapi.PutTelemetry400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: err.Error()}}, nil + } + + wasActive := s.telemetrySession.Active() + + if allDisabled { + if !wasActive { + // Already cleared; all-disabled is idempotent. + return oapi.PutTelemetry200JSONResponse(oapi.TelemetryState{Config: disabledConfig(), Seq: int64(s.telemetrySession.Seq())}), nil + } + // All categories disabled: clear the configuration. + s.cdpMonitor.Stop() + s.telemetrySession.Stop() + return oapi.PutTelemetry200JSONResponse(oapi.TelemetryState{Config: disabledConfig(), Seq: int64(s.telemetrySession.Seq())}), nil + } + + if wasActive { + // Replace config on the running session. + s.telemetrySession.UpdateConfig(cfg) + return oapi.PutTelemetry200JSONResponse(s.buildTelemetryResponse()), nil + } + + // Start a new telemetry session. + id := cuid2.Generate() + s.telemetrySession.Start(id, cfg) + + if err := s.cdpMonitor.Start(s.lifecycleCtx); err != nil { + // Roll back: clear the session so a retry can succeed. + s.telemetrySession.Stop() + logger.FromContext(ctx).Error("failed to start telemetry monitor", "err", err) + return oapi.PutTelemetry500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to start telemetry"}}, nil + } + + return oapi.PutTelemetry201JSONResponse(s.buildTelemetryResponse()), nil +} + +// PatchTelemetry handles PATCH /telemetry. +// Partially updates the telemetry configuration. Returns 404 if not configured. +// Setting all four categories to enabled:false clears the configuration (200). +func (s *ApiService) PatchTelemetry(_ context.Context, req oapi.PatchTelemetryRequestObject) (oapi.PatchTelemetryResponseObject, error) { + s.monitorMu.Lock() + defer s.monitorMu.Unlock() + + if !s.telemetrySession.Active() { + return oapi.PatchTelemetry404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "telemetry is not configured"}}, nil + } + + if req.Body != nil && req.Body.Browser != nil { + // PATCH merges: only categories explicitly set in the request are updated; + // omitted categories retain their current enabled/disabled state. + current := s.telemetrySession.Config() + cfg, allDisabled := mergeTelemetryConfig(current, req.Body.Browser) + if allDisabled { + // All categories disabled: clear the configuration. + s.cdpMonitor.Stop() + s.telemetrySession.Stop() + return oapi.PatchTelemetry200JSONResponse(oapi.TelemetryState{Config: disabledConfig(), Seq: int64(s.telemetrySession.Seq())}), nil + } + s.telemetrySession.UpdateConfig(cfg) + } + + return oapi.PatchTelemetry200JSONResponse(s.buildTelemetryResponse()), nil +} + +// 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()), + } + if appliedAt := s.telemetrySession.AppliedAt(); !appliedAt.IsZero() { + resp.AppliedAt = &appliedAt + } + return resp +} + +// telemetryConfigFromOAPI converts an *oapi.BrowserTelemetryConfig to a telemetry.TelemetryConfig. +// Returns the config, a boolean indicating whether all user-facing categories are explicitly +// disabled (stop signal), and any validation error. +func telemetryConfigFromOAPI(cfg *oapi.BrowserTelemetryConfig) (telemetry.TelemetryConfig, bool, error) { + if cfg == nil || cfg.Browser == nil { + // No config provided: capture all categories. + return telemetry.TelemetryConfig{}, false, nil + } + + b := cfg.Browser + // A nil or omitted Enabled field defaults to true (capture the category). + isEnabled := func(c *oapi.BrowserTelemetryCategoryConfig) bool { + return c == nil || c.Enabled == nil || *c.Enabled + } + + consoleOn := isEnabled(b.Console) + networkOn := isEnabled(b.Network) + pageOn := isEnabled(b.Page) + interactionOn := isEnabled(b.Interaction) + + allDisabled := !consoleOn && !networkOn && !pageOn && !interactionOn + if allDisabled { + return telemetry.TelemetryConfig{}, true, nil + } + + cats := make([]oapi.TelemetryEventCategory, 0, 5) + if consoleOn { + cats = append(cats, events.Console) + } + if networkOn { + cats = append(cats, events.Network) + } + if pageOn { + cats = append(cats, events.Page) + } + if interactionOn { + cats = append(cats, events.Interaction) + } + // CategorySystem is always appended by TelemetrySession.Start/UpdateConfig; + // no need to include it here. + return telemetry.TelemetryConfig{Categories: cats}, false, nil +} + +// mergeTelemetryConfig applies patch overrides onto current, returning the merged config and +// whether all user-facing categories ended up disabled (stop signal). Only categories with an +// explicit Enabled field in patch are changed; omitted categories keep their current state. +func mergeTelemetryConfig(current telemetry.TelemetryConfig, patch *oapi.BrowserTelemetryCategoriesConfig) (telemetry.TelemetryConfig, bool) { + active := make(map[oapi.TelemetryEventCategory]struct{}, len(current.Categories)) + for _, c := range current.Categories { + if c != events.System { // system is managed internally by TelemetrySession + active[c] = struct{}{} + } + } + + override := func(cat oapi.TelemetryEventCategory, field *oapi.BrowserTelemetryCategoryConfig) { + if field == nil || field.Enabled == nil { + return // not mentioned in patch — keep current state + } + if *field.Enabled { + active[cat] = struct{}{} + } else { + delete(active, cat) + } + } + + override(events.Console, patch.Console) + override(events.Network, patch.Network) + override(events.Page, patch.Page) + override(events.Interaction, patch.Interaction) + + // CategorySystem is managed internally by TelemetrySession; exclude from the + // user-facing allDisabled check. + userCats := []oapi.TelemetryEventCategory{ + events.Console, + events.Network, + events.Page, + events.Interaction, + } + allDisabled := true + for _, c := range userCats { + if _, ok := active[c]; ok { + allDisabled = false + break + } + } + if allDisabled { + return telemetry.TelemetryConfig{}, true + } + + cats := make([]oapi.TelemetryEventCategory, 0, len(active)) + for c := range active { + cats = append(cats, c) + } + return telemetry.TelemetryConfig{Categories: cats}, false +} + +// disabledConfig returns a BrowserTelemetryConfig with all four user-facing categories explicitly disabled. +func disabledConfig() oapi.BrowserTelemetryConfig { + f := false + cat := &oapi.BrowserTelemetryCategoryConfig{Enabled: &f} + return oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: cat, + Network: cat, + Page: cat, + Interaction: cat, + }, + } +} + +// telemetryConfigToOAPI converts a telemetry.TelemetryConfig to an oapi.BrowserTelemetryConfig +// suitable for API responses. +func telemetryConfigToOAPI(cfg telemetry.TelemetryConfig) oapi.BrowserTelemetryConfig { + // Build a set of active categories for O(1) lookup. + active := make(map[oapi.TelemetryEventCategory]struct{}, len(cfg.Categories)) + for _, c := range cfg.Categories { + active[c] = struct{}{} + } + + enabled := func(cat oapi.TelemetryEventCategory) *oapi.BrowserTelemetryCategoryConfig { + _, on := active[cat] + return &oapi.BrowserTelemetryCategoryConfig{Enabled: &on} + } + + return oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: enabled(events.Console), + Network: enabled(events.Network), + Page: enabled(events.Page), + Interaction: enabled(events.Interaction), + }, + } +} + diff --git a/server/cmd/api/api/telemetry_test.go b/server/cmd/api/api/telemetry_test.go new file mode 100644 index 00000000..91df62e9 --- /dev/null +++ b/server/cmd/api/api/telemetry_test.go @@ -0,0 +1,300 @@ +package api + +import ( + "context" + "testing" + + "github.com/kernel/kernel-images/server/lib/events" + 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/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTelemetryConfigFromOAPI(t *testing.T) { + t.Run("nil body returns defaults (all categories)", func(t *testing.T) { + cfg, allDisabled, err := telemetryConfigFromOAPI(nil) + require.NoError(t, err) + assert.False(t, allDisabled) + assert.Empty(t, cfg.Categories) + }) + + t.Run("nil browser key returns defaults", func(t *testing.T) { + cfg, allDisabled, err := telemetryConfigFromOAPI(&oapi.BrowserTelemetryConfig{}) + require.NoError(t, err) + assert.False(t, allDisabled) + assert.Empty(t, cfg.Categories) + }) + + t.Run("omitted enabled defaults to true", func(t *testing.T) { + cfg, allDisabled, err := telemetryConfigFromOAPI(&oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{}, // Enabled is nil → defaults to true + }, + }) + require.NoError(t, err) + assert.False(t, allDisabled) + assert.Contains(t, cfg.Categories, events.Console) + }) + + t.Run("all false returns allDisabled=true", func(t *testing.T) { + f := false + _, allDisabled, err := telemetryConfigFromOAPI(&oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Page: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Interaction: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }) + require.NoError(t, err) + assert.True(t, allDisabled) + }) + + t.Run("mixed enabled flags", func(t *testing.T) { + tr, f := true, false + cfg, allDisabled, err := telemetryConfigFromOAPI(&oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}, + Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }) + require.NoError(t, err) + assert.False(t, allDisabled) + assert.Len(t, cfg.Categories, 3) // console + page + interaction (network=false, others default true) + }) +} + +func TestPutTelemetry(t *testing.T) { + ctx := context.Background() + + t.Run("creates session with no body (201)", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + r201, ok := resp.(oapi.PutTelemetry201JSONResponse) + require.True(t, ok, "expected 201, got %T", resp) + require.NotNil(t, r201.Config.Browser) + require.NotNil(t, r201.AppliedAt) + assert.False(t, r201.AppliedAt.IsZero()) + }) + + t.Run("creates session with config (201)", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + tr := true + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}, + }, + }, + }) + require.NoError(t, err) + _, ok := resp.(oapi.PutTelemetry201JSONResponse) + require.True(t, ok, "expected 201, got %T", resp) + }) + + t.Run("replaces config on active session (200)", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + + tr := true + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}, + }, + }, + }) + require.NoError(t, err) + _, ok := resp.(oapi.PutTelemetry200JSONResponse) + assert.True(t, ok, "expected 200 on replace, got %T", resp) + }) + + t.Run("all-false clears active configuration (200, all-disabled config)", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + + f := false + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Page: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Interaction: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }, + }) + require.NoError(t, err) + r200, ok := resp.(oapi.PutTelemetry200JSONResponse) + require.True(t, ok, "expected 200, got %T", resp) + require.NotNil(t, r200.Config.Browser) + require.NotNil(t, r200.Config.Browser.Console) + assert.False(t, *r200.Config.Browser.Console.Enabled) + assert.False(t, *r200.Config.Browser.Network.Enabled) + assert.False(t, *r200.Config.Browser.Page.Enabled) + assert.False(t, *r200.Config.Browser.Interaction.Enabled) + assert.Nil(t, r200.AppliedAt, "applied_at must be omitted when telemetry is unconfigured") + }) +} + +func TestGetTelemetry(t *testing.T) { + ctx := context.Background() + + t.Run("no session returns 404", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + resp, err := svc.GetTelemetry(ctx, oapi.GetTelemetryRequestObject{}) + require.NoError(t, err) + assert.IsType(t, oapi.GetTelemetry404JSONResponse{}, resp) + }) + + t.Run("active session returns 200", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + startResp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + started := startResp.(oapi.PutTelemetry201JSONResponse) + + resp, err := svc.GetTelemetry(ctx, oapi.GetTelemetryRequestObject{}) + require.NoError(t, err) + r200, ok := resp.(oapi.GetTelemetry200JSONResponse) + require.True(t, ok) + assert.Equal(t, started.Config, r200.Config) + }) +} + +func TestPatchTelemetry(t *testing.T) { + ctx := context.Background() + + t.Run("no session returns 404", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + resp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{}, + }) + require.NoError(t, err) + assert.IsType(t, oapi.PatchTelemetry404JSONResponse{}, resp) + }) + + t.Run("update config", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + + tr, f := true, false + resp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &tr}, + Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Page: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Interaction: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }, + }) + require.NoError(t, err) + r200, ok := resp.(oapi.PatchTelemetry200JSONResponse) + require.True(t, ok) + require.NotNil(t, r200.Config.Browser) + require.NotNil(t, r200.Config.Browser.Console) + assert.True(t, *r200.Config.Browser.Console.Enabled) + }) + + t.Run("nil body is no-op", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + startResp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + started := startResp.(oapi.PutTelemetry201JSONResponse) + + resp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{}) + require.NoError(t, err) + r200, ok := resp.(oapi.PatchTelemetry200JSONResponse) + require.True(t, ok) + assert.Equal(t, started.Config, r200.Config) + }) + + t.Run("all-false clears configuration", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + + f := false + resp, err := svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Page: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Interaction: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }, + }) + require.NoError(t, err) + r200, ok := resp.(oapi.PatchTelemetry200JSONResponse) + require.True(t, ok, "expected 200, got %T", resp) + require.NotNil(t, r200.Config.Browser) + require.NotNil(t, r200.Config.Browser.Console) + assert.False(t, *r200.Config.Browser.Console.Enabled) + assert.False(t, *r200.Config.Browser.Network.Enabled) + assert.False(t, *r200.Config.Browser.Page.Enabled) + assert.False(t, *r200.Config.Browser.Interaction.Enabled) + }) + + t.Run("put returns 201 after patch clears configuration", func(t *testing.T) { + svc := newTestService(t, newMockRecordManager()) + _, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + + f := false + _, err = svc.PatchTelemetry(ctx, oapi.PatchTelemetryRequestObject{ + Body: &oapi.BrowserTelemetryConfig{ + Browser: &oapi.BrowserTelemetryCategoriesConfig{ + Console: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Network: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Page: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Interaction: &oapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }, + }) + require.NoError(t, err) + + resp, err := svc.PutTelemetry(ctx, oapi.PutTelemetryRequestObject{}) + require.NoError(t, err) + _, ok := resp.(oapi.PutTelemetry201JSONResponse) + assert.True(t, ok, "expected 201 after clear, got %T", resp) + }) +} + +// newMockRecordManager returns a minimal record manager for tests that don't +// exercise recording. +func newMockRecordManager() *mockRecordManager { + return &mockRecordManager{} +} + +type mockRecordManager struct{} + +func (m *mockRecordManager) RegisterRecorder(_ context.Context, _ recorder.Recorder) error { return nil } +func (m *mockRecordManager) DeregisterRecorder(_ context.Context, _ recorder.Recorder) error { + return nil +} +func (m *mockRecordManager) GetRecorder(_ string) (recorder.Recorder, bool) { return nil, false } +func (m *mockRecordManager) ListActiveRecorders(_ context.Context) []recorder.Recorder { return nil } +func (m *mockRecordManager) StopAll(_ context.Context) error { return nil } + +// newTestService builds an ApiService with minimal dependencies for telemetry tests. +func newTestService(t *testing.T, mgr recorder.RecordManager) *ApiService { + t.Helper() + ts, es := newTelemetrySession(t) + svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), ts, es, 0) + require.NoError(t, err) + svc.cdpMonitor = &stubCdpMonitor{} + return svc +} + +type stubCdpMonitor struct{} + +func (s *stubCdpMonitor) Start(_ context.Context) error { return nil } +func (s *stubCdpMonitor) Stop() {} +func (s *stubCdpMonitor) IsRunning() bool { return false } diff --git a/server/cmd/api/main.go b/server/cmd/api/main.go index dfaa05b6..90bc636b 100644 --- a/server/cmd/api/main.go +++ b/server/cmd/api/main.go @@ -22,8 +22,8 @@ import ( serverpkg "github.com/kernel/kernel-images/server" "github.com/kernel/kernel-images/server/cmd/api/api" "github.com/kernel/kernel-images/server/cmd/config" - "github.com/kernel/kernel-images/server/lib/capturesession" "github.com/kernel/kernel-images/server/lib/chromedriverproxy" + "github.com/kernel/kernel-images/server/lib/telemetry" "github.com/kernel/kernel-images/server/lib/devtoolsproxy" "github.com/kernel/kernel-images/server/lib/events" "github.com/kernel/kernel-images/server/lib/logger" @@ -100,7 +100,7 @@ func main() { slogger.Error("failed to create event stream", "err", err) os.Exit(1) } - captureSession := capturesession.NewCaptureSession(eventStream) + telemetrySession := telemetry.NewTelemetrySession(eventStream) // Optional S2 storage sink. var s2Writer *events.S2StorageWriter @@ -119,7 +119,7 @@ func main() { upstreamMgr, stz, nekoAuthClient, - captureSession, + telemetrySession, eventStream, config.DisplayNum, ) diff --git a/server/e2e/e2e_s2_storage_test.go b/server/e2e/e2e_s2_storage_test.go index 64d8c9a7..d51bdcf9 100644 --- a/server/e2e/e2e_s2_storage_test.go +++ b/server/e2e/e2e_s2_storage_test.go @@ -16,7 +16,7 @@ import ( ) // TestS2StorageWriter starts a headless container with S2 credentials, runs a -// capture session, and verifies that events land in the configured S2 stream. +// telemetry session, and verifies that events land in the configured S2 stream. // // Skips automatically when S2_BASIN, S2_ACCESS_TOKEN, or S2_STREAM are unset. func TestS2StorageWriter(t *testing.T) { @@ -58,23 +58,29 @@ func TestS2StorageWriter(t *testing.T) { require.NoError(t, err, "check tail before test") startSeq := checkResp.Tail.SeqNum - // Start a capture session. - startResp, err := client.StartCaptureSessionWithResponse(ctx, instanceoapi.StartCaptureSessionJSONRequestBody{}) + // Start a telemetry session. + startResp, err := client.PutTelemetryWithResponse(ctx, instanceoapi.PutTelemetryJSONRequestBody{}) require.NoError(t, err) - require.Equal(t, http.StatusCreated, startResp.StatusCode(), "start capture session: %s", string(startResp.Body)) + require.Equal(t, http.StatusCreated, startResp.StatusCode(), "put telemetry: %s", string(startResp.Body)) require.NotNil(t, startResp.JSON201) - sessionID := startResp.JSON201.Id - t.Logf("capture session started: %s", sessionID) + t.Log("telemetry configured") - // Let the session run briefly so at least one event is published (the - // session_started system event is emitted on session start). + // Let the session run briefly so at least one event is published. time.Sleep(500 * time.Millisecond) - // Stop the capture session. - stopResp, err := client.StopCaptureSessionWithResponse(ctx) + // Stop telemetry by disabling all categories. + f := false + stopResp, err := client.PatchTelemetryWithResponse(ctx, instanceoapi.PatchTelemetryJSONRequestBody{ + Browser: &instanceoapi.BrowserTelemetryCategoriesConfig{ + Console: &instanceoapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Network: &instanceoapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Page: &instanceoapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + Interaction: &instanceoapi.BrowserTelemetryCategoryConfig{Enabled: &f}, + }, + }) require.NoError(t, err) - require.Equal(t, http.StatusOK, stopResp.StatusCode(), "stop capture session: %s", string(stopResp.Body)) - t.Log("capture session stopped") + require.Equal(t, http.StatusOK, stopResp.StatusCode(), "patch telemetry: %s", string(stopResp.Body)) + t.Log("telemetry configuration cleared") // Give the storage writer time to flush to S2 (batcher linger + network). time.Sleep(2 * time.Second) diff --git a/server/lib/capturesession/capturesession.go b/server/lib/capturesession/capturesession.go deleted file mode 100644 index 9a7fa106..00000000 --- a/server/lib/capturesession/capturesession.go +++ /dev/null @@ -1,154 +0,0 @@ -package capturesession - -import ( - "sync" - "time" - - "github.com/kernel/kernel-images/server/lib/events" -) - -// CaptureConfig holds caller-supplied capture preferences. All fields are -// optional; zero values mean "use server defaults" (all categories). -type CaptureConfig struct { - // Categories limits which event categories are captured. - // nil or empty includes all categories. - Categories []events.EventCategory -} - -// CaptureSession manages a capture session against a shared EventStream. -// It is responsible for (a) category-filtering Publish calls, (b) tracking -// session-scoped metadata (ID, config, timestamps), and (c) embedding -// capture_session_id into Event.Data before forwarding to the bus. -type CaptureSession struct { - es *events.EventStream - mu sync.Mutex - captureSessionID string - sessionStartSeq uint64 - categories map[events.EventCategory]struct{} - createdAt time.Time -} - -func NewCaptureSession(es *events.EventStream) *CaptureSession { - cats := make(map[events.EventCategory]struct{}, len(events.AllCategories)) - for _, c := range events.AllCategories { - cats[c] = struct{}{} - } - return &CaptureSession{es: es, categories: cats} -} - -// Start begins a new capture 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. -func (s *CaptureSession) Start(captureSessionID string, cfg CaptureConfig) { - s.mu.Lock() - defer s.mu.Unlock() - s.captureSessionID = captureSessionID - s.sessionStartSeq = s.es.Seq() - s.createdAt = time.Now() - cats := cfg.Categories - if len(cats) == 0 { - cats = events.AllCategories - } - s.categories = make(map[events.EventCategory]struct{}, len(cats)) - for _, c := range cats { - s.categories[c] = struct{}{} - } -} - -// publishLocked stamps capture_session_id into ev.Source.Metadata and forwards to the bus. -// Requires s.mu to be held. -func (s *CaptureSession) publishLocked(ev events.Event) events.Envelope { - if ev.Ts == 0 { - ev.Ts = time.Now().UnixMicro() - } - if ev.Source.Metadata == nil { - ev.Source.Metadata = make(map[string]string) - } - ev.Source.Metadata["capture_session_id"] = s.captureSessionID - return s.es.Publish(events.Envelope{Event: ev}) -} - -// Publish applies the category filter then forwards ev to the EventStream. -func (s *CaptureSession) Publish(ev events.Event) { - s.mu.Lock() - defer s.mu.Unlock() - if s.captureSessionID == "" { - return - } - if _, ok := s.categories[ev.Category]; !ok { - return - } - s.publishLocked(ev) -} - -// NewReader returns a Reader from the EventStream positioned after afterSeq. -func (s *CaptureSession) NewReader(afterSeq uint64) *events.Reader { - return s.es.NewReader(afterSeq) -} - -// ID returns the current capture session ID, or "" if no session is active. -func (s *CaptureSession) ID() string { - s.mu.Lock() - defer s.mu.Unlock() - return s.captureSessionID -} - -// Seq returns the sequence number of the last published event. -func (s *CaptureSession) Seq() uint64 { - return s.es.Seq() -} - -// SessionStartSeq returns the sequence number at which the current session -// started. Fresh SSE connections with no Last-Event-ID should begin here. -func (s *CaptureSession) SessionStartSeq() uint64 { - s.mu.Lock() - defer s.mu.Unlock() - return s.sessionStartSeq -} - -// Config returns the current capture configuration. -func (s *CaptureSession) Config() CaptureConfig { - s.mu.Lock() - defer s.mu.Unlock() - cats := make([]events.EventCategory, 0, len(s.categories)) - for c := range s.categories { - cats = append(cats, c) - } - return CaptureConfig{Categories: cats} -} - -// CreatedAt returns when the current session was started. -func (s *CaptureSession) CreatedAt() time.Time { - s.mu.Lock() - defer s.mu.Unlock() - return s.createdAt -} - -// UpdateConfig applies a new CaptureConfig to the running session. -func (s *CaptureSession) UpdateConfig(cfg CaptureConfig) { - s.mu.Lock() - defer s.mu.Unlock() - cats := cfg.Categories - if len(cats) == 0 { - cats = events.AllCategories - } - s.categories = make(map[events.EventCategory]struct{}, len(cats)) - for _, c := range cats { - s.categories[c] = struct{}{} - } -} - -// Active reports whether a capture session is currently running. -func (s *CaptureSession) Active() bool { - s.mu.Lock() - defer s.mu.Unlock() - return s.captureSessionID != "" -} - -// Stop ends the current session. The ring buffer is left intact so existing -// readers can finish draining. -func (s *CaptureSession) Stop() { - s.mu.Lock() - defer s.mu.Unlock() - s.captureSessionID = "" -} diff --git a/server/lib/capturesession/capturesession_test.go b/server/lib/capturesession/capturesession_test.go deleted file mode 100644 index 8f424e23..00000000 --- a/server/lib/capturesession/capturesession_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package capturesession - -import ( - "context" - "encoding/json" - "strings" - "sync" - "testing" - "time" - - "github.com/kernel/kernel-images/server/lib/events" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func newTestEventStream(t *testing.T, capacity int) *events.EventStream { - t.Helper() - es, err := events.NewEventStream(events.EventStreamConfig{RingCapacity: capacity}) - require.NoError(t, err) - return es -} - -func newTestCaptureSession(t *testing.T) *CaptureSession { - t.Helper() - p := NewCaptureSession(newTestEventStream(t, 100)) - p.Start("test-session", CaptureConfig{}) - return p -} - -func readEnvelope(t *testing.T, r *events.Reader, ctx context.Context) events.Envelope { - t.Helper() - res, err := r.Read(ctx) - require.NoError(t, err) - require.NotNil(t, res.Envelope, "expected envelope, got drop") - return *res.Envelope -} - -func cdpEvent(typ string, cat events.EventCategory) events.Event { - return events.Event{Type: typ, Category: cat, Source: events.Source{Kind: events.KindCDP}} -} - -func sessionIDFromMetadata(t *testing.T, src events.Source) string { - t.Helper() - id, ok := src.Metadata["capture_session_id"] - require.True(t, ok, "capture_session_id not found in source.metadata") - return id -} - -func TestCaptureSession(t *testing.T) { - t.Run("concurrent_publish_seq_order", func(t *testing.T) { - const goroutines = 8 - const eventsEach = 50 - const total = goroutines * eventsEach - - p := NewCaptureSession(newTestEventStream(t, total)) - p.Start("test-concurrent", CaptureConfig{}) - reader := p.NewReader(0) - - var wg sync.WaitGroup - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < eventsEach; j++ { - p.Publish(cdpEvent("console.log", events.CategoryConsole)) - } - }() - } - wg.Wait() - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - for want := uint64(1); want <= total; want++ { - env := readEnvelope(t, reader, ctx) - assert.Equal(t, want, env.Seq, "events must arrive in seq order") - } - }) - - t.Run("seq_continues_across_sessions", func(t *testing.T) { - p := NewCaptureSession(newTestEventStream(t, 100)) - p.Start("session-1", CaptureConfig{}) - p.Publish(cdpEvent("ev.one", events.CategorySystem)) - p.Publish(cdpEvent("ev.two", events.CategorySystem)) - - p.Start("session-2", CaptureConfig{}) - p.Publish(cdpEvent("ev.three", events.CategorySystem)) - - assert.Equal(t, uint64(2), p.SessionStartSeq(), "session-2 starts after seq 2") - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - reader := p.NewReader(2) - env := readEnvelope(t, reader, ctx) - assert.Equal(t, uint64(3), env.Seq) - assert.Equal(t, "session-2", sessionIDFromMetadata(t, env.Event.Source)) - assert.Equal(t, "ev.three", env.Event.Type) - }) - - t.Run("publish_increments_seq", func(t *testing.T) { - p := newTestCaptureSession(t) - reader := p.NewReader(0) - - for i := 0; i < 3; i++ { - p.Publish(events.Event{Type: "page.navigation", Category: events.CategoryPage, Source: events.Source{Kind: events.KindCDP}, Ts: 1}) - } - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - for want := uint64(1); want <= 3; want++ { - env := readEnvelope(t, reader, ctx) - assert.Equal(t, want, env.Seq, "expected seq %d got %d", want, env.Seq) - } - }) - - t.Run("publish_sets_ts", func(t *testing.T) { - p := newTestCaptureSession(t) - reader := p.NewReader(0) - - before := time.Now().UnixMicro() - p.Publish(events.Event{Type: "page.navigation", Category: events.CategoryPage, Source: events.Source{Kind: events.KindCDP}}) - after := time.Now().UnixMicro() - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - env := readEnvelope(t, reader, ctx) - assert.GreaterOrEqual(t, env.Event.Ts, before) - assert.LessOrEqual(t, env.Event.Ts, after) - }) - - t.Run("publish_writes_ring", func(t *testing.T) { - p := newTestCaptureSession(t) - - reader := p.NewReader(0) - p.Publish(events.Event{Type: "page.navigation", Category: events.CategoryPage, Source: events.Source{Kind: events.KindCDP}, Ts: 1}) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - env := readEnvelope(t, reader, ctx) - assert.Equal(t, "page.navigation", env.Event.Type) - assert.Equal(t, events.CategoryPage, env.Event.Category) - }) - - t.Run("start_sets_capture_session_id_in_source_metadata", func(t *testing.T) { - p := newTestCaptureSession(t) - p.Start("test-uuid", CaptureConfig{}) - - reader := p.NewReader(0) - p.Publish(events.Event{Type: "page.navigation", Category: events.CategoryPage, Source: events.Source{Kind: events.KindCDP}, Ts: 1}) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - env := readEnvelope(t, reader, ctx) - assert.Equal(t, "test-uuid", sessionIDFromMetadata(t, env.Event.Source)) - }) - - t.Run("data_unchanged_when_session_id_in_metadata", func(t *testing.T) { - p := newTestCaptureSession(t) - p.Start("merge-session", CaptureConfig{}) - - reader := p.NewReader(0) - p.Publish(events.Event{ - Type: "page.navigation", - Category: events.CategoryPage, - Source: events.Source{Kind: events.KindCDP}, - Ts: 1, - Data: json.RawMessage(`{"url":"https://example.com"}`), - }) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - env := readEnvelope(t, reader, ctx) - assert.Equal(t, "merge-session", sessionIDFromMetadata(t, env.Event.Source)) - assert.JSONEq(t, `{"url":"https://example.com"}`, string(env.Event.Data)) - }) - - t.Run("truncation_applied", func(t *testing.T) { - p := newTestCaptureSession(t) - reader := p.NewReader(0) - - largeData := strings.Repeat("x", 1_100_000) - rawData, err := json.Marshal(map[string]string{"payload": largeData}) - require.NoError(t, err) - - p.Publish(events.Event{ - Type: "page.navigation", - Category: events.CategoryPage, - Source: events.Source{Kind: events.KindCDP}, - Ts: 1, - Data: json.RawMessage(rawData), - }) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - env := readEnvelope(t, reader, ctx) - assert.True(t, env.Event.Truncated) - assert.True(t, json.Valid(env.Event.Data)) - }) -} - diff --git a/server/lib/cdpmonitor/README.md b/server/lib/cdpmonitor/README.md index 9bf3a86f..4c972e1d 100644 --- a/server/lib/cdpmonitor/README.md +++ b/server/lib/cdpmonitor/README.md @@ -10,7 +10,7 @@ Chrome can restart independently of the monitor. When that happens, `UpstreamPro ## Event taxonomy -**CDP-derived** (1-to-1 with a CDP notification): `console_log`, `console_error`, `network_request`, `network_response`, `network_loading_failed`, `page_tab_opened`, `page_navigation`, `page_dom_content_loaded`, `page_load`, `page_layout_shift` +**CDP-derived** (1-to-1 with a CDP notification): `console_log`, `console_error`, `network_request`, `network_response`, `network_loading_failed`, `page_tab_opened`, `page_navigation`, `page_dom_content_loaded`, `page_load`, `page_layout_shift`, `page_lcp` **Computed** (inferred from sequences of CDP events): `network_idle` (fires when in-flight requests drop to zero), `page_layout_settled` (1 s after `page_load` with no intervening layout shifts), `page_navigation_settled` (fires once `page_dom_content_loaded` and `page_layout_settled` have both fired for the same navigation; intentionally independent of `network_idle` so that a single hung request cannot stall the event). @@ -29,7 +29,7 @@ Chrome can restart independently of the monitor. When that happens, `UpstreamPro | Screenshot capture via ffmpeg | `screenshot.go` | | CDP protocol types | `cdp_proto.go`, `types.go` | | Interaction tracking injected into the page | `interaction.js` | -| Body/MIME capture sizing and text truncation helpers | `util.go` | +| Body/MIME capture sizing, text truncation, and typed payload helpers | `util.go` | ## Internals @@ -68,7 +68,7 @@ restartMu -> lifeMu -> sessionsMu | Lock | Protects | | --- | --- | | `restartMu` | Serializes `handleUpstreamRestart` to prevent overlapping reconnects from rapid Chrome restarts | -| `lifeMu` | `conn`, `lifecycleCtx`, `cancel`, `done`, `readReady` -- all fields that change during Start / Stop / reconnect | +| `lifeMu` | `conn`, `lifecycleCtx`, `cancel`, `done`, `readReady`: all fields that change during Start / Stop / reconnect | | `pendReqMu` | `pendingRequests` (requestId -> `networkReqState`): in-flight network requests accumulating request/response metadata until `loadingFinished` | | `computed.mu` | All `computedState` fields: counters and timers for the `network_idle`, `page_layout_settled`, and `page_navigation_settled` state machines | | `pendMu` | `pending` (id -> reply channel): in-flight CDP commands waiting for a response from Chrome | @@ -79,7 +79,7 @@ Fields that need no mutex use `sync/atomic`: `nextID`, `mainSessionID`, `running ### WebSocket concurrency -`coder/websocket` guarantees one concurrent `Read` and one concurrent `Write` are safe on the same connection. `readLoop` is the sole reader. All writes go through `send`, which calls `conn.Write` directly -- `conn.Write` is internally serialized by the library, so no external write mutex is needed. +`coder/websocket` guarantees one concurrent `Read` and one concurrent `Write` are safe on the same connection. `readLoop` is the sole reader. All writes go through `send`, which calls `conn.Write` directly; `conn.Write` is internally serialized by the library, so no external write mutex is needed. ## Event data model @@ -89,13 +89,21 @@ Every event arrives as an `Envelope`: ```json { - "capture_session_id": "cs_abc123", "seq": 42, "event": { "ts": 1746123456789000, "type": "network_request", "category": "network", - "source": { ... }, + "source": { + "kind": "cdp", + "event": "Network.requestWillBeSent", + "metadata": { + "telemetry_session_id": "cs_abc123", + "cdp_session_id": "...", + "target_id": "...", + "target_type": "page" + } + }, "data": { ... }, "truncated": false } @@ -104,12 +112,12 @@ Every event arrives as an `Envelope`: | Field | Type | Description | | --- | --- | --- | -| `capture_session_id` | string | Pipeline-assigned ID for the capture session (not a CDP concept). | -| `seq` | uint64 | Monotonically increasing per-capture-session sequence number. | +| `seq` | uint64 | Process-monotonic sequence number; does not reset across telemetry config changes. | | `event.ts` | int64 | Wall-clock time the monitor emitted the event, as **Unix microseconds** (µs since epoch). | | `event.type` | string | See [Event taxonomy](#event-taxonomy). | | `event.category` | string | One of: `console`, `network`, `page`, `interaction`, `system`. | | `event.truncated` | bool | `true` if `data` was nulled to fit the 1 MB pipeline limit. | +| `event.source.metadata.telemetry_session_id` | string | Pipeline-assigned ID for the telemetry session, stamped by the telemetry layer. | ### Source object @@ -149,7 +157,7 @@ target_id <- one per tab/window; stable across navigations | `target_id` | `source.metadata`, most `data` objects | The browser tab. Use this to group all events from one tab session. | | `cdp_session_id` | `source.metadata` | The WebSocket sub-channel. Not stable across reconnects. | | `frame_id` | `page_navigation`, `network_request`, `network_response`, `network_loading_failed` | The frame the request or navigation belongs to. Top-level frame has no `parent_frame_id`. | -| `source_frame_id` | `page_layout_shift` | The frame where the layout shift occurred. Distinct from the nav context `frame_id`, which is always the top-level navigated frame. | +| `source_frame_id` | `page_layout_shift`, `page_lcp` | The frame where the layout shift or LCP element occurred. Distinct from the nav context `frame_id`, which is always the top-level navigated frame. | | `loader_id` | `page_navigation`, `network_request`, `network_response` | The document load that owns a request. Join `network_request.loader_id` to `page_navigation.loader_id` to correlate requests with the navigation that triggered them. | | `request_id` | `network_request`, `network_response`, `network_loading_failed` | A single request chain (including redirects). Links request to its eventual response or failure. | @@ -163,11 +171,22 @@ Most event `data` objects include a nav context block stamped at the last `page_ | `frame_id` | Frame ID of the navigated top-level frame. | | `loader_id` | Loader ID of the current document. | | `url` | URL of the current page at the time of the last navigation. | -| `nav_seq` | Monotonically increasing counter, incremented on each `page_navigation`. Use it to detect that the page has navigated between two events in the same session. | +| `nav_seq` | Monotonically increasing counter, incremented on each `page_navigation`. Use it to detect that the page has navigated between two events in the same session. For `network_request`/`network_response`/`network_loading_failed`, the `nav_seq` is captured at request-send time and carried forward to the response so a request/response pair always shares an epoch. | + +### Events that do not compose `BrowserEventContext` + +Of the 22 event types, two intentionally omit the standard nav context block: + +- `page_tab_opened`: fires before a CDP session is attached to the new target, so `session_id`, `frame_id`, `loader_id`, and `nav_seq` are absent. Only `target_id`, `target_type`, and the tab's initial `url`/`title`/`opener_id` are populated. +- `page_navigation`: resets the navigation epoch, so it carries the new context fields inline but omits `nav_seq` (the value reported by subsequent events for this epoch is `nav_seq + 1`). + +Consumers that destructure `BrowserEventContext` generically should treat these two events as special cases. ### Per-event data fields -Fields below are the unique additions per event type. Unless otherwise noted, events also include the nav context fields described above. Network events are the exception: they carry their own `loader_id` and `frame_id` directly and do not include nav context. +The canonical schema for each event's `data` payload is defined in `openapi.yaml` under the corresponding `Browser*EventData` schema (e.g. `BrowserNetworkRequestEventData`). All 22 event shapes are collected into the `KnownBrowserTelemetryEvent` discriminated union, which maps each `type` string to its concrete schema; use that as the entry point when looking up a specific event's fields. The table below summarises the key fields; refer to the schema for the authoritative field list and types. + +Unless otherwise noted, events also include the nav context fields described above. Network events are the exception: they carry their own `loader_id` and `frame_id` directly and do not include nav context. #### Console events @@ -190,9 +209,10 @@ Fields below are the unique additions per event type. Unless otherwise noted, ev | --- | --- | | `page_tab_opened` | `target_id`, `target_type`, `url`, `opener_id`, `title`. Emitted before the first navigation; no nav context. | | `page_navigation` | `session_id`, `target_id`, `target_type`, `url`, `frame_id`, `parent_frame_id` (absent for top-level frames), `loader_id`. This event establishes the nav context stamped on all subsequent events for the session. | -| `page_dom_content_loaded` | Nav context + `cdp_timestamp` (CDP monotonic seconds; not a wall-clock timestamp -- use `event.ts` for ordering). | +| `page_dom_content_loaded` | Nav context + `cdp_timestamp` (CDP monotonic seconds; not a wall-clock timestamp, use `event.ts` for ordering). | | `page_load` | Nav context + `cdp_timestamp` (CDP monotonic seconds). | -| `page_layout_shift` | Nav context + `source_frame_id`, `time`, `duration`. Optional `layout_shift_details` object: `value`, `had_recent_input`. Optional `lcp_details` object: `render_time`, `load_time`, `size`, `element_id`, `url`, `node_id`. Chrome multiplexes LCP candidate data through the same `PerformanceTimeline.timelineEventAdded` notification, so both may appear on a single event. | +| `page_layout_shift` | Nav context + `source_frame_id`, `time`, `duration`. Optional `layout_shift_details`: `value`, `had_recent_input`. | +| `page_lcp` | Nav context + `source_frame_id`, `time`. Optional `lcp_details`: `render_time`, `load_time`, `size`, `element_id`, `url`, `node_id`. | #### Computed events @@ -218,4 +238,4 @@ Lifecycle events use `source.kind = "local_process"` and carry no nav context, e | `monitor_disconnected` | `reason: "chrome_restarted"`. | | `monitor_reconnected` | `reconnect_duration_ms`. | | `monitor_reconnect_failed` | `reason: "reconnect_exhausted"`. | -| `monitor_init_failed` | `step` (name of the init step that failed, e.g. `"Target.setAutoAttach"`). | \ No newline at end of file +| `monitor_init_failed` | `step` (name of the init step that failed, e.g. `"Target.setAutoAttach"`). | diff --git a/server/lib/cdpmonitor/computed.go b/server/lib/cdpmonitor/computed.go index f18b8bb6..3921089b 100644 --- a/server/lib/cdpmonitor/computed.go +++ b/server/lib/cdpmonitor/computed.go @@ -7,6 +7,7 @@ import ( "time" "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" ) const ( @@ -76,6 +77,13 @@ func (s *computedState) navSnapshot() (json.RawMessage, map[string]string) { return s.navData, s.navMeta } +// currentNavSeq returns the current navigation sequence number under mu. +func (s *computedState) currentNavSeq() int { + s.mu.Lock() + defer s.mu.Unlock() + return s.navSeq +} + // navDataWith merges extra fields into the current nav payload. // Nav context fields (session_id, target_id, etc.) always take precedence over // caller-supplied extra so a page-controlled payload cannot forge nav identity. @@ -92,6 +100,20 @@ func (s *computedState) navDataWith(extra map[string]any) json.RawMessage { return out } + +// currentNavCtxFields returns the current nav context fields for constructing typed event payloads. +// Returns zero values if s is nil (before first navigation). +func (s *computedState) currentNavCtxFields() (sessionID, targetID, targetType, frameID, loaderID, url string, navSeq int64) { + if s == nil { + return + } + s.mu.Lock() + ctx := s.navCtx + seq := int64(s.navSeq) + s.mu.Unlock() + return ctx.sessionID, ctx.targetID, ctx.targetType, ctx.frameID, ctx.loaderID, ctx.url, seq +} + func stopTimer(t *time.Timer) { if t == nil { return @@ -203,7 +225,6 @@ func (s *computedState) startNetIdleTimer() { } stopTimer(s.netTimer) navSeq := s.navSeq - navData := s.navData navMeta := s.navMeta s.netTimer = time.AfterFunc(networkIdleDebounce, func() { s.mu.Lock() @@ -212,16 +233,15 @@ func (s *computedState) startNetIdleTimer() { return } s.netFired = true + ctx := s.navCtx + seq := s.navSeq s.mu.Unlock() s.publish(events.Event{ Ts: time.Now().UnixMicro(), Type: EventNetworkIdle, - Category: events.CategoryNetwork, - Source: events.Source{ - Kind: events.KindCDP, - Metadata: navMeta, - }, - Data: navData, + Category: events.Network, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp, Metadata: &navMeta}, + Data: marshalNavEventContext(ctx, seq), }) }) } @@ -265,17 +285,15 @@ func (s *computedState) emitLayoutSettled(navSeq int) { } s.layoutFired = true s.navLayoutSettled = true - navData := s.navData + ctx := s.navCtx + seq := s.navSeq navMeta := s.navMeta evs := []events.Event{{ Ts: time.Now().UnixMicro(), Type: EventLayoutSettled, - Category: events.CategoryPage, - Source: events.Source{ - Kind: events.KindCDP, - Metadata: navMeta, - }, - Data: navData, + Category: events.Page, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp, Metadata: &navMeta}, + Data: marshalNavEventContext(ctx, seq), }} evs = append(evs, s.pendingNavigationSettled()...) s.mu.Unlock() @@ -303,15 +321,15 @@ func (s *computedState) pendingNavigationSettled() []events.Event { } if s.navDOMLoaded && s.navLayoutSettled && !s.navFired { s.navFired = true + ctx := s.navCtx + seq := s.navSeq + navMeta := s.navMeta return []events.Event{{ Ts: time.Now().UnixMicro(), Type: EventNavigationSettled, - Category: events.CategoryPage, - Source: events.Source{ - Kind: events.KindCDP, - Metadata: s.navMeta, - }, - Data: s.navData, + Category: events.Page, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp, Metadata: &navMeta}, + Data: marshalNavEventContext(ctx, seq), }} } return nil diff --git a/server/lib/cdpmonitor/computed_test.go b/server/lib/cdpmonitor/computed_test.go index e2ec519e..c67ac06f 100644 --- a/server/lib/cdpmonitor/computed_test.go +++ b/server/lib/cdpmonitor/computed_test.go @@ -34,7 +34,7 @@ func TestNetworkIdle(t *testing.T) { ev := ec.waitFor(t, "network_idle", 2*time.Second) assert.GreaterOrEqual(t, time.Since(t0).Milliseconds(), int64(400), "fired too early") - assert.Equal(t, events.CategoryNetwork, ev.Category) + assert.Equal(t, events.Network, ev.Category) }) t.Run("timer_reset_on_new_request", func(t *testing.T) { @@ -64,7 +64,7 @@ func TestLayoutSettled(t *testing.T) { ev := ec.waitFor(t, "page_layout_settled", 3*time.Second) assert.GreaterOrEqual(t, time.Since(t0).Milliseconds(), int64(900), "fired too early") - assert.Equal(t, events.CategoryPage, ev.Category) + assert.Equal(t, events.Page, ev.Category) }) t.Run("layout_shift_before_page_load_ignored", func(t *testing.T) { @@ -104,7 +104,7 @@ func TestNavigationSettled(t *testing.T) { cs.onPageLoad() ev := ec.waitFor(t, "page_navigation_settled", 3*time.Second) - assert.Equal(t, events.CategoryPage, ev.Category) + assert.Equal(t, events.Page, ev.Category) }) t.Run("not_blocked_by_pending_network_request", func(t *testing.T) { @@ -148,10 +148,10 @@ func TestNavDataMetadata(t *testing.T) { cs.onPageLoad() ev := ec.waitFor(t, "page_layout_settled", 3*time.Second) - assert.Equal(t, events.CategoryPage, ev.Category) - assert.Equal(t, "s1", ev.Source.Metadata[MetadataKeyCDPSessionID]) - assert.Equal(t, "t1", ev.Source.Metadata[MetadataKeyTargetID]) - assert.Equal(t, "page", ev.Source.Metadata[MetadataKeyTargetType]) + assert.Equal(t, events.Page, ev.Category) + assert.Equal(t, "s1", (*ev.Source.Metadata)[MetadataKeyCDPSessionID]) + assert.Equal(t, "t1", (*ev.Source.Metadata)[MetadataKeyTargetID]) + assert.Equal(t, "page", (*ev.Source.Metadata)[MetadataKeyTargetType]) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "s1", data["session_id"]) @@ -167,9 +167,9 @@ func TestNavDataMetadata(t *testing.T) { cs.onPageLoad() ev := ec.waitFor(t, "page_navigation_settled", 3*time.Second) - assert.Equal(t, events.CategoryPage, ev.Category) - assert.Equal(t, "s1", ev.Source.Metadata[MetadataKeyCDPSessionID]) - assert.Equal(t, "t1", ev.Source.Metadata[MetadataKeyTargetID]) + assert.Equal(t, events.Page, ev.Category) + assert.Equal(t, "s1", (*ev.Source.Metadata)[MetadataKeyCDPSessionID]) + assert.Equal(t, "t1", (*ev.Source.Metadata)[MetadataKeyTargetID]) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "s1", data["session_id"]) diff --git a/server/lib/cdpmonitor/handlers.go b/server/lib/cdpmonitor/handlers.go index 15bca908..ddf19b2c 100644 --- a/server/lib/cdpmonitor/handlers.go +++ b/server/lib/cdpmonitor/handlers.go @@ -7,6 +7,7 @@ import ( "time" "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" ) // logUnmarshalErr logs a Debug message when a handler can't parse CDP params. @@ -17,19 +18,23 @@ func (m *Monitor) logUnmarshalErr(method string, err error) { } // publishEvent stamps common fields and publishes an event. -func (m *Monitor) publishEvent(eventType string, category events.EventCategory, source events.Source, sourceEvent string, data json.RawMessage, sessionID string) { +func (m *Monitor) publishEvent(eventType string, category oapi.TelemetryEventCategory, source oapi.BrowserEventSource, sourceEvent string, data json.RawMessage, sessionID string) { src := source - src.Event = sourceEvent + src.Event = &sourceEvent if sessionID != "" { - if src.Metadata == nil { - src.Metadata = make(map[string]string) + meta := make(map[string]string) + if src.Metadata != nil { + for k, v := range *src.Metadata { + meta[k] = v + } } - src.Metadata[MetadataKeyCDPSessionID] = sessionID + meta[MetadataKeyCDPSessionID] = sessionID m.sessionsMu.RLock() info := m.sessions[sessionID] m.sessionsMu.RUnlock() - src.Metadata[MetadataKeyTargetID] = info.targetID - src.Metadata[MetadataKeyTargetType] = info.targetType + meta[MetadataKeyTargetID] = info.targetID + meta[MetadataKeyTargetType] = info.targetType + src.Metadata = &meta } m.publish(events.Event{ Ts: time.Now().UnixMicro(), @@ -139,26 +144,52 @@ func (m *Monitor) handleConsole(p cdpRuntimeConsoleAPICalledParams, sessionID st eventType = EventConsoleError } cs := m.computedFor(sessionID) - data := cs.navDataWith(map[string]any{ - "level": p.Type, - "text": text, - "args": argValues, - "stack_trace": p.StackTrace, + sid, tid, ttype, fid, lid, url, nseq := cs.currentNavCtxFields() + var stackTrace *oapi.BrowserCallStack + if len(p.StackTrace) > 0 { + stackTrace = &oapi.BrowserCallStack{} + _ = json.Unmarshal(p.StackTrace, stackTrace) + } + data, _ := json.Marshal(oapi.BrowserConsoleLogEventData{ + SessionId: sid, + TargetId: tid, + TargetType: oapi.BrowserTargetType(ttype), + FrameId: ptrOf(fid), + LoaderId: ptrOf(lid), + Url: ptrOf(url), + NavSeq: nseq, + Level: p.Type, + Text: text, + Args: ptrOf(argValues), + StackTrace: stackTrace, }) - m.publishEvent(eventType, events.CategoryConsole, events.Source{Kind: events.KindCDP}, "Runtime.consoleAPICalled", data, sessionID) + m.publishEvent(eventType, events.Console, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Runtime.consoleAPICalled", data, sessionID) } func (m *Monitor) handleExceptionThrown(ctx context.Context, p cdpRuntimeExceptionThrownParams, sessionID string) { cs := m.computedFor(sessionID) + sid, tid, ttype, fid, lid, url, nseq := cs.currentNavCtxFields() + var stackTrace *oapi.BrowserCallStack + if len(p.ExceptionDetails.StackTrace) > 0 { + stackTrace = &oapi.BrowserCallStack{} + _ = json.Unmarshal(p.ExceptionDetails.StackTrace, stackTrace) + } // source_url is the script file URL; distinct from nav context's url (the page URL). - data := cs.navDataWith(map[string]any{ - "text": p.ExceptionDetails.Text, - "line": p.ExceptionDetails.LineNumber, - "column": p.ExceptionDetails.ColumnNumber, - "source_url": p.ExceptionDetails.URL, - "stack_trace": p.ExceptionDetails.StackTrace, + data, _ := json.Marshal(oapi.BrowserConsoleErrorEventData{ + SessionId: sid, + TargetId: tid, + TargetType: oapi.BrowserTargetType(ttype), + FrameId: ptrOf(fid), + LoaderId: ptrOf(lid), + Url: ptrOf(url), + NavSeq: nseq, + Text: p.ExceptionDetails.Text, + Line: ptrOf(p.ExceptionDetails.LineNumber), + Column: ptrOf(p.ExceptionDetails.ColumnNumber), + SourceUrl: ptrOf(p.ExceptionDetails.URL), + StackTrace: stackTrace, }) - m.publishEvent(EventConsoleError, events.CategoryConsole, events.Source{Kind: events.KindCDP}, "Runtime.exceptionThrown", data, sessionID) + m.publishEvent(EventConsoleError, events.Console, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Runtime.exceptionThrown", data, sessionID) m.tryScreenshot(ctx, "Runtime.exceptionThrown", sessionID) } @@ -205,7 +236,7 @@ func (m *Monitor) handleBindingCalled(p cdpRuntimeBindingCalledParams, sessionID var payloadMap map[string]any _ = json.Unmarshal(payload, &payloadMap) cs := m.computedFor(sessionID) - m.publishEvent(header.Type, events.CategoryInteraction, events.Source{Kind: events.KindCDP}, "Runtime.bindingCalled", cs.navDataWith(payloadMap), sessionID) + m.publishEvent(header.Type, events.Interaction, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Runtime.bindingCalled", cs.navDataWith(payloadMap), sessionID) } // handleTimelineEvent processes PerformanceTimeline layout-shift and LCP events. @@ -214,44 +245,70 @@ func (m *Monitor) handleTimelineEvent(p cdpPerformanceTimelineEventAddedParams, case timelineEventLayoutShift: // source_frame_id is the frame where the shift occurred; distinct from nav // context's frame_id (the top-level navigated frame). - ev := map[string]any{ - "source_frame_id": p.Event.FrameID, - "time": p.Event.Time, - "duration": p.Event.Duration, + cs := m.computedFor(sessionID) + sid, tid, ttype, fid, lid, url, nseq := cs.currentNavCtxFields() + payload := oapi.BrowserPageLayoutShiftEventData{ + SessionId: sid, + TargetId: tid, + TargetType: oapi.BrowserTargetType(ttype), + FrameId: ptrOf(fid), + LoaderId: ptrOf(lid), + Url: ptrOf(url), + NavSeq: nseq, + SourceFrameId: p.Event.FrameID, + Time: float32(p.Event.Time), + Duration: float32(p.Event.Duration), } var shift cdpLayoutShiftDetails if p.Event.LayoutShiftDetails != nil && json.Unmarshal(p.Event.LayoutShiftDetails, &shift) == nil { - ev["layout_shift_details"] = map[string]any{ - "value": shift.Value, - "had_recent_input": shift.HadRecentInput, + payload.LayoutShiftDetails = &struct { + HadRecentInput *bool `json:"had_recent_input,omitempty"` + Value *float32 `json:"value,omitempty"` + }{ + Value: ptrOf(float32(shift.Value)), + HadRecentInput: ptrOf(shift.HadRecentInput), } } - cs := m.computedFor(sessionID) - data := cs.navDataWith(ev) - m.publishEvent(EventLayoutShift, events.CategoryPage, events.Source{Kind: events.KindCDP}, "PerformanceTimeline.timelineEventAdded", data, sessionID) + data, _ := json.Marshal(payload) + m.publishEvent(EventLayoutShift, events.Page, oapi.BrowserEventSource{Kind: oapi.Cdp}, "PerformanceTimeline.timelineEventAdded", data, sessionID) if cs != nil { cs.onLayoutShift() } case timelineEventLCP: - ev := map[string]any{ - "source_frame_id": p.Event.FrameID, - "time": p.Event.Time, + cs := m.computedFor(sessionID) + sid, tid, ttype, fid, lid, url, nseq := cs.currentNavCtxFields() + lcpPayload := oapi.BrowserPageLcpEventData{ + SessionId: sid, + TargetId: tid, + TargetType: oapi.BrowserTargetType(ttype), + FrameId: ptrOf(fid), + LoaderId: ptrOf(lid), + Url: ptrOf(url), + NavSeq: nseq, + SourceFrameId: p.Event.FrameID, + Time: float32(p.Event.Time), } var lcp cdpLcpDetails if p.Event.LcpDetails != nil && json.Unmarshal(p.Event.LcpDetails, &lcp) == nil { - ev["lcp_details"] = map[string]any{ - "render_time": lcp.RenderTime, - "load_time": lcp.LoadTime, - "size": lcp.Size, - "element_id": lcp.ElementID, - "url": lcp.URL, - "node_id": lcp.NodeID, + lcpPayload.LcpDetails = &struct { + ElementId *string `json:"element_id,omitempty"` + LoadTime *float32 `json:"load_time,omitempty"` + NodeId *int `json:"node_id,omitempty"` + RenderTime *float32 `json:"render_time,omitempty"` + Size *float32 `json:"size,omitempty"` + Url *string `json:"url,omitempty"` + }{ + RenderTime: ptrOf(float32(lcp.RenderTime)), + LoadTime: ptrOf(float32(lcp.LoadTime)), + Size: ptrOf(float32(lcp.Size)), + ElementId: ptrOf(lcp.ElementID), + Url: ptrOf(lcp.URL), + NodeId: ptrOf(lcp.NodeID), } } - cs := m.computedFor(sessionID) - data := cs.navDataWith(ev) - m.publishEvent(EventLCP, events.CategoryPage, events.Source{Kind: events.KindCDP}, "PerformanceTimeline.timelineEventAdded", data, sessionID) + data, _ := json.Marshal(lcpPayload) + m.publishEvent(EventLCP, events.Page, oapi.BrowserEventSource{Kind: oapi.Cdp}, "PerformanceTimeline.timelineEventAdded", data, sessionID) } } @@ -270,6 +327,15 @@ func (m *Monitor) handleNetworkRequest(p cdpNetworkRequestWillBeSentParams, sess initiatorType = raw.Type } + m.sessionsMu.RLock() + info := m.sessions[sessionID] + m.sessionsMu.RUnlock() + cs := m.computedFor(sessionID) + var navSeq int64 + if cs != nil { + navSeq = int64(cs.currentNavSeq()) + } + // Redirects reuse the same requestId and fire additional requestWillBeSent // events, but only a single loadingFinished fires per chain. Only increment // netPending for genuinely new requests to avoid permanently inflating the @@ -279,6 +345,8 @@ func (m *Monitor) handleNetworkRequest(p cdpNetworkRequestWillBeSentParams, sess addedAt := existing.addedAt if !isRedirect { addedAt = time.Now() + } else { + navSeq = existing.navSeq } m.pendingRequests[p.RequestID] = networkReqState{ sessionID: sessionID, @@ -289,35 +357,40 @@ func (m *Monitor) handleNetworkRequest(p cdpNetworkRequestWillBeSentParams, sess resourceType: p.Type, loaderID: p.LoaderID, frameID: p.FrameID, + navSeq: navSeq, addedAt: addedAt, } m.pendReqMu.Unlock() - ev := map[string]any{ - "request_id": p.RequestID, - "loader_id": p.LoaderID, - "frame_id": p.FrameID, - "document_url": p.DocumentURL, - "method": p.Request.Method, - "url": p.Request.URL, - "headers": p.Request.Headers, - "initiator_type": initiatorType, + var hdrs oapi.BrowserHttpHeaders + _ = json.Unmarshal(p.Request.Headers, &hdrs) + payload := oapi.BrowserNetworkRequestEventData{ + SessionId: sessionID, + TargetId: info.targetID, + TargetType: oapi.BrowserTargetType(info.targetType), + FrameId: ptrOf(p.FrameID), + LoaderId: ptrOf(p.LoaderID), + Url: ptrOf(p.Request.URL), + NavSeq: navSeq, + RequestId: p.RequestID, + Method: p.Request.Method, + DocumentUrl: p.DocumentURL, + Headers: hdrs, + InitiatorType: initiatorType, } if p.Request.PostData != "" { - ev["post_data"] = p.Request.PostData + payload.PostData = ptrOf(p.Request.PostData) } if p.Type != "" { - ev["resource_type"] = p.Type + payload.ResourceType = ptrOf(p.Type) } if isRedirect { - ev["is_redirect"] = true - ev["redirect_url"] = existing.url + payload.IsRedirect = ptrOf(true) + payload.RedirectUrl = ptrOf(existing.url) } - data, _ := json.Marshal(ev) - m.publishEvent(EventNetworkRequest, events.CategoryNetwork, events.Source{Kind: events.KindCDP}, "Network.requestWillBeSent", data, sessionID) - if !isRedirect { - if cs := m.computedFor(sessionID); cs != nil { - cs.onRequest() - } + data, _ := json.Marshal(payload) + m.publishEvent(EventNetworkRequest, events.Network, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Network.requestWillBeSent", data, sessionID) + if !isRedirect && cs != nil { + cs.onRequest() } } @@ -343,35 +416,44 @@ func (m *Monitor) handleLoadingFinished(ctx context.Context, p cdpNetworkLoading if !ok { return } + m.sessionsMu.RLock() + info := m.sessions[sessionID] + m.sessionsMu.RUnlock() if cs := m.computedFor(state.sessionID); cs != nil { cs.onLoadingFinished() } // Fetch response body async to avoid blocking readLoop; binary types are skipped. m.asyncWg.Go(func() { body := m.fetchResponseBody(ctx, p.RequestID, sessionID, state) - ev := map[string]any{ - "request_id": p.RequestID, - "loader_id": state.loaderID, - "frame_id": state.frameID, - "method": state.method, - "url": state.url, - "status": state.status, - "headers": state.resHeaders, + var hdrs oapi.BrowserHttpHeaders + _ = json.Unmarshal(state.resHeaders, &hdrs) + resPayload := oapi.BrowserNetworkResponseEventData{ + SessionId: sessionID, + TargetId: info.targetID, + TargetType: oapi.BrowserTargetType(info.targetType), + FrameId: ptrOf(state.frameID), + LoaderId: ptrOf(state.loaderID), + Url: ptrOf(state.url), + NavSeq: state.navSeq, + RequestId: p.RequestID, + Method: state.method, + Status: state.status, + Headers: hdrs, } if state.statusText != "" { - ev["status_text"] = state.statusText + resPayload.StatusText = ptrOf(state.statusText) } if state.mimeType != "" { - ev["mime_type"] = state.mimeType + resPayload.MimeType = ptrOf(state.mimeType) } if state.resourceType != "" { - ev["resource_type"] = state.resourceType + resPayload.ResourceType = ptrOf(state.resourceType) } if body != "" { - ev["body"] = body + resPayload.Body = ptrOf(body) } - data, _ := json.Marshal(ev) - m.publishEvent(EventNetworkResponse, events.CategoryNetwork, events.Source{Kind: events.KindCDP}, "Network.loadingFinished", data, sessionID) + data, _ := json.Marshal(resPayload) + m.publishEvent(EventNetworkResponse, events.Network, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Network.loadingFinished", data, sessionID) }) } @@ -412,19 +494,37 @@ func (m *Monitor) handleLoadingFailed(p cdpNetworkLoadingFailedParams, sessionID } m.pendReqMu.Unlock() - ev := map[string]any{ - "request_id": p.RequestID, - "error_text": p.ErrorText, - "canceled": p.Canceled, + m.sessionsMu.RLock() + info := m.sessions[sessionID] + m.sessionsMu.RUnlock() + // Prefer the navSeq captured at requestWillBeSent time so request/failure + // pairs share an epoch. For untracked requests (in flight at CDP attach), + // fall back to the current navSeq. + var nseq int64 + if ok { + nseq = state.navSeq + } else if cs := m.computedFor(sessionID); cs != nil { + nseq = int64(cs.currentNavSeq()) + } + failPayload := oapi.BrowserNetworkLoadingFailedEventData{ + SessionId: sessionID, + TargetId: info.targetID, + TargetType: oapi.BrowserTargetType(info.targetType), + NavSeq: nseq, + RequestId: p.RequestID, + ErrorText: p.ErrorText, + Canceled: p.Canceled, } if ok { - ev["url"] = state.url - ev["loader_id"] = state.loaderID - ev["frame_id"] = state.frameID - ev["resource_type"] = state.resourceType + failPayload.Url = ptrOf(state.url) + failPayload.LoaderId = ptrOf(state.loaderID) + failPayload.FrameId = ptrOf(state.frameID) + if state.resourceType != "" { + failPayload.ResourceType = ptrOf(state.resourceType) + } } - data, _ := json.Marshal(ev) - m.publishEvent(EventNetworkLoadingFailed, events.CategoryNetwork, events.Source{Kind: events.KindCDP}, "Network.loadingFailed", data, sessionID) + data, _ := json.Marshal(failPayload) + m.publishEvent(EventNetworkLoadingFailed, events.Network, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Network.loadingFailed", data, sessionID) if ok { if cs := m.computedFor(state.sessionID); cs != nil { cs.onLoadingFinished() @@ -440,16 +540,16 @@ func (m *Monitor) handleFrameNavigated(p cdpPageFrameNavigatedParams, sessionID cs := m.computedStates[sessionID] m.sessionsMu.RUnlock() - data, _ := json.Marshal(map[string]any{ - "session_id": sessionID, - "target_id": info.targetID, - "target_type": info.targetType, - "url": p.Frame.URL, - "frame_id": p.Frame.ID, - "parent_frame_id": p.Frame.ParentID, - "loader_id": p.Frame.LoaderID, + data, _ := json.Marshal(oapi.BrowserPageNavigationEventData{ + SessionId: sessionID, + TargetId: info.targetID, + TargetType: oapi.BrowserTargetType(info.targetType), + Url: p.Frame.URL, + FrameId: p.Frame.ID, + ParentFrameId: ptrOf(p.Frame.ParentID), + LoaderId: p.Frame.LoaderID, }) - m.publishEvent(EventNavigation, events.CategoryPage, events.Source{Kind: events.KindCDP}, "Page.frameNavigated", data, sessionID) + m.publishEvent(EventNavigation, events.Page, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Page.frameNavigated", data, sessionID) // Only reset state for top-level navigations; subframe (iframe) navigations // should not disrupt main-page tracking. @@ -487,8 +587,18 @@ func (m *Monitor) handleFrameNavigated(p cdpPageFrameNavigatedParams, sessionID func (m *Monitor) handleDOMContentLoaded(p cdpPageDomContentEventFiredParams, sessionID string) { cs := m.computedFor(sessionID) - data := cs.navDataWith(map[string]any{"cdp_timestamp": p.Timestamp}) - m.publishEvent(EventDOMContentLoaded, events.CategoryPage, events.Source{Kind: events.KindCDP}, "Page.domContentEventFired", data, sessionID) + sid, tid, ttype, fid, lid, url, nseq := cs.currentNavCtxFields() + data, _ := json.Marshal(oapi.BrowserPageDomContentLoadedEventData{ + SessionId: sid, + TargetId: tid, + TargetType: oapi.BrowserTargetType(ttype), + FrameId: ptrOf(fid), + LoaderId: ptrOf(lid), + Url: ptrOf(url), + NavSeq: nseq, + CdpTimestamp: float32(p.Timestamp), + }) + m.publishEvent(EventDOMContentLoaded, events.Page, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Page.domContentEventFired", data, sessionID) if cs != nil { cs.onDOMContentLoaded() } @@ -496,8 +606,18 @@ func (m *Monitor) handleDOMContentLoaded(p cdpPageDomContentEventFiredParams, se func (m *Monitor) handleLoadEventFired(ctx context.Context, p cdpPageLoadEventFiredParams, sessionID string) { cs := m.computedFor(sessionID) - data := cs.navDataWith(map[string]any{"cdp_timestamp": p.Timestamp}) - m.publishEvent(EventPageLoad, events.CategoryPage, events.Source{Kind: events.KindCDP}, "Page.loadEventFired", data, sessionID) + sid, tid, ttype, fid, lid, url, nseq := cs.currentNavCtxFields() + data, _ := json.Marshal(oapi.BrowserPageLoadEventData{ + SessionId: sid, + TargetId: tid, + TargetType: oapi.BrowserTargetType(ttype), + FrameId: ptrOf(fid), + LoaderId: ptrOf(lid), + Url: ptrOf(url), + NavSeq: nseq, + CdpTimestamp: float32(p.Timestamp), + }) + m.publishEvent(EventPageLoad, events.Page, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Page.loadEventFired", data, sessionID) if cs != nil { cs.onPageLoad() } @@ -522,14 +642,14 @@ func (m *Monitor) handleAttachedToTarget(ctx context.Context, p cdpTargetAttache m.sessionsMu.Unlock() if p.TargetInfo.Type == targetTypePage { - data, _ := json.Marshal(map[string]any{ - "target_id": p.TargetInfo.TargetID, - "target_type": p.TargetInfo.Type, - "url": p.TargetInfo.URL, - "opener_id": p.TargetInfo.OpenerID, - "title": p.TargetInfo.Title, + data, _ := json.Marshal(oapi.BrowserPageTabOpenedEventData{ + TargetId: p.TargetInfo.TargetID, + TargetType: oapi.BrowserTargetType(p.TargetInfo.Type), + Url: p.TargetInfo.URL, + OpenerId: ptrOf(p.TargetInfo.OpenerID), + Title: ptrOf(p.TargetInfo.Title), }) - m.publishEvent(EventTabOpened, events.CategoryPage, events.Source{Kind: events.KindCDP}, "Target.attachedToTarget", data, p.SessionID) + m.publishEvent(EventTabOpened, events.Page, oapi.BrowserEventSource{Kind: oapi.Cdp}, "Target.attachedToTarget", data, p.SessionID) } targetType := p.TargetInfo.Type diff --git a/server/lib/cdpmonitor/handlers_test.go b/server/lib/cdpmonitor/handlers_test.go index cac89b16..cb7fe6e4 100644 --- a/server/lib/cdpmonitor/handlers_test.go +++ b/server/lib/cdpmonitor/handlers_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -27,9 +28,9 @@ func TestConsoleEvents(t *testing.T) { }, }) ev := ec.waitFor(t, "console_log", 2*time.Second) - assert.Equal(t, events.CategoryConsole, ev.Category) - assert.Equal(t, events.KindCDP, ev.Source.Kind) - assert.Equal(t, "Runtime.consoleAPICalled", ev.Source.Event) + assert.Equal(t, events.Console, ev.Category) + assert.Equal(t, oapi.Cdp, ev.Source.Kind) + assert.Equal(t, "Runtime.consoleAPICalled", *ev.Source.Event) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "log", data["level"]) @@ -50,7 +51,7 @@ func TestConsoleEvents(t *testing.T) { }, }) ev := ec.waitFor(t, "console_error", 2*time.Second) - assert.Equal(t, events.CategoryConsole, ev.Category) + assert.Equal(t, events.Console, ev.Category) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "Uncaught TypeError", data["text"]) @@ -112,8 +113,8 @@ func TestNetworkEvents(t *testing.T) { }, }) ev := ec.waitFor(t, "network_request", 2*time.Second) - assert.Equal(t, events.CategoryNetwork, ev.Category) - assert.Equal(t, "Network.requestWillBeSent", ev.Source.Event) + assert.Equal(t, events.Network, ev.Category) + assert.Equal(t, "Network.requestWillBeSent", *ev.Source.Event) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) @@ -137,7 +138,7 @@ func TestNetworkEvents(t *testing.T) { }) ev2 := ec.waitFor(t, "network_response", 3*time.Second) - assert.Equal(t, "Network.loadingFinished", ev2.Source.Event) + assert.Equal(t, "Network.loadingFinished", *ev2.Source.Event) var data2 map[string]any require.NoError(t, json.Unmarshal(ev2.Data, &data2)) assert.Equal(t, float64(200), data2["status"]) @@ -163,7 +164,7 @@ func TestNetworkEvents(t *testing.T) { }, }) ev := ec.waitFor(t, "network_loading_failed", 2*time.Second) - assert.Equal(t, events.CategoryNetwork, ev.Category) + assert.Equal(t, events.Network, ev.Category) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "net::ERR_CONNECTION_REFUSED", data["error_text"]) @@ -232,8 +233,8 @@ func TestPageEvents(t *testing.T) { }, }) ev := ec.waitFor(t, "page_navigation", 2*time.Second) - assert.Equal(t, events.CategoryPage, ev.Category) - assert.Equal(t, "Page.frameNavigated", ev.Source.Event) + assert.Equal(t, events.Page, ev.Category) + assert.Equal(t, "Page.frameNavigated", *ev.Source.Event) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "https://example.com/page", data["url"]) @@ -243,7 +244,7 @@ func TestPageEvents(t *testing.T) { "params": map[string]any{"timestamp": 1000.0}, }) ev2 := ec.waitFor(t, "page_dom_content_loaded", 2*time.Second) - assert.Equal(t, events.CategoryPage, ev2.Category) + assert.Equal(t, events.Page, ev2.Category) var data2 map[string]any require.NoError(t, json.Unmarshal(ev2.Data, &data2)) assert.Equal(t, float64(1000.0), data2["cdp_timestamp"]) @@ -255,7 +256,7 @@ func TestPageEvents(t *testing.T) { "params": map[string]any{"timestamp": 1001.0}, }) ev3 := ec.waitFor(t, "page_load", 2*time.Second) - assert.Equal(t, events.CategoryPage, ev3.Category) + assert.Equal(t, events.Page, ev3.Category) var data3 map[string]any require.NoError(t, json.Unmarshal(ev3.Data, &data3)) assert.Equal(t, float64(1001.0), data3["cdp_timestamp"]) @@ -283,8 +284,8 @@ func TestTabOpened(t *testing.T) { }, }) ev := ec.waitFor(t, "page_tab_opened", 2*time.Second) - assert.Equal(t, events.CategoryPage, ev.Category) - assert.Equal(t, "Target.attachedToTarget", ev.Source.Event) + assert.Equal(t, events.Page, ev.Category) + assert.Equal(t, "Target.attachedToTarget", *ev.Source.Event) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "target-tab", data["target_id"]) @@ -325,8 +326,8 @@ func TestBindingAndTimeline(t *testing.T) { }, }) ev := ec.waitFor(t, "interaction_click", 2*time.Second) - assert.Equal(t, events.CategoryInteraction, ev.Category) - assert.Equal(t, "Runtime.bindingCalled", ev.Source.Event) + assert.Equal(t, events.Interaction, ev.Category) + assert.Equal(t, "Runtime.bindingCalled", *ev.Source.Event) }) t.Run("interaction_scroll_settled", func(t *testing.T) { @@ -338,7 +339,7 @@ func TestBindingAndTimeline(t *testing.T) { }, }) ev := ec.waitFor(t, "interaction_scroll_settled", 2*time.Second) - assert.Equal(t, events.CategoryInteraction, ev.Category) + assert.Equal(t, events.Interaction, ev.Category) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, float64(500), data["to_y"]) @@ -361,8 +362,8 @@ func TestBindingAndTimeline(t *testing.T) { }, }) ev := ec.waitFor(t, "page_layout_shift", 2*time.Second) - assert.Equal(t, events.KindCDP, ev.Source.Kind) - assert.Equal(t, "PerformanceTimeline.timelineEventAdded", ev.Source.Event) + assert.Equal(t, oapi.Cdp, ev.Source.Kind) + assert.Equal(t, "PerformanceTimeline.timelineEventAdded", *ev.Source.Event) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.Equal(t, "frame-ls", data["source_frame_id"]) diff --git a/server/lib/cdpmonitor/monitor.go b/server/lib/cdpmonitor/monitor.go index c25dfac0..c34be25c 100644 --- a/server/lib/cdpmonitor/monitor.go +++ b/server/lib/cdpmonitor/monitor.go @@ -11,6 +11,7 @@ import ( "github.com/coder/websocket" "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" ) // UpstreamProvider abstracts *devtoolsproxy.UpstreamManager for testability. @@ -383,19 +384,22 @@ func (m *Monitor) initSession(ctx context.Context) { return } - if _, err := m.send(ctx, "Target.setAutoAttach", map[string]any{ + if _, err := m.send(ctx, cdpMethodSetAutoAttach, map[string]any{ "autoAttach": true, "waitForDebuggerOnStart": false, "flatten": true, }, ""); err != nil && ctx.Err() == nil { // Without auto-attach the monitor will never see new targets: treat as fatal. m.log.Error("cdpmonitor: Target.setAutoAttach failed — monitor will not observe new targets", "err", err) + initFailedData, _ := json.Marshal(oapi.BrowserMonitorInitFailedEventData{ + Step: cdpMethodSetAutoAttach, + }) m.publish(events.Event{ Ts: time.Now().UnixMicro(), Type: EventMonitorInitFailed, - Category: events.CategorySystem, - Source: events.Source{Kind: events.KindLocalProcess}, - Data: json.RawMessage(`{"step":"Target.setAutoAttach"}`), + Category: events.System, + Source: oapi.BrowserEventSource{Kind: oapi.LocalProcess}, + Data: initFailedData, }) return } @@ -488,12 +492,15 @@ func (m *Monitor) handleUpstreamRestart(ctx context.Context, newURL string) { if ctx.Err() != nil { return } + disconnectedData, _ := json.Marshal(oapi.BrowserMonitorDisconnectedEventData{ + Reason: oapi.ChromeRestarted, + }) m.publish(events.Event{ Ts: time.Now().UnixMicro(), Type: EventMonitorDisconnected, - Category: events.CategorySystem, - Source: events.Source{Kind: events.KindLocalProcess}, - Data: json.RawMessage(`{"reason":"` + ReasonChromeRestarted + `"}`), + Category: events.System, + Source: oapi.BrowserEventSource{Kind: oapi.LocalProcess}, + Data: disconnectedData, }) startReconnect := time.Now() @@ -519,12 +526,15 @@ func (m *Monitor) handleUpstreamRestart(ctx context.Context, newURL string) { m.lifeMu.Unlock() m.clearState() m.running.Store(false) + reconnectFailedData, _ := json.Marshal(oapi.BrowserMonitorReconnectFailedEventData{ + Reason: oapi.ReconnectExhausted, + }) m.publish(events.Event{ Ts: time.Now().UnixMicro(), Type: EventMonitorReconnectFailed, - Category: events.CategorySystem, - Source: events.Source{Kind: events.KindLocalProcess}, - Data: json.RawMessage(`{"reason":"` + ReasonReconnectExhausted + `"}`), + Category: events.System, + Source: oapi.BrowserEventSource{Kind: oapi.LocalProcess}, + Data: reconnectFailedData, }) } return @@ -541,12 +551,15 @@ func (m *Monitor) handleUpstreamRestart(ctx context.Context, newURL string) { reconnectDurationMs := time.Since(startReconnect).Milliseconds() m.log.Info("cdpmonitor: reconnected", "url", newURL, "duration_ms", reconnectDurationMs) + reconnectedData, _ := json.Marshal(oapi.BrowserMonitorReconnectedEventData{ + ReconnectDurationMs: reconnectDurationMs, + }) m.publish(events.Event{ Ts: time.Now().UnixMicro(), Type: EventMonitorReconnected, - Category: events.CategorySystem, - Source: events.Source{Kind: events.KindLocalProcess}, - Data: json.RawMessage(fmt.Sprintf(`{"reconnect_duration_ms":%d}`, reconnectDurationMs)), + Category: events.System, + Source: oapi.BrowserEventSource{Kind: oapi.LocalProcess}, + Data: reconnectedData, }) } diff --git a/server/lib/cdpmonitor/monitor_test.go b/server/lib/cdpmonitor/monitor_test.go index 07267078..33c277fa 100644 --- a/server/lib/cdpmonitor/monitor_test.go +++ b/server/lib/cdpmonitor/monitor_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -87,9 +88,10 @@ func TestScreenshot(t *testing.T) { require.Eventually(t, func() bool { return captureCount.Load() == 1 }, 2*time.Second, 20*time.Millisecond) ev := ec.waitFor(t, "monitor_screenshot", 2*time.Second) - assert.Equal(t, events.CategorySystem, ev.Category) - assert.Equal(t, events.KindLocalProcess, ev.Source.Kind) - assert.Equal(t, "Page.loadEventFired", ev.Source.Event) + assert.Equal(t, events.System, ev.Category) + assert.Equal(t, oapi.LocalProcess, ev.Source.Kind) + require.NotNil(t, ev.Source.Event) + assert.Equal(t, "Page.loadEventFired", *ev.Source.Event) var data map[string]any require.NoError(t, json.Unmarshal(ev.Data, &data)) assert.NotEmpty(t, data["png"]) diff --git a/server/lib/cdpmonitor/screenshot.go b/server/lib/cdpmonitor/screenshot.go index b8f17f4a..72b7b007 100644 --- a/server/lib/cdpmonitor/screenshot.go +++ b/server/lib/cdpmonitor/screenshot.go @@ -3,14 +3,13 @@ package cdpmonitor import ( "bytes" "context" - "encoding/base64" "encoding/json" "fmt" - "maps" "os/exec" "time" "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" ) // tryScreenshot fires a screenshot if the 2s rate-limit window has elapsed. @@ -33,14 +32,13 @@ func (m *Monitor) tryScreenshot(ctx context.Context, sourceEvent, sessionID stri m.screenshotInFlight.Store(false) return } - var navData json.RawMessage var navMeta map[string]string if cs := m.computedFor(sessionID); cs != nil { - navData, navMeta = cs.navSnapshot() + _, navMeta = cs.navSnapshot() } m.asyncWg.Go(func() { defer m.screenshotInFlight.Store(false) - m.captureScreenshot(ctx, sourceEvent, navData, navMeta) + m.captureScreenshot(ctx, sourceEvent, navMeta) }) } @@ -48,9 +46,9 @@ const screenshotTimeout = 10 * time.Second // captureScreenshot takes a screenshot via ffmpeg x11grab (or the screenshotFn // seam in tests), optionally downscales it, and publishes a screenshot event. -// navData and navMeta are pre-snapped from the owning session's computedState; -// they may be nil if no state machine exists for the session. -func (m *Monitor) captureScreenshot(parentCtx context.Context, sourceEvent string, navData json.RawMessage, navMeta map[string]string) { +// navMeta is pre-snapped from the owning session's computedState; it may be nil +// if no state machine exists for the session. +func (m *Monitor) captureScreenshot(parentCtx context.Context, sourceEvent string, navMeta map[string]string) { ctx, cancel := context.WithTimeout(parentCtx, screenshotTimeout) defer cancel() var pngBytes []byte @@ -76,21 +74,19 @@ func (m *Monitor) captureScreenshot(parentCtx context.Context, sourceEvent strin } } - encoded := base64.StdEncoding.EncodeToString(pngBytes) - payload := map[string]any{screenshotDataKey: encoded} - if navData != nil { - var nav map[string]any - if json.Unmarshal(navData, &nav) == nil { - maps.Copy(payload, nav) - } - } - data, _ := json.Marshal(payload) + data, _ := json.Marshal(oapi.BrowserMonitorScreenshotEventData{ + Png: pngBytes, + }) + src := oapi.BrowserEventSource{Kind: oapi.LocalProcess, Event: &sourceEvent} + if navMeta != nil { + src.Metadata = &navMeta + } m.publish(events.Event{ Ts: time.Now().UnixMicro(), Type: EventScreenshot, - Category: events.CategorySystem, - Source: events.Source{Kind: events.KindLocalProcess, Event: sourceEvent, Metadata: navMeta}, + Category: events.System, + Source: src, Data: data, }) } diff --git a/server/lib/cdpmonitor/types.go b/server/lib/cdpmonitor/types.go index 76769180..2d5b0422 100644 --- a/server/lib/cdpmonitor/types.go +++ b/server/lib/cdpmonitor/types.go @@ -68,18 +68,11 @@ const ( timelineEventLCP = "largest-contentful-paint" ) +const cdpMethodSetAutoAttach = "Target.setAutoAttach" + // CDP target type for browser pages (as opposed to workers, iframes, etc.). const targetTypePage = "page" -// screenshot event payload key for the base64-encoded PNG data. -const screenshotDataKey = "png" - -// Reason values carried in monitor lifecycle event payloads. -const ( - ReasonChromeRestarted = "chrome_restarted" - ReasonReconnectExhausted = "reconnect_exhausted" -) - // targetInfo holds metadata about an attached CDP target/session. type targetInfo struct { targetID string @@ -119,6 +112,7 @@ type networkReqState struct { resourceType string loaderID string frameID string + navSeq int64 status int statusText string resHeaders json.RawMessage diff --git a/server/lib/cdpmonitor/util.go b/server/lib/cdpmonitor/util.go index 2c5b6d34..df03e44a 100644 --- a/server/lib/cdpmonitor/util.go +++ b/server/lib/cdpmonitor/util.go @@ -4,8 +4,28 @@ import ( "encoding/json" "strings" "unicode/utf8" + + oapi "github.com/kernel/kernel-images/server/lib/oapi" ) +func ptrOf[T any](v T) *T { return &v } + +// marshalNavEventContext marshals a navContext and sequence number into the +// BrowserEventContext JSON payload used by network_idle, page_layout_settled, +// and page_navigation_settled events. +func marshalNavEventContext(ctx navContext, seq int) json.RawMessage { + data, _ := json.Marshal(oapi.BrowserEventContext{ + SessionId: ctx.sessionID, + TargetId: ctx.targetID, + TargetType: oapi.BrowserTargetType(ctx.targetType), + FrameId: ptrOf(ctx.frameID), + LoaderId: ptrOf(ctx.loaderID), + Url: ptrOf(ctx.url), + NavSeq: int64(seq), + }) + return data +} + // consoleArgString extracts a display string from a CDP console argument. // For strings it unquotes the JSON value; for other types it returns the raw JSON. func consoleArgString(a cdpRuntimeRemoteObject) string { diff --git a/server/lib/events/event.go b/server/lib/events/event.go index aa0e6b77..892c7718 100644 --- a/server/lib/events/event.go +++ b/server/lib/events/event.go @@ -3,70 +3,40 @@ package events import ( "encoding/json" "log/slog" + + oapi "github.com/kernel/kernel-images/server/lib/oapi" ) // maxS2RecordBytes is the maximum record size for the S2 event pipeline (1 MB). const maxS2RecordBytes = 1_000_000 -// EventCategory determines type of logging -type EventCategory string - -const ( - CategoryConsole EventCategory = "console" - CategoryNetwork EventCategory = "network" - CategoryPage EventCategory = "page" - CategoryInteraction EventCategory = "interaction" - CategoryLiveview EventCategory = "liveview" - CategoryCaptcha EventCategory = "captcha" - CategorySystem EventCategory = "system" -) - -// AllCategories is the canonical list of all known event categories. -var AllCategories = []EventCategory{ - CategoryConsole, CategoryNetwork, CategoryPage, CategoryInteraction, - CategoryLiveview, CategoryCaptcha, CategorySystem, -} - -var validCategories = func() map[EventCategory]struct{} { - m := make(map[EventCategory]struct{}, len(AllCategories)) - for _, c := range AllCategories { - m[c] = struct{}{} - } - return m -}() - -// ValidCategory reports whether c is a known EventCategory. -func ValidCategory(c EventCategory) bool { - _, ok := validCategories[c] - return ok -} - -type SourceKind string - const ( - KindCDP SourceKind = "cdp" - KindKernelAPI SourceKind = "kernel_api" - KindExtension SourceKind = "extension" - KindLocalProcess SourceKind = "local_process" + Console = oapi.TelemetryEventCategory("console") + Network = oapi.TelemetryEventCategory("network") + Page = oapi.TelemetryEventCategory("page") + Interaction = oapi.TelemetryEventCategory("interaction") + System = oapi.TelemetryEventCategory("system") ) -// Source captures provenance: which producer emitted the event and any -// producer-specific context (e.g. CDP target/session/frame IDs). -type Source struct { - Kind SourceKind `json:"kind"` - Event string `json:"event,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` +// AllCategories is the canonical list of all configurable event categories. +// System events are always captured regardless of telemetry config. +var AllCategories = []oapi.TelemetryEventCategory{ + Console, + Network, + Page, + Interaction, + System, } // Event is the portable event schema. It contains only producer-emitted content; // pipeline metadata (seq) lives on the Envelope. type Event struct { - Ts int64 `json:"ts"` // Unix microseconds (µs since epoch) - Type string `json:"type"` - Category EventCategory `json:"category"` - Source Source `json:"source"` - Data json.RawMessage `json:"data,omitempty"` - Truncated bool `json:"truncated,omitempty"` + Ts int64 `json:"ts"` // Unix microseconds (µs since epoch) + Type string `json:"type"` + Category oapi.TelemetryEventCategory `json:"category"` + Source oapi.BrowserEventSource `json:"source"` + Data json.RawMessage `json:"data,omitempty"` + Truncated bool `json:"truncated,omitempty"` } // Envelope wraps an Event with pipeline-assigned metadata. diff --git a/server/lib/events/events_test.go b/server/lib/events/events_test.go index a7eea87b..24a5ad5c 100644 --- a/server/lib/events/events_test.go +++ b/server/lib/events/events_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + oapi "github.com/kernel/kernel-images/server/lib/oapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,16 +25,19 @@ func TestEventSerialization(t *testing.T) { ev := Event{ Ts: 1234567890000, Type: "console.log", - Category: CategoryConsole, - Source: Source{ - Kind: KindCDP, - Event: "Runtime.consoleAPICalled", - Metadata: map[string]string{ - "target_id": "target-1", - "cdp_session_id": "cdp-session-1", - "frame_id": "frame-1", - "parent_frame_id": "parent-frame-1", - }, + Category: Console, + Source: oapi.BrowserEventSource{ + Kind: oapi.Cdp, + Event: func() *string { s := "Runtime.consoleAPICalled"; return &s }(), + Metadata: func() *map[string]string { + m := map[string]string{ + "target_id": "target-1", + "cdp_session_id": "cdp-session-1", + "frame_id": "frame-1", + "parent_frame_id": "parent-frame-1", + } + return &m + }(), }, Data: json.RawMessage(`{"message":"hello"}`), } @@ -63,8 +67,8 @@ func TestEnvelopeSerialization(t *testing.T) { Event: Event{ Ts: 1000, Type: "console.log", - Category: CategoryConsole, - Source: Source{Kind: KindCDP}, + Category: Console, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, }, } @@ -75,7 +79,7 @@ func TestEnvelopeSerialization(t *testing.T) { require.NoError(t, json.Unmarshal(b, &decoded)) assert.Equal(t, float64(1), decoded["seq"]) - assert.NotContains(t, decoded, "capture_session_id") + assert.NotContains(t, decoded, "telemetry_session_id") inner, ok := decoded["event"].(map[string]any) require.True(t, ok) assert.Equal(t, "console.log", inner["type"]) @@ -86,8 +90,8 @@ func TestEventData(t *testing.T) { ev := Event{ Ts: 1000, Type: "page.navigation", - Category: CategoryPage, - Source: Source{Kind: KindCDP}, + Category: Page, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, Data: rawData, } @@ -103,8 +107,8 @@ func TestEventOmitEmpty(t *testing.T) { ev := Event{ Ts: 1000, Type: "console.log", - Category: CategoryConsole, - Source: Source{Kind: KindCDP}, + Category: Console, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, } b, err := json.Marshal(ev) @@ -118,8 +122,8 @@ func mkEnv(seq uint64, ev Event) Envelope { return Envelope{Seq: seq, Event: ev} } -func cdpEvent(typ string, cat EventCategory) Event { - return Event{Type: typ, Category: cat, Source: Source{Kind: KindCDP}} +func cdpEvent(typ string, cat oapi.TelemetryEventCategory) Event { + return Event{Type: typ, Category: cat, Source: oapi.BrowserEventSource{Kind: oapi.Cdp}} } func newTestRingBuffer(t *testing.T, capacity int) *ringBuffer { @@ -135,9 +139,9 @@ func TestRingBuffer(t *testing.T) { reader := rb.newReader(0) envelopes := []Envelope{ - mkEnv(1, cdpEvent("console.log", CategoryConsole)), - mkEnv(2, cdpEvent("network.request", CategoryNetwork)), - mkEnv(3, cdpEvent("page.navigation", CategoryPage)), + mkEnv(1, cdpEvent("console.log", Console)), + mkEnv(2, cdpEvent("network.request", Network)), + mkEnv(3, cdpEvent("page.navigation", Page)), } for _, env := range envelopes { @@ -160,9 +164,9 @@ func TestRingBufferOverflowNoBlock(t *testing.T) { done := make(chan struct{}) go func() { - rb.publish(mkEnv(1, cdpEvent("console.log", CategoryConsole))) - rb.publish(mkEnv(2, cdpEvent("console.log", CategoryConsole))) - rb.publish(mkEnv(3, cdpEvent("console.log", CategoryConsole))) + rb.publish(mkEnv(1, cdpEvent("console.log", Console))) + rb.publish(mkEnv(2, cdpEvent("console.log", Console))) + rb.publish(mkEnv(3, cdpEvent("console.log", Console))) close(done) }() @@ -186,9 +190,9 @@ func TestRingBufferOverflowExistingReader(t *testing.T) { rb := newTestRingBuffer(t,2) reader := rb.newReader(0) - rb.publish(mkEnv(1, cdpEvent("console.log", CategoryConsole))) - rb.publish(mkEnv(2, cdpEvent("console.log", CategoryConsole))) - rb.publish(mkEnv(3, cdpEvent("console.log", CategoryConsole))) + rb.publish(mkEnv(1, cdpEvent("console.log", Console))) + rb.publish(mkEnv(2, cdpEvent("console.log", Console))) + rb.publish(mkEnv(3, cdpEvent("console.log", Console))) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -210,7 +214,7 @@ func TestRingBufferOverflowExistingReader(t *testing.T) { func TestNewReaderResume(t *testing.T) { rb := newTestRingBuffer(t,10) for i := uint64(1); i <= 5; i++ { - rb.publish(mkEnv(i, cdpEvent("console.log", CategoryConsole))) + rb.publish(mkEnv(i, cdpEvent("console.log", Console))) } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) @@ -234,7 +238,7 @@ func TestNewReaderResume(t *testing.T) { t.Run("resume_before_oldest_triggers_drop", func(t *testing.T) { small := newTestRingBuffer(t, 3) for i := uint64(1); i <= 5; i++ { - small.publish(mkEnv(i, cdpEvent("console.log", CategoryConsole))) + small.publish(mkEnv(i, cdpEvent("console.log", Console))) } // oldest in ring is seq 3, requesting resume after seq 1 reader := small.newReader(1) @@ -274,7 +278,7 @@ func TestConcurrentPublishRead(t *testing.T) { go func() { defer wg.Done() for i := 1; i <= numEvents; i++ { - rb.publish(mkEnv(uint64(i), cdpEvent("console.log", CategoryConsole))) + rb.publish(mkEnv(uint64(i), cdpEvent("console.log", Console))) } }() @@ -293,7 +297,7 @@ func TestConcurrentReaders(t *testing.T) { } for i := 0; i < numEvents; i++ { - rb.publish(mkEnv(uint64(i+1), cdpEvent("console.log", CategoryConsole))) + rb.publish(mkEnv(uint64(i+1), cdpEvent("console.log", Console))) } var wg sync.WaitGroup @@ -332,7 +336,7 @@ func TestRingBufferResetWithActiveReader(t *testing.T) { // Publish some events so the reader advances. for i := uint64(1); i <= 5; i++ { - rb.publish(mkEnv(i, cdpEvent("console.log", CategoryConsole))) + rb.publish(mkEnv(i, cdpEvent("console.log", Console))) } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -350,7 +354,7 @@ func TestRingBufferResetWithActiveReader(t *testing.T) { assert.ErrorIs(t, err, context.DeadlineExceeded, "reader should block after reset") // Publish new events; reader should resume from seq 1. - rb.publish(mkEnv(1, cdpEvent("page.navigation", CategoryPage))) + rb.publish(mkEnv(1, cdpEvent("page.navigation", Page))) env := readEnvelope(t, reader, ctx) assert.Equal(t, uint64(1), env.Seq) assert.Equal(t, "page.navigation", env.Event.Type) diff --git a/server/lib/events/eventsstorage_writer_test.go b/server/lib/events/eventsstorage_writer_test.go index ec9631ad..2513cb12 100644 --- a/server/lib/events/eventsstorage_writer_test.go +++ b/server/lib/events/eventsstorage_writer_test.go @@ -60,7 +60,7 @@ func newTestStream(t *testing.T, capacity int) *EventStream { } func makeEvent(typ string) Event { - return Event{Type: typ, Category: CategorySystem} + return Event{Type: typ, Category: System} } func TestStorageWriter_NormalAppend(t *testing.T) { diff --git a/server/lib/oapi/oapi.go b/server/lib/oapi/oapi.go index 525b2c47..2269d704 100644 --- a/server/lib/oapi/oapi.go +++ b/server/lib/oapi/oapi.go @@ -26,481 +26,1831 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) -// Defines values for CaptureConfigCategories. +// Defines values for BrowserConsoleErrorEventType. const ( - CaptureConfigCategoriesCaptcha CaptureConfigCategories = "captcha" - CaptureConfigCategoriesConsole CaptureConfigCategories = "console" - CaptureConfigCategoriesInteraction CaptureConfigCategories = "interaction" - CaptureConfigCategoriesLiveview CaptureConfigCategories = "liveview" - CaptureConfigCategoriesNetwork CaptureConfigCategories = "network" - CaptureConfigCategoriesPage CaptureConfigCategories = "page" - CaptureConfigCategoriesSystem CaptureConfigCategories = "system" + ConsoleError BrowserConsoleErrorEventType = "console_error" ) -// Valid indicates whether the value is a known member of the CaptureConfigCategories enum. -func (e CaptureConfigCategories) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserConsoleErrorEventType enum. +func (e BrowserConsoleErrorEventType) Valid() bool { switch e { - case CaptureConfigCategoriesCaptcha: - return true - case CaptureConfigCategoriesConsole: - return true - case CaptureConfigCategoriesInteraction: - return true - case CaptureConfigCategoriesLiveview: - return true - case CaptureConfigCategoriesNetwork: - return true - case CaptureConfigCategoriesPage: - return true - case CaptureConfigCategoriesSystem: + case ConsoleError: return true default: return false } } -// Defines values for CaptureSessionStatus. +// Defines values for BrowserConsoleLogEventType. const ( - CaptureSessionStatusRunning CaptureSessionStatus = "running" - CaptureSessionStatusStopped CaptureSessionStatus = "stopped" + ConsoleLog BrowserConsoleLogEventType = "console_log" ) -// Valid indicates whether the value is a known member of the CaptureSessionStatus enum. -func (e CaptureSessionStatus) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserConsoleLogEventType enum. +func (e BrowserConsoleLogEventType) Valid() bool { switch e { - case CaptureSessionStatusRunning: - return true - case CaptureSessionStatusStopped: + case ConsoleLog: return true default: return false } } -// Defines values for ClickMouseRequestButton. +// Defines values for BrowserEventSourceKind. const ( - ClickMouseRequestButtonBack ClickMouseRequestButton = "back" - ClickMouseRequestButtonForward ClickMouseRequestButton = "forward" - ClickMouseRequestButtonLeft ClickMouseRequestButton = "left" - ClickMouseRequestButtonMiddle ClickMouseRequestButton = "middle" - ClickMouseRequestButtonRight ClickMouseRequestButton = "right" + 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 ClickMouseRequestButton enum. -func (e ClickMouseRequestButton) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserEventSourceKind enum. +func (e BrowserEventSourceKind) Valid() bool { switch e { - case ClickMouseRequestButtonBack: - return true - case ClickMouseRequestButtonForward: + case Cdp: return true - case ClickMouseRequestButtonLeft: + case Extension: return true - case ClickMouseRequestButtonMiddle: + case KernelApi: return true - case ClickMouseRequestButtonRight: + case LocalProcess: return true default: return false } } -// Defines values for ClickMouseRequestClickType. +// Defines values for BrowserInteractionClickEventType. const ( - Click ClickMouseRequestClickType = "click" - Down ClickMouseRequestClickType = "down" - Up ClickMouseRequestClickType = "up" + InteractionClick BrowserInteractionClickEventType = "interaction_click" ) -// 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 BrowserInteractionClickEventType enum. +func (e BrowserInteractionClickEventType) Valid() bool { switch e { - case Click: - return true - case Down: - return true - case Up: + case InteractionClick: return true default: return false } } -// Defines values for ComputerActionType. +// Defines values for BrowserInteractionKeyEventType. 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" + InteractionKey BrowserInteractionKeyEventType = "interaction_key" ) -// 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 BrowserInteractionKeyEventType enum. +func (e BrowserInteractionKeyEventType) 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 InteractionKey: return true default: return false } } -// Defines values for DragMouseRequestButton. +// Defines values for BrowserInteractionScrollSettledEventType. const ( - DragMouseRequestButtonLeft DragMouseRequestButton = "left" - DragMouseRequestButtonMiddle DragMouseRequestButton = "middle" - DragMouseRequestButtonRight DragMouseRequestButton = "right" + InteractionScrollSettled BrowserInteractionScrollSettledEventType = "interaction_scroll_settled" ) -// 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 BrowserInteractionScrollSettledEventType enum. +func (e BrowserInteractionScrollSettledEventType) Valid() bool { switch e { - case DragMouseRequestButtonLeft: - return true - case DragMouseRequestButtonMiddle: - return true - case DragMouseRequestButtonRight: + case InteractionScrollSettled: return true default: return false } } -// Defines values for EventCategory. +// Defines values for BrowserMonitorDisconnectedEventType. const ( - EventCategoryCaptcha EventCategory = "captcha" - EventCategoryConsole EventCategory = "console" - EventCategoryInteraction EventCategory = "interaction" - EventCategoryLiveview EventCategory = "liveview" - EventCategoryNetwork EventCategory = "network" - EventCategoryPage EventCategory = "page" - EventCategorySystem EventCategory = "system" + MonitorDisconnected BrowserMonitorDisconnectedEventType = "monitor_disconnected" ) -// Valid indicates whether the value is a known member of the EventCategory enum. -func (e EventCategory) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventType enum. +func (e BrowserMonitorDisconnectedEventType) Valid() bool { switch e { - case EventCategoryCaptcha: - return true - case EventCategoryConsole: - return true - case EventCategoryInteraction: - return true - case EventCategoryLiveview: - return true - case EventCategoryNetwork: - return true - case EventCategoryPage: - return true - case EventCategorySystem: + case MonitorDisconnected: return true default: return false } } -// Defines values for EventSourceKind. +// Defines values for BrowserMonitorDisconnectedEventDataReason. const ( - Cdp EventSourceKind = "cdp" - Extension EventSourceKind = "extension" - KernelApi EventSourceKind = "kernel_api" - LocalProcess EventSourceKind = "local_process" + ChromeRestarted BrowserMonitorDisconnectedEventDataReason = "chrome_restarted" ) -// Valid indicates whether the value is a known member of the EventSourceKind enum. -func (e EventSourceKind) Valid() bool { +// Valid indicates whether the value is a known member of the BrowserMonitorDisconnectedEventDataReason enum. +func (e BrowserMonitorDisconnectedEventDataReason) Valid() bool { switch e { - case Cdp: - return true - case Extension: - return true - case KernelApi: - return true - case LocalProcess: + case ChromeRestarted: return true default: return false } } -// Defines values for FileSystemEventType. +// Defines values for BrowserMonitorInitFailedEventType. const ( - CREATE FileSystemEventType = "CREATE" - DELETE FileSystemEventType = "DELETE" - RENAME FileSystemEventType = "RENAME" - WRITE FileSystemEventType = "WRITE" + MonitorInitFailed BrowserMonitorInitFailedEventType = "monitor_init_failed" ) -// 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 BrowserMonitorInitFailedEventType enum. +func (e BrowserMonitorInitFailedEventType) Valid() bool { switch e { - case CREATE: - return true - case DELETE: - return true - case RENAME: - return true - case WRITE: + case MonitorInitFailed: return true default: return false } } -// Defines values for PatchDisplayRequestRefreshRate. +// Defines values for BrowserMonitorReconnectFailedEventType. const ( - N10 PatchDisplayRequestRefreshRate = 10 - N25 PatchDisplayRequestRefreshRate = 25 - N30 PatchDisplayRequestRefreshRate = 30 - N60 PatchDisplayRequestRefreshRate = 60 + MonitorReconnectFailed BrowserMonitorReconnectFailedEventType = "monitor_reconnect_failed" ) -// 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 BrowserMonitorReconnectFailedEventType enum. +func (e BrowserMonitorReconnectFailedEventType) Valid() bool { switch e { - case N10: - return true - case N25: - return true - case N30: - return true - case N60: + case MonitorReconnectFailed: return true default: return false } } -// Defines values for ProcessKillRequestSignal. +// Defines values for BrowserMonitorReconnectFailedEventDataReason. const ( - HUP ProcessKillRequestSignal = "HUP" - INT ProcessKillRequestSignal = "INT" - KILL ProcessKillRequestSignal = "KILL" - TERM ProcessKillRequestSignal = "TERM" + ReconnectExhausted BrowserMonitorReconnectFailedEventDataReason = "reconnect_exhausted" ) -// 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 BrowserMonitorReconnectFailedEventDataReason enum. +func (e BrowserMonitorReconnectFailedEventDataReason) Valid() bool { switch e { - case HUP: - return true - case INT: - return true - case KILL: - return true - case TERM: + case ReconnectExhausted: return true default: return false } } -// Defines values for ProcessStatusState. +// Defines values for BrowserMonitorReconnectedEventType. const ( - ProcessStatusStateExited ProcessStatusState = "exited" - ProcessStatusStateRunning ProcessStatusState = "running" + MonitorReconnected BrowserMonitorReconnectedEventType = "monitor_reconnected" ) -// 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 BrowserMonitorReconnectedEventType enum. +func (e BrowserMonitorReconnectedEventType) Valid() bool { switch e { - case ProcessStatusStateExited: - return true - case ProcessStatusStateRunning: + case MonitorReconnected: return true default: return false } } -// Defines values for ProcessStreamEventEvent. +// Defines values for BrowserMonitorScreenshotEventType. const ( - Exit ProcessStreamEventEvent = "exit" + MonitorScreenshot BrowserMonitorScreenshotEventType = "monitor_screenshot" ) -// 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 BrowserMonitorScreenshotEventType enum. +func (e BrowserMonitorScreenshotEventType) Valid() bool { switch e { - case Exit: + case MonitorScreenshot: return true default: return false } } -// Defines values for ProcessStreamEventStream. +// Defines values for BrowserNetworkIdleEventType. const ( - Stderr ProcessStreamEventStream = "stderr" - Stdout ProcessStreamEventStream = "stdout" + NetworkIdle BrowserNetworkIdleEventType = "network_idle" ) -// 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 BrowserNetworkIdleEventType enum. +func (e BrowserNetworkIdleEventType) Valid() bool { switch e { - case Stderr: - return true - case Stdout: + case NetworkIdle: return true default: return false } } -// Defines values for PublishEventRequestCategory. +// Defines values for BrowserNetworkLoadingFailedEventType. const ( - Captcha PublishEventRequestCategory = "captcha" - Console PublishEventRequestCategory = "console" - Interaction PublishEventRequestCategory = "interaction" - Liveview PublishEventRequestCategory = "liveview" - Network PublishEventRequestCategory = "network" - Page PublishEventRequestCategory = "page" - System PublishEventRequestCategory = "system" + NetworkLoadingFailed BrowserNetworkLoadingFailedEventType = "network_loading_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 BrowserNetworkLoadingFailedEventType enum. +func (e BrowserNetworkLoadingFailedEventType) Valid() bool { switch e { - case Captcha: - return true - case Console: - return true - case Interaction: - return true - case Liveview: - return true - case Network: - return true - case Page: - return true - case System: + case NetworkLoadingFailed: return true default: return false } } -// Defines values for DownloadDirZstdParamsCompressionLevel. +// Defines values for BrowserNetworkRequestEventType. const ( - Best DownloadDirZstdParamsCompressionLevel = "best" - Better DownloadDirZstdParamsCompressionLevel = "better" - Default DownloadDirZstdParamsCompressionLevel = "default" - Fastest DownloadDirZstdParamsCompressionLevel = "fastest" + NetworkRequest BrowserNetworkRequestEventType = "network_request" ) -// 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 BrowserNetworkRequestEventType enum. +func (e BrowserNetworkRequestEventType) Valid() bool { switch e { - case Best: - return true - case Better: - return true - case Default: - return true - case Fastest: + case NetworkRequest: return true default: return false } } -// Defines values for LogsStreamParamsSource. +// Defines values for BrowserNetworkResponseEventType. const ( - Path LogsStreamParamsSource = "path" - Supervisor LogsStreamParamsSource = "supervisor" + NetworkResponse BrowserNetworkResponseEventType = "network_response" ) -// 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 BrowserNetworkResponseEventType enum. +func (e BrowserNetworkResponseEventType) Valid() bool { switch e { - case Path: - return true - case Supervisor: + case NetworkResponse: 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 BrowserPageDomContentLoadedEventType. +const ( + PageDomContentLoaded BrowserPageDomContentLoadedEventType = "page_dom_content_loaded" +) -// CaptureConfig Capture filtering preferences. -type CaptureConfig struct { - // Categories Event categories to capture. When omitted or empty, all categories are captured. - Categories *[]CaptureConfigCategories `json:"categories,omitempty"` +// 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 + } } -// CaptureConfigCategories defines model for CaptureConfig.Categories. -type CaptureConfigCategories string - -// CaptureSession A capture session resource. -type CaptureSession struct { - // Config Capture filtering preferences. - Config CaptureConfig `json:"config"` - CreatedAt time.Time `json:"created_at"` - Id string `json:"id"` +// Defines values for BrowserPageLayoutSettledEventType. +const ( + PageLayoutSettled BrowserPageLayoutSettledEventType = "page_layout_settled" +) - // Seq Process-monotonic sequence number of the last published event. Does not reset between sessions. - Seq int64 `json:"seq"` - Status CaptureSessionStatus `json:"status"` +// 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 + } } -// CaptureSessionStatus defines model for CaptureSession.Status. -type CaptureSessionStatus string - -// ClickMouseRequest defines model for ClickMouseRequest. -type ClickMouseRequest struct { - // Button Mouse button to interact with - Button *ClickMouseRequestButton `json:"button,omitempty"` +// Defines values for BrowserPageLayoutShiftEventType. +const ( + PageLayoutShift BrowserPageLayoutShiftEventType = "page_layout_shift" +) - // ClickType Type of click action - ClickType *ClickMouseRequestClickType `json:"click_type,omitempty"` +// 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 + } +} - // HoldKeys Modifier keys to hold during the click - HoldKeys *[]string `json:"hold_keys,omitempty"` +// Defines values for BrowserPageLcpEventType. +const ( + PageLcp BrowserPageLcpEventType = "page_lcp" +) - // NumClicks Number of times to repeat the click - NumClicks *int `json:"num_clicks,omitempty"` +// 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 + } +} - // X X coordinate of the click position - X int `json:"x"` +// Defines values for BrowserPageLoadEventType. +const ( + PageLoad BrowserPageLoadEventType = "page_load" +) - // Y Y coordinate of the click position - Y int `json:"y"` +// 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 + } } -// ClickMouseRequestButton Mouse button to interact with -type ClickMouseRequestButton string - -// ClickMouseRequestClickType Type of click action -type ClickMouseRequestClickType string +// Defines values for BrowserPageNavigationEventType. +const ( + PageNavigation BrowserPageNavigationEventType = "page_navigation" +) -// ClipboardContent defines model for ClipboardContent. -type ClipboardContent struct { - // Text Current clipboard text content - Text string `json:"text"` +// 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 + } } -// ComputerAction A single computer action to execute as part of a batch. The `type` field selects which +// 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 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 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 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 ( + Exited ProcessStatusState = "exited" + Running ProcessStatusState = "running" +) + +// Valid indicates whether the value is a known member of the ProcessStatusState enum. +func (e ProcessStatusState) Valid() bool { + switch e { + case Exited: + return true + case Running: + 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 ( + PublishEventRequestCategoryConsole PublishEventRequestCategory = "console" + PublishEventRequestCategoryInteraction PublishEventRequestCategory = "interaction" + PublishEventRequestCategoryNetwork PublishEventRequestCategory = "network" + PublishEventRequestCategoryPage PublishEventRequestCategory = "page" + PublishEventRequestCategorySystem PublishEventRequestCategory = "system" +) + +// Valid indicates whether the value is a known member of the PublishEventRequestCategory enum. +func (e PublishEventRequestCategory) Valid() bool { + switch e { + case PublishEventRequestCategoryConsole: + return true + case PublishEventRequestCategoryInteraction: + return true + case PublishEventRequestCategoryNetwork: + return true + case PublishEventRequestCategoryPage: + return true + case PublishEventRequestCategorySystem: + return true + default: + return false + } +} + +// Defines values for TelemetryEventCategory. +const ( + Console TelemetryEventCategory = "console" + Interaction TelemetryEventCategory = "interaction" + Network TelemetryEventCategory = "network" + Page TelemetryEventCategory = "page" + System TelemetryEventCategory = "system" +) + +// Valid indicates whether the value is a known member of the TelemetryEventCategory enum. +func (e TelemetryEventCategory) Valid() bool { + switch e { + case Console: + return true + case Interaction: + return true + case Network: + return true + case Page: + return true + case System: + 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 + } +} + +// 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"` +} + +// 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"` +} + +// 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 { + 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"` +} + +// 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 Error message text. Present in both source paths. + 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 { + 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"` +} + +// 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 { + 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"` +} + +// 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 { + 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"` +} + +// 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 { + 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"` +} + +// 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"` +} + +// 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 { + Data *BrowserMonitorDisconnectedEventData `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 BrowserMonitorDisconnectedEventType `json:"type"` +} + +// BrowserMonitorDisconnectedEventType defines model for BrowserMonitorDisconnectedEvent.Type. +type BrowserMonitorDisconnectedEventType string + +// 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 { + Data *BrowserMonitorInitFailedEventData `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 BrowserMonitorInitFailedEventType `json:"type"` +} + +// BrowserMonitorInitFailedEventType defines model for BrowserMonitorInitFailedEvent.Type. +type BrowserMonitorInitFailedEventType string + +// 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"` +} + +// 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 { + Data *BrowserMonitorReconnectFailedEventData `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 BrowserMonitorReconnectFailedEventType `json:"type"` +} + +// BrowserMonitorReconnectFailedEventType defines model for BrowserMonitorReconnectFailedEvent.Type. +type BrowserMonitorReconnectFailedEventType string + +// 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 { + 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"` +} + +// 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 { + 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"` +} + +// 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 { + // 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"` + + // 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 BrowserNetworkIdleEventType `json:"type"` +} + +// 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 { + Data *BrowserNetworkLoadingFailedEventData `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 BrowserNetworkLoadingFailedEventType `json:"type"` +} + +// 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"` + + // 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"` +} + +// BrowserNetworkRequestEvent A browser network request sent event. +type BrowserNetworkRequestEvent struct { + Data *BrowserNetworkRequestEventData `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 BrowserNetworkRequestEventType `json:"type"` +} + +// 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"` + + // NavSeq Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + NavSeq int64 `json:"nav_seq"` + + // PostData Request body for POST/PUT requests, if available. + PostData *string `json:"post_data,omitempty"` + + // RedirectUrl Original URL before the redirect, present when is_redirect is true. + RedirectUrl *string `json:"redirect_url,omitempty"` + + // RequestId CDP request identifier, unique within the session. + 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"` +} + +// BrowserNetworkResponseEvent A browser network response received event. Fired after the response body is fully received, not when headers arrive. +type BrowserNetworkResponseEvent struct { + Data *BrowserNetworkResponseEventData `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 BrowserNetworkResponseEventType `json:"type"` +} + +// 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"` + + // 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"` + + // 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"` + + // 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"` + + // 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"` +} + +// BrowserPageDomContentLoadedEvent A browser DOMContentLoaded event (CDP Page.domContentEventFired). +type BrowserPageDomContentLoadedEvent struct { + Data *BrowserPageDomContentLoadedEventData `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 BrowserPageDomContentLoadedEventType `json:"type"` +} + +// 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"` + + // 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"` +} + +// 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 { + // 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"` + + // 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 BrowserPageLayoutSettledEventType `json:"type"` +} + +// BrowserPageLayoutSettledEventType defines model for BrowserPageLayoutSettledEvent.Type. +type BrowserPageLayoutSettledEventType string + +// BrowserPageLayoutShiftEvent A browser cumulative layout shift (CLS) event from the Performance Timeline API. +type BrowserPageLayoutShiftEvent struct { + Data *BrowserPageLayoutShiftEventData `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 BrowserPageLayoutShiftEventType `json:"type"` +} + +// BrowserPageLayoutShiftEventType defines model for BrowserPageLayoutShiftEvent.Type. +type BrowserPageLayoutShiftEventType string + +// 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"` + + // 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"` +} + +// BrowserPageLcpEvent A browser Largest Contentful Paint (LCP) event from the Performance Timeline API. +type BrowserPageLcpEvent struct { + Data *BrowserPageLcpEventData `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 BrowserPageLcpEventType `json:"type"` +} + +// BrowserPageLcpEventType defines model for BrowserPageLcpEvent.Type. +type BrowserPageLcpEventType string + +// BrowserPageLcpEventData defines model for BrowserPageLcpEventData. +type BrowserPageLcpEventData struct { + // FrameId CDP frame identifier within the target. + FrameId *string `json:"frame_id,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"` + + // LoadTime Load time of the LCP element in milliseconds. + LoadTime *float32 `json:"load_time,omitempty"` + + // NodeId CDP DOM node identifier of the LCP element. + NodeId *int `json:"node_id,omitempty"` + + // RenderTime Render time of the LCP element in milliseconds; 0 for cross-origin images without Timing-Allow-Origin. + RenderTime *float32 `json:"render_time,omitempty"` + + // Size Visible area of the LCP element in pixels squared. + Size *float32 `json:"size,omitempty"` + + // Url URL of the LCP element for image or video elements. + Url *string `json:"url,omitempty"` + } `json:"lcp_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 LCP element was rendered. + 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 LCP entry 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"` +} + +// BrowserPageLoadEvent A browser page load event (CDP Page.loadEventFired). +type BrowserPageLoadEvent struct { + Data *BrowserPageLoadEventData `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 BrowserPageLoadEventType `json:"type"` +} + +// BrowserPageLoadEventType defines model for BrowserPageLoadEvent.Type. +type BrowserPageLoadEventType string + +// 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"` +} + +// 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 { + Data *BrowserPageNavigationEventData `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 BrowserPageNavigationEventType `json:"type"` +} + +// BrowserPageNavigationEventType defines model for BrowserPageNavigationEvent.Type. +type BrowserPageNavigationEventType string + +// 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"` +} + +// 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 { + // 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"` + + // 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 BrowserPageNavigationSettledEventType `json:"type"` +} + +// BrowserPageNavigationSettledEventType defines model for BrowserPageNavigationSettledEvent.Type. +type BrowserPageNavigationSettledEventType string + +// 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 { + Data *BrowserPageTabOpenedEventData `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 BrowserPageTabOpenedEventType `json:"type"` +} + +// BrowserPageTabOpenedEventType defines model for BrowserPageTabOpenedEvent.Type. +type BrowserPageTabOpenedEventType string + +// 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 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"` + + // Title Initial page title of the new tab. + Title *string `json:"title,omitempty"` + + // Url Initial URL of the new tab. + Url string `json:"url"` +} + +// BrowserTargetType CDP target type of the page that produced the event. +type BrowserTargetType string + +// BrowserTelemetryCategoriesConfig Per-category telemetry capture settings for browser events. +type BrowserTelemetryCategoriesConfig struct { + // Console Console output (log, warn, error) and uncaught exceptions. + Console *BrowserTelemetryCategoryConfig `json:"console,omitempty"` + + // 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"` +} + +// BrowserTelemetryCategoryConfig Configuration for a single telemetry category. +type BrowserTelemetryCategoryConfig struct { + // Enabled Whether this category is captured. In PUT requests, omitting this field defaults to true (category enabled). 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"` +} + +// BrowserTelemetryConfig Telemetry configuration for a browser. Per-category capture settings. Omit a category or set enabled: true to capture it. Set enabled: false to exclude it. Omit the browser key entirely to capture all categories. Set all four categories to enabled: false to clear the telemetry configuration. +type BrowserTelemetryConfig struct { + // Browser Per-category telemetry capture settings for browser events. + Browser *BrowserTelemetryCategoriesConfig `json:"browser,omitempty"` +} + +// ClickMouseRequest defines model for ClickMouseRequest. +type ClickMouseRequest struct { + // Button Mouse button to interact with + Button *ClickMouseRequestButton `json:"button,omitempty"` + + // ClickType Type of click action + ClickType *ClickMouseRequestClickType `json:"click_type,omitempty"` + + // HoldKeys Modifier keys to hold during the click + HoldKeys *[]string `json:"hold_keys,omitempty"` + + // NumClicks Number of times to repeat the click + NumClicks *int `json:"num_clicks,omitempty"` + + // X X coordinate of the click position + X int `json:"x"` + + // Y Y coordinate of the click position + Y int `json:"y"` +} + +// ClickMouseRequestButton Mouse button to interact with +type ClickMouseRequestButton string + +// ClickMouseRequestClickType Type of click action +type ClickMouseRequestClickType string + +// ClipboardContent defines model for ClipboardContent. +type ClipboardContent struct { + // Text Current clipboard text content + Text string `json:"text"` +} + +// 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 { @@ -511,95 +1861,545 @@ type ComputerAction struct { Scroll *ScrollRequest `json:"scroll,omitempty"` SetCursor *SetCursorRequest `json:"set_cursor,omitempty"` - // Sleep Pause execution for a specified duration. - Sleep *SleepAction `json:"sleep,omitempty"` + // Sleep Pause execution for a specified duration. + Sleep *SleepAction `json:"sleep,omitempty"` + + // Type The type of action to perform. + Type ComputerActionType `json:"type"` + TypeText *TypeTextRequest `json:"type_text,omitempty"` +} + +// ComputerActionType The type of action to perform. +type ComputerActionType string + +// 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"` + + // Path Absolute directory path to create. + Path string `json:"path"` +} + +// DeletePathRequest defines model for DeletePathRequest. +type DeletePathRequest struct { + // Path Absolute path to delete. + Path string `json:"path"` +} + +// DeleteRecordingRequest defines model for DeleteRecordingRequest. +type DeleteRecordingRequest struct { + // Id Identifier of the recording to delete. Alphanumeric or hyphen. + Id *string `json:"id,omitempty"` +} + +// DisplayConfig defines model for DisplayConfig. +type DisplayConfig struct { + // Height Current display height in pixels + Height *int `json:"height,omitempty"` + + // RefreshRate Current display refresh rate in Hz (may be null if not detectable) + RefreshRate *int `json:"refresh_rate,omitempty"` + + // Width Current display width in pixels + Width *int `json:"width,omitempty"` +} + +// DragMouseRequest defines model for DragMouseRequest. +type DragMouseRequest struct { + // Button Mouse button to drag with + Button *DragMouseRequestButton `json:"button,omitempty"` + + // Delay Delay in milliseconds between button down and starting to move along the path. + Delay *int `json:"delay,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"` + + // HoldKeys Modifier keys to hold during the drag + HoldKeys *[]string `json:"hold_keys,omitempty"` + + // Path Ordered list of [x, y] coordinate pairs to move through while dragging. Must contain at least 2 points. + Path [][]int `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"` + + // 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"` +} + +// DragMouseRequestButton Mouse button to drag with +type DragMouseRequestButton string + +// Error defines model for Error. +type Error struct { + Message string `json:"message"` +} + +// 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"` + + // TimeoutSec Maximum execution time in seconds. Default is 60. + TimeoutSec *int `json:"timeout_sec,omitempty"` +} + +// ExecutePlaywrightResult Result of Playwright code execution +type ExecutePlaywrightResult struct { + // Error Error message if execution failed + Error *string `json:"error,omitempty"` + + // Result The value returned by the code (if any) + Result interface{} `json:"result,omitempty"` + + // Stderr Standard error from the execution + Stderr *string `json:"stderr,omitempty"` + + // Stdout Standard output from the execution + Stdout *string `json:"stdout,omitempty"` + + // Success Whether the code executed successfully + Success bool `json:"success"` +} + +// FileInfo defines model for FileInfo. +type FileInfo struct { + // IsDir Whether the path is a directory. + IsDir bool `json:"is_dir"` + + // ModTime Last modification time. + ModTime time.Time `json:"mod_time"` + + // Mode File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--"). + Mode string `json:"mode"` + + // Name Base name of the file or directory. + Name string `json:"name"` + + // Path Absolute path. + Path string `json:"path"` + + // SizeBytes Size in bytes. 0 for directories. + SizeBytes int `json:"size_bytes"` +} + +// 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"` + + // Path Absolute path of the file or directory. + Path string `json:"path"` + + // Type Event type. + Type FileSystemEventType `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 +} + +// ListFiles Array of file or directory information entries. +type ListFiles = []FileInfo + +// LogEvent A log entry from the application. +type LogEvent struct { + // Message Log message text. + Message string `json:"message"` + + // Timestamp Time the log entry was produced. + Timestamp time.Time `json:"timestamp"` +} + +// MousePositionResponse defines model for MousePositionResponse. +type MousePositionResponse struct { + // X X coordinate of the cursor + X int `json:"x"` + + // Y Y coordinate of the cursor + Y int `json:"y"` +} + +// 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"` + + // HoldKeys Modifier keys to hold during the move + HoldKeys *[]string `json:"hold_keys,omitempty"` + + // Smooth Use human-like Bezier curve path instead of instant mouse movement. + Smooth *bool `json:"smooth,omitempty"` + + // X X coordinate to move the cursor to + X int `json:"x"` + + // Y Y coordinate to move the cursor to + Y int `json:"y"` +} + +// MovePathRequest defines model for MovePathRequest. +type MovePathRequest struct { + // DestPath Absolute destination path. + DestPath string `json:"dest_path"` + + // SrcPath Absolute source path. + SrcPath string `json:"src_path"` +} + +// OkResponse Generic OK response. +type OkResponse struct { + // Ok Indicates success. + Ok bool `json:"ok"` +} + +// PatchDisplayRequest defines model for PatchDisplayRequest. +type PatchDisplayRequest struct { + // Height Display height in pixels + Height *int `json:"height,omitempty"` + + // RefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. + RefreshRate *PatchDisplayRequestRefreshRate `json:"refresh_rate,omitempty"` + + // RequireIdle If true, refuse to resize when live view or recording/replay is active. + RequireIdle *bool `json:"require_idle,omitempty"` + + // 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"` + + // Width Display width in pixels + Width *int `json:"width,omitempty"` +} + +// PatchDisplayRequestRefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. +type PatchDisplayRequestRefreshRate int + +// 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"` + + // HoldKeys Optional modifier keys to hold during the key press sequence. + HoldKeys *[]string `json:"hold_keys,omitempty"` + + // 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"` +} + +// ProcessExecRequest Request to execute a command synchronously. +type ProcessExecRequest struct { + // Args Command arguments. + Args *[]string `json:"args,omitempty"` + + // AsRoot Run the process with root privileges. + AsRoot *bool `json:"as_root,omitempty"` + + // AsUser Run the process as this user. + AsUser *string `json:"as_user,omitempty"` + + // Command Executable or shell command to run. + Command string `json:"command"` + + // Cwd Working directory (absolute path) to run the command in. + Cwd *string `json:"cwd,omitempty"` + + // Env Environment variables to set for the process. + Env *map[string]string `json:"env,omitempty"` + + // TimeoutSec Maximum execution time in seconds. + TimeoutSec *int `json:"timeout_sec,omitempty"` +} + +// 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"` + + // StderrB64 Base64-encoded stderr buffer. + StderrB64 *string `json:"stderr_b64,omitempty"` + + // StdoutB64 Base64-encoded stdout buffer. + StdoutB64 *string `json:"stdout_b64,omitempty"` +} + +// ProcessKillRequest Signal to send to the process. +type ProcessKillRequest struct { + // Signal Signal to send. + Signal ProcessKillRequestSignal `json:"signal"` +} + +// ProcessKillRequestSignal Signal to send. +type ProcessKillRequestSignal string - // Type The type of action to perform. - Type ComputerActionType `json:"type"` - TypeText *TypeTextRequest `json:"type_text,omitempty"` +// ProcessResizeRequest Resize a PTY-backed process. +type ProcessResizeRequest struct { + // Cols New terminal columns. + Cols int `json:"cols"` + + // Rows New terminal rows. + Rows int `json:"rows"` } -// ComputerActionType The type of action to perform. -type ComputerActionType 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"` -// 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"` + // Args Command arguments. + Args *[]string `json:"args,omitempty"` - // Path Absolute directory path to create. - Path string `json:"path"` + // AsRoot Run the process with root privileges. + AsRoot *bool `json:"as_root,omitempty"` + + // AsUser Run the process as this user. + AsUser *string `json:"as_user,omitempty"` + + // Cols Initial terminal columns when allocate_tty is true. + Cols *int `json:"cols,omitempty"` + + // Command Executable or shell command to run. + Command string `json:"command"` + + // Cwd Working directory (absolute path) to run the command in. + Cwd *string `json:"cwd,omitempty"` + + // Env Environment variables to set for the process. + Env *map[string]string `json:"env,omitempty"` + + // Rows Initial terminal rows when allocate_tty is true. + Rows *int `json:"rows,omitempty"` + + // TimeoutSec Maximum execution time in seconds. + TimeoutSec *int `json:"timeout_sec,omitempty"` } -// DeletePathRequest defines model for DeletePathRequest. -type DeletePathRequest struct { - // Path Absolute path to delete. - Path string `json:"path"` +// ProcessSpawnResult Information about a spawned process. +type ProcessSpawnResult struct { + // Pid OS process ID. + Pid *int `json:"pid,omitempty"` + + // ProcessId Server-assigned identifier for the process. + ProcessId *openapi_types.UUID `json:"process_id,omitempty"` + + // StartedAt Timestamp when the process started. + StartedAt *time.Time `json:"started_at,omitempty"` } -// DeleteRecordingRequest defines model for DeleteRecordingRequest. -type DeleteRecordingRequest struct { - // Id Identifier of the recording to delete. Alphanumeric or hyphen. - Id *string `json:"id,omitempty"` +// ProcessStatus Current status of a process. +type ProcessStatus struct { + // CpuPct Estimated CPU usage percentage. + CpuPct *float32 `json:"cpu_pct,omitempty"` + + // ExitCode Exit code if the process has exited. + ExitCode *int `json:"exit_code,omitempty"` + + // MemBytes Estimated resident memory usage in bytes. + MemBytes *int `json:"mem_bytes,omitempty"` + + // State Process state. + State *ProcessStatusState `json:"state,omitempty"` } -// DisplayConfig defines model for DisplayConfig. -type DisplayConfig struct { - // Height Current display height in pixels - Height *int `json:"height,omitempty"` +// ProcessStatusState Process state. +type ProcessStatusState string - // RefreshRate Current display refresh rate in Hz (may be null if not detectable) - RefreshRate *int `json:"refresh_rate,omitempty"` +// ProcessStdinRequest Data to write to the process standard input. +type ProcessStdinRequest struct { + // DataB64 Base64-encoded data to write. + DataB64 string `json:"data_b64"` +} - // Width Current display width in pixels - Width *int `json:"width,omitempty"` +// ProcessStdinResult Result of writing to stdin. +type ProcessStdinResult struct { + // WrittenBytes Number of bytes written. + WrittenBytes *int `json:"written_bytes,omitempty"` } -// DragMouseRequest defines model for DragMouseRequest. -type DragMouseRequest struct { - // Button Mouse button to drag with - Button *DragMouseRequestButton `json:"button,omitempty"` +// 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"` - // Delay Delay in milliseconds between button down and starting to move along the path. - Delay *int `json:"delay,omitempty"` + // Event Lifecycle event type. + Event *ProcessStreamEventEvent `json:"event,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"` + // ExitCode Exit code when the event is "exit". + ExitCode *int `json:"exit_code,omitempty"` - // HoldKeys Modifier keys to hold during the drag + // Stream Source stream of the data chunk. + Stream *ProcessStreamEventStream `json:"stream,omitempty"` +} + +// ProcessStreamEventEvent Lifecycle event type. +type ProcessStreamEventEvent string + +// ProcessStreamEventStream Source stream of the data chunk. +type ProcessStreamEventStream string + +// PublishEventRequest Request body for publishing an event into the telemetry stream. +type PublishEventRequest struct { + // Category Event category. + Category *PublishEventRequestCategory `json:"category,omitempty"` + + // Data Telemetry event payload. + Data interface{} `json:"data,omitempty"` + + // Source Provenance metadata identifying which producer emitted the event. + Source *BrowserEventSource `json:"source,omitempty"` + + // Type Event type identifier. + Type string `json:"type"` +} + +// PublishEventRequestCategory Event category. +type PublishEventRequestCategory string + +// 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"` + + // StartedAt Timestamp when recording started + StartedAt *time.Time `json:"started_at,omitempty"` +} + +// ScreenshotRegion defines model for ScreenshotRegion. +type ScreenshotRegion struct { + // Height Height of the region in pixels + Height int `json:"height"` + + // Width Width of the region in pixels + Width int `json:"width"` + + // X X coordinate of the region's top-left corner + X int `json:"x"` + + // Y Y coordinate of the region's top-left corner + Y int `json:"y"` +} + +// ScreenshotRequest defines model for ScreenshotRequest. +type ScreenshotRequest struct { + Region *ScreenshotRegion `json:"region,omitempty"` +} + +// 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"` - // Path Ordered list of [x, y] coordinate pairs to move through while dragging. Must contain at least 2 points. - Path [][]int `json:"path"` + // X X coordinate at which to perform the scroll + X int `json:"x"` - // 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"` + // Y Y coordinate at which to perform the scroll + Y int `json:"y"` +} - // StepDelayMs Delay in milliseconds between relative steps while dragging. Ignored when smooth=true. - StepDelayMs *int `json:"step_delay_ms,omitempty"` +// SetCursorRequest defines model for SetCursorRequest. +type SetCursorRequest struct { + // Hidden Whether the cursor should be hidden + Hidden bool `json:"hidden"` +} - // StepsPerSegment Number of relative move steps per segment in the path. Ignored when smooth=true. Minimum 1. - StepsPerSegment *int `json:"steps_per_segment,omitempty"` +// SetFilePermissionsRequest defines model for SetFilePermissionsRequest. +type SetFilePermissionsRequest struct { + // Group New group name or GID. + Group *string `json:"group,omitempty"` + + // Mode File mode bits (octal string, e.g. 644). + Mode string `json:"mode"` + + // Owner New owner username or UID. + Owner *string `json:"owner,omitempty"` + + // Path Absolute path whose permissions are to be changed. + Path string `json:"path"` +} + +// SleepAction Pause execution for a specified duration. +type SleepAction struct { + // DurationMs Duration to sleep in milliseconds. + DurationMs int `json:"duration_ms"` +} + +// StartFsWatchRequest defines model for StartFsWatchRequest. +type StartFsWatchRequest struct { + // Path Directory to watch. + Path string `json:"path"` + + // Recursive Whether to watch recursively. + Recursive *bool `json:"recursive,omitempty"` +} + +// 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 the recording session. Alphanumeric or hyphen. + Id *string `json:"id,omitempty"` + + // MaxDurationInSeconds Maximum recording duration in seconds (overrides server default) + MaxDurationInSeconds *int `json:"maxDurationInSeconds,omitempty"` + + // MaxFileSizeInMB Maximum file size in MB (overrides server default) + MaxFileSizeInMB *int `json:"maxFileSizeInMB,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"` + + // Id Identifier of the recorder to stop. Alphanumeric or hyphen. + Id *string `json:"id,omitempty"` } -// DragMouseRequestButton Mouse button to drag with -type DragMouseRequestButton string +// 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"` -// Error defines model for Error. -type Error struct { - Message string `json:"message"` + // 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"` } -// Event A capture event. -type Event struct { +// 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 *EventCategory `json:"category,omitempty"` + Category *TelemetryEventCategory `json:"category,omitempty"` - // Data Arbitrary event payload. + // Data Arbitrary JSON payload. For browser events listed in `KnownBrowserTelemetryEvent`, the payload conforms to the corresponding `Browser*EventData` schema. Data interface{} `json:"data,omitempty"` - // Source Provenance of the event. - Source *EventSource `json:"source,omitempty"` + // Source Provenance metadata identifying which producer emitted the event. + Source *BrowserEventSource `json:"source,omitempty"` // Truncated Set by the server when the data field was truncated to fit the size limit. Truncated *bool `json:"truncated,omitempty"` @@ -611,746 +2411,978 @@ type Event struct { Type string `json:"type"` } -// EventCategory Event category. -type EventCategory string - -// EventSource Provenance of the event. -type EventSource struct { - Event *string `json:"event,omitempty"` - - // Kind Source kind. "kernel_api" is reserved for server-generated events. - Kind *EventSourceKind `json:"kind,omitempty"` - Metadata *map[string]string `json:"metadata,omitempty"` -} +// TelemetryEventCategory Event category. +type TelemetryEventCategory string -// EventSourceKind Source kind. "kernel_api" is reserved for server-generated events. -type EventSourceKind string +// 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"` -// 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"` + // Config Telemetry configuration for a browser. Per-category capture settings. Omit a category or set enabled: true to capture it. Set enabled: false to exclude it. Omit the browser key entirely to capture all categories. Set all four categories to enabled: false to clear the telemetry configuration. + Config BrowserTelemetryConfig `json:"config"` - // TimeoutSec Maximum execution time in seconds. Default is 60. - TimeoutSec *int `json:"timeout_sec,omitempty"` + // Seq Process-monotonic sequence number of the last published event. Does not reset across configuration changes. + Seq int64 `json:"seq"` } -// ExecutePlaywrightResult Result of Playwright code execution -type ExecutePlaywrightResult struct { - // Error Error message if execution failed - Error *string `json:"error,omitempty"` - - // Result The value returned by the code (if any) - Result interface{} `json:"result,omitempty"` +// TypeTextRequest defines model for TypeTextRequest. +type TypeTextRequest struct { + // Delay Delay in milliseconds between keystrokes. Ignored when smooth is true. + Delay *int `json:"delay,omitempty"` - // Stderr Standard error from the execution - Stderr *string `json:"stderr,omitempty"` + // 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"` - // Stdout Standard output from the execution - Stdout *string `json:"stdout,omitempty"` + // Text Text to type on the host computer + Text string `json:"text"` - // Success Whether the code executed successfully - Success bool `json:"success"` + // 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"` } -// FileInfo defines model for FileInfo. -type FileInfo struct { - // IsDir Whether the path is a directory. - IsDir bool `json:"is_dir"` - - // ModTime Last modification time. - ModTime time.Time `json:"mod_time"` - - // Mode File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--"). - Mode string `json:"mode"` - - // Name Base name of the file or directory. - Name string `json:"name"` - - // Path Absolute path. - Path string `json:"path"` - - // SizeBytes Size in bytes. 0 for directories. - SizeBytes int `json:"size_bytes"` +// WriteClipboardRequest defines model for WriteClipboardRequest. +type WriteClipboardRequest struct { + // Text Text to write to the system clipboard + Text string `json:"text"` } -// FileSystemEvent Filesystem change event. -type FileSystemEvent struct { - // IsDir Whether the affected path is a directory. - IsDir *bool `json:"is_dir,omitempty"` +// BadRequestError defines model for BadRequestError. +type BadRequestError = Error - // Name Base name of the file or directory affected. - Name *string `json:"name,omitempty"` +// ConflictError defines model for ConflictError. +type ConflictError = Error - // Path Absolute path of the file or directory. - Path string `json:"path"` +// InternalError defines model for InternalError. +type InternalError = Error - // Type Event type. - Type FileSystemEventType `json:"type"` -} +// NotFoundError defines model for NotFoundError. +type NotFoundError = Error -// FileSystemEventType Event type. -type FileSystemEventType string +// PatchChromiumFlagsJSONBody defines parameters for PatchChromiumFlags. +type PatchChromiumFlagsJSONBody struct { + // Flags Chromium flags to merge (e.g., ["--kiosk", "--disable-gpu"]) + Flags []string `json:"flags"` +} -// ListFiles Array of file or directory information entries. -type ListFiles = []FileInfo +// PatchChromiumPoliciesJSONBody defines parameters for PatchChromiumPolicies. +type PatchChromiumPoliciesJSONBody map[string]interface{} -// LogEvent A log entry from the application. -type LogEvent struct { - // Message Log message text. - Message string `json:"message"` +// 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"` - // Timestamp Time the log entry was produced. - Timestamp time.Time `json:"timestamp"` + // ZipFile Zip archive containing an unpacked Chromium extension (must include manifest.json) + ZipFile openapi_types.File `json:"zip_file"` + } `json:"extensions"` } -// MousePositionResponse defines model for MousePositionResponse. -type MousePositionResponse struct { - // X X coordinate of the cursor - X int `json:"x"` - - // Y Y coordinate of the cursor - Y int `json:"y"` +// DownloadDirZipParams defines parameters for DownloadDirZip. +type DownloadDirZipParams struct { + // Path Absolute directory path to archive and download. + Path string `form:"path" json:"path"` } -// 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"` - - // HoldKeys Modifier keys to hold during the move - HoldKeys *[]string `json:"hold_keys,omitempty"` - - // Smooth Use human-like Bezier curve path instead of instant mouse movement. - Smooth *bool `json:"smooth,omitempty"` - - // X X coordinate to move the cursor to - X int `json:"x"` +// DownloadDirZstdParams defines parameters for DownloadDirZstd. +type DownloadDirZstdParams struct { + // Path Absolute directory path to archive and download. + Path string `form:"path" json:"path"` - // Y Y coordinate to move the cursor to - Y int `json:"y"` + // 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"` } -// MovePathRequest defines model for MovePathRequest. -type MovePathRequest struct { - // DestPath Absolute destination path. - DestPath string `json:"dest_path"` +// DownloadDirZstdParamsCompressionLevel defines parameters for DownloadDirZstd. +type DownloadDirZstdParamsCompressionLevel string - // SrcPath Absolute source path. - SrcPath string `json:"src_path"` +// FileInfoParams defines parameters for FileInfo. +type FileInfoParams struct { + // Path Absolute path of the file or directory. + Path string `form:"path" json:"path"` } -// OkResponse Generic OK response. -type OkResponse struct { - // Ok Indicates success. - Ok bool `json:"ok"` +// ListFilesParams defines parameters for ListFiles. +type ListFilesParams struct { + // Path Absolute directory path. + Path string `form:"path" json:"path"` } -// PatchDisplayRequest defines model for PatchDisplayRequest. -type PatchDisplayRequest struct { - // Height Display height in pixels - Height *int `json:"height,omitempty"` - - // RefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. - RefreshRate *PatchDisplayRequestRefreshRate `json:"refresh_rate,omitempty"` - - // RequireIdle If true, refuse to resize when live view or recording/replay is active. - RequireIdle *bool `json:"require_idle,omitempty"` - - // 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"` - - // Width Display width in pixels - Width *int `json:"width,omitempty"` +// ReadFileParams defines parameters for ReadFile. +type ReadFileParams struct { + // Path Absolute file path to read. + Path string `form:"path" json:"path"` } -// PatchDisplayRequestRefreshRate Display refresh rate in Hz. If omitted, uses the highest available rate for the resolution. -type PatchDisplayRequestRefreshRate int - -// 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"` - - // HoldKeys Optional modifier keys to hold during the key press sequence. - HoldKeys *[]string `json:"hold_keys,omitempty"` - - // 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"` +// 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"` } -// ProcessExecRequest Request to execute a command synchronously. -type ProcessExecRequest struct { - // Args Command arguments. - Args *[]string `json:"args,omitempty"` - - // AsRoot Run the process with root privileges. - AsRoot *bool `json:"as_root,omitempty"` - - // AsUser Run the process as this user. - AsUser *string `json:"as_user,omitempty"` - - // Command Executable or shell command to run. - Command string `json:"command"` - - // Cwd Working directory (absolute path) to run the command in. - Cwd *string `json:"cwd,omitempty"` - - // Env Environment variables to set for the process. - Env *map[string]string `json:"env,omitempty"` - - // TimeoutSec Maximum execution time in seconds. - 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"` } -// 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"` - - // StderrB64 Base64-encoded stderr buffer. - StderrB64 *string `json:"stderr_b64,omitempty"` +// UploadZstdMultipartBody defines parameters for UploadZstd. +type UploadZstdMultipartBody struct { + // Archive The tar.zst archive file. + Archive openapi_types.File `json:"archive"` - // StdoutB64 Base64-encoded stdout buffer. - StdoutB64 *string `json:"stdout_b64,omitempty"` + // DestPath Absolute destination directory to extract the archive to. + DestPath string `json:"dest_path"` + + // StripComponents Number of leading path components to strip during extraction (like tar --strip-components). + StripComponents *int `json:"strip_components,omitempty"` } -// ProcessKillRequest Signal to send to the process. -type ProcessKillRequest struct { - // Signal Signal to send. - Signal ProcessKillRequestSignal `json:"signal"` +// WriteFileParams defines parameters for WriteFile. +type WriteFileParams struct { + // Path Destination absolute file path. + Path string `form:"path" json:"path"` + + // Mode Optional file mode (octal string, e.g. 644). Defaults to 644. + Mode *string `form:"mode,omitempty" json:"mode,omitempty"` } -// ProcessKillRequestSignal Signal to send. -type ProcessKillRequestSignal string +// LogsStreamParams defines parameters for LogsStream. +type LogsStreamParams struct { + Source LogsStreamParamsSource `form:"source" json:"source"` + Follow *bool `form:"follow,omitempty" json:"follow,omitempty"` -// ProcessResizeRequest Resize a PTY-backed process. -type ProcessResizeRequest struct { - // Cols New terminal columns. - Cols int `json:"cols"` + // Path only required if source is path + Path *string `form:"path,omitempty" json:"path,omitempty"` - // Rows New terminal rows. - Rows int `json:"rows"` + // SupervisorProcess only required if source is supervisor + SupervisorProcess *string `form:"supervisor_process,omitempty" json:"supervisor_process,omitempty"` } -// 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"` +// LogsStreamParamsSource defines parameters for LogsStream. +type LogsStreamParamsSource string - // Args Command arguments. - Args *[]string `json:"args,omitempty"` +// DownloadRecordingParams defines parameters for DownloadRecording. +type DownloadRecordingParams struct { + // Id Optional recorder identifier. When omitted, the server uses the default recorder. + Id *string `form:"id,omitempty" json:"id,omitempty"` +} - // AsRoot Run the process with root privileges. - AsRoot *bool `json:"as_root,omitempty"` +// StreamTelemetryEventsParams defines parameters for StreamTelemetryEvents. +type StreamTelemetryEventsParams struct { + // 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. + LastEventID *string `json:"Last-Event-ID,omitempty"` +} - // AsUser Run the process as this user. - AsUser *string `json:"as_user,omitempty"` +// PatchChromiumFlagsJSONRequestBody defines body for PatchChromiumFlags for application/json ContentType. +type PatchChromiumFlagsJSONRequestBody PatchChromiumFlagsJSONBody - // Cols Initial terminal columns when allocate_tty is true. - Cols *int `json:"cols,omitempty"` +// PatchChromiumPoliciesJSONRequestBody defines body for PatchChromiumPolicies for application/json ContentType. +type PatchChromiumPoliciesJSONRequestBody PatchChromiumPoliciesJSONBody - // Command Executable or shell command to run. - Command string `json:"command"` +// UploadExtensionsAndRestartMultipartRequestBody defines body for UploadExtensionsAndRestart for multipart/form-data ContentType. +type UploadExtensionsAndRestartMultipartRequestBody UploadExtensionsAndRestartMultipartBody - // Cwd Working directory (absolute path) to run the command in. - Cwd *string `json:"cwd,omitempty"` +// BatchComputerActionJSONRequestBody defines body for BatchComputerAction for application/json ContentType. +type BatchComputerActionJSONRequestBody = BatchComputerActionRequest - // Env Environment variables to set for the process. - Env *map[string]string `json:"env,omitempty"` +// ClickMouseJSONRequestBody defines body for ClickMouse for application/json ContentType. +type ClickMouseJSONRequestBody = ClickMouseRequest - // Rows Initial terminal rows when allocate_tty is true. - Rows *int `json:"rows,omitempty"` +// WriteClipboardJSONRequestBody defines body for WriteClipboard for application/json ContentType. +type WriteClipboardJSONRequestBody = WriteClipboardRequest - // TimeoutSec Maximum execution time in seconds. - TimeoutSec *int `json:"timeout_sec,omitempty"` -} +// SetCursorJSONRequestBody defines body for SetCursor for application/json ContentType. +type SetCursorJSONRequestBody = SetCursorRequest -// ProcessSpawnResult Information about a spawned process. -type ProcessSpawnResult struct { - // Pid OS process ID. - Pid *int `json:"pid,omitempty"` +// DragMouseJSONRequestBody defines body for DragMouse for application/json ContentType. +type DragMouseJSONRequestBody = DragMouseRequest - // ProcessId Server-assigned identifier for the process. - ProcessId *openapi_types.UUID `json:"process_id,omitempty"` +// MoveMouseJSONRequestBody defines body for MoveMouse for application/json ContentType. +type MoveMouseJSONRequestBody = MoveMouseRequest - // StartedAt Timestamp when the process started. - StartedAt *time.Time `json:"started_at,omitempty"` -} +// PressKeyJSONRequestBody defines body for PressKey for application/json ContentType. +type PressKeyJSONRequestBody = PressKeyRequest -// ProcessStatus Current status of a process. -type ProcessStatus struct { - // CpuPct Estimated CPU usage percentage. - CpuPct *float32 `json:"cpu_pct,omitempty"` +// TakeScreenshotJSONRequestBody defines body for TakeScreenshot for application/json ContentType. +type TakeScreenshotJSONRequestBody = ScreenshotRequest - // ExitCode Exit code if the process has exited. - ExitCode *int `json:"exit_code,omitempty"` +// ScrollJSONRequestBody defines body for Scroll for application/json ContentType. +type ScrollJSONRequestBody = ScrollRequest - // MemBytes Estimated resident memory usage in bytes. - MemBytes *int `json:"mem_bytes,omitempty"` +// TypeTextJSONRequestBody defines body for TypeText for application/json ContentType. +type TypeTextJSONRequestBody = TypeTextRequest - // State Process state. - State *ProcessStatusState `json:"state,omitempty"` -} +// PatchDisplayJSONRequestBody defines body for PatchDisplay for application/json ContentType. +type PatchDisplayJSONRequestBody = PatchDisplayRequest -// ProcessStatusState Process state. -type ProcessStatusState string +// CreateDirectoryJSONRequestBody defines body for CreateDirectory for application/json ContentType. +type CreateDirectoryJSONRequestBody = CreateDirectoryRequest -// ProcessStdinRequest Data to write to the process standard input. -type ProcessStdinRequest struct { - // DataB64 Base64-encoded data to write. - DataB64 string `json:"data_b64"` -} +// DeleteDirectoryJSONRequestBody defines body for DeleteDirectory for application/json ContentType. +type DeleteDirectoryJSONRequestBody = DeletePathRequest -// ProcessStdinResult Result of writing to stdin. -type ProcessStdinResult struct { - // WrittenBytes Number of bytes written. - WrittenBytes *int `json:"written_bytes,omitempty"` -} +// DeleteFileJSONRequestBody defines body for DeleteFile for application/json ContentType. +type DeleteFileJSONRequestBody = DeletePathRequest -// 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"` +// MovePathJSONRequestBody defines body for MovePath for application/json ContentType. +type MovePathJSONRequestBody = MovePathRequest - // Event Lifecycle event type. - Event *ProcessStreamEventEvent `json:"event,omitempty"` +// SetFilePermissionsJSONRequestBody defines body for SetFilePermissions for application/json ContentType. +type SetFilePermissionsJSONRequestBody = SetFilePermissionsRequest - // ExitCode Exit code when the event is "exit". - ExitCode *int `json:"exit_code,omitempty"` +// UploadFilesMultipartRequestBody defines body for UploadFiles for multipart/form-data ContentType. +type UploadFilesMultipartRequestBody UploadFilesMultipartBody - // Stream Source stream of the data chunk. - Stream *ProcessStreamEventStream `json:"stream,omitempty"` -} +// UploadZipMultipartRequestBody defines body for UploadZip for multipart/form-data ContentType. +type UploadZipMultipartRequestBody UploadZipMultipartBody -// ProcessStreamEventEvent Lifecycle event type. -type ProcessStreamEventEvent string +// UploadZstdMultipartRequestBody defines body for UploadZstd for multipart/form-data ContentType. +type UploadZstdMultipartRequestBody UploadZstdMultipartBody -// ProcessStreamEventStream Source stream of the data chunk. -type ProcessStreamEventStream string +// StartFsWatchJSONRequestBody defines body for StartFsWatch for application/json ContentType. +type StartFsWatchJSONRequestBody = StartFsWatchRequest -// PublishEventRequest Request body for publishing an event into the event bus. -type PublishEventRequest struct { - // Category Event category. - Category *PublishEventRequestCategory `json:"category,omitempty"` +// ExecutePlaywrightCodeJSONRequestBody defines body for ExecutePlaywrightCode for application/json ContentType. +type ExecutePlaywrightCodeJSONRequestBody = ExecutePlaywrightRequest - // Data Capture Session Event Payload - Data interface{} `json:"data,omitempty"` +// ProcessExecJSONRequestBody defines body for ProcessExec for application/json ContentType. +type ProcessExecJSONRequestBody = ProcessExecRequest - // Source Provenance of the event. - Source *EventSource `json:"source,omitempty"` +// ProcessSpawnJSONRequestBody defines body for ProcessSpawn for application/json ContentType. +type ProcessSpawnJSONRequestBody = ProcessSpawnRequest - // Type Event type identifier. - Type string `json:"type"` -} +// ProcessKillJSONRequestBody defines body for ProcessKill for application/json ContentType. +type ProcessKillJSONRequestBody = ProcessKillRequest -// PublishEventRequestCategory Event category. -type PublishEventRequestCategory string +// ProcessResizeJSONRequestBody defines body for ProcessResize for application/json ContentType. +type ProcessResizeJSONRequestBody = ProcessResizeRequest -// PublishedEnvelope The envelope assigned to a successfully published event. -type PublishedEnvelope struct { - // Event A capture event. - Event Event `json:"event"` +// ProcessStdinJSONRequestBody defines body for ProcessStdin for application/json ContentType. +type ProcessStdinJSONRequestBody = ProcessStdinRequest - // 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"` -} +// DeleteRecordingJSONRequestBody defines body for DeleteRecording for application/json ContentType. +type DeleteRecordingJSONRequestBody = DeleteRecordingRequest -// 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"` +// StartRecordingJSONRequestBody defines body for StartRecording for application/json ContentType. +type StartRecordingJSONRequestBody = StartRecordingRequest - // StartedAt Timestamp when recording started - StartedAt *time.Time `json:"started_at,omitempty"` -} +// StopRecordingJSONRequestBody defines body for StopRecording for application/json ContentType. +type StopRecordingJSONRequestBody = StopRecordingRequest -// ScreenshotRegion defines model for ScreenshotRegion. -type ScreenshotRegion struct { - // Height Height of the region in pixels - Height int `json:"height"` +// PatchTelemetryJSONRequestBody defines body for PatchTelemetry for application/json ContentType. +type PatchTelemetryJSONRequestBody = BrowserTelemetryConfig - // Width Width of the region in pixels - Width int `json:"width"` +// PutTelemetryJSONRequestBody defines body for PutTelemetry for application/json ContentType. +type PutTelemetryJSONRequestBody = BrowserTelemetryConfig - // X X coordinate of the region's top-left corner - X int `json:"x"` +// PublishTelemetryEventJSONRequestBody defines body for PublishTelemetryEvent for application/json ContentType. +type PublishTelemetryEventJSONRequestBody = PublishEventRequest - // Y Y coordinate of the region's top-left corner - Y int `json:"y"` +// AsBrowserConsoleLogEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserConsoleLogEvent +func (t KnownBrowserTelemetryEvent) AsBrowserConsoleLogEvent() (BrowserConsoleLogEvent, error) { + var body BrowserConsoleLogEvent + err := json.Unmarshal(t.union, &body) + return body, err } -// ScreenshotRequest defines model for ScreenshotRequest. -type ScreenshotRequest struct { - Region *ScreenshotRegion `json:"region,omitempty"` +// FromBrowserConsoleLogEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserConsoleLogEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserConsoleLogEvent(v BrowserConsoleLogEvent) error { + v.Type = "console_log" + b, err := json.Marshal(v) + t.union = b + return 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"` - - // X X coordinate at which to perform the scroll - X int `json:"x"` +// MergeBrowserConsoleLogEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserConsoleLogEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserConsoleLogEvent(v BrowserConsoleLogEvent) error { + v.Type = "console_log" + 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"` +// AsBrowserConsoleErrorEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserConsoleErrorEvent +func (t KnownBrowserTelemetryEvent) AsBrowserConsoleErrorEvent() (BrowserConsoleErrorEvent, error) { + var body BrowserConsoleErrorEvent + 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"` - - // Mode File mode bits (octal string, e.g. 644). - Mode string `json:"mode"` +// FromBrowserConsoleErrorEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserConsoleErrorEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserConsoleErrorEvent(v BrowserConsoleErrorEvent) error { + v.Type = "console_error" + b, err := json.Marshal(v) + t.union = b + return err +} - // Owner New owner username or UID. - Owner *string `json:"owner,omitempty"` +// MergeBrowserConsoleErrorEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserConsoleErrorEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserConsoleErrorEvent(v BrowserConsoleErrorEvent) error { + v.Type = "console_error" + b, err := json.Marshal(v) + if err != nil { + return err + } - // Path Absolute path whose permissions are to be changed. - Path string `json:"path"` + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// SleepAction Pause execution for a specified duration. -type SleepAction struct { - // DurationMs Duration to sleep in milliseconds. - DurationMs int `json:"duration_ms"` +// AsBrowserNetworkRequestEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserNetworkRequestEvent +func (t KnownBrowserTelemetryEvent) AsBrowserNetworkRequestEvent() (BrowserNetworkRequestEvent, error) { + var body BrowserNetworkRequestEvent + err := json.Unmarshal(t.union, &body) + return body, err } -// StartCaptureSessionRequest Optional capture configuration. All fields default to the server-defined profile when omitted or when no body is sent. -type StartCaptureSessionRequest struct { - // Config Capture filtering preferences. - Config *CaptureConfig `json:"config,omitempty"` +// FromBrowserNetworkRequestEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserNetworkRequestEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserNetworkRequestEvent(v BrowserNetworkRequestEvent) error { + v.Type = "network_request" + 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"` +// MergeBrowserNetworkRequestEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserNetworkRequestEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserNetworkRequestEvent(v BrowserNetworkRequestEvent) error { + v.Type = "network_request" + 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"` +// AsBrowserNetworkResponseEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserNetworkResponseEvent +func (t KnownBrowserTelemetryEvent) AsBrowserNetworkResponseEvent() (BrowserNetworkResponseEvent, error) { + var body BrowserNetworkResponseEvent + err := json.Unmarshal(t.union, &body) + return body, err +} - // Id Optional identifier for the recording session. Alphanumeric or hyphen. - Id *string `json:"id,omitempty"` +// FromBrowserNetworkResponseEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserNetworkResponseEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserNetworkResponseEvent(v BrowserNetworkResponseEvent) error { + v.Type = "network_response" + b, err := json.Marshal(v) + t.union = b + return err +} - // MaxDurationInSeconds Maximum recording duration in seconds (overrides server default) - MaxDurationInSeconds *int `json:"maxDurationInSeconds,omitempty"` +// MergeBrowserNetworkResponseEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserNetworkResponseEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserNetworkResponseEvent(v BrowserNetworkResponseEvent) error { + v.Type = "network_response" + b, err := json.Marshal(v) + if err != nil { + return err + } - // MaxFileSizeInMB Maximum file size in MB (overrides server default) - MaxFileSizeInMB *int `json:"maxFileSizeInMB,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"` +// AsBrowserNetworkLoadingFailedEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserNetworkLoadingFailedEvent +func (t KnownBrowserTelemetryEvent) AsBrowserNetworkLoadingFailedEvent() (BrowserNetworkLoadingFailedEvent, error) { + var body BrowserNetworkLoadingFailedEvent + err := json.Unmarshal(t.union, &body) + return body, err +} - // Id Identifier of the recorder to stop. Alphanumeric or hyphen. - Id *string `json:"id,omitempty"` +// FromBrowserNetworkLoadingFailedEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserNetworkLoadingFailedEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserNetworkLoadingFailedEvent(v BrowserNetworkLoadingFailedEvent) error { + v.Type = "network_loading_failed" + 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"` +// MergeBrowserNetworkLoadingFailedEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserNetworkLoadingFailedEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserNetworkLoadingFailedEvent(v BrowserNetworkLoadingFailedEvent) error { + v.Type = "network_loading_failed" + 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 +} - // Text Text to type on the host computer - Text string `json:"text"` +// AsBrowserNetworkIdleEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserNetworkIdleEvent +func (t KnownBrowserTelemetryEvent) AsBrowserNetworkIdleEvent() (BrowserNetworkIdleEvent, error) { + var body BrowserNetworkIdleEvent + err := json.Unmarshal(t.union, &body) + return body, 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"` +// FromBrowserNetworkIdleEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserNetworkIdleEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserNetworkIdleEvent(v BrowserNetworkIdleEvent) error { + v.Type = "network_idle" + b, err := json.Marshal(v) + t.union = b + return err } -// UpdateCaptureSessionRequest Fields to update on the capture session. -type UpdateCaptureSessionRequest struct { - // Config Capture filtering preferences. - Config *CaptureConfig `json:"config,omitempty"` +// MergeBrowserNetworkIdleEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserNetworkIdleEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserNetworkIdleEvent(v BrowserNetworkIdleEvent) error { + v.Type = "network_idle" + 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"` +// AsBrowserPageNavigationEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageNavigationEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageNavigationEvent() (BrowserPageNavigationEvent, error) { + var body BrowserPageNavigationEvent + err := json.Unmarshal(t.union, &body) + return body, err } -// BadRequestError defines model for BadRequestError. -type BadRequestError = Error +// FromBrowserPageNavigationEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageNavigationEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageNavigationEvent(v BrowserPageNavigationEvent) error { + v.Type = "page_navigation" + b, err := json.Marshal(v) + t.union = b + return err +} -// ConflictError defines model for ConflictError. -type ConflictError = Error +// MergeBrowserPageNavigationEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageNavigationEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageNavigationEvent(v BrowserPageNavigationEvent) error { + v.Type = "page_navigation" + 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 +// AsBrowserPageDomContentLoadedEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageDomContentLoadedEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageDomContentLoadedEvent() (BrowserPageDomContentLoadedEvent, error) { + var body BrowserPageDomContentLoadedEvent + 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"` +// FromBrowserPageDomContentLoadedEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageDomContentLoadedEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageDomContentLoadedEvent(v BrowserPageDomContentLoadedEvent) error { + v.Type = "page_dom_content_loaded" + b, err := json.Marshal(v) + t.union = b + return err } -// PatchChromiumPoliciesJSONBody defines parameters for PatchChromiumPolicies. -type PatchChromiumPoliciesJSONBody map[string]interface{} +// MergeBrowserPageDomContentLoadedEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageDomContentLoadedEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageDomContentLoadedEvent(v BrowserPageDomContentLoadedEvent) error { + v.Type = "page_dom_content_loaded" + 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"` +// AsBrowserPageLoadEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageLoadEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageLoadEvent() (BrowserPageLoadEvent, error) { + var body BrowserPageLoadEvent + err := json.Unmarshal(t.union, &body) + return body, err } -// StreamEventsParams defines parameters for StreamEvents. -type StreamEventsParams struct { - // LastEventID Resume after this sequence number. Omit or send 0 to start from the current position. Sequence numbers are process-monotonic, so a value from a previous session resumes correctly from that point. - LastEventID *string `json:"Last-Event-ID,omitempty"` +// FromBrowserPageLoadEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageLoadEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageLoadEvent(v BrowserPageLoadEvent) error { + v.Type = "page_load" + b, err := json.Marshal(v) + t.union = b + return err } -// DownloadDirZipParams defines parameters for DownloadDirZip. -type DownloadDirZipParams struct { - // Path Absolute directory path to archive and download. - Path string `form:"path" json:"path"` +// MergeBrowserPageLoadEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageLoadEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageLoadEvent(v BrowserPageLoadEvent) error { + v.Type = "page_load" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// DownloadDirZstdParams defines parameters for DownloadDirZstd. -type DownloadDirZstdParams struct { - // Path Absolute directory path to archive and download. - Path string `form:"path" json:"path"` +// AsBrowserPageTabOpenedEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageTabOpenedEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageTabOpenedEvent() (BrowserPageTabOpenedEvent, error) { + var body BrowserPageTabOpenedEvent + err := json.Unmarshal(t.union, &body) + return body, err +} - // 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"` +// FromBrowserPageTabOpenedEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageTabOpenedEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageTabOpenedEvent(v BrowserPageTabOpenedEvent) error { + v.Type = "page_tab_opened" + b, err := json.Marshal(v) + t.union = b + return err } -// DownloadDirZstdParamsCompressionLevel defines parameters for DownloadDirZstd. -type DownloadDirZstdParamsCompressionLevel string +// MergeBrowserPageTabOpenedEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageTabOpenedEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageTabOpenedEvent(v BrowserPageTabOpenedEvent) error { + v.Type = "page_tab_opened" + 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"` +// AsBrowserPageLayoutShiftEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageLayoutShiftEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageLayoutShiftEvent() (BrowserPageLayoutShiftEvent, error) { + var body BrowserPageLayoutShiftEvent + 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"` +// FromBrowserPageLayoutShiftEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageLayoutShiftEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageLayoutShiftEvent(v BrowserPageLayoutShiftEvent) error { + v.Type = "page_layout_shift" + 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"` +// MergeBrowserPageLayoutShiftEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageLayoutShiftEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageLayoutShiftEvent(v BrowserPageLayoutShiftEvent) error { + v.Type = "page_layout_shift" + 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"` +// AsBrowserPageLcpEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageLcpEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageLcpEvent() (BrowserPageLcpEvent, error) { + var body BrowserPageLcpEvent + 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"` +// FromBrowserPageLcpEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageLcpEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageLcpEvent(v BrowserPageLcpEvent) error { + v.Type = "page_lcp" + b, err := json.Marshal(v) + t.union = b + return err +} - // DestPath Absolute destination directory to extract the archive to. - DestPath string `json:"dest_path"` +// MergeBrowserPageLcpEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageLcpEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageLcpEvent(v BrowserPageLcpEvent) error { + v.Type = "page_lcp" + 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"` +// AsBrowserPageLayoutSettledEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageLayoutSettledEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageLayoutSettledEvent() (BrowserPageLayoutSettledEvent, error) { + var body BrowserPageLayoutSettledEvent + 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"` +// FromBrowserPageLayoutSettledEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageLayoutSettledEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageLayoutSettledEvent(v BrowserPageLayoutSettledEvent) error { + v.Type = "page_layout_settled" + 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"` +// MergeBrowserPageLayoutSettledEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageLayoutSettledEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageLayoutSettledEvent(v BrowserPageLayoutSettledEvent) error { + v.Type = "page_layout_settled" + 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"` +// AsBrowserPageNavigationSettledEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserPageNavigationSettledEvent +func (t KnownBrowserTelemetryEvent) AsBrowserPageNavigationSettledEvent() (BrowserPageNavigationSettledEvent, error) { + var body BrowserPageNavigationSettledEvent + err := json.Unmarshal(t.union, &body) + return body, err } -// LogsStreamParamsSource defines parameters for LogsStream. -type LogsStreamParamsSource string +// FromBrowserPageNavigationSettledEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserPageNavigationSettledEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserPageNavigationSettledEvent(v BrowserPageNavigationSettledEvent) error { + v.Type = "page_navigation_settled" + b, err := json.Marshal(v) + t.union = b + return err +} -// DownloadRecordingParams defines parameters for DownloadRecording. -type DownloadRecordingParams struct { - // Id Optional recorder identifier. When omitted, the server uses the default recorder. - Id *string `form:"id,omitempty" json:"id,omitempty"` +// MergeBrowserPageNavigationSettledEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserPageNavigationSettledEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserPageNavigationSettledEvent(v BrowserPageNavigationSettledEvent) error { + v.Type = "page_navigation_settled" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err } -// PatchChromiumFlagsJSONRequestBody defines body for PatchChromiumFlags for application/json ContentType. -type PatchChromiumFlagsJSONRequestBody PatchChromiumFlagsJSONBody +// AsBrowserInteractionClickEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserInteractionClickEvent +func (t KnownBrowserTelemetryEvent) AsBrowserInteractionClickEvent() (BrowserInteractionClickEvent, error) { + var body BrowserInteractionClickEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// PatchChromiumPoliciesJSONRequestBody defines body for PatchChromiumPolicies for application/json ContentType. -type PatchChromiumPoliciesJSONRequestBody PatchChromiumPoliciesJSONBody +// FromBrowserInteractionClickEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserInteractionClickEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserInteractionClickEvent(v BrowserInteractionClickEvent) error { + v.Type = "interaction_click" + b, err := json.Marshal(v) + t.union = b + return err +} -// UploadExtensionsAndRestartMultipartRequestBody defines body for UploadExtensionsAndRestart for multipart/form-data ContentType. -type UploadExtensionsAndRestartMultipartRequestBody UploadExtensionsAndRestartMultipartBody +// MergeBrowserInteractionClickEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserInteractionClickEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserInteractionClickEvent(v BrowserInteractionClickEvent) error { + v.Type = "interaction_click" + b, err := json.Marshal(v) + if err != nil { + return err + } -// BatchComputerActionJSONRequestBody defines body for BatchComputerAction for application/json ContentType. -type BatchComputerActionJSONRequestBody = BatchComputerActionRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// ClickMouseJSONRequestBody defines body for ClickMouse for application/json ContentType. -type ClickMouseJSONRequestBody = ClickMouseRequest +// AsBrowserInteractionKeyEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserInteractionKeyEvent +func (t KnownBrowserTelemetryEvent) AsBrowserInteractionKeyEvent() (BrowserInteractionKeyEvent, error) { + var body BrowserInteractionKeyEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// WriteClipboardJSONRequestBody defines body for WriteClipboard for application/json ContentType. -type WriteClipboardJSONRequestBody = WriteClipboardRequest +// FromBrowserInteractionKeyEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserInteractionKeyEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserInteractionKeyEvent(v BrowserInteractionKeyEvent) error { + v.Type = "interaction_key" + b, err := json.Marshal(v) + t.union = b + return err +} -// SetCursorJSONRequestBody defines body for SetCursor for application/json ContentType. -type SetCursorJSONRequestBody = SetCursorRequest +// MergeBrowserInteractionKeyEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserInteractionKeyEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserInteractionKeyEvent(v BrowserInteractionKeyEvent) error { + v.Type = "interaction_key" + b, err := json.Marshal(v) + if err != nil { + return err + } -// DragMouseJSONRequestBody defines body for DragMouse for application/json ContentType. -type DragMouseJSONRequestBody = DragMouseRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// MoveMouseJSONRequestBody defines body for MoveMouse for application/json ContentType. -type MoveMouseJSONRequestBody = MoveMouseRequest +// AsBrowserInteractionScrollSettledEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserInteractionScrollSettledEvent +func (t KnownBrowserTelemetryEvent) AsBrowserInteractionScrollSettledEvent() (BrowserInteractionScrollSettledEvent, error) { + var body BrowserInteractionScrollSettledEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// PressKeyJSONRequestBody defines body for PressKey for application/json ContentType. -type PressKeyJSONRequestBody = PressKeyRequest +// FromBrowserInteractionScrollSettledEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserInteractionScrollSettledEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserInteractionScrollSettledEvent(v BrowserInteractionScrollSettledEvent) error { + v.Type = "interaction_scroll_settled" + b, err := json.Marshal(v) + t.union = b + return err +} -// TakeScreenshotJSONRequestBody defines body for TakeScreenshot for application/json ContentType. -type TakeScreenshotJSONRequestBody = ScreenshotRequest +// MergeBrowserInteractionScrollSettledEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserInteractionScrollSettledEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserInteractionScrollSettledEvent(v BrowserInteractionScrollSettledEvent) error { + v.Type = "interaction_scroll_settled" + b, err := json.Marshal(v) + if err != nil { + return err + } -// ScrollJSONRequestBody defines body for Scroll for application/json ContentType. -type ScrollJSONRequestBody = ScrollRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// TypeTextJSONRequestBody defines body for TypeText for application/json ContentType. -type TypeTextJSONRequestBody = TypeTextRequest +// AsBrowserMonitorScreenshotEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserMonitorScreenshotEvent +func (t KnownBrowserTelemetryEvent) AsBrowserMonitorScreenshotEvent() (BrowserMonitorScreenshotEvent, error) { + var body BrowserMonitorScreenshotEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// PatchDisplayJSONRequestBody defines body for PatchDisplay for application/json ContentType. -type PatchDisplayJSONRequestBody = PatchDisplayRequest +// FromBrowserMonitorScreenshotEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserMonitorScreenshotEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserMonitorScreenshotEvent(v BrowserMonitorScreenshotEvent) error { + v.Type = "monitor_screenshot" + b, err := json.Marshal(v) + t.union = b + return err +} -// UpdateCaptureSessionJSONRequestBody defines body for UpdateCaptureSession for application/json ContentType. -type UpdateCaptureSessionJSONRequestBody = UpdateCaptureSessionRequest +// MergeBrowserMonitorScreenshotEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserMonitorScreenshotEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserMonitorScreenshotEvent(v BrowserMonitorScreenshotEvent) error { + v.Type = "monitor_screenshot" + b, err := json.Marshal(v) + if err != nil { + return err + } -// StartCaptureSessionJSONRequestBody defines body for StartCaptureSession for application/json ContentType. -type StartCaptureSessionJSONRequestBody = StartCaptureSessionRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// PublishEventJSONRequestBody defines body for PublishEvent for application/json ContentType. -type PublishEventJSONRequestBody = PublishEventRequest +// AsBrowserMonitorDisconnectedEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserMonitorDisconnectedEvent +func (t KnownBrowserTelemetryEvent) AsBrowserMonitorDisconnectedEvent() (BrowserMonitorDisconnectedEvent, error) { + var body BrowserMonitorDisconnectedEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// CreateDirectoryJSONRequestBody defines body for CreateDirectory for application/json ContentType. -type CreateDirectoryJSONRequestBody = CreateDirectoryRequest +// FromBrowserMonitorDisconnectedEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserMonitorDisconnectedEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserMonitorDisconnectedEvent(v BrowserMonitorDisconnectedEvent) error { + v.Type = "monitor_disconnected" + b, err := json.Marshal(v) + t.union = b + return err +} -// DeleteDirectoryJSONRequestBody defines body for DeleteDirectory for application/json ContentType. -type DeleteDirectoryJSONRequestBody = DeletePathRequest +// MergeBrowserMonitorDisconnectedEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserMonitorDisconnectedEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserMonitorDisconnectedEvent(v BrowserMonitorDisconnectedEvent) error { + v.Type = "monitor_disconnected" + b, err := json.Marshal(v) + if err != nil { + return err + } -// DeleteFileJSONRequestBody defines body for DeleteFile for application/json ContentType. -type DeleteFileJSONRequestBody = DeletePathRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// MovePathJSONRequestBody defines body for MovePath for application/json ContentType. -type MovePathJSONRequestBody = MovePathRequest +// AsBrowserMonitorReconnectedEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserMonitorReconnectedEvent +func (t KnownBrowserTelemetryEvent) AsBrowserMonitorReconnectedEvent() (BrowserMonitorReconnectedEvent, error) { + var body BrowserMonitorReconnectedEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// SetFilePermissionsJSONRequestBody defines body for SetFilePermissions for application/json ContentType. -type SetFilePermissionsJSONRequestBody = SetFilePermissionsRequest +// FromBrowserMonitorReconnectedEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserMonitorReconnectedEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserMonitorReconnectedEvent(v BrowserMonitorReconnectedEvent) error { + v.Type = "monitor_reconnected" + b, err := json.Marshal(v) + t.union = b + return err +} -// UploadFilesMultipartRequestBody defines body for UploadFiles for multipart/form-data ContentType. -type UploadFilesMultipartRequestBody UploadFilesMultipartBody +// MergeBrowserMonitorReconnectedEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserMonitorReconnectedEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserMonitorReconnectedEvent(v BrowserMonitorReconnectedEvent) error { + v.Type = "monitor_reconnected" + b, err := json.Marshal(v) + if err != nil { + return err + } -// UploadZipMultipartRequestBody defines body for UploadZip for multipart/form-data ContentType. -type UploadZipMultipartRequestBody UploadZipMultipartBody + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// UploadZstdMultipartRequestBody defines body for UploadZstd for multipart/form-data ContentType. -type UploadZstdMultipartRequestBody UploadZstdMultipartBody +// AsBrowserMonitorReconnectFailedEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserMonitorReconnectFailedEvent +func (t KnownBrowserTelemetryEvent) AsBrowserMonitorReconnectFailedEvent() (BrowserMonitorReconnectFailedEvent, error) { + var body BrowserMonitorReconnectFailedEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// StartFsWatchJSONRequestBody defines body for StartFsWatch for application/json ContentType. -type StartFsWatchJSONRequestBody = StartFsWatchRequest +// FromBrowserMonitorReconnectFailedEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserMonitorReconnectFailedEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserMonitorReconnectFailedEvent(v BrowserMonitorReconnectFailedEvent) error { + v.Type = "monitor_reconnect_failed" + b, err := json.Marshal(v) + t.union = b + return err +} -// ExecutePlaywrightCodeJSONRequestBody defines body for ExecutePlaywrightCode for application/json ContentType. -type ExecutePlaywrightCodeJSONRequestBody = ExecutePlaywrightRequest +// MergeBrowserMonitorReconnectFailedEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserMonitorReconnectFailedEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserMonitorReconnectFailedEvent(v BrowserMonitorReconnectFailedEvent) error { + v.Type = "monitor_reconnect_failed" + b, err := json.Marshal(v) + if err != nil { + return err + } -// ProcessExecJSONRequestBody defines body for ProcessExec for application/json ContentType. -type ProcessExecJSONRequestBody = ProcessExecRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// ProcessSpawnJSONRequestBody defines body for ProcessSpawn for application/json ContentType. -type ProcessSpawnJSONRequestBody = ProcessSpawnRequest +// AsBrowserMonitorInitFailedEvent returns the union data inside the KnownBrowserTelemetryEvent as a BrowserMonitorInitFailedEvent +func (t KnownBrowserTelemetryEvent) AsBrowserMonitorInitFailedEvent() (BrowserMonitorInitFailedEvent, error) { + var body BrowserMonitorInitFailedEvent + err := json.Unmarshal(t.union, &body) + return body, err +} -// ProcessKillJSONRequestBody defines body for ProcessKill for application/json ContentType. -type ProcessKillJSONRequestBody = ProcessKillRequest +// FromBrowserMonitorInitFailedEvent overwrites any union data inside the KnownBrowserTelemetryEvent as the provided BrowserMonitorInitFailedEvent +func (t *KnownBrowserTelemetryEvent) FromBrowserMonitorInitFailedEvent(v BrowserMonitorInitFailedEvent) error { + v.Type = "monitor_init_failed" + b, err := json.Marshal(v) + t.union = b + return err +} -// ProcessResizeJSONRequestBody defines body for ProcessResize for application/json ContentType. -type ProcessResizeJSONRequestBody = ProcessResizeRequest +// MergeBrowserMonitorInitFailedEvent performs a merge with any union data inside the KnownBrowserTelemetryEvent, using the provided BrowserMonitorInitFailedEvent +func (t *KnownBrowserTelemetryEvent) MergeBrowserMonitorInitFailedEvent(v BrowserMonitorInitFailedEvent) error { + v.Type = "monitor_init_failed" + b, err := json.Marshal(v) + if err != nil { + return err + } -// ProcessStdinJSONRequestBody defines body for ProcessStdin for application/json ContentType. -type ProcessStdinJSONRequestBody = ProcessStdinRequest + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} -// DeleteRecordingJSONRequestBody defines body for DeleteRecording for application/json ContentType. -type DeleteRecordingJSONRequestBody = DeleteRecordingRequest +func (t KnownBrowserTelemetryEvent) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t KnownBrowserTelemetryEvent) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "console_error": + return t.AsBrowserConsoleErrorEvent() + case "console_log": + return t.AsBrowserConsoleLogEvent() + case "interaction_click": + return t.AsBrowserInteractionClickEvent() + case "interaction_key": + return t.AsBrowserInteractionKeyEvent() + case "interaction_scroll_settled": + return t.AsBrowserInteractionScrollSettledEvent() + case "monitor_disconnected": + return t.AsBrowserMonitorDisconnectedEvent() + case "monitor_init_failed": + return t.AsBrowserMonitorInitFailedEvent() + case "monitor_reconnect_failed": + return t.AsBrowserMonitorReconnectFailedEvent() + case "monitor_reconnected": + return t.AsBrowserMonitorReconnectedEvent() + case "monitor_screenshot": + return t.AsBrowserMonitorScreenshotEvent() + case "network_idle": + return t.AsBrowserNetworkIdleEvent() + case "network_loading_failed": + return t.AsBrowserNetworkLoadingFailedEvent() + case "network_request": + return t.AsBrowserNetworkRequestEvent() + case "network_response": + return t.AsBrowserNetworkResponseEvent() + case "page_dom_content_loaded": + return t.AsBrowserPageDomContentLoadedEvent() + case "page_layout_settled": + return t.AsBrowserPageLayoutSettledEvent() + case "page_layout_shift": + return t.AsBrowserPageLayoutShiftEvent() + case "page_lcp": + return t.AsBrowserPageLcpEvent() + case "page_load": + return t.AsBrowserPageLoadEvent() + case "page_navigation": + return t.AsBrowserPageNavigationEvent() + case "page_navigation_settled": + return t.AsBrowserPageNavigationSettledEvent() + case "page_tab_opened": + return t.AsBrowserPageTabOpenedEvent() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} -// StartRecordingJSONRequestBody defines body for StartRecording for application/json ContentType. -type StartRecordingJSONRequestBody = StartRecordingRequest +func (t KnownBrowserTelemetryEvent) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} -// StopRecordingJSONRequestBody defines body for StopRecording for application/json ContentType. -type StopRecordingJSONRequestBody = StopRecordingRequest +func (t *KnownBrowserTelemetryEvent) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} // RequestEditorFn is the function signature for the RequestEditor callback function type RequestEditorFn func(ctx context.Context, req *http.Request) error @@ -1499,30 +3531,6 @@ type ClientInterface interface { PatchDisplay(ctx context.Context, body PatchDisplayJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // StopCaptureSession request - StopCaptureSession(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - - // GetCaptureSession request - GetCaptureSession(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - - // UpdateCaptureSessionWithBody request with any body - UpdateCaptureSessionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - UpdateCaptureSession(ctx context.Context, body UpdateCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // StartCaptureSessionWithBody request with any body - StartCaptureSessionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - StartCaptureSession(ctx context.Context, body StartCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // PublishEventWithBody request with any body - PublishEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - PublishEvent(ctx context.Context, body PublishEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // StreamEvents request - StreamEvents(ctx context.Context, params *StreamEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreateDirectoryWithBody request with any body CreateDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -1651,6 +3659,27 @@ type ClientInterface interface { // EnableScaleToZero request EnableScaleToZero(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTelemetry request + GetTelemetry(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PatchTelemetryWithBody request with any body + PatchTelemetryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PatchTelemetry(ctx context.Context, body PatchTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PutTelemetryWithBody request with any body + PutTelemetryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PutTelemetry(ctx context.Context, body PutTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PublishTelemetryEventWithBody request with any body + PublishTelemetryEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PublishTelemetryEvent(ctx context.Context, body PublishTelemetryEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // StreamTelemetryEvents request + StreamTelemetryEvents(ctx context.Context, params *StreamTelemetryEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) } func (c *Client) PatchChromiumFlagsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -2001,20 +4030,8 @@ func (c *Client) PatchDisplay(ctx context.Context, body PatchDisplayJSONRequestB return c.Client.Do(req) } -func (c *Client) StopCaptureSession(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStopCaptureSessionRequest(c.Server) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) GetCaptureSession(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewGetCaptureSessionRequest(c.Server) +func (c *Client) CreateDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateDirectoryRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2025,8 +4042,8 @@ func (c *Client) GetCaptureSession(ctx context.Context, reqEditors ...RequestEdi return c.Client.Do(req) } -func (c *Client) UpdateCaptureSessionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewUpdateCaptureSessionRequestWithBody(c.Server, contentType, body) +func (c *Client) CreateDirectory(ctx context.Context, body CreateDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateDirectoryRequest(c.Server, body) if err != nil { return nil, err } @@ -2037,8 +4054,8 @@ func (c *Client) UpdateCaptureSessionWithBody(ctx context.Context, contentType s return c.Client.Do(req) } -func (c *Client) UpdateCaptureSession(ctx context.Context, body UpdateCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewUpdateCaptureSessionRequest(c.Server, body) +func (c *Client) DeleteDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteDirectoryRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2049,8 +4066,8 @@ func (c *Client) UpdateCaptureSession(ctx context.Context, body UpdateCaptureSes return c.Client.Do(req) } -func (c *Client) StartCaptureSessionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStartCaptureSessionRequestWithBody(c.Server, contentType, body) +func (c *Client) DeleteDirectory(ctx context.Context, body DeleteDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteDirectoryRequest(c.Server, body) if err != nil { return nil, err } @@ -2061,8 +4078,8 @@ func (c *Client) StartCaptureSessionWithBody(ctx context.Context, contentType st return c.Client.Do(req) } -func (c *Client) StartCaptureSession(ctx context.Context, body StartCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStartCaptureSessionRequest(c.Server, body) +func (c *Client) DeleteFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteFileRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2073,8 +4090,8 @@ func (c *Client) StartCaptureSession(ctx context.Context, body StartCaptureSessi return c.Client.Do(req) } -func (c *Client) PublishEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPublishEventRequestWithBody(c.Server, contentType, body) +func (c *Client) DeleteFile(ctx context.Context, body DeleteFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteFileRequest(c.Server, body) if err != nil { return nil, err } @@ -2085,8 +4102,8 @@ func (c *Client) PublishEventWithBody(ctx context.Context, contentType string, b return c.Client.Do(req) } -func (c *Client) PublishEvent(ctx context.Context, body PublishEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewPublishEventRequest(c.Server, body) +func (c *Client) DownloadDirZip(ctx context.Context, params *DownloadDirZipParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDownloadDirZipRequest(c.Server, params) if err != nil { return nil, err } @@ -2097,8 +4114,8 @@ func (c *Client) PublishEvent(ctx context.Context, body PublishEventJSONRequestB return c.Client.Do(req) } -func (c *Client) StreamEvents(ctx context.Context, params *StreamEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStreamEventsRequest(c.Server, params) +func (c *Client) DownloadDirZstd(ctx context.Context, params *DownloadDirZstdParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDownloadDirZstdRequest(c.Server, params) if err != nil { return nil, err } @@ -2109,8 +4126,8 @@ func (c *Client) StreamEvents(ctx context.Context, params *StreamEventsParams, r return c.Client.Do(req) } -func (c *Client) CreateDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateDirectoryRequestWithBody(c.Server, contentType, body) +func (c *Client) FileInfo(ctx context.Context, params *FileInfoParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFileInfoRequest(c.Server, params) if err != nil { return nil, err } @@ -2121,8 +4138,8 @@ func (c *Client) CreateDirectoryWithBody(ctx context.Context, contentType string return c.Client.Do(req) } -func (c *Client) CreateDirectory(ctx context.Context, body CreateDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCreateDirectoryRequest(c.Server, body) +func (c *Client) ListFiles(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListFilesRequest(c.Server, params) if err != nil { return nil, err } @@ -2133,8 +4150,8 @@ func (c *Client) CreateDirectory(ctx context.Context, body CreateDirectoryJSONRe return c.Client.Do(req) } -func (c *Client) DeleteDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteDirectoryRequestWithBody(c.Server, contentType, body) +func (c *Client) MovePathWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMovePathRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2145,8 +4162,8 @@ func (c *Client) DeleteDirectoryWithBody(ctx context.Context, contentType string return c.Client.Do(req) } -func (c *Client) DeleteDirectory(ctx context.Context, body DeleteDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteDirectoryRequest(c.Server, body) +func (c *Client) MovePath(ctx context.Context, body MovePathJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMovePathRequest(c.Server, body) if err != nil { return nil, err } @@ -2157,8 +4174,8 @@ func (c *Client) DeleteDirectory(ctx context.Context, body DeleteDirectoryJSONRe return c.Client.Do(req) } -func (c *Client) DeleteFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteFileRequestWithBody(c.Server, contentType, body) +func (c *Client) ReadFile(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewReadFileRequest(c.Server, params) if err != nil { return nil, err } @@ -2169,8 +4186,8 @@ func (c *Client) DeleteFileWithBody(ctx context.Context, contentType string, bod return c.Client.Do(req) } -func (c *Client) DeleteFile(ctx context.Context, body DeleteFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteFileRequest(c.Server, body) +func (c *Client) SetFilePermissionsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetFilePermissionsRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2181,8 +4198,8 @@ func (c *Client) DeleteFile(ctx context.Context, body DeleteFileJSONRequestBody, return c.Client.Do(req) } -func (c *Client) DownloadDirZip(ctx context.Context, params *DownloadDirZipParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDownloadDirZipRequest(c.Server, params) +func (c *Client) SetFilePermissions(ctx context.Context, body SetFilePermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetFilePermissionsRequest(c.Server, body) if err != nil { return nil, err } @@ -2193,8 +4210,8 @@ func (c *Client) DownloadDirZip(ctx context.Context, params *DownloadDirZipParam return c.Client.Do(req) } -func (c *Client) DownloadDirZstd(ctx context.Context, params *DownloadDirZstdParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDownloadDirZstdRequest(c.Server, params) +func (c *Client) UploadFilesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUploadFilesRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2205,8 +4222,8 @@ func (c *Client) DownloadDirZstd(ctx context.Context, params *DownloadDirZstdPar return c.Client.Do(req) } -func (c *Client) FileInfo(ctx context.Context, params *FileInfoParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewFileInfoRequest(c.Server, params) +func (c *Client) UploadZipWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUploadZipRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2217,8 +4234,8 @@ func (c *Client) FileInfo(ctx context.Context, params *FileInfoParams, reqEditor return c.Client.Do(req) } -func (c *Client) ListFiles(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewListFilesRequest(c.Server, params) +func (c *Client) UploadZstdWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUploadZstdRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2229,8 +4246,8 @@ func (c *Client) ListFiles(ctx context.Context, params *ListFilesParams, reqEdit return c.Client.Do(req) } -func (c *Client) MovePathWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewMovePathRequestWithBody(c.Server, contentType, body) +func (c *Client) StartFsWatchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStartFsWatchRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2241,8 +4258,8 @@ func (c *Client) MovePathWithBody(ctx context.Context, contentType string, body return c.Client.Do(req) } -func (c *Client) MovePath(ctx context.Context, body MovePathJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewMovePathRequest(c.Server, body) +func (c *Client) StartFsWatch(ctx context.Context, body StartFsWatchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStartFsWatchRequest(c.Server, body) if err != nil { return nil, err } @@ -2253,8 +4270,8 @@ func (c *Client) MovePath(ctx context.Context, body MovePathJSONRequestBody, req return c.Client.Do(req) } -func (c *Client) ReadFile(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewReadFileRequest(c.Server, params) +func (c *Client) StopFsWatch(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStopFsWatchRequest(c.Server, watchId) if err != nil { return nil, err } @@ -2265,8 +4282,8 @@ func (c *Client) ReadFile(ctx context.Context, params *ReadFileParams, reqEditor return c.Client.Do(req) } -func (c *Client) SetFilePermissionsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSetFilePermissionsRequestWithBody(c.Server, contentType, body) +func (c *Client) StreamFsEvents(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStreamFsEventsRequest(c.Server, watchId) if err != nil { return nil, err } @@ -2277,8 +4294,8 @@ func (c *Client) SetFilePermissionsWithBody(ctx context.Context, contentType str return c.Client.Do(req) } -func (c *Client) SetFilePermissions(ctx context.Context, body SetFilePermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewSetFilePermissionsRequest(c.Server, body) +func (c *Client) WriteFileWithBody(ctx context.Context, params *WriteFileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewWriteFileRequestWithBody(c.Server, params, contentType, body) if err != nil { return nil, err } @@ -2289,8 +4306,8 @@ func (c *Client) SetFilePermissions(ctx context.Context, body SetFilePermissions return c.Client.Do(req) } -func (c *Client) UploadFilesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewUploadFilesRequestWithBody(c.Server, contentType, body) +func (c *Client) LogsStream(ctx context.Context, params *LogsStreamParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewLogsStreamRequest(c.Server, params) if err != nil { return nil, err } @@ -2301,8 +4318,8 @@ func (c *Client) UploadFilesWithBody(ctx context.Context, contentType string, bo return c.Client.Do(req) } -func (c *Client) UploadZipWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewUploadZipRequestWithBody(c.Server, contentType, body) +func (c *Client) ExecutePlaywrightCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewExecutePlaywrightCodeRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2313,8 +4330,8 @@ func (c *Client) UploadZipWithBody(ctx context.Context, contentType string, body return c.Client.Do(req) } -func (c *Client) UploadZstdWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewUploadZstdRequestWithBody(c.Server, contentType, body) +func (c *Client) ExecutePlaywrightCode(ctx context.Context, body ExecutePlaywrightCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewExecutePlaywrightCodeRequest(c.Server, body) if err != nil { return nil, err } @@ -2325,8 +4342,8 @@ func (c *Client) UploadZstdWithBody(ctx context.Context, contentType string, bod return c.Client.Do(req) } -func (c *Client) StartFsWatchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStartFsWatchRequestWithBody(c.Server, contentType, body) +func (c *Client) ProcessExecWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessExecRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2337,8 +4354,8 @@ func (c *Client) StartFsWatchWithBody(ctx context.Context, contentType string, b return c.Client.Do(req) } -func (c *Client) StartFsWatch(ctx context.Context, body StartFsWatchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStartFsWatchRequest(c.Server, body) +func (c *Client) ProcessExec(ctx context.Context, body ProcessExecJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessExecRequest(c.Server, body) if err != nil { return nil, err } @@ -2349,8 +4366,8 @@ func (c *Client) StartFsWatch(ctx context.Context, body StartFsWatchJSONRequestB return c.Client.Do(req) } -func (c *Client) StopFsWatch(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStopFsWatchRequest(c.Server, watchId) +func (c *Client) ProcessSpawnWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessSpawnRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2361,8 +4378,8 @@ func (c *Client) StopFsWatch(ctx context.Context, watchId string, reqEditors ... return c.Client.Do(req) } -func (c *Client) StreamFsEvents(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStreamFsEventsRequest(c.Server, watchId) +func (c *Client) ProcessSpawn(ctx context.Context, body ProcessSpawnJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessSpawnRequest(c.Server, body) if err != nil { return nil, err } @@ -2373,8 +4390,8 @@ func (c *Client) StreamFsEvents(ctx context.Context, watchId string, reqEditors return c.Client.Do(req) } -func (c *Client) WriteFileWithBody(ctx context.Context, params *WriteFileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewWriteFileRequestWithBody(c.Server, params, contentType, body) +func (c *Client) ProcessKillWithBody(ctx context.Context, processId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessKillRequestWithBody(c.Server, processId, contentType, body) if err != nil { return nil, err } @@ -2385,8 +4402,8 @@ func (c *Client) WriteFileWithBody(ctx context.Context, params *WriteFileParams, return c.Client.Do(req) } -func (c *Client) LogsStream(ctx context.Context, params *LogsStreamParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewLogsStreamRequest(c.Server, params) +func (c *Client) ProcessKill(ctx context.Context, processId openapi_types.UUID, body ProcessKillJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessKillRequest(c.Server, processId, body) if err != nil { return nil, err } @@ -2397,8 +4414,8 @@ func (c *Client) LogsStream(ctx context.Context, params *LogsStreamParams, reqEd return c.Client.Do(req) } -func (c *Client) ExecutePlaywrightCodeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewExecutePlaywrightCodeRequestWithBody(c.Server, contentType, body) +func (c *Client) ProcessResizeWithBody(ctx context.Context, processId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessResizeRequestWithBody(c.Server, processId, contentType, body) if err != nil { return nil, err } @@ -2409,8 +4426,8 @@ func (c *Client) ExecutePlaywrightCodeWithBody(ctx context.Context, contentType return c.Client.Do(req) } -func (c *Client) ExecutePlaywrightCode(ctx context.Context, body ExecutePlaywrightCodeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewExecutePlaywrightCodeRequest(c.Server, body) +func (c *Client) ProcessResize(ctx context.Context, processId openapi_types.UUID, body ProcessResizeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessResizeRequest(c.Server, processId, body) if err != nil { return nil, err } @@ -2421,8 +4438,8 @@ func (c *Client) ExecutePlaywrightCode(ctx context.Context, body ExecutePlaywrig return c.Client.Do(req) } -func (c *Client) ProcessExecWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessExecRequestWithBody(c.Server, contentType, body) +func (c *Client) ProcessStatus(ctx context.Context, processId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessStatusRequest(c.Server, processId) if err != nil { return nil, err } @@ -2433,8 +4450,8 @@ func (c *Client) ProcessExecWithBody(ctx context.Context, contentType string, bo return c.Client.Do(req) } -func (c *Client) ProcessExec(ctx context.Context, body ProcessExecJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessExecRequest(c.Server, body) +func (c *Client) ProcessStdinWithBody(ctx context.Context, processId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessStdinRequestWithBody(c.Server, processId, contentType, body) if err != nil { return nil, err } @@ -2445,8 +4462,8 @@ func (c *Client) ProcessExec(ctx context.Context, body ProcessExecJSONRequestBod return c.Client.Do(req) } -func (c *Client) ProcessSpawnWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessSpawnRequestWithBody(c.Server, contentType, body) +func (c *Client) ProcessStdin(ctx context.Context, processId openapi_types.UUID, body ProcessStdinJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessStdinRequest(c.Server, processId, body) if err != nil { return nil, err } @@ -2457,8 +4474,8 @@ func (c *Client) ProcessSpawnWithBody(ctx context.Context, contentType string, b return c.Client.Do(req) } -func (c *Client) ProcessSpawn(ctx context.Context, body ProcessSpawnJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessSpawnRequest(c.Server, body) +func (c *Client) ProcessStdoutStream(ctx context.Context, processId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewProcessStdoutStreamRequest(c.Server, processId) if err != nil { return nil, err } @@ -2468,9 +4485,9 @@ func (c *Client) ProcessSpawn(ctx context.Context, body ProcessSpawnJSONRequestB } return c.Client.Do(req) } - -func (c *Client) ProcessKillWithBody(ctx context.Context, processId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessKillRequestWithBody(c.Server, processId, contentType, body) + +func (c *Client) DeleteRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteRecordingRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2481,8 +4498,8 @@ func (c *Client) ProcessKillWithBody(ctx context.Context, processId openapi_type return c.Client.Do(req) } -func (c *Client) ProcessKill(ctx context.Context, processId openapi_types.UUID, body ProcessKillJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessKillRequest(c.Server, processId, body) +func (c *Client) DeleteRecording(ctx context.Context, body DeleteRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteRecordingRequest(c.Server, body) if err != nil { return nil, err } @@ -2493,8 +4510,8 @@ func (c *Client) ProcessKill(ctx context.Context, processId openapi_types.UUID, return c.Client.Do(req) } -func (c *Client) ProcessResizeWithBody(ctx context.Context, processId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessResizeRequestWithBody(c.Server, processId, contentType, body) +func (c *Client) DownloadRecording(ctx context.Context, params *DownloadRecordingParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDownloadRecordingRequest(c.Server, params) if err != nil { return nil, err } @@ -2505,8 +4522,8 @@ func (c *Client) ProcessResizeWithBody(ctx context.Context, processId openapi_ty return c.Client.Do(req) } -func (c *Client) ProcessResize(ctx context.Context, processId openapi_types.UUID, body ProcessResizeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessResizeRequest(c.Server, processId, body) +func (c *Client) ListRecorders(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListRecordersRequest(c.Server) if err != nil { return nil, err } @@ -2517,8 +4534,8 @@ func (c *Client) ProcessResize(ctx context.Context, processId openapi_types.UUID return c.Client.Do(req) } -func (c *Client) ProcessStatus(ctx context.Context, processId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessStatusRequest(c.Server, processId) +func (c *Client) StartRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStartRecordingRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2529,8 +4546,8 @@ func (c *Client) ProcessStatus(ctx context.Context, processId openapi_types.UUID return c.Client.Do(req) } -func (c *Client) ProcessStdinWithBody(ctx context.Context, processId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessStdinRequestWithBody(c.Server, processId, contentType, body) +func (c *Client) StartRecording(ctx context.Context, body StartRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStartRecordingRequest(c.Server, body) if err != nil { return nil, err } @@ -2541,8 +4558,8 @@ func (c *Client) ProcessStdinWithBody(ctx context.Context, processId openapi_typ return c.Client.Do(req) } -func (c *Client) ProcessStdin(ctx context.Context, processId openapi_types.UUID, body ProcessStdinJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessStdinRequest(c.Server, processId, body) +func (c *Client) StopRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStopRecordingRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2553,8 +4570,8 @@ func (c *Client) ProcessStdin(ctx context.Context, processId openapi_types.UUID, return c.Client.Do(req) } -func (c *Client) ProcessStdoutStream(ctx context.Context, processId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewProcessStdoutStreamRequest(c.Server, processId) +func (c *Client) StopRecording(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStopRecordingRequest(c.Server, body) if err != nil { return nil, err } @@ -2565,8 +4582,8 @@ func (c *Client) ProcessStdoutStream(ctx context.Context, processId openapi_type return c.Client.Do(req) } -func (c *Client) DeleteRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteRecordingRequestWithBody(c.Server, contentType, body) +func (c *Client) DisableScaleToZero(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDisableScaleToZeroRequest(c.Server) if err != nil { return nil, err } @@ -2577,8 +4594,8 @@ func (c *Client) DeleteRecordingWithBody(ctx context.Context, contentType string return c.Client.Do(req) } -func (c *Client) DeleteRecording(ctx context.Context, body DeleteRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDeleteRecordingRequest(c.Server, body) +func (c *Client) EnableScaleToZero(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewEnableScaleToZeroRequest(c.Server) if err != nil { return nil, err } @@ -2589,8 +4606,8 @@ func (c *Client) DeleteRecording(ctx context.Context, body DeleteRecordingJSONRe return c.Client.Do(req) } -func (c *Client) DownloadRecording(ctx context.Context, params *DownloadRecordingParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDownloadRecordingRequest(c.Server, params) +func (c *Client) GetTelemetry(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTelemetryRequest(c.Server) if err != nil { return nil, err } @@ -2601,8 +4618,8 @@ func (c *Client) DownloadRecording(ctx context.Context, params *DownloadRecordin return c.Client.Do(req) } -func (c *Client) ListRecorders(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewListRecordersRequest(c.Server) +func (c *Client) PatchTelemetryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchTelemetryRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2613,8 +4630,8 @@ func (c *Client) ListRecorders(ctx context.Context, reqEditors ...RequestEditorF return c.Client.Do(req) } -func (c *Client) StartRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStartRecordingRequestWithBody(c.Server, contentType, body) +func (c *Client) PatchTelemetry(ctx context.Context, body PatchTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchTelemetryRequest(c.Server, body) if err != nil { return nil, err } @@ -2625,8 +4642,8 @@ func (c *Client) StartRecordingWithBody(ctx context.Context, contentType string, return c.Client.Do(req) } -func (c *Client) StartRecording(ctx context.Context, body StartRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStartRecordingRequest(c.Server, body) +func (c *Client) PutTelemetryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutTelemetryRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2637,8 +4654,8 @@ func (c *Client) StartRecording(ctx context.Context, body StartRecordingJSONRequ return c.Client.Do(req) } -func (c *Client) StopRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStopRecordingRequestWithBody(c.Server, contentType, body) +func (c *Client) PutTelemetry(ctx context.Context, body PutTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutTelemetryRequest(c.Server, body) if err != nil { return nil, err } @@ -2649,8 +4666,8 @@ func (c *Client) StopRecordingWithBody(ctx context.Context, contentType string, return c.Client.Do(req) } -func (c *Client) StopRecording(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewStopRecordingRequest(c.Server, body) +func (c *Client) PublishTelemetryEventWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPublishTelemetryEventRequestWithBody(c.Server, contentType, body) if err != nil { return nil, err } @@ -2661,8 +4678,8 @@ func (c *Client) StopRecording(ctx context.Context, body StopRecordingJSONReques return c.Client.Do(req) } -func (c *Client) DisableScaleToZero(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewDisableScaleToZeroRequest(c.Server) +func (c *Client) PublishTelemetryEvent(ctx context.Context, body PublishTelemetryEventJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPublishTelemetryEventRequest(c.Server, body) if err != nil { return nil, err } @@ -2673,8 +4690,8 @@ func (c *Client) DisableScaleToZero(ctx context.Context, reqEditors ...RequestEd return c.Client.Do(req) } -func (c *Client) EnableScaleToZero(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewEnableScaleToZeroRequest(c.Server) +func (c *Client) StreamTelemetryEvents(ctx context.Context, params *StreamTelemetryEventsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStreamTelemetryEventsRequest(c.Server, params) if err != nil { return nil, err } @@ -3020,207 +5037,9 @@ func NewDragMouseRequestWithBody(server string, contentType string, body io.Read return req, nil } - -// NewGetMousePositionRequest generates requests for GetMousePosition -func NewGetMousePositionRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/computer/get_mouse_position") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewMoveMouseRequest calls the generic MoveMouse builder with application/json body -func NewMoveMouseRequest(server string, body MoveMouseJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewMoveMouseRequestWithBody(server, "application/json", bodyReader) -} - -// NewMoveMouseRequestWithBody generates requests for MoveMouse with any type of body -func NewMoveMouseRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/computer/move_mouse") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewPressKeyRequest calls the generic PressKey builder with application/json body -func NewPressKeyRequest(server string, body PressKeyJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewPressKeyRequestWithBody(server, "application/json", bodyReader) -} - -// NewPressKeyRequestWithBody generates requests for PressKey with any type of body -func NewPressKeyRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/computer/press_key") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewTakeScreenshotRequest calls the generic TakeScreenshot builder with application/json body -func NewTakeScreenshotRequest(server string, body TakeScreenshotJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewTakeScreenshotRequestWithBody(server, "application/json", bodyReader) -} - -// NewTakeScreenshotRequestWithBody generates requests for TakeScreenshot with any type of body -func NewTakeScreenshotRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/computer/screenshot") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewScrollRequest calls the generic Scroll builder with application/json body -func NewScrollRequest(server string, body ScrollJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewScrollRequestWithBody(server, "application/json", bodyReader) -} - -// NewScrollRequestWithBody generates requests for Scroll with any type of body -func NewScrollRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/computer/scroll") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewTypeTextRequest calls the generic TypeText builder with application/json body -func NewTypeTextRequest(server string, body TypeTextJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewTypeTextRequestWithBody(server, "application/json", bodyReader) -} - -// NewTypeTextRequestWithBody generates requests for TypeText with any type of body -func NewTypeTextRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + +// NewGetMousePositionRequest generates requests for GetMousePosition +func NewGetMousePositionRequest(server string) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3228,7 +5047,7 @@ func NewTypeTextRequestWithBody(server string, contentType string, body io.Reade return nil, err } - operationPath := fmt.Sprintf("/computer/type") + operationPath := fmt.Sprintf("/computer/get_mouse_position") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3238,29 +5057,27 @@ func NewTypeTextRequestWithBody(server string, contentType string, body io.Reade return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest("POST", queryURL.String(), nil) if err != nil { return nil, err } - req.Header.Add("Content-Type", contentType) - return req, nil } -// NewPatchDisplayRequest calls the generic PatchDisplay builder with application/json body -func NewPatchDisplayRequest(server string, body PatchDisplayJSONRequestBody) (*http.Request, error) { +// NewMoveMouseRequest calls the generic MoveMouse builder with application/json body +func NewMoveMouseRequest(server string, body MoveMouseJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPatchDisplayRequestWithBody(server, "application/json", bodyReader) + return NewMoveMouseRequestWithBody(server, "application/json", bodyReader) } -// NewPatchDisplayRequestWithBody generates requests for PatchDisplay with any type of body -func NewPatchDisplayRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewMoveMouseRequestWithBody generates requests for MoveMouse with any type of body +func NewMoveMouseRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3268,7 +5085,7 @@ func NewPatchDisplayRequestWithBody(server string, contentType string, body io.R return nil, err } - operationPath := fmt.Sprintf("/display") + operationPath := fmt.Sprintf("/computer/move_mouse") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3278,7 +5095,7 @@ func NewPatchDisplayRequestWithBody(server string, contentType string, body io.R return nil, err } - req, err := http.NewRequest("PATCH", queryURL.String(), body) + req, err := http.NewRequest("POST", queryURL.String(), body) if err != nil { return nil, err } @@ -3288,35 +5105,19 @@ func NewPatchDisplayRequestWithBody(server string, contentType string, body io.R return req, nil } -// NewStopCaptureSessionRequest generates requests for StopCaptureSession -func NewStopCaptureSessionRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/events/capture_session") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("DELETE", queryURL.String(), nil) +// NewPressKeyRequest calls the generic PressKey builder with application/json body +func NewPressKeyRequest(server string, body PressKeyJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) if err != nil { return nil, err } - - return req, nil + bodyReader = bytes.NewReader(buf) + return NewPressKeyRequestWithBody(server, "application/json", bodyReader) } -// NewGetCaptureSessionRequest generates requests for GetCaptureSession -func NewGetCaptureSessionRequest(server string) (*http.Request, error) { +// NewPressKeyRequestWithBody generates requests for PressKey with any type of body +func NewPressKeyRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3324,7 +5125,7 @@ func NewGetCaptureSessionRequest(server string) (*http.Request, error) { return nil, err } - operationPath := fmt.Sprintf("/events/capture_session") + operationPath := fmt.Sprintf("/computer/press_key") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3334,27 +5135,29 @@ func NewGetCaptureSessionRequest(server string) (*http.Request, error) { return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest("POST", queryURL.String(), body) if err != nil { return nil, err } + req.Header.Add("Content-Type", contentType) + return req, nil } -// NewUpdateCaptureSessionRequest calls the generic UpdateCaptureSession builder with application/json body -func NewUpdateCaptureSessionRequest(server string, body UpdateCaptureSessionJSONRequestBody) (*http.Request, error) { +// NewTakeScreenshotRequest calls the generic TakeScreenshot builder with application/json body +func NewTakeScreenshotRequest(server string, body TakeScreenshotJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewUpdateCaptureSessionRequestWithBody(server, "application/json", bodyReader) + return NewTakeScreenshotRequestWithBody(server, "application/json", bodyReader) } -// NewUpdateCaptureSessionRequestWithBody generates requests for UpdateCaptureSession with any type of body -func NewUpdateCaptureSessionRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewTakeScreenshotRequestWithBody generates requests for TakeScreenshot with any type of body +func NewTakeScreenshotRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3362,7 +5165,7 @@ func NewUpdateCaptureSessionRequestWithBody(server string, contentType string, b return nil, err } - operationPath := fmt.Sprintf("/events/capture_session") + operationPath := fmt.Sprintf("/computer/screenshot") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3372,7 +5175,7 @@ func NewUpdateCaptureSessionRequestWithBody(server string, contentType string, b return nil, err } - req, err := http.NewRequest("PATCH", queryURL.String(), body) + req, err := http.NewRequest("POST", queryURL.String(), body) if err != nil { return nil, err } @@ -3382,19 +5185,19 @@ func NewUpdateCaptureSessionRequestWithBody(server string, contentType string, b return req, nil } -// NewStartCaptureSessionRequest calls the generic StartCaptureSession builder with application/json body -func NewStartCaptureSessionRequest(server string, body StartCaptureSessionJSONRequestBody) (*http.Request, error) { +// NewScrollRequest calls the generic Scroll builder with application/json body +func NewScrollRequest(server string, body ScrollJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewStartCaptureSessionRequestWithBody(server, "application/json", bodyReader) + return NewScrollRequestWithBody(server, "application/json", bodyReader) } -// NewStartCaptureSessionRequestWithBody generates requests for StartCaptureSession with any type of body -func NewStartCaptureSessionRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewScrollRequestWithBody generates requests for Scroll with any type of body +func NewScrollRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3402,7 +5205,7 @@ func NewStartCaptureSessionRequestWithBody(server string, contentType string, bo return nil, err } - operationPath := fmt.Sprintf("/events/capture_session") + operationPath := fmt.Sprintf("/computer/scroll") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3422,19 +5225,19 @@ func NewStartCaptureSessionRequestWithBody(server string, contentType string, bo return req, nil } -// NewPublishEventRequest calls the generic PublishEvent builder with application/json body -func NewPublishEventRequest(server string, body PublishEventJSONRequestBody) (*http.Request, error) { +// NewTypeTextRequest calls the generic TypeText builder with application/json body +func NewTypeTextRequest(server string, body TypeTextJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewPublishEventRequestWithBody(server, "application/json", bodyReader) + return NewTypeTextRequestWithBody(server, "application/json", bodyReader) } -// NewPublishEventRequestWithBody generates requests for PublishEvent with any type of body -func NewPublishEventRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewTypeTextRequestWithBody generates requests for TypeText with any type of body +func NewTypeTextRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3442,7 +5245,7 @@ func NewPublishEventRequestWithBody(server string, contentType string, body io.R return nil, err } - operationPath := fmt.Sprintf("/events/publish") + operationPath := fmt.Sprintf("/computer/type") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3462,8 +5265,19 @@ func NewPublishEventRequestWithBody(server string, contentType string, body io.R return req, nil } -// NewStreamEventsRequest generates requests for StreamEvents -func NewStreamEventsRequest(server string, params *StreamEventsParams) (*http.Request, error) { +// NewPatchDisplayRequest calls the generic PatchDisplay builder with application/json body +func NewPatchDisplayRequest(server string, body PatchDisplayJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPatchDisplayRequestWithBody(server, "application/json", bodyReader) +} + +// NewPatchDisplayRequestWithBody generates requests for PatchDisplay with any type of body +func NewPatchDisplayRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -3471,7 +5285,7 @@ func NewStreamEventsRequest(server string, params *StreamEventsParams) (*http.Re return nil, err } - operationPath := fmt.Sprintf("/events/stream") + operationPath := fmt.Sprintf("/display") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -3481,25 +5295,12 @@ func NewStreamEventsRequest(server string, params *StreamEventsParams) (*http.Re return nil, err } - req, err := http.NewRequest("GET", queryURL.String(), nil) + req, err := http.NewRequest("PATCH", queryURL.String(), body) if err != nil { return nil, err } - if params != nil { - - if params.LastEventID != nil { - var headerParam0 string - - headerParam0, err = runtime.StyleParamWithOptions("simple", false, "Last-Event-ID", *params.LastEventID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - req.Header.Set("Last-Event-ID", headerParam0) - } - - } + req.Header.Add("Content-Type", contentType) return req, nil } @@ -4848,8 +6649,182 @@ func NewDisableScaleToZeroRequest(server string) (*http.Request, error) { return req, nil } -// NewEnableScaleToZeroRequest generates requests for EnableScaleToZero -func NewEnableScaleToZeroRequest(server string) (*http.Request, error) { +// NewEnableScaleToZeroRequest generates requests for EnableScaleToZero +func NewEnableScaleToZeroRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/scaletozero/enable") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTelemetryRequest generates requests for GetTelemetry +func NewGetTelemetryRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/telemetry") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPatchTelemetryRequest calls the generic PatchTelemetry builder with application/json body +func NewPatchTelemetryRequest(server string, body PatchTelemetryJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPatchTelemetryRequestWithBody(server, "application/json", bodyReader) +} + +// NewPatchTelemetryRequestWithBody generates requests for PatchTelemetry with any type of body +func NewPatchTelemetryRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/telemetry") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PATCH", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPutTelemetryRequest calls the generic PutTelemetry builder with application/json body +func NewPutTelemetryRequest(server string, body PutTelemetryJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPutTelemetryRequestWithBody(server, "application/json", bodyReader) +} + +// NewPutTelemetryRequestWithBody generates requests for PutTelemetry with any type of body +func NewPutTelemetryRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/telemetry") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPublishTelemetryEventRequest calls the generic PublishTelemetryEvent builder with application/json body +func NewPublishTelemetryEventRequest(server string, body PublishTelemetryEventJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPublishTelemetryEventRequestWithBody(server, "application/json", bodyReader) +} + +// NewPublishTelemetryEventRequestWithBody generates requests for PublishTelemetryEvent with any type of body +func NewPublishTelemetryEventRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/telemetry/events") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewStreamTelemetryEventsRequest generates requests for StreamTelemetryEvents +func NewStreamTelemetryEventsRequest(server string, params *StreamTelemetryEventsParams) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -4857,7 +6832,7 @@ func NewEnableScaleToZeroRequest(server string) (*http.Request, error) { return nil, err } - operationPath := fmt.Sprintf("/scaletozero/enable") + operationPath := fmt.Sprintf("/telemetry/stream") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -4867,11 +6842,26 @@ func NewEnableScaleToZeroRequest(server string) (*http.Request, error) { return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), nil) + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err } + if params != nil { + + if params.LastEventID != nil { + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "Last-Event-ID", *params.LastEventID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("Last-Event-ID", headerParam0) + } + + } + return req, nil } @@ -4992,30 +6982,6 @@ type ClientWithResponsesInterface interface { PatchDisplayWithResponse(ctx context.Context, body PatchDisplayJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchDisplayResponse, error) - // StopCaptureSessionWithResponse request - StopCaptureSessionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*StopCaptureSessionResponse, error) - - // GetCaptureSessionWithResponse request - GetCaptureSessionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetCaptureSessionResponse, error) - - // UpdateCaptureSessionWithBodyWithResponse request with any body - UpdateCaptureSessionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateCaptureSessionResponse, error) - - UpdateCaptureSessionWithResponse(ctx context.Context, body UpdateCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateCaptureSessionResponse, error) - - // StartCaptureSessionWithBodyWithResponse request with any body - StartCaptureSessionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartCaptureSessionResponse, error) - - StartCaptureSessionWithResponse(ctx context.Context, body StartCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*StartCaptureSessionResponse, error) - - // PublishEventWithBodyWithResponse request with any body - PublishEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PublishEventResponse, error) - - PublishEventWithResponse(ctx context.Context, body PublishEventJSONRequestBody, reqEditors ...RequestEditorFn) (*PublishEventResponse, error) - - // StreamEventsWithResponse request - StreamEventsWithResponse(ctx context.Context, params *StreamEventsParams, reqEditors ...RequestEditorFn) (*StreamEventsResponse, error) - // CreateDirectoryWithBodyWithResponse request with any body CreateDirectoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateDirectoryResponse, error) @@ -5144,6 +7110,27 @@ type ClientWithResponsesInterface interface { // EnableScaleToZeroWithResponse request EnableScaleToZeroWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*EnableScaleToZeroResponse, error) + + // GetTelemetryWithResponse request + GetTelemetryWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetTelemetryResponse, error) + + // PatchTelemetryWithBodyWithResponse request with any body + PatchTelemetryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchTelemetryResponse, error) + + PatchTelemetryWithResponse(ctx context.Context, body PatchTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchTelemetryResponse, error) + + // PutTelemetryWithBodyWithResponse request with any body + PutTelemetryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutTelemetryResponse, error) + + PutTelemetryWithResponse(ctx context.Context, body PutTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*PutTelemetryResponse, error) + + // PublishTelemetryEventWithBodyWithResponse request with any body + PublishTelemetryEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PublishTelemetryEventResponse, error) + + PublishTelemetryEventWithResponse(ctx context.Context, body PublishTelemetryEventJSONRequestBody, reqEditors ...RequestEditorFn) (*PublishTelemetryEventResponse, error) + + // StreamTelemetryEventsWithResponse request + StreamTelemetryEventsWithResponse(ctx context.Context, params *StreamTelemetryEventsParams, reqEditors ...RequestEditorFn) (*StreamTelemetryEventsResponse, error) } type PatchChromiumFlagsResponse struct { @@ -5517,145 +7504,6 @@ func (r PatchDisplayResponse) StatusCode() int { return 0 } -type StopCaptureSessionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *CaptureSession - JSON404 *NotFoundError -} - -// Status returns HTTPResponse.Status -func (r StopCaptureSessionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r StopCaptureSessionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -type GetCaptureSessionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *CaptureSession - JSON404 *NotFoundError -} - -// Status returns HTTPResponse.Status -func (r GetCaptureSessionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r GetCaptureSessionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -type UpdateCaptureSessionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *CaptureSession - JSON400 *BadRequestError - JSON404 *NotFoundError -} - -// Status returns HTTPResponse.Status -func (r UpdateCaptureSessionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r UpdateCaptureSessionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -type StartCaptureSessionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *CaptureSession - JSON400 *BadRequestError - JSON409 *ConflictError - JSON500 *InternalError -} - -// Status returns HTTPResponse.Status -func (r StartCaptureSessionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r StartCaptureSessionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -type PublishEventResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *PublishedEnvelope - JSON400 *BadRequestError -} - -// Status returns HTTPResponse.Status -func (r PublishEventResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r PublishEventResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -type StreamEventsResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r StreamEventsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r StreamEventsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - type CreateDirectoryResponse struct { Body []byte HTTPResponse *http.Response @@ -6427,14 +8275,130 @@ func (r DisableScaleToZeroResponse) StatusCode() int { return 0 } -type EnableScaleToZeroResponse struct { +type EnableScaleToZeroResponse struct { + Body []byte + HTTPResponse *http.Response + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r EnableScaleToZeroResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r EnableScaleToZeroResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetTelemetryResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TelemetryState + JSON404 *NotFoundError +} + +// Status returns HTTPResponse.Status +func (r GetTelemetryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTelemetryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PatchTelemetryResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TelemetryState + JSON400 *BadRequestError + JSON404 *NotFoundError +} + +// Status returns HTTPResponse.Status +func (r PatchTelemetryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PatchTelemetryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PutTelemetryResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TelemetryState + JSON201 *TelemetryState + JSON400 *BadRequestError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r PutTelemetryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PutTelemetryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PublishTelemetryEventResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TelemetryEnvelope + JSON400 *BadRequestError +} + +// Status returns HTTPResponse.Status +func (r PublishTelemetryEventResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PublishTelemetryEventResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type StreamTelemetryEventsResponse struct { Body []byte HTTPResponse *http.Response - JSON500 *InternalError } // Status returns HTTPResponse.Status -func (r EnableScaleToZeroResponse) Status() string { +func (r StreamTelemetryEventsResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -6442,7 +8406,7 @@ func (r EnableScaleToZeroResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r EnableScaleToZeroResponse) StatusCode() int { +func (r StreamTelemetryEventsResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } @@ -6697,84 +8661,6 @@ func (c *ClientWithResponses) PatchDisplayWithResponse(ctx context.Context, body return ParsePatchDisplayResponse(rsp) } -// StopCaptureSessionWithResponse request returning *StopCaptureSessionResponse -func (c *ClientWithResponses) StopCaptureSessionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*StopCaptureSessionResponse, error) { - rsp, err := c.StopCaptureSession(ctx, reqEditors...) - if err != nil { - return nil, err - } - return ParseStopCaptureSessionResponse(rsp) -} - -// GetCaptureSessionWithResponse request returning *GetCaptureSessionResponse -func (c *ClientWithResponses) GetCaptureSessionWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetCaptureSessionResponse, error) { - rsp, err := c.GetCaptureSession(ctx, reqEditors...) - if err != nil { - return nil, err - } - return ParseGetCaptureSessionResponse(rsp) -} - -// UpdateCaptureSessionWithBodyWithResponse request with arbitrary body returning *UpdateCaptureSessionResponse -func (c *ClientWithResponses) UpdateCaptureSessionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateCaptureSessionResponse, error) { - rsp, err := c.UpdateCaptureSessionWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseUpdateCaptureSessionResponse(rsp) -} - -func (c *ClientWithResponses) UpdateCaptureSessionWithResponse(ctx context.Context, body UpdateCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateCaptureSessionResponse, error) { - rsp, err := c.UpdateCaptureSession(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseUpdateCaptureSessionResponse(rsp) -} - -// StartCaptureSessionWithBodyWithResponse request with arbitrary body returning *StartCaptureSessionResponse -func (c *ClientWithResponses) StartCaptureSessionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartCaptureSessionResponse, error) { - rsp, err := c.StartCaptureSessionWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseStartCaptureSessionResponse(rsp) -} - -func (c *ClientWithResponses) StartCaptureSessionWithResponse(ctx context.Context, body StartCaptureSessionJSONRequestBody, reqEditors ...RequestEditorFn) (*StartCaptureSessionResponse, error) { - rsp, err := c.StartCaptureSession(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseStartCaptureSessionResponse(rsp) -} - -// PublishEventWithBodyWithResponse request with arbitrary body returning *PublishEventResponse -func (c *ClientWithResponses) PublishEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PublishEventResponse, error) { - rsp, err := c.PublishEventWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParsePublishEventResponse(rsp) -} - -func (c *ClientWithResponses) PublishEventWithResponse(ctx context.Context, body PublishEventJSONRequestBody, reqEditors ...RequestEditorFn) (*PublishEventResponse, error) { - rsp, err := c.PublishEvent(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParsePublishEventResponse(rsp) -} - -// StreamEventsWithResponse request returning *StreamEventsResponse -func (c *ClientWithResponses) StreamEventsWithResponse(ctx context.Context, params *StreamEventsParams, reqEditors ...RequestEditorFn) (*StreamEventsResponse, error) { - rsp, err := c.StreamEvents(ctx, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseStreamEventsResponse(rsp) -} - // CreateDirectoryWithBodyWithResponse request with arbitrary body returning *CreateDirectoryResponse func (c *ClientWithResponses) CreateDirectoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateDirectoryResponse, error) { rsp, err := c.CreateDirectoryWithBody(ctx, contentType, body, reqEditors...) @@ -7192,6 +9078,75 @@ func (c *ClientWithResponses) EnableScaleToZeroWithResponse(ctx context.Context, return ParseEnableScaleToZeroResponse(rsp) } +// GetTelemetryWithResponse request returning *GetTelemetryResponse +func (c *ClientWithResponses) GetTelemetryWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetTelemetryResponse, error) { + rsp, err := c.GetTelemetry(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTelemetryResponse(rsp) +} + +// PatchTelemetryWithBodyWithResponse request with arbitrary body returning *PatchTelemetryResponse +func (c *ClientWithResponses) PatchTelemetryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchTelemetryResponse, error) { + rsp, err := c.PatchTelemetryWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchTelemetryResponse(rsp) +} + +func (c *ClientWithResponses) PatchTelemetryWithResponse(ctx context.Context, body PatchTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchTelemetryResponse, error) { + rsp, err := c.PatchTelemetry(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchTelemetryResponse(rsp) +} + +// PutTelemetryWithBodyWithResponse request with arbitrary body returning *PutTelemetryResponse +func (c *ClientWithResponses) PutTelemetryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutTelemetryResponse, error) { + rsp, err := c.PutTelemetryWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePutTelemetryResponse(rsp) +} + +func (c *ClientWithResponses) PutTelemetryWithResponse(ctx context.Context, body PutTelemetryJSONRequestBody, reqEditors ...RequestEditorFn) (*PutTelemetryResponse, error) { + rsp, err := c.PutTelemetry(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePutTelemetryResponse(rsp) +} + +// PublishTelemetryEventWithBodyWithResponse request with arbitrary body returning *PublishTelemetryEventResponse +func (c *ClientWithResponses) PublishTelemetryEventWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PublishTelemetryEventResponse, error) { + rsp, err := c.PublishTelemetryEventWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePublishTelemetryEventResponse(rsp) +} + +func (c *ClientWithResponses) PublishTelemetryEventWithResponse(ctx context.Context, body PublishTelemetryEventJSONRequestBody, reqEditors ...RequestEditorFn) (*PublishTelemetryEventResponse, error) { + rsp, err := c.PublishTelemetryEvent(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePublishTelemetryEventResponse(rsp) +} + +// StreamTelemetryEventsWithResponse request returning *StreamTelemetryEventsResponse +func (c *ClientWithResponses) StreamTelemetryEventsWithResponse(ctx context.Context, params *StreamTelemetryEventsParams, reqEditors ...RequestEditorFn) (*StreamTelemetryEventsResponse, error) { + rsp, err := c.StreamTelemetryEvents(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseStreamTelemetryEventsResponse(rsp) +} + // ParsePatchChromiumFlagsResponse parses an HTTP response from a PatchChromiumFlagsWithResponse call func ParsePatchChromiumFlagsResponse(rsp *http.Response) (*PatchChromiumFlagsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -7529,192 +9484,20 @@ func ParseGetMousePositionResponse(rsp *http.Response) (*GetMousePositionRespons return response, nil } -// ParseMoveMouseResponse parses an HTTP response from a MoveMouseWithResponse call -func ParseMoveMouseResponse(rsp *http.Response) (*MoveMouseResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &MoveMouseResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - - return response, nil -} - -// ParsePressKeyResponse parses an HTTP response from a PressKeyWithResponse call -func ParsePressKeyResponse(rsp *http.Response) (*PressKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &PressKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - - return response, nil -} - -// ParseTakeScreenshotResponse parses an HTTP response from a TakeScreenshotWithResponse call -func ParseTakeScreenshotResponse(rsp *http.Response) (*TakeScreenshotResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &TakeScreenshotResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - - return response, nil -} - -// ParseScrollResponse parses an HTTP response from a ScrollWithResponse call -func ParseScrollResponse(rsp *http.Response) (*ScrollResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &ScrollResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - - return response, nil -} - -// ParseTypeTextResponse parses an HTTP response from a TypeTextWithResponse call -func ParseTypeTextResponse(rsp *http.Response) (*TypeTextResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &TypeTextResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - - return response, nil -} - -// ParsePatchDisplayResponse parses an HTTP response from a PatchDisplayWithResponse call -func ParsePatchDisplayResponse(rsp *http.Response) (*PatchDisplayResponse, error) { +// ParseMoveMouseResponse parses an HTTP response from a MoveMouseWithResponse call +func ParseMoveMouseResponse(rsp *http.Response) (*MoveMouseResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PatchDisplayResponse{ + response := &MoveMouseResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DisplayConfig - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: var dest BadRequestError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -7722,13 +9505,6 @@ func ParsePatchDisplayResponse(rsp *http.Response) (*PatchDisplayResponse, error } response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: - var dest ConflictError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON409 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -7741,93 +9517,86 @@ func ParsePatchDisplayResponse(rsp *http.Response) (*PatchDisplayResponse, error return response, nil } -// ParseStopCaptureSessionResponse parses an HTTP response from a StopCaptureSessionWithResponse call -func ParseStopCaptureSessionResponse(rsp *http.Response) (*StopCaptureSessionResponse, error) { +// ParsePressKeyResponse parses an HTTP response from a PressKeyWithResponse call +func ParsePressKeyResponse(rsp *http.Response) (*PressKeyResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &StopCaptureSessionResponse{ + response := &PressKeyResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest CaptureSession + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestError if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON200 = &dest + response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON500 = &dest } return response, nil } -// ParseGetCaptureSessionResponse parses an HTTP response from a GetCaptureSessionWithResponse call -func ParseGetCaptureSessionResponse(rsp *http.Response) (*GetCaptureSessionResponse, error) { +// ParseTakeScreenshotResponse parses an HTTP response from a TakeScreenshotWithResponse call +func ParseTakeScreenshotResponse(rsp *http.Response) (*TakeScreenshotResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &GetCaptureSessionResponse{ + response := &TakeScreenshotResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest CaptureSession + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestError if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON200 = &dest + response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON500 = &dest } return response, nil } -// ParseUpdateCaptureSessionResponse parses an HTTP response from a UpdateCaptureSessionWithResponse call -func ParseUpdateCaptureSessionResponse(rsp *http.Response) (*UpdateCaptureSessionResponse, error) { +// ParseScrollResponse parses an HTTP response from a ScrollWithResponse call +func ParseScrollResponse(rsp *http.Response) (*ScrollResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &UpdateCaptureSessionResponse{ + response := &ScrollResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest CaptureSession - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: var dest BadRequestError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -7835,39 +9604,32 @@ func ParseUpdateCaptureSessionResponse(rsp *http.Response) (*UpdateCaptureSessio } response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON500 = &dest } return response, nil } -// ParseStartCaptureSessionResponse parses an HTTP response from a StartCaptureSessionWithResponse call -func ParseStartCaptureSessionResponse(rsp *http.Response) (*StartCaptureSessionResponse, error) { +// ParseTypeTextResponse parses an HTTP response from a TypeTextWithResponse call +func ParseTypeTextResponse(rsp *http.Response) (*TypeTextResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &StartCaptureSessionResponse{ + response := &TypeTextResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CaptureSession - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: var dest BadRequestError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -7875,13 +9637,6 @@ func ParseStartCaptureSessionResponse(rsp *http.Response) (*StartCaptureSessionR } response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: - var dest ConflictError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON409 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: var dest InternalError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -7894,22 +9649,22 @@ func ParseStartCaptureSessionResponse(rsp *http.Response) (*StartCaptureSessionR return response, nil } -// ParsePublishEventResponse parses an HTTP response from a PublishEventWithResponse call -func ParsePublishEventResponse(rsp *http.Response) (*PublishEventResponse, error) { +// ParsePatchDisplayResponse parses an HTTP response from a PatchDisplayWithResponse call +func ParsePatchDisplayResponse(rsp *http.Response) (*PatchDisplayResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &PublishEventResponse{ + response := &PatchDisplayResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest PublishedEnvelope + var dest DisplayConfig if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -7922,22 +9677,20 @@ func ParsePublishEventResponse(rsp *http.Response) (*PublishEventResponse, error } response.JSON400 = &dest - } - - return response, nil -} + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest ConflictError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest -// ParseStreamEventsResponse parses an HTTP response from a StreamEventsWithResponse call -func ParseStreamEventsResponse(rsp *http.Response) (*StreamEventsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest - response := &StreamEventsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, } return response, nil @@ -9132,52 +10885,224 @@ func ParseStartRecordingResponse(rsp *http.Response) (*StartRecordingResponse, e return nil, err } - response := &StartRecordingResponse{ + response := &StartRecordingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest ConflictError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseStopRecordingResponse parses an HTTP response from a StopRecordingWithResponse call +func ParseStopRecordingResponse(rsp *http.Response) (*StopRecordingResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &StopRecordingResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDisableScaleToZeroResponse parses an HTTP response from a DisableScaleToZeroWithResponse call +func ParseDisableScaleToZeroResponse(rsp *http.Response) (*DisableScaleToZeroResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DisableScaleToZeroResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseEnableScaleToZeroResponse parses an HTTP response from a EnableScaleToZeroWithResponse call +func ParseEnableScaleToZeroResponse(rsp *http.Response) (*EnableScaleToZeroResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &EnableScaleToZeroResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetTelemetryResponse parses an HTTP response from a GetTelemetryWithResponse call +func ParseGetTelemetryResponse(rsp *http.Response) (*GetTelemetryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTelemetryResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TelemetryState + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + } + + return response, nil +} + +// ParsePatchTelemetryResponse parses an HTTP response from a PatchTelemetryWithResponse call +func ParsePatchTelemetryResponse(rsp *http.Response) (*PatchTelemetryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PatchTelemetryResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TelemetryState if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON400 = &dest + response.JSON200 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: - var dest ConflictError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestError if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON409 = &dest + response.JSON400 = &dest - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest NotFoundError if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON500 = &dest + response.JSON404 = &dest } return response, nil } -// ParseStopRecordingResponse parses an HTTP response from a StopRecordingWithResponse call -func ParseStopRecordingResponse(rsp *http.Response) (*StopRecordingResponse, error) { +// ParsePutTelemetryResponse parses an HTTP response from a PutTelemetryWithResponse call +func ParsePutTelemetryResponse(rsp *http.Response) (*PutTelemetryResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &StopRecordingResponse{ + response := &PutTelemetryResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TelemetryState + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest TelemetryState + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: var dest BadRequestError if err := json.Unmarshal(bodyBytes, &dest); err != nil { @@ -9197,55 +11122,52 @@ func ParseStopRecordingResponse(rsp *http.Response) (*StopRecordingResponse, err return response, nil } -// ParseDisableScaleToZeroResponse parses an HTTP response from a DisableScaleToZeroWithResponse call -func ParseDisableScaleToZeroResponse(rsp *http.Response) (*DisableScaleToZeroResponse, error) { +// ParsePublishTelemetryEventResponse parses an HTTP response from a PublishTelemetryEventWithResponse call +func ParsePublishTelemetryEventResponse(rsp *http.Response) (*PublishTelemetryEventResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &DisableScaleToZeroResponse{ + response := &PublishTelemetryEventResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TelemetryEnvelope if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON500 = &dest + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequestError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest } return response, nil } -// ParseEnableScaleToZeroResponse parses an HTTP response from a EnableScaleToZeroWithResponse call -func ParseEnableScaleToZeroResponse(rsp *http.Response) (*EnableScaleToZeroResponse, error) { +// ParseStreamTelemetryEventsResponse parses an HTTP response from a StreamTelemetryEventsWithResponse call +func ParseStreamTelemetryEventsResponse(rsp *http.Response) (*StreamTelemetryEventsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &EnableScaleToZeroResponse{ + response := &StreamTelemetryEventsResponse{ Body: bodyBytes, HTTPResponse: rsp, } - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON500 = &dest - - } - return response, nil } @@ -9299,24 +11221,6 @@ type ServerInterface interface { // Update display configuration // (PATCH /display) PatchDisplay(w http.ResponseWriter, r *http.Request) - // Stop the capture session - // (DELETE /events/capture_session) - StopCaptureSession(w http.ResponseWriter, r *http.Request) - // Get the capture session - // (GET /events/capture_session) - GetCaptureSession(w http.ResponseWriter, r *http.Request) - // Update the capture session - // (PATCH /events/capture_session) - UpdateCaptureSession(w http.ResponseWriter, r *http.Request) - // Start the capture session - // (POST /events/capture_session) - StartCaptureSession(w http.ResponseWriter, r *http.Request) - // Publish an event into the event bus - // (POST /events/publish) - PublishEvent(w http.ResponseWriter, r *http.Request) - // Stream events as Server-Sent Events - // (GET /events/stream) - StreamEvents(w http.ResponseWriter, r *http.Request, params StreamEventsParams) // Create a new directory // (PUT /fs/create_directory) CreateDirectory(w http.ResponseWriter, r *http.Request) @@ -9416,6 +11320,21 @@ type ServerInterface interface { // Idempotently enable scale to zero on this VM. // (POST /scaletozero/enable) EnableScaleToZero(w http.ResponseWriter, r *http.Request) + // Get telemetry configuration + // (GET /telemetry) + GetTelemetry(w http.ResponseWriter, r *http.Request) + // Update telemetry configuration + // (PATCH /telemetry) + PatchTelemetry(w http.ResponseWriter, r *http.Request) + // Set telemetry configuration + // (PUT /telemetry) + PutTelemetry(w http.ResponseWriter, r *http.Request) + // Publish an event into the telemetry stream + // (POST /telemetry/events) + PublishTelemetryEvent(w http.ResponseWriter, r *http.Request) + // Stream telemetry events as Server-Sent Events + // (GET /telemetry/stream) + StreamTelemetryEvents(w http.ResponseWriter, r *http.Request, params StreamTelemetryEventsParams) } // Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. @@ -9518,42 +11437,6 @@ func (_ Unimplemented) PatchDisplay(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) } -// Stop the capture session -// (DELETE /events/capture_session) -func (_ Unimplemented) StopCaptureSession(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Get the capture session -// (GET /events/capture_session) -func (_ Unimplemented) GetCaptureSession(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Update the capture session -// (PATCH /events/capture_session) -func (_ Unimplemented) UpdateCaptureSession(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Start the capture session -// (POST /events/capture_session) -func (_ Unimplemented) StartCaptureSession(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Publish an event into the event bus -// (POST /events/publish) -func (_ Unimplemented) PublishEvent(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Stream events as Server-Sent Events -// (GET /events/stream) -func (_ Unimplemented) StreamEvents(w http.ResponseWriter, r *http.Request, params StreamEventsParams) { - w.WriteHeader(http.StatusNotImplemented) -} - // Create a new directory // (PUT /fs/create_directory) func (_ Unimplemented) CreateDirectory(w http.ResponseWriter, r *http.Request) { @@ -9752,6 +11635,36 @@ func (_ Unimplemented) EnableScaleToZero(w http.ResponseWriter, r *http.Request) w.WriteHeader(http.StatusNotImplemented) } +// Get telemetry configuration +// (GET /telemetry) +func (_ Unimplemented) GetTelemetry(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Update telemetry configuration +// (PATCH /telemetry) +func (_ Unimplemented) PatchTelemetry(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Set telemetry configuration +// (PUT /telemetry) +func (_ Unimplemented) PutTelemetry(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Publish an event into the telemetry stream +// (POST /telemetry/events) +func (_ Unimplemented) PublishTelemetryEvent(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Stream telemetry events as Server-Sent Events +// (GET /telemetry/stream) +func (_ Unimplemented) StreamTelemetryEvents(w http.ResponseWriter, r *http.Request, params StreamTelemetryEventsParams) { + w.WriteHeader(http.StatusNotImplemented) +} + // ServerInterfaceWrapper converts contexts to parameters. type ServerInterfaceWrapper struct { Handler ServerInterface @@ -9985,116 +11898,6 @@ func (siw *ServerInterfaceWrapper) PatchDisplay(w http.ResponseWriter, r *http.R handler.ServeHTTP(w, r) } -// StopCaptureSession operation middleware -func (siw *ServerInterfaceWrapper) StopCaptureSession(w http.ResponseWriter, r *http.Request) { - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.StopCaptureSession(w, r) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - -// GetCaptureSession operation middleware -func (siw *ServerInterfaceWrapper) GetCaptureSession(w http.ResponseWriter, r *http.Request) { - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.GetCaptureSession(w, r) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - -// UpdateCaptureSession operation middleware -func (siw *ServerInterfaceWrapper) UpdateCaptureSession(w http.ResponseWriter, r *http.Request) { - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.UpdateCaptureSession(w, r) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - -// StartCaptureSession operation middleware -func (siw *ServerInterfaceWrapper) StartCaptureSession(w http.ResponseWriter, r *http.Request) { - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.StartCaptureSession(w, r) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - -// PublishEvent operation middleware -func (siw *ServerInterfaceWrapper) PublishEvent(w http.ResponseWriter, r *http.Request) { - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.PublishEvent(w, r) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - -// StreamEvents operation middleware -func (siw *ServerInterfaceWrapper) StreamEvents(w http.ResponseWriter, r *http.Request) { - - var err error - - // Parameter object where we will unmarshal all parameters from the context - var params StreamEventsParams - - headers := r.Header - - // ------------- Optional header parameter "Last-Event-ID" ------------- - if valueList, found := headers[http.CanonicalHeaderKey("Last-Event-ID")]; found { - var LastEventID string - n := len(valueList) - if n != 1 { - siw.ErrorHandlerFunc(w, r, &TooManyValuesForParamError{ParamName: "Last-Event-ID", Count: n}) - return - } - - err = runtime.BindStyledParameterWithOptions("simple", "Last-Event-ID", valueList[0], &LastEventID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false, Type: "string", Format: ""}) - if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "Last-Event-ID", Err: err}) - return - } - - params.LastEventID = &LastEventID - - } - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.StreamEvents(w, r, params) - })) - - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } - - handler.ServeHTTP(w, r) -} - // CreateDirectory operation middleware func (siw *ServerInterfaceWrapper) CreateDirectory(w http.ResponseWriter, r *http.Request) { @@ -10735,19 +12538,89 @@ func (siw *ServerInterfaceWrapper) DownloadRecording(w http.ResponseWriter, r *h var err error - // Parameter object where we will unmarshal all parameters from the context - var params DownloadRecordingParams + // Parameter object where we will unmarshal all parameters from the context + var params DownloadRecordingParams + + // ------------- Optional query parameter "id" ------------- + + err = runtime.BindQueryParameterWithOptions("form", true, false, "id", r.URL.Query(), ¶ms.Id, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DownloadRecording(w, r, params) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// ListRecorders operation middleware +func (siw *ServerInterfaceWrapper) ListRecorders(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ListRecorders(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// StartRecording operation middleware +func (siw *ServerInterfaceWrapper) StartRecording(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.StartRecording(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// StopRecording operation middleware +func (siw *ServerInterfaceWrapper) StopRecording(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.StopRecording(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// DisableScaleToZero operation middleware +func (siw *ServerInterfaceWrapper) DisableScaleToZero(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.DisableScaleToZero(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } - // ------------- Optional query parameter "id" ------------- + handler.ServeHTTP(w, r) +} - err = runtime.BindQueryParameterWithOptions("form", true, false, "id", r.URL.Query(), ¶ms.Id, runtime.BindQueryParameterOptions{Type: "string", Format: ""}) - if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) - return - } +// EnableScaleToZero operation middleware +func (siw *ServerInterfaceWrapper) EnableScaleToZero(w http.ResponseWriter, r *http.Request) { handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.DownloadRecording(w, r, params) + siw.Handler.EnableScaleToZero(w, r) })) for _, middleware := range siw.HandlerMiddlewares { @@ -10757,11 +12630,11 @@ func (siw *ServerInterfaceWrapper) DownloadRecording(w http.ResponseWriter, r *h handler.ServeHTTP(w, r) } -// ListRecorders operation middleware -func (siw *ServerInterfaceWrapper) ListRecorders(w http.ResponseWriter, r *http.Request) { +// GetTelemetry operation middleware +func (siw *ServerInterfaceWrapper) GetTelemetry(w http.ResponseWriter, r *http.Request) { handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.ListRecorders(w, r) + siw.Handler.GetTelemetry(w, r) })) for _, middleware := range siw.HandlerMiddlewares { @@ -10771,11 +12644,11 @@ func (siw *ServerInterfaceWrapper) ListRecorders(w http.ResponseWriter, r *http. handler.ServeHTTP(w, r) } -// StartRecording operation middleware -func (siw *ServerInterfaceWrapper) StartRecording(w http.ResponseWriter, r *http.Request) { +// PatchTelemetry operation middleware +func (siw *ServerInterfaceWrapper) PatchTelemetry(w http.ResponseWriter, r *http.Request) { handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.StartRecording(w, r) + siw.Handler.PatchTelemetry(w, r) })) for _, middleware := range siw.HandlerMiddlewares { @@ -10785,11 +12658,11 @@ func (siw *ServerInterfaceWrapper) StartRecording(w http.ResponseWriter, r *http handler.ServeHTTP(w, r) } -// StopRecording operation middleware -func (siw *ServerInterfaceWrapper) StopRecording(w http.ResponseWriter, r *http.Request) { +// PutTelemetry operation middleware +func (siw *ServerInterfaceWrapper) PutTelemetry(w http.ResponseWriter, r *http.Request) { handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.StopRecording(w, r) + siw.Handler.PutTelemetry(w, r) })) for _, middleware := range siw.HandlerMiddlewares { @@ -10799,11 +12672,11 @@ func (siw *ServerInterfaceWrapper) StopRecording(w http.ResponseWriter, r *http. handler.ServeHTTP(w, r) } -// DisableScaleToZero operation middleware -func (siw *ServerInterfaceWrapper) DisableScaleToZero(w http.ResponseWriter, r *http.Request) { +// PublishTelemetryEvent operation middleware +func (siw *ServerInterfaceWrapper) PublishTelemetryEvent(w http.ResponseWriter, r *http.Request) { handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.DisableScaleToZero(w, r) + siw.Handler.PublishTelemetryEvent(w, r) })) for _, middleware := range siw.HandlerMiddlewares { @@ -10813,11 +12686,37 @@ func (siw *ServerInterfaceWrapper) DisableScaleToZero(w http.ResponseWriter, r * handler.ServeHTTP(w, r) } -// EnableScaleToZero operation middleware -func (siw *ServerInterfaceWrapper) EnableScaleToZero(w http.ResponseWriter, r *http.Request) { +// StreamTelemetryEvents operation middleware +func (siw *ServerInterfaceWrapper) StreamTelemetryEvents(w http.ResponseWriter, r *http.Request) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params StreamTelemetryEventsParams + + headers := r.Header + + // ------------- Optional header parameter "Last-Event-ID" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("Last-Event-ID")]; found { + var LastEventID string + n := len(valueList) + if n != 1 { + siw.ErrorHandlerFunc(w, r, &TooManyValuesForParamError{ParamName: "Last-Event-ID", Count: n}) + return + } + + err = runtime.BindStyledParameterWithOptions("simple", "Last-Event-ID", valueList[0], &LastEventID, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false, Type: "string", Format: ""}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "Last-Event-ID", Err: err}) + return + } + + params.LastEventID = &LastEventID + + } handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.EnableScaleToZero(w, r) + siw.Handler.StreamTelemetryEvents(w, r, params) })) for _, middleware := range siw.HandlerMiddlewares { @@ -10988,24 +12887,6 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Patch(options.BaseURL+"/display", wrapper.PatchDisplay) }) - r.Group(func(r chi.Router) { - r.Delete(options.BaseURL+"/events/capture_session", wrapper.StopCaptureSession) - }) - r.Group(func(r chi.Router) { - r.Get(options.BaseURL+"/events/capture_session", wrapper.GetCaptureSession) - }) - r.Group(func(r chi.Router) { - r.Patch(options.BaseURL+"/events/capture_session", wrapper.UpdateCaptureSession) - }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/events/capture_session", wrapper.StartCaptureSession) - }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/events/publish", wrapper.PublishEvent) - }) - r.Group(func(r chi.Router) { - r.Get(options.BaseURL+"/events/stream", wrapper.StreamEvents) - }) r.Group(func(r chi.Router) { r.Put(options.BaseURL+"/fs/create_directory", wrapper.CreateDirectory) }) @@ -11105,6 +12986,21 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/scaletozero/enable", wrapper.EnableScaleToZero) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/telemetry", wrapper.GetTelemetry) + }) + r.Group(func(r chi.Router) { + r.Patch(options.BaseURL+"/telemetry", wrapper.PatchTelemetry) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/telemetry", wrapper.PutTelemetry) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/telemetry/events", wrapper.PublishTelemetryEvent) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/telemetry/stream", wrapper.StreamTelemetryEvents) + }) return r } @@ -11499,382 +13395,170 @@ func (response PressKey400JSONResponse) VisitPressKeyResponse(w http.ResponseWri return json.NewEncoder(w).Encode(response) } -type PressKey500JSONResponse struct{ InternalErrorJSONResponse } - -func (response PressKey500JSONResponse) VisitPressKeyResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type TakeScreenshotRequestObject struct { - Body *TakeScreenshotJSONRequestBody -} - -type TakeScreenshotResponseObject interface { - VisitTakeScreenshotResponse(w http.ResponseWriter) error -} - -type TakeScreenshot200ImagepngResponse struct { - Body io.Reader - ContentLength int64 -} - -func (response TakeScreenshot200ImagepngResponse) VisitTakeScreenshotResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "image/png") - if response.ContentLength != 0 { - w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) - } - w.WriteHeader(200) - - if closer, ok := response.Body.(io.ReadCloser); ok { - defer closer.Close() - } - _, err := io.Copy(w, response.Body) - return err -} - -type TakeScreenshot400JSONResponse struct{ BadRequestErrorJSONResponse } - -func (response TakeScreenshot400JSONResponse) VisitTakeScreenshotResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type TakeScreenshot500JSONResponse struct{ InternalErrorJSONResponse } - -func (response TakeScreenshot500JSONResponse) VisitTakeScreenshotResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type ScrollRequestObject struct { - Body *ScrollJSONRequestBody -} - -type ScrollResponseObject interface { - VisitScrollResponse(w http.ResponseWriter) error -} - -type Scroll200Response struct { -} - -func (response Scroll200Response) VisitScrollResponse(w http.ResponseWriter) error { - w.WriteHeader(200) - return nil -} - -type Scroll400JSONResponse struct{ BadRequestErrorJSONResponse } - -func (response Scroll400JSONResponse) VisitScrollResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type Scroll500JSONResponse struct{ InternalErrorJSONResponse } - -func (response Scroll500JSONResponse) VisitScrollResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type TypeTextRequestObject struct { - Body *TypeTextJSONRequestBody -} - -type TypeTextResponseObject interface { - VisitTypeTextResponse(w http.ResponseWriter) error -} - -type TypeText200Response struct { -} - -func (response TypeText200Response) VisitTypeTextResponse(w http.ResponseWriter) error { - w.WriteHeader(200) - return nil -} - -type TypeText400JSONResponse struct{ BadRequestErrorJSONResponse } - -func (response TypeText400JSONResponse) VisitTypeTextResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type TypeText500JSONResponse struct{ InternalErrorJSONResponse } - -func (response TypeText500JSONResponse) VisitTypeTextResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type PatchDisplayRequestObject struct { - Body *PatchDisplayJSONRequestBody -} - -type PatchDisplayResponseObject interface { - VisitPatchDisplayResponse(w http.ResponseWriter) error -} - -type PatchDisplay200JSONResponse DisplayConfig - -func (response PatchDisplay200JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type PatchDisplay400JSONResponse struct{ BadRequestErrorJSONResponse } - -func (response PatchDisplay400JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) -} - -type PatchDisplay409JSONResponse struct{ ConflictErrorJSONResponse } - -func (response PatchDisplay409JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(409) - - return json.NewEncoder(w).Encode(response) -} - -type PatchDisplay500JSONResponse struct{ InternalErrorJSONResponse } - -func (response PatchDisplay500JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(500) - - return json.NewEncoder(w).Encode(response) -} - -type StopCaptureSessionRequestObject struct { -} - -type StopCaptureSessionResponseObject interface { - VisitStopCaptureSessionResponse(w http.ResponseWriter) error -} - -type StopCaptureSession200JSONResponse CaptureSession - -func (response StopCaptureSession200JSONResponse) VisitStopCaptureSessionResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type StopCaptureSession404JSONResponse struct{ NotFoundErrorJSONResponse } +type PressKey500JSONResponse struct{ InternalErrorJSONResponse } -func (response StopCaptureSession404JSONResponse) VisitStopCaptureSessionResponse(w http.ResponseWriter) error { +func (response PressKey500JSONResponse) VisitPressKeyResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) + w.WriteHeader(500) return json.NewEncoder(w).Encode(response) } -type GetCaptureSessionRequestObject struct { +type TakeScreenshotRequestObject struct { + Body *TakeScreenshotJSONRequestBody } -type GetCaptureSessionResponseObject interface { - VisitGetCaptureSessionResponse(w http.ResponseWriter) error +type TakeScreenshotResponseObject interface { + VisitTakeScreenshotResponse(w http.ResponseWriter) error } -type GetCaptureSession200JSONResponse CaptureSession +type TakeScreenshot200ImagepngResponse struct { + Body io.Reader + ContentLength int64 +} -func (response GetCaptureSession200JSONResponse) VisitGetCaptureSessionResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") +func (response TakeScreenshot200ImagepngResponse) VisitTakeScreenshotResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "image/png") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } w.WriteHeader(200) + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type TakeScreenshot400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response TakeScreenshot400JSONResponse) VisitTakeScreenshotResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + return json.NewEncoder(w).Encode(response) } -type GetCaptureSession404JSONResponse struct{ NotFoundErrorJSONResponse } +type TakeScreenshot500JSONResponse struct{ InternalErrorJSONResponse } -func (response GetCaptureSession404JSONResponse) VisitGetCaptureSessionResponse(w http.ResponseWriter) error { +func (response TakeScreenshot500JSONResponse) VisitTakeScreenshotResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) + w.WriteHeader(500) return json.NewEncoder(w).Encode(response) } -type UpdateCaptureSessionRequestObject struct { - Body *UpdateCaptureSessionJSONRequestBody +type ScrollRequestObject struct { + Body *ScrollJSONRequestBody } -type UpdateCaptureSessionResponseObject interface { - VisitUpdateCaptureSessionResponse(w http.ResponseWriter) error +type ScrollResponseObject interface { + VisitScrollResponse(w http.ResponseWriter) error } -type UpdateCaptureSession200JSONResponse CaptureSession +type Scroll200Response struct { +} -func (response UpdateCaptureSession200JSONResponse) VisitUpdateCaptureSessionResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") +func (response Scroll200Response) VisitScrollResponse(w http.ResponseWriter) error { w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) + return nil } -type UpdateCaptureSession400JSONResponse struct{ BadRequestErrorJSONResponse } +type Scroll400JSONResponse struct{ BadRequestErrorJSONResponse } -func (response UpdateCaptureSession400JSONResponse) VisitUpdateCaptureSessionResponse(w http.ResponseWriter) error { +func (response Scroll400JSONResponse) VisitScrollResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(400) return json.NewEncoder(w).Encode(response) } -type UpdateCaptureSession404JSONResponse struct{ NotFoundErrorJSONResponse } +type Scroll500JSONResponse struct{ InternalErrorJSONResponse } -func (response UpdateCaptureSession404JSONResponse) VisitUpdateCaptureSessionResponse(w http.ResponseWriter) error { +func (response Scroll500JSONResponse) VisitScrollResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") - w.WriteHeader(404) + w.WriteHeader(500) return json.NewEncoder(w).Encode(response) } -type StartCaptureSessionRequestObject struct { - Body *StartCaptureSessionJSONRequestBody +type TypeTextRequestObject struct { + Body *TypeTextJSONRequestBody } -type StartCaptureSessionResponseObject interface { - VisitStartCaptureSessionResponse(w http.ResponseWriter) error +type TypeTextResponseObject interface { + VisitTypeTextResponse(w http.ResponseWriter) error } -type StartCaptureSession201JSONResponse CaptureSession - -func (response StartCaptureSession201JSONResponse) VisitStartCaptureSessionResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(201) - - return json.NewEncoder(w).Encode(response) +type TypeText200Response struct { } -type StartCaptureSession400JSONResponse struct{ BadRequestErrorJSONResponse } - -func (response StartCaptureSession400JSONResponse) VisitStartCaptureSessionResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) - - return json.NewEncoder(w).Encode(response) +func (response TypeText200Response) VisitTypeTextResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil } -type StartCaptureSession409JSONResponse struct{ ConflictErrorJSONResponse } +type TypeText400JSONResponse struct{ BadRequestErrorJSONResponse } -func (response StartCaptureSession409JSONResponse) VisitStartCaptureSessionResponse(w http.ResponseWriter) error { +func (response TypeText400JSONResponse) VisitTypeTextResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") - w.WriteHeader(409) + w.WriteHeader(400) return json.NewEncoder(w).Encode(response) } -type StartCaptureSession500JSONResponse struct{ InternalErrorJSONResponse } +type TypeText500JSONResponse struct{ InternalErrorJSONResponse } -func (response StartCaptureSession500JSONResponse) VisitStartCaptureSessionResponse(w http.ResponseWriter) error { +func (response TypeText500JSONResponse) VisitTypeTextResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(500) return json.NewEncoder(w).Encode(response) } -type PublishEventRequestObject struct { - Body *PublishEventJSONRequestBody +type PatchDisplayRequestObject struct { + Body *PatchDisplayJSONRequestBody } -type PublishEventResponseObject interface { - VisitPublishEventResponse(w http.ResponseWriter) error +type PatchDisplayResponseObject interface { + VisitPatchDisplayResponse(w http.ResponseWriter) error } -type PublishEvent200JSONResponse PublishedEnvelope +type PatchDisplay200JSONResponse DisplayConfig -func (response PublishEvent200JSONResponse) VisitPublishEventResponse(w http.ResponseWriter) error { +func (response PatchDisplay200JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) return json.NewEncoder(w).Encode(response) } -type PublishEvent400JSONResponse struct{ BadRequestErrorJSONResponse } +type PatchDisplay400JSONResponse struct{ BadRequestErrorJSONResponse } -func (response PublishEvent400JSONResponse) VisitPublishEventResponse(w http.ResponseWriter) error { +func (response PatchDisplay400JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(400) return json.NewEncoder(w).Encode(response) } -type StreamEventsRequestObject struct { - Params StreamEventsParams -} - -type StreamEventsResponseObject interface { - VisitStreamEventsResponse(w http.ResponseWriter) error -} +type PatchDisplay409JSONResponse struct{ ConflictErrorJSONResponse } -type StreamEvents200ResponseHeaders struct { - XSSEContentType string -} +func (response PatchDisplay409JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(409) -type StreamEvents200TexteventStreamResponse struct { - Body io.Reader - Headers StreamEvents200ResponseHeaders - ContentLength int64 + return json.NewEncoder(w).Encode(response) } -func (response StreamEvents200TexteventStreamResponse) VisitStreamEventsResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "text/event-stream") - if response.ContentLength != 0 { - w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) - } - w.Header().Set("X-SSE-Content-Type", fmt.Sprint(response.Headers.XSSEContentType)) - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("X-Accel-Buffering", "no") - w.WriteHeader(200) +type PatchDisplay500JSONResponse struct{ InternalErrorJSONResponse } - if closer, ok := response.Body.(io.ReadCloser); ok { - defer closer.Close() - } - flusher, ok := w.(http.Flusher) - if !ok { - // If w doesn't support flushing, might as well use io.Copy - _, err := io.Copy(w, response.Body) - return err - } +func (response PatchDisplay500JSONResponse) VisitPatchDisplayResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) - // Use a buffer for efficient copying and flushing - buf := make([]byte, 4096) // text/event-stream are usually very small messages - for { - n, err := response.Body.Read(buf) - if n > 0 { - if _, werr := w.Write(buf[:n]); werr != nil { - return werr - } - flusher.Flush() // Flush after each write - } - if err != nil { - if err == io.EOF { - return nil // End of file, no error - } - return err - } - } + return json.NewEncoder(w).Encode(response) } type CreateDirectoryRequestObject struct { @@ -13380,7 +15064,194 @@ func (response EnableScaleToZero500JSONResponse) VisitEnableScaleToZeroResponse( w.Header().Set("Content-Type", "application/json") w.WriteHeader(500) - return json.NewEncoder(w).Encode(response) + return json.NewEncoder(w).Encode(response) +} + +type GetTelemetryRequestObject struct { +} + +type GetTelemetryResponseObject interface { + VisitGetTelemetryResponse(w http.ResponseWriter) error +} + +type GetTelemetry200JSONResponse TelemetryState + +func (response GetTelemetry200JSONResponse) VisitGetTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTelemetry404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response GetTelemetry404JSONResponse) VisitGetTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type PatchTelemetryRequestObject struct { + Body *PatchTelemetryJSONRequestBody +} + +type PatchTelemetryResponseObject interface { + VisitPatchTelemetryResponse(w http.ResponseWriter) error +} + +type PatchTelemetry200JSONResponse TelemetryState + +func (response PatchTelemetry200JSONResponse) VisitPatchTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PatchTelemetry400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response PatchTelemetry400JSONResponse) VisitPatchTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type PatchTelemetry404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response PatchTelemetry404JSONResponse) VisitPatchTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type PutTelemetryRequestObject struct { + Body *PutTelemetryJSONRequestBody +} + +type PutTelemetryResponseObject interface { + VisitPutTelemetryResponse(w http.ResponseWriter) error +} + +type PutTelemetry200JSONResponse TelemetryState + +func (response PutTelemetry200JSONResponse) VisitPutTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PutTelemetry201JSONResponse TelemetryState + +func (response PutTelemetry201JSONResponse) VisitPutTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(201) + + return json.NewEncoder(w).Encode(response) +} + +type PutTelemetry400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response PutTelemetry400JSONResponse) VisitPutTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type PutTelemetry500JSONResponse struct{ InternalErrorJSONResponse } + +func (response PutTelemetry500JSONResponse) VisitPutTelemetryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type PublishTelemetryEventRequestObject struct { + Body *PublishTelemetryEventJSONRequestBody +} + +type PublishTelemetryEventResponseObject interface { + VisitPublishTelemetryEventResponse(w http.ResponseWriter) error +} + +type PublishTelemetryEvent200JSONResponse TelemetryEnvelope + +func (response PublishTelemetryEvent200JSONResponse) VisitPublishTelemetryEventResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PublishTelemetryEvent400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response PublishTelemetryEvent400JSONResponse) VisitPublishTelemetryEventResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type StreamTelemetryEventsRequestObject struct { + Params StreamTelemetryEventsParams +} + +type StreamTelemetryEventsResponseObject interface { + VisitStreamTelemetryEventsResponse(w http.ResponseWriter) error +} + +type StreamTelemetryEvents200ResponseHeaders struct { + XSSEContentType string +} + +type StreamTelemetryEvents200TexteventStreamResponse struct { + Body io.Reader + Headers StreamTelemetryEvents200ResponseHeaders + ContentLength int64 +} + +func (response StreamTelemetryEvents200TexteventStreamResponse) VisitStreamTelemetryEventsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "text/event-stream") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.Header().Set("X-SSE-Content-Type", fmt.Sprint(response.Headers.XSSEContentType)) + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("X-Accel-Buffering", "no") + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + flusher, ok := w.(http.Flusher) + if !ok { + // If w doesn't support flushing, might as well use io.Copy + _, err := io.Copy(w, response.Body) + return err + } + + // Use a buffer for efficient copying and flushing + buf := make([]byte, 4096) // text/event-stream are usually very small messages + for { + n, err := response.Body.Read(buf) + if n > 0 { + if _, werr := w.Write(buf[:n]); werr != nil { + return werr + } + flusher.Flush() // Flush after each write + } + if err != nil { + if err == io.EOF { + return nil // End of file, no error + } + return err + } + } } // StrictServerInterface represents all server handlers. @@ -13433,24 +15304,6 @@ type StrictServerInterface interface { // Update display configuration // (PATCH /display) PatchDisplay(ctx context.Context, request PatchDisplayRequestObject) (PatchDisplayResponseObject, error) - // Stop the capture session - // (DELETE /events/capture_session) - StopCaptureSession(ctx context.Context, request StopCaptureSessionRequestObject) (StopCaptureSessionResponseObject, error) - // Get the capture session - // (GET /events/capture_session) - GetCaptureSession(ctx context.Context, request GetCaptureSessionRequestObject) (GetCaptureSessionResponseObject, error) - // Update the capture session - // (PATCH /events/capture_session) - UpdateCaptureSession(ctx context.Context, request UpdateCaptureSessionRequestObject) (UpdateCaptureSessionResponseObject, error) - // Start the capture session - // (POST /events/capture_session) - StartCaptureSession(ctx context.Context, request StartCaptureSessionRequestObject) (StartCaptureSessionResponseObject, error) - // Publish an event into the event bus - // (POST /events/publish) - PublishEvent(ctx context.Context, request PublishEventRequestObject) (PublishEventResponseObject, error) - // Stream events as Server-Sent Events - // (GET /events/stream) - StreamEvents(ctx context.Context, request StreamEventsRequestObject) (StreamEventsResponseObject, error) // Create a new directory // (PUT /fs/create_directory) CreateDirectory(ctx context.Context, request CreateDirectoryRequestObject) (CreateDirectoryResponseObject, error) @@ -13550,6 +15403,21 @@ type StrictServerInterface interface { // Idempotently enable scale to zero on this VM. // (POST /scaletozero/enable) EnableScaleToZero(ctx context.Context, request EnableScaleToZeroRequestObject) (EnableScaleToZeroResponseObject, error) + // Get telemetry configuration + // (GET /telemetry) + GetTelemetry(ctx context.Context, request GetTelemetryRequestObject) (GetTelemetryResponseObject, error) + // Update telemetry configuration + // (PATCH /telemetry) + PatchTelemetry(ctx context.Context, request PatchTelemetryRequestObject) (PatchTelemetryResponseObject, error) + // Set telemetry configuration + // (PUT /telemetry) + PutTelemetry(ctx context.Context, request PutTelemetryRequestObject) (PutTelemetryResponseObject, error) + // Publish an event into the telemetry stream + // (POST /telemetry/events) + PublishTelemetryEvent(ctx context.Context, request PublishTelemetryEventRequestObject) (PublishTelemetryEventResponseObject, error) + // Stream telemetry events as Server-Sent Events + // (GET /telemetry/stream) + StreamTelemetryEvents(ctx context.Context, request StreamTelemetryEventsRequestObject) (StreamTelemetryEventsResponseObject, error) } type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc @@ -14066,176 +15934,6 @@ func (sh *strictHandler) PatchDisplay(w http.ResponseWriter, r *http.Request) { } } -// StopCaptureSession operation middleware -func (sh *strictHandler) StopCaptureSession(w http.ResponseWriter, r *http.Request) { - var request StopCaptureSessionRequestObject - - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.StopCaptureSession(ctx, request.(StopCaptureSessionRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "StopCaptureSession") - } - - response, err := handler(r.Context(), w, r, request) - - if err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(StopCaptureSessionResponseObject); ok { - if err := validResponse.VisitStopCaptureSessionResponse(w); err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } - } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) - } -} - -// GetCaptureSession operation middleware -func (sh *strictHandler) GetCaptureSession(w http.ResponseWriter, r *http.Request) { - var request GetCaptureSessionRequestObject - - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.GetCaptureSession(ctx, request.(GetCaptureSessionRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetCaptureSession") - } - - response, err := handler(r.Context(), w, r, request) - - if err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(GetCaptureSessionResponseObject); ok { - if err := validResponse.VisitGetCaptureSessionResponse(w); err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } - } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) - } -} - -// UpdateCaptureSession operation middleware -func (sh *strictHandler) UpdateCaptureSession(w http.ResponseWriter, r *http.Request) { - var request UpdateCaptureSessionRequestObject - - var body UpdateCaptureSessionJSONRequestBody - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) - return - } - request.Body = &body - - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.UpdateCaptureSession(ctx, request.(UpdateCaptureSessionRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "UpdateCaptureSession") - } - - response, err := handler(r.Context(), w, r, request) - - if err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(UpdateCaptureSessionResponseObject); ok { - if err := validResponse.VisitUpdateCaptureSessionResponse(w); err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } - } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) - } -} - -// StartCaptureSession operation middleware -func (sh *strictHandler) StartCaptureSession(w http.ResponseWriter, r *http.Request) { - var request StartCaptureSessionRequestObject - - var body StartCaptureSessionJSONRequestBody - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - if !errors.Is(err, io.EOF) { - sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) - return - } - } else { - request.Body = &body - } - - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.StartCaptureSession(ctx, request.(StartCaptureSessionRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "StartCaptureSession") - } - - response, err := handler(r.Context(), w, r, request) - - if err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(StartCaptureSessionResponseObject); ok { - if err := validResponse.VisitStartCaptureSessionResponse(w); err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } - } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) - } -} - -// PublishEvent operation middleware -func (sh *strictHandler) PublishEvent(w http.ResponseWriter, r *http.Request) { - var request PublishEventRequestObject - - var body PublishEventJSONRequestBody - if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) - return - } - request.Body = &body - - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.PublishEvent(ctx, request.(PublishEventRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PublishEvent") - } - - response, err := handler(r.Context(), w, r, request) - - if err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(PublishEventResponseObject); ok { - if err := validResponse.VisitPublishEventResponse(w); err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } - } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) - } -} - -// StreamEvents operation middleware -func (sh *strictHandler) StreamEvents(w http.ResponseWriter, r *http.Request, params StreamEventsParams) { - var request StreamEventsRequestObject - - request.Params = params - - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.StreamEvents(ctx, request.(StreamEventsRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "StreamEvents") - } - - response, err := handler(r.Context(), w, r, request) - - if err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(StreamEventsResponseObject); ok { - if err := validResponse.VisitStreamEventsResponse(w); err != nil { - sh.options.ResponseErrorHandlerFunc(w, r, err) - } - } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) - } -} - // CreateDirectory operation middleware func (sh *strictHandler) CreateDirectory(w http.ResponseWriter, r *http.Request) { var request CreateDirectoryRequestObject @@ -15195,191 +16893,432 @@ func (sh *strictHandler) EnableScaleToZero(w http.ResponseWriter, r *http.Reques } } +// GetTelemetry operation middleware +func (sh *strictHandler) GetTelemetry(w http.ResponseWriter, r *http.Request) { + var request GetTelemetryRequestObject + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetTelemetry(ctx, request.(GetTelemetryRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTelemetry") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetTelemetryResponseObject); ok { + if err := validResponse.VisitGetTelemetryResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchTelemetry operation middleware +func (sh *strictHandler) PatchTelemetry(w http.ResponseWriter, r *http.Request) { + var request PatchTelemetryRequestObject + + var body PatchTelemetryJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.PatchTelemetry(ctx, request.(PatchTelemetryRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchTelemetry") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(PatchTelemetryResponseObject); ok { + if err := validResponse.VisitPatchTelemetryResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PutTelemetry operation middleware +func (sh *strictHandler) PutTelemetry(w http.ResponseWriter, r *http.Request) { + var request PutTelemetryRequestObject + + var body PutTelemetryJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + if !errors.Is(err, io.EOF) { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + } else { + request.Body = &body + } + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.PutTelemetry(ctx, request.(PutTelemetryRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PutTelemetry") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(PutTelemetryResponseObject); ok { + if err := validResponse.VisitPutTelemetryResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PublishTelemetryEvent operation middleware +func (sh *strictHandler) PublishTelemetryEvent(w http.ResponseWriter, r *http.Request) { + var request PublishTelemetryEventRequestObject + + var body PublishTelemetryEventJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.PublishTelemetryEvent(ctx, request.(PublishTelemetryEventRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PublishTelemetryEvent") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(PublishTelemetryEventResponseObject); ok { + if err := validResponse.VisitPublishTelemetryEventResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// StreamTelemetryEvents operation middleware +func (sh *strictHandler) StreamTelemetryEvents(w http.ResponseWriter, r *http.Request, params StreamTelemetryEventsParams) { + var request StreamTelemetryEventsRequestObject + + request.Params = params + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.StreamTelemetryEvents(ctx, request.(StreamTelemetryEventsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "StreamTelemetryEvents") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(StreamTelemetryEventsResponseObject); ok { + if err := validResponse.VisitStreamTelemetryEventsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "/wEAAP//c4aJWK9pAQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/server/lib/telemetry/telemetry.go b/server/lib/telemetry/telemetry.go new file mode 100644 index 00000000..538a2f63 --- /dev/null +++ b/server/lib/telemetry/telemetry.go @@ -0,0 +1,165 @@ +package telemetry + +import ( + "sync" + "time" + + "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" +) + +// TelemetryConfig holds caller-supplied telemetry preferences. All fields are +// optional; zero values mean "use server defaults" (all user-facing categories +// plus system events). +type TelemetryConfig struct { + // Categories limits which event categories are captured. + // nil or empty captures all user-facing categories plus system events. + Categories []oapi.TelemetryEventCategory +} + +// TelemetrySession manages a telemetry session against a shared EventStream. +// It is responsible for (a) category-filtering Publish calls, (b) tracking +// session-scoped metadata (ID, config, timestamps), and (c) embedding +// telemetry_session_id into Event.Source.Metadata before forwarding to the bus. +type TelemetrySession struct { + es *events.EventStream + mu sync.Mutex + id string + sessionStartSeq uint64 + categories map[oapi.TelemetryEventCategory]struct{} + appliedAt time.Time +} + +func NewTelemetrySession(es *events.EventStream) *TelemetrySession { + cats := make(map[oapi.TelemetryEventCategory]struct{}, len(events.AllCategories)) + for _, c := range events.AllCategories { + cats[c] = struct{}{} + } + return &TelemetrySession{es: es, categories: cats} +} + +// 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. +func (s *TelemetrySession) Start(telemetrySessionID string, cfg TelemetryConfig) { + s.mu.Lock() + defer s.mu.Unlock() + s.id = telemetrySessionID + s.sessionStartSeq = s.es.Seq() + s.appliedAt = time.Now() + + // Build the category filter. CategorySystem is always included so + // kernel_api events (e.g. monitor_disconnected) are never dropped. + cats := cfg.Categories + if len(cats) == 0 { + cats = events.AllCategories + } + s.categories = make(map[oapi.TelemetryEventCategory]struct{}, len(cats)+1) + for _, c := range cats { + s.categories[c] = struct{}{} + } + s.categories[events.System] = struct{}{} +} + +// publishLocked stamps telemetry_session_id into ev.Source.Metadata and forwards to the bus. +// Requires s.mu to be held. +func (s *TelemetrySession) publishLocked(ev events.Event) events.Envelope { + if ev.Ts == 0 { + ev.Ts = time.Now().UnixMicro() + } + if ev.Source.Metadata == nil { + m := make(map[string]string) + ev.Source.Metadata = &m + } + (*ev.Source.Metadata)["telemetry_session_id"] = s.id + return s.es.Publish(events.Envelope{Event: ev}) +} + +// Publish applies the category filter then forwards ev to the EventStream. +func (s *TelemetrySession) Publish(ev events.Event) { + s.mu.Lock() + defer s.mu.Unlock() + if s.id == "" { + return + } + if _, ok := s.categories[ev.Category]; !ok { + return + } + s.publishLocked(ev) +} + +// NewReader returns a Reader from the EventStream positioned after afterSeq. +func (s *TelemetrySession) NewReader(afterSeq uint64) *events.Reader { + return s.es.NewReader(afterSeq) +} + +// ID returns the current telemetry session ID, or "" if no session is active. +func (s *TelemetrySession) ID() string { + s.mu.Lock() + defer s.mu.Unlock() + return s.id +} + +// Seq returns the sequence number of the last published event. +func (s *TelemetrySession) Seq() uint64 { + return s.es.Seq() +} + +// SessionStartSeq returns the sequence number at which the current session +// started. Fresh SSE connections with no Last-Event-ID should begin here. +func (s *TelemetrySession) SessionStartSeq() uint64 { + s.mu.Lock() + defer s.mu.Unlock() + return s.sessionStartSeq +} + +// Config returns the current telemetry configuration. +func (s *TelemetrySession) Config() TelemetryConfig { + s.mu.Lock() + defer s.mu.Unlock() + cats := make([]oapi.TelemetryEventCategory, 0, len(s.categories)) + for c := range s.categories { + cats = append(cats, c) + } + return TelemetryConfig{Categories: cats} +} + +// AppliedAt returns when the current configuration was applied, or the zero +// time if telemetry is not configured. +func (s *TelemetrySession) AppliedAt() time.Time { + s.mu.Lock() + defer s.mu.Unlock() + return s.appliedAt +} + +// UpdateConfig applies a new TelemetryConfig to the running session. +func (s *TelemetrySession) UpdateConfig(cfg TelemetryConfig) { + s.mu.Lock() + defer s.mu.Unlock() + // CategorySystem is always included so kernel_api events are never dropped. + cats := cfg.Categories + if len(cats) == 0 { + cats = events.AllCategories + } + s.categories = make(map[oapi.TelemetryEventCategory]struct{}, len(cats)+1) + for _, c := range cats { + s.categories[c] = struct{}{} + } + s.categories[events.System] = struct{}{} +} + +// Active reports whether a telemetry session is currently running. +func (s *TelemetrySession) Active() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.id != "" +} + +// Stop ends the current telemetry session. The ring buffer is left intact so +// existing readers can finish draining. +func (s *TelemetrySession) Stop() { + s.mu.Lock() + defer s.mu.Unlock() + s.id = "" + s.appliedAt = time.Time{} +} diff --git a/server/lib/telemetry/telemetry_test.go b/server/lib/telemetry/telemetry_test.go new file mode 100644 index 00000000..126d8a82 --- /dev/null +++ b/server/lib/telemetry/telemetry_test.go @@ -0,0 +1,223 @@ +package telemetry + +import ( + "context" + "encoding/json" + "strings" + "sync" + "testing" + "time" + + "github.com/kernel/kernel-images/server/lib/events" + oapi "github.com/kernel/kernel-images/server/lib/oapi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestEventStream(t *testing.T, capacity int) *events.EventStream { + t.Helper() + es, err := events.NewEventStream(events.EventStreamConfig{RingCapacity: capacity}) + require.NoError(t, err) + return es +} + +func newTestTelemetrySession(t *testing.T) *TelemetrySession { + t.Helper() + ts := NewTelemetrySession(newTestEventStream(t, 100)) + ts.Start("test-session", TelemetryConfig{}) + return ts +} + +func readEnvelope(t *testing.T, r *events.Reader, ctx context.Context) events.Envelope { + t.Helper() + res, err := r.Read(ctx) + require.NoError(t, err) + require.NotNil(t, res.Envelope, "expected envelope, got drop") + return *res.Envelope +} + +func cdpEvent(typ string, cat oapi.TelemetryEventCategory) events.Event { + return events.Event{Type: typ, Category: cat, Source: oapi.BrowserEventSource{Kind: oapi.Cdp}} +} + +func telemetrySessionIDFromMetadata(t *testing.T, src oapi.BrowserEventSource) string { + t.Helper() + require.NotNil(t, src.Metadata, "source.metadata is nil") + id, ok := (*src.Metadata)["telemetry_session_id"] + require.True(t, ok, "telemetry_session_id not found in source.metadata") + return id +} + +func TestTelemetrySession(t *testing.T) { + t.Run("concurrent_publish_seq_order", func(t *testing.T) { + const goroutines = 8 + const eventsEach = 50 + const total = goroutines * eventsEach + + ts := NewTelemetrySession(newTestEventStream(t, total)) + ts.Start("test-concurrent", TelemetryConfig{}) + reader := ts.NewReader(0) + + var wg sync.WaitGroup + for i := 0; i < goroutines; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < eventsEach; j++ { + ts.Publish(cdpEvent("console.log", events.Console)) + } + }() + } + wg.Wait() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + for want := uint64(1); want <= total; want++ { + env := readEnvelope(t, reader, ctx) + assert.Equal(t, want, env.Seq, "events must arrive in seq order") + } + }) + + t.Run("seq_continues_across_sessions", func(t *testing.T) { + ts := NewTelemetrySession(newTestEventStream(t, 100)) + ts.Start("session-1", TelemetryConfig{}) + ts.Publish(cdpEvent("ev.one", events.System)) + ts.Publish(cdpEvent("ev.two", events.System)) + + ts.Start("session-2", TelemetryConfig{}) + ts.Publish(cdpEvent("ev.three", events.System)) + + assert.Equal(t, uint64(2), ts.SessionStartSeq(), "session-2 starts after seq 2") + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + reader := ts.NewReader(2) + env := readEnvelope(t, reader, ctx) + assert.Equal(t, uint64(3), env.Seq) + assert.Equal(t, "session-2", telemetrySessionIDFromMetadata(t, env.Event.Source)) + assert.Equal(t, "ev.three", env.Event.Type) + }) + + t.Run("publish_increments_seq", func(t *testing.T) { + ts := newTestTelemetrySession(t) + reader := ts.NewReader(0) + + for i := 0; i < 3; i++ { + ts.Publish(events.Event{Type: "page.navigation", Category: events.Page, Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, Ts: 1}) + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + for want := uint64(1); want <= 3; want++ { + env := readEnvelope(t, reader, ctx) + assert.Equal(t, want, env.Seq, "expected seq %d got %d", want, env.Seq) + } + }) + + t.Run("publish_sets_ts", func(t *testing.T) { + ts := newTestTelemetrySession(t) + reader := ts.NewReader(0) + + before := time.Now().UnixMicro() + ts.Publish(events.Event{Type: "page.navigation", Category: events.Page, Source: oapi.BrowserEventSource{Kind: oapi.Cdp}}) + after := time.Now().UnixMicro() + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + env := readEnvelope(t, reader, ctx) + assert.GreaterOrEqual(t, env.Event.Ts, before) + assert.LessOrEqual(t, env.Event.Ts, after) + }) + + t.Run("publish_writes_ring", func(t *testing.T) { + ts := newTestTelemetrySession(t) + + reader := ts.NewReader(0) + ts.Publish(events.Event{Type: "page.navigation", Category: events.Page, Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, Ts: 1}) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + env := readEnvelope(t, reader, ctx) + assert.Equal(t, "page.navigation", env.Event.Type) + assert.Equal(t, events.Page, env.Event.Category) + }) + + t.Run("start_sets_telemetry_session_id_in_source_metadata", func(t *testing.T) { + ts := newTestTelemetrySession(t) + ts.Start("test-uuid", TelemetryConfig{}) + + reader := ts.NewReader(0) + ts.Publish(events.Event{Type: "page.navigation", Category: events.Page, Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, Ts: 1}) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + env := readEnvelope(t, reader, ctx) + assert.Equal(t, "test-uuid", telemetrySessionIDFromMetadata(t, env.Event.Source)) + }) + + t.Run("data_unchanged_when_telemetry_session_id_in_metadata", func(t *testing.T) { + ts := newTestTelemetrySession(t) + ts.Start("merge-session", TelemetryConfig{}) + + reader := ts.NewReader(0) + ts.Publish(events.Event{ + Type: "page.navigation", + Category: events.Page, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, + Ts: 1, + Data: json.RawMessage(`{"url":"https://example.com"}`), + }) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + env := readEnvelope(t, reader, ctx) + assert.Equal(t, "merge-session", telemetrySessionIDFromMetadata(t, env.Event.Source)) + assert.JSONEq(t, `{"url":"https://example.com"}`, string(env.Event.Data)) + }) + + t.Run("system_events_always_captured_regardless_of_config", func(t *testing.T) { + ts := NewTelemetrySession(newTestEventStream(t, 10)) + // Start with only console category — system should still pass through. + ts.Start("sys-test", TelemetryConfig{Categories: []oapi.TelemetryEventCategory{events.Console}}) + reader := ts.NewReader(0) + + ts.Publish(events.Event{Type: "monitor.disconnected", Category: events.System, Source: oapi.BrowserEventSource{Kind: oapi.KernelApi}, Ts: 1}) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + env := readEnvelope(t, reader, ctx) + assert.Equal(t, events.System, env.Event.Category) + }) + + t.Run("truncation_applied", func(t *testing.T) { + ts := newTestTelemetrySession(t) + reader := ts.NewReader(0) + + largeData := strings.Repeat("x", 1_100_000) + rawData, err := json.Marshal(map[string]string{"payload": largeData}) + require.NoError(t, err) + + ts.Publish(events.Event{ + Type: "page.navigation", + Category: events.Page, + Source: oapi.BrowserEventSource{Kind: oapi.Cdp}, + Ts: 1, + Data: json.RawMessage(rawData), + }) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + env := readEnvelope(t, reader, ctx) + assert.True(t, env.Event.Truncated) + assert.True(t, json.Valid(env.Event.Data)) + }) +} diff --git a/server/oapi-codegen.yaml b/server/oapi-codegen.yaml index ae65d3a3..d814eac1 100644 --- a/server/oapi-codegen.yaml +++ b/server/oapi-codegen.yaml @@ -6,4 +6,7 @@ generate: models: true embedded-spec: true chi-server: true -output: lib/oapi/oapi.go +output: lib/oapi/oapi.go +output-options: + # Emit schemas not reachable from any operation (e.g. documented event taxonomy). + skip-prune: true diff --git a/server/openapi.yaml b/server/openapi.yaml index c29fd98b..1c913ed1 100644 --- a/server/openapi.yaml +++ b/server/openapi.yaml @@ -1194,94 +1194,86 @@ paths: $ref: "#/components/responses/BadRequestError" "500": $ref: "#/components/responses/InternalError" - /events/capture_session: + /telemetry: get: - summary: Get the capture session + summary: Get telemetry configuration description: > - Returns the current state of the capture session. Returns 404 if - no session is active. - operationId: getCaptureSession + Returns the current telemetry configuration. Returns 404 if telemetry + is not configured. + operationId: getTelemetry responses: "200": - description: Session state + description: Telemetry configuration content: application/json: schema: - $ref: "#/components/schemas/CaptureSession" + $ref: "#/components/schemas/TelemetryState" "404": $ref: "#/components/responses/NotFoundError" - post: - summary: Start the capture session + put: + summary: Set telemetry configuration description: > - Starts the capture session. At most one session may exist at a - time; returns 409 if a session is already active. The caller must - DELETE the existing session before starting a new one. - operationId: startCaptureSession + Sets the telemetry configuration. Returns 201 if telemetry was not + previously configured; returns 200 if it was. Setting all four categories + to enabled: false clears the configuration; this is idempotent when + telemetry is not configured. + operationId: putTelemetry requestBody: required: false content: application/json: schema: - $ref: "#/components/schemas/StartCaptureSessionRequest" + $ref: "#/components/schemas/BrowserTelemetryConfig" responses: + "200": + description: Telemetry configuration updated + content: + application/json: + schema: + $ref: "#/components/schemas/TelemetryState" "201": - description: Session started + description: Telemetry configuration set content: application/json: schema: - $ref: "#/components/schemas/CaptureSession" + $ref: "#/components/schemas/TelemetryState" "400": $ref: "#/components/responses/BadRequestError" - "409": - $ref: "#/components/responses/ConflictError" "500": $ref: "#/components/responses/InternalError" patch: - summary: Update the capture session + summary: Update telemetry configuration description: > - Updates the active capture session. Returns 404 if no session is - active. - operationId: updateCaptureSession + Partially updates the telemetry configuration. Only categories explicitly + set in the request body are changed; omitted categories retain their + current settings. Returns 404 if telemetry is not configured. Setting + all four categories to enabled: false clears the configuration. + operationId: patchTelemetry requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/UpdateCaptureSessionRequest" + $ref: "#/components/schemas/BrowserTelemetryConfig" responses: "200": - description: Session updated + description: Telemetry configuration updated content: application/json: schema: - $ref: "#/components/schemas/CaptureSession" + $ref: "#/components/schemas/TelemetryState" "400": $ref: "#/components/responses/BadRequestError" "404": $ref: "#/components/responses/NotFoundError" - delete: - summary: Stop the capture session - description: > - Stops the capture session and its CDP monitor, returning the final - session state. Returns 404 if no session is active. - operationId: stopCaptureSession - responses: - "200": - description: Session stopped - content: - application/json: - schema: - $ref: "#/components/schemas/CaptureSession" - "404": - $ref: "#/components/responses/NotFoundError" - /events/publish: + /telemetry/events: post: - summary: Publish an event into the event bus + summary: Publish an event into the telemetry stream description: > - Injects an event into the global event bus. + Injects an event into the telemetry stream. The event is assigned a monotonically increasing sequence number and fanned out to all open stream connections. - operationId: publishEvent + operationId: publishTelemetryEvent requestBody: required: true content: @@ -1294,23 +1286,27 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/PublishedEnvelope" + $ref: "#/components/schemas/TelemetryEnvelope" "400": $ref: "#/components/responses/BadRequestError" - /events/stream: + /telemetry/stream: get: - summary: Stream events as Server-Sent Events + summary: Stream telemetry events as Server-Sent Events description: > - Opens a live SSE stream of events from the global event bus. + Opens a live SSE stream of telemetry events. Each frame has the form `id: {seq}\ndata: {envelope-json}\n\n`. - Sequence numbers are process-monotonic and do not reset between sessions; - a Last-Event-ID from any previous session is valid for resuming the stream. + Sequence numbers are process-monotonic and persist across configuration + changes; a Last-Event-ID from any previous connection is valid for resuming the stream. Drops are signaled by gaps in the `id` field: if the received `id` is more than 1 ahead of the last-seen `id`, events were evicted from the buffer. Clients should track the last-seen `id` to detect gaps. Fresh connections with no Last-Event-ID start from the current sequence and only see new events. - operationId: streamEvents + The wire shape is the generic `TelemetryEvent`. For browser events + emitted by the Kernel image (or by callers using a documented `type`), + the event's `data` conforms to the matching `Browser*EventData` + schema, selected by `type`. + operationId: streamTelemetryEvents parameters: - in: header name: Last-Event-ID @@ -1320,10 +1316,10 @@ paths: description: > Resume after this sequence number. Omit or send 0 to start from the current position. Sequence numbers are process-monotonic, so - a value from a previous session resumes correctly from that point. + any previous value resumes correctly from that point. responses: "200": - description: Live SSE stream of events. + description: Live SSE stream of telemetry events. headers: X-SSE-Content-Type: description: Media type of SSE data events (application/json) @@ -1333,7 +1329,7 @@ paths: content: text/event-stream: schema: - $ref: "#/components/schemas/PublishedEnvelope" + $ref: "#/components/schemas/TelemetryEnvelope" /scaletozero/disable: post: summary: Idempotently disable scale to zero on this VM. @@ -1360,9 +1356,14 @@ paths: $ref: "#/components/responses/InternalError" components: schemas: - Event: + TelemetryEvent: type: object - description: A capture event. + description: > + 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`. required: - type properties: @@ -1381,24 +1382,29 @@ components: - network - page - interaction - - liveview - - captcha - system source: - $ref: "#/components/schemas/EventSource" + $ref: "#/components/schemas/BrowserEventSource" data: - description: Arbitrary event payload. + description: > + Arbitrary JSON payload. For browser events listed in + `KnownBrowserTelemetryEvent`, the payload conforms to the + corresponding `Browser*EventData` schema. truncated: type: boolean description: Set by the server when the data field was truncated to fit the size limit. - EventSource: + BrowserEventSource: type: object - description: Provenance of the event. + description: Provenance metadata identifying which producer emitted the event. + required: [kind] properties: kind: type: string description: > - Source kind. "kernel_api" is reserved for server-generated events. + 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. enum: - cdp - kernel_api @@ -1406,14 +1412,16 @@ components: - local_process event: type: string + description: Producer-specific event name (e.g. `Runtime.consoleAPICalled` for CDP-sourced console events). metadata: type: object + description: Producer-specific context (e.g. CDP target/session/frame IDs). additionalProperties: type: string additionalProperties: false PublishEventRequest: type: object - description: Request body for publishing an event into the event bus. + description: Request body for publishing an event into the telemetry stream. required: - type properties: @@ -1428,16 +1436,14 @@ components: - network - page - interaction - - liveview - - captcha - system default: system source: - $ref: "#/components/schemas/EventSource" + $ref: "#/components/schemas/BrowserEventSource" data: - description: Capture Session Event Payload + description: Telemetry event payload. additionalProperties: false - PublishedEnvelope: + TelemetryEnvelope: type: object description: The envelope assigned to a successfully published event. required: @@ -1453,73 +1459,997 @@ components: the server. Use with Last-Event-ID to resume the SSE stream from this point. event: - $ref: "#/components/schemas/Event" + $ref: "#/components/schemas/TelemetryEvent" additionalProperties: false - CaptureConfig: + BrowserTelemetryConfig: type: object - description: Capture filtering preferences. + description: > + Telemetry configuration for a browser. Per-category capture settings. + Omit a category or set enabled: true to capture it. + Set enabled: false to exclude it. + Omit the browser key entirely to capture all categories. + Set all four categories to enabled: false to clear the telemetry configuration. properties: - categories: - type: array + browser: + $ref: "#/components/schemas/BrowserTelemetryCategoriesConfig" + additionalProperties: false + BrowserTelemetryCategoriesConfig: + type: object + description: Per-category telemetry capture settings for browser events. + properties: + console: + $ref: "#/components/schemas/BrowserTelemetryCategoryConfig" + description: Console output (log, warn, error) and uncaught exceptions. + page: + $ref: "#/components/schemas/BrowserTelemetryCategoryConfig" + description: Page lifecycle events (navigation, load, layout shifts, LCP). + interaction: + $ref: "#/components/schemas/BrowserTelemetryCategoryConfig" + description: User interaction events (clicks, keydowns, scroll). + network: + $ref: "#/components/schemas/BrowserTelemetryCategoryConfig" + description: HTTP request/response metadata. + additionalProperties: false + BrowserTelemetryCategoryConfig: + type: object + description: Configuration for a single telemetry category. + properties: + enabled: + type: boolean description: > - Event categories to capture. When omitted or empty, all - categories are captured. + Whether this category is captured. In PUT requests, omitting this field + defaults to true (category enabled). 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`. + additionalProperties: false + BrowserCallStack: + type: object + description: > + 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. + required: [callFrames] + properties: + description: + type: string + description: Optional label for the stack trace (e.g. async cause). + callFrames: + type: array + description: Ordered list of call frames, outermost first. items: - type: string - enum: - - console - - network - - page - - interaction - - liveview - - captcha - - system + type: object + required: [functionName, scriptId, url, lineNumber, columnNumber] + properties: + functionName: + type: string + description: JavaScript function name, or empty string for anonymous functions. + scriptId: + type: string + description: CDP script identifier. + url: + type: string + description: URL or name of the script file. + lineNumber: + type: integer + description: Zero-based line number within the script. + columnNumber: + type: integer + description: Zero-based column number within the line. + parent: + $ref: "#/components/schemas/BrowserCallStack" + description: Parent stack trace for async stacks. + BrowserHttpHeaders: + type: object + description: > + HTTP headers map forwarded as-is from CDP without normalization. + Values are typically strings but may be any JSON type. + additionalProperties: true + BrowserTargetType: + type: string + description: CDP target type of the page that produced the event. + enum: [page, background_page, service_worker, shared_worker, other] + BrowserEventContext: + type: object + description: > + 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. + required: [session_id, target_id, target_type, nav_seq] + properties: + session_id: + type: string + description: CDP session identifier for the target connection. + target_id: + type: string + description: Browser target identifier (stable across navigations within a tab). + target_type: + $ref: "#/components/schemas/BrowserTargetType" + frame_id: + type: string + description: CDP frame identifier within the target. + loader_id: + type: string + description: CDP document loader identifier, reset on each navigation. + url: + type: string + description: URL relevant to this event; page URL for navigation and page events, request URL for network events. + nav_seq: + type: integer + format: int64 + description: Monotonically increasing navigation sequence number, incremented on each top-level navigation within the target. + BrowserConsoleLogEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [level, text] + properties: + level: + type: string + description: > + 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. + text: + type: string + description: First console argument coerced to string. + args: + type: array + description: All console arguments coerced to strings. + items: + type: string + stack_trace: + $ref: "#/components/schemas/BrowserCallStack" + BrowserConsoleLogEvent: + type: object + description: A browser console log event (console.log, console.info, console.warn, etc.). + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: console_log + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserConsoleLogEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserConsoleErrorEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [text] + properties: + text: + type: string + description: Error message text. Present in both source paths. + stack_trace: + $ref: "#/components/schemas/BrowserCallStack" + level: + type: string + description: CDP console type value, always "error". Present only when sourced from Runtime.consoleAPICalled. + args: + type: array + description: All console arguments coerced to strings. Present only when sourced from Runtime.consoleAPICalled. + items: + type: string + line: + type: integer + description: Line number in the script where the exception was thrown. Present only when sourced from Runtime.exceptionThrown. + column: + type: integer + description: Column number in the script where the exception was thrown. Present only when sourced from Runtime.exceptionThrown. + source_url: + type: string + description: URL of the script file that threw the exception. Present only when sourced from Runtime.exceptionThrown. + BrowserConsoleErrorEvent: + type: object + description: > + 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. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: console_error + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserConsoleErrorEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserNetworkRequestEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [request_id, method, document_url, headers, initiator_type] + properties: + request_id: + type: string + description: CDP request identifier, unique within the session. + method: + type: string + description: HTTP method as sent on the wire (e.g. GET, POST). + document_url: + type: string + description: URL of the document that initiated the request. + headers: + $ref: "#/components/schemas/BrowserHttpHeaders" + description: Request headers. + resource_type: + type: string + description: 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. + initiator_type: + type: string + description: CDP Initiator.type indicating what caused the request, passed through as-is from Chrome. Known values include script, parser, preload, and other. + post_data: + type: string + description: Request body for POST/PUT requests, if available. + is_redirect: + type: boolean + description: True if this request is the result of a redirect. + redirect_url: + type: string + description: Original URL before the redirect, present when is_redirect is true. + BrowserNetworkRequestEvent: + type: object + description: A browser network request sent event. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: network_request + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserNetworkRequestEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserNetworkResponseEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [request_id, method, status, headers] + properties: + request_id: + type: string + description: CDP request identifier matching the originating network_request event. + method: + type: string + description: HTTP method of the original request. + status: + type: integer + description: HTTP response status code. + status_text: + type: string + description: HTTP response status text (e.g. OK, Not Found). + headers: + $ref: "#/components/schemas/BrowserHttpHeaders" + description: Response headers. + mime_type: + type: string + description: MIME type of the response (e.g. text/html, application/json). + resource_type: + type: string + description: 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. + body: + type: string + description: Truncated response body, present only for text MIME types. + BrowserNetworkResponseEvent: + type: object + description: A browser network response received event. Fired after the response body is fully received, not when headers arrive. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: network_response + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserNetworkResponseEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserNetworkLoadingFailedEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [request_id, error_text, canceled] + properties: + request_id: + type: string + description: CDP request identifier matching the originating network_request event. + error_text: + type: string + description: Network error description (e.g. net::ERR_CONNECTION_REFUSED). + canceled: + type: boolean + description: True if the request was canceled by the browser or page script. + resource_type: + type: string + description: 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. + BrowserNetworkLoadingFailedEvent: + type: object + description: > + 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. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: network_loading_failed + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserNetworkLoadingFailedEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserNetworkIdleEvent: + type: object + description: A browser network idle event emitted after a 500ms quiet period with no in-flight HTTP requests. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: network_idle + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserEventContext" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageNavigationEventData: + type: object additionalProperties: false - CaptureSession: + required: [session_id, target_id, target_type, url, frame_id, loader_id] + properties: + url: + type: string + description: URL navigated to. + frame_id: + type: string + description: CDP frame identifier of the navigated frame. + parent_frame_id: + type: string + description: Parent frame identifier for subframe navigations; absent for top-level navigations. + loader_id: + type: string + description: New CDP document loader identifier assigned for this navigation. + session_id: + type: string + description: CDP session identifier. + target_id: + type: string + description: Browser target identifier. + target_type: + $ref: "#/components/schemas/BrowserTargetType" + BrowserPageNavigationEvent: type: object - description: A capture session resource. - required: - - id - - status - - config - - seq - - created_at + description: 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. + required: [ts, type, source] properties: - id: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: type: string - status: + const: page_navigation + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserPageNavigationEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageDomContentLoadedEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [cdp_timestamp] + properties: + cdp_timestamp: + type: number + description: Chrome monotonic clock value in seconds at which DOMContentLoaded fired, relative to browser process start (not Unix epoch). Use `ts` for wall-clock time. + BrowserPageDomContentLoadedEvent: + type: object + description: A browser DOMContentLoaded event (CDP Page.domContentEventFired). + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: type: string - enum: - - running - - stopped - config: - $ref: "#/components/schemas/CaptureConfig" - seq: + const: page_dom_content_loaded + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserPageDomContentLoadedEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageLoadEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [cdp_timestamp] + properties: + cdp_timestamp: + type: number + description: 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. + BrowserPageLoadEvent: + type: object + description: A browser page load event (CDP Page.loadEventFired). + required: [ts, type, source] + properties: + ts: type: integer format: int64 - description: >- - Process-monotonic sequence number of the last published event. - Does not reset between sessions. - minimum: 0 - created_at: + description: Event timestamp in Unix microseconds. + type: type: string - format: date-time + const: page_load + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserPageLoadEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageTabOpenedEventData: + type: object additionalProperties: false - StartCaptureSessionRequest: + required: [target_id, target_type, url] + properties: + target_id: + type: string + description: CDP target identifier for the newly opened tab. + target_type: + $ref: "#/components/schemas/BrowserTargetType" + url: + type: string + description: Initial URL of the new tab. + title: + type: string + description: Initial page title of the new tab. + opener_id: + type: string + description: Target identifier of the tab that opened this one, if any. + BrowserPageTabOpenedEvent: type: object description: > - Optional capture configuration. All fields default to the - server-defined profile when omitted or when no body is sent. + 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. + required: [ts, type, source] properties: - config: - $ref: "#/components/schemas/CaptureConfig" + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: page_tab_opened + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserPageTabOpenedEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageLayoutShiftEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [source_frame_id, time, duration] + properties: + source_frame_id: + type: string + description: CDP frame identifier of the frame where the layout shift occurred. + time: + type: number + description: Performance Timeline timestamp of the layout shift in milliseconds. + duration: + type: number + description: Duration of the layout shift entry in milliseconds (always 0 for layout shifts per spec). + layout_shift_details: + type: object + additionalProperties: false + description: PerformanceLayoutShift attributes from the Performance Timeline entry. + properties: + value: + type: number + description: Layout shift score for this entry (contribution to CLS). + had_recent_input: + type: boolean + description: True if the layout shift was preceded by user input within 500ms, excluding it from CLS. + BrowserPageLayoutShiftEvent: + type: object + description: A browser cumulative layout shift (CLS) event from the Performance Timeline API. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: page_layout_shift + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserPageLayoutShiftEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageLcpEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [source_frame_id, time] + properties: + source_frame_id: + type: string + description: CDP frame identifier of the frame where the LCP element was rendered. + time: + type: number + description: Performance Timeline timestamp of the LCP entry in milliseconds. + lcp_details: + type: object + additionalProperties: false + description: LargestContentfulPaint attributes from the Performance Timeline entry. + properties: + render_time: + type: number + description: Render time of the LCP element in milliseconds; 0 for cross-origin images without Timing-Allow-Origin. + load_time: + type: number + description: Load time of the LCP element in milliseconds. + size: + type: number + description: Visible area of the LCP element in pixels squared. + element_id: + type: string + description: id attribute of the LCP element, if present. + url: + type: string + description: URL of the LCP element for image or video elements. + node_id: + type: integer + description: CDP DOM node identifier of the LCP element. + BrowserPageLcpEvent: + type: object + description: A browser Largest Contentful Paint (LCP) event from the Performance Timeline API. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: page_lcp + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserPageLcpEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageLayoutSettledEvent: + type: object + description: 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. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: page_layout_settled + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserEventContext" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserPageNavigationSettledEvent: + type: object + description: > + 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. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: page_navigation_settled + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserEventContext" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserInteractionClickEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [x, y, selector, tag] + properties: + x: + type: integer + description: Viewport x-coordinate of the click in CSS pixels. + y: + type: integer + description: Viewport y-coordinate of the click in CSS pixels. + selector: + type: string + description: CSS selector path to the clicked element. + tag: + type: string + description: HTML tag name of the clicked element in uppercase (e.g. BUTTON, A, DIV). + text: + type: string + description: Visible text content of the clicked element, trimmed. + BrowserInteractionClickEvent: + type: object + description: A browser user click event captured via injected page script. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: interaction_click + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserInteractionClickEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserInteractionKeyEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [key, selector, tag] + properties: + key: + type: string + description: Key value from the KeyboardEvent (e.g. Enter, Backspace, a). + selector: + type: string + description: CSS selector path to the element that had focus when the key was pressed. + tag: + type: string + description: HTML tag name of the focused element in uppercase (e.g. INPUT, TEXTAREA, DIV). + BrowserInteractionKeyEvent: + type: object + description: A browser keyboard event captured via injected page script. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: interaction_key + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserInteractionKeyEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserInteractionScrollSettledEventData: + allOf: + - $ref: "#/components/schemas/BrowserEventContext" + - type: object + required: [from_x, from_y, to_x, to_y, target_selector] + properties: + from_x: + type: integer + description: Scroll x-position at the start of the scroll gesture in CSS pixels. + from_y: + type: integer + description: Scroll y-position at the start of the scroll gesture in CSS pixels. + to_x: + type: integer + description: Final scroll x-position after the gesture settled in CSS pixels. + to_y: + type: integer + description: Final scroll y-position after the gesture settled in CSS pixels. + target_selector: + type: string + description: CSS selector path to the scrolled element. + BrowserInteractionScrollSettledEvent: + type: object + description: A browser scroll settled event emitted after scroll position stops changing, captured via injected page script. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: interaction_scroll_settled + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserInteractionScrollSettledEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserMonitorScreenshotEventData: + type: object + additionalProperties: false + required: [png] + properties: + png: + type: string + contentEncoding: base64 + description: Base64-encoded PNG screenshot of the browser viewport. + BrowserMonitorScreenshotEvent: + type: object + description: A periodic screenshot of the browser viewport. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: monitor_screenshot + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserMonitorScreenshotEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserMonitorDisconnectedEventData: + type: object + additionalProperties: false + required: [reason] + properties: + reason: + type: string + description: 'Reason for the disconnection. chrome_restarted: Chrome process restarted.' + enum: [chrome_restarted] + BrowserMonitorDisconnectedEvent: + type: object + description: 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. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: monitor_disconnected + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserMonitorDisconnectedEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserMonitorReconnectedEventData: + type: object + additionalProperties: false + required: [reconnect_duration_ms] + properties: + reconnect_duration_ms: + type: integer + format: int64 + description: Wall-clock time in milliseconds taken to reconnect after the disconnection. + BrowserMonitorReconnectedEvent: + type: object + description: 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. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: monitor_reconnected + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserMonitorReconnectedEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserMonitorReconnectFailedEventData: + type: object + additionalProperties: false + required: [reason] + properties: + reason: + type: string + description: 'Reason for the reconnection failure. reconnect_exhausted: all retry attempts were used up without successfully restoring the CDP connection.' + enum: [reconnect_exhausted] + BrowserMonitorReconnectFailedEvent: + type: object + description: The CDP connection to Chrome could not be re-established after exhausting all reconnection attempts. No further telemetry events will arrive on this session. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: monitor_reconnect_failed + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserMonitorReconnectFailedEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + BrowserMonitorInitFailedEventData: + type: object additionalProperties: false - UpdateCaptureSessionRequest: + required: [step] + properties: + step: + type: string + description: The CDP method or initialization step that failed (e.g. Target.setAutoAttach). + BrowserMonitorInitFailedEvent: type: object - description: Fields to update on the capture session. + description: The CDP session could not be initialized. + required: [ts, type, source] + properties: + ts: + type: integer + format: int64 + description: Event timestamp in Unix microseconds. + type: + type: string + const: monitor_init_failed + source: + $ref: "#/components/schemas/BrowserEventSource" + data: + $ref: "#/components/schemas/BrowserMonitorInitFailedEventData" + truncated: + type: boolean + description: True if the data field was truncated due to size limits. + KnownBrowserTelemetryEvent: + description: > + 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. + oneOf: + - $ref: "#/components/schemas/BrowserConsoleLogEvent" + - $ref: "#/components/schemas/BrowserConsoleErrorEvent" + - $ref: "#/components/schemas/BrowserNetworkRequestEvent" + - $ref: "#/components/schemas/BrowserNetworkResponseEvent" + - $ref: "#/components/schemas/BrowserNetworkLoadingFailedEvent" + - $ref: "#/components/schemas/BrowserNetworkIdleEvent" + - $ref: "#/components/schemas/BrowserPageNavigationEvent" + - $ref: "#/components/schemas/BrowserPageDomContentLoadedEvent" + - $ref: "#/components/schemas/BrowserPageLoadEvent" + - $ref: "#/components/schemas/BrowserPageTabOpenedEvent" + - $ref: "#/components/schemas/BrowserPageLayoutShiftEvent" + - $ref: "#/components/schemas/BrowserPageLcpEvent" + - $ref: "#/components/schemas/BrowserPageLayoutSettledEvent" + - $ref: "#/components/schemas/BrowserPageNavigationSettledEvent" + - $ref: "#/components/schemas/BrowserInteractionClickEvent" + - $ref: "#/components/schemas/BrowserInteractionKeyEvent" + - $ref: "#/components/schemas/BrowserInteractionScrollSettledEvent" + - $ref: "#/components/schemas/BrowserMonitorScreenshotEvent" + - $ref: "#/components/schemas/BrowserMonitorDisconnectedEvent" + - $ref: "#/components/schemas/BrowserMonitorReconnectedEvent" + - $ref: "#/components/schemas/BrowserMonitorReconnectFailedEvent" + - $ref: "#/components/schemas/BrowserMonitorInitFailedEvent" + discriminator: + propertyName: type + mapping: + console_log: "#/components/schemas/BrowserConsoleLogEvent" + console_error: "#/components/schemas/BrowserConsoleErrorEvent" + network_request: "#/components/schemas/BrowserNetworkRequestEvent" + network_response: "#/components/schemas/BrowserNetworkResponseEvent" + network_loading_failed: "#/components/schemas/BrowserNetworkLoadingFailedEvent" + network_idle: "#/components/schemas/BrowserNetworkIdleEvent" + page_navigation: "#/components/schemas/BrowserPageNavigationEvent" + page_dom_content_loaded: "#/components/schemas/BrowserPageDomContentLoadedEvent" + page_load: "#/components/schemas/BrowserPageLoadEvent" + page_tab_opened: "#/components/schemas/BrowserPageTabOpenedEvent" + page_layout_shift: "#/components/schemas/BrowserPageLayoutShiftEvent" + page_lcp: "#/components/schemas/BrowserPageLcpEvent" + page_layout_settled: "#/components/schemas/BrowserPageLayoutSettledEvent" + page_navigation_settled: "#/components/schemas/BrowserPageNavigationSettledEvent" + interaction_click: "#/components/schemas/BrowserInteractionClickEvent" + interaction_key: "#/components/schemas/BrowserInteractionKeyEvent" + interaction_scroll_settled: "#/components/schemas/BrowserInteractionScrollSettledEvent" + monitor_screenshot: "#/components/schemas/BrowserMonitorScreenshotEvent" + monitor_disconnected: "#/components/schemas/BrowserMonitorDisconnectedEvent" + monitor_reconnected: "#/components/schemas/BrowserMonitorReconnectedEvent" + monitor_reconnect_failed: "#/components/schemas/BrowserMonitorReconnectFailedEvent" + monitor_init_failed: "#/components/schemas/BrowserMonitorInitFailedEvent" + TelemetryState: + type: object + description: Current telemetry configuration. + required: + - config + - seq properties: config: - $ref: "#/components/schemas/CaptureConfig" + $ref: "#/components/schemas/BrowserTelemetryConfig" + seq: + type: integer + format: int64 + description: >- + Process-monotonic sequence number of the last published event. + Does not reset across configuration changes. + minimum: 0 + applied_at: + type: string + format: date-time + description: >- + Wall-clock time at which the current configuration was applied. + Omitted when telemetry is not configured. additionalProperties: false StartRecordingRequest: type: object