Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
ab738a1
feat: add CDP pipeline, cdpmonitor, and wire into API service
archandatta Apr 2, 2026
a7b1714
refactor: rename Pipeline to CaptureSession and delete pipeline.go
archandatta Apr 2, 2026
04b440d
review: fix request context leak in StartCapture, add missing categor…
archandatta Apr 2, 2026
86ef8f5
review: clean up syntax
archandatta Apr 6, 2026
4de35ba
review: move CategoryFor to cdpmonitor package
archandatta Apr 7, 2026
4ebdcf5
review: internalize ring buffer and file writer in CaptureSession con…
archandatta Apr 7, 2026
33efe9a
review: write logs under /var/log/kernel and ensure dir exists
archandatta Apr 7, 2026
6c6f3c9
review: add capture config to /events/start and OpenAPI spec
archandatta Apr 7, 2026
ade2f4b
review: fix lifecycle context, stop-before-reset ordering, seq reset,…
archandatta Apr 7, 2026
3f9ada7
fix: oapi version
archandatta Apr 7, 2026
ace55a5
fix: Shutdown cancels context outside lock, racing with StartCapture
archandatta Apr 7, 2026
be34cc5
review: validate DetailLevel with generated Valid
archandatta Apr 7, 2026
8294a54
fix: reset ring buffer on session restart to unstrand existing readers
archandatta Apr 7, 2026
12261d5
chore: remove dead categoryFor function
archandatta Apr 7, 2026
4ff30ce
review: guard zero-capacity ring buffer and fix reader reset after bu…
archandatta Apr 7, 2026
1c8ae0a
review: use t.TempDir in test helper, map-based ValidCategory, avoid …
archandatta Apr 7, 2026
67eefa7
review: add captureConfigFrom and StartCapture/StopCapture handler tests
archandatta Apr 7, 2026
c6f509f
feat: refactor events API to resource-style capture sessions
archandatta Apr 8, 2026
34aef8e
review: update file writer to be internal to the package
archandatta Apr 9, 2026
edf7cab
review: tighten to `Write(filename string, data []byte) error`
archandatta Apr 9, 2026
57871d5
review: update panic -> error
archandatta Apr 9, 2026
2358564
review: update oapi and remove detail level
archandatta Apr 9, 2026
37a155e
review: remove url
archandatta Apr 9, 2026
8f24624
chore: restore server/api on branch
archandatta Apr 9, 2026
147ca41
review: harden captureConfigFromOAPI and clarify stop comment
archandatta Apr 9, 2026
95e7b6e
review: unexport ringBuffer and drop AllCategories wrapper
archandatta Apr 9, 2026
3b8c0a2
review: replace event producers with cdp monitor in stop description
archandatta Apr 9, 2026
97b8374
remove test line
archandatta Apr 9, 2026
ddbaabc
review: update uuid to cuid2
archandatta Apr 10, 2026
0210a6e
feat: add cdpmonitor foundation — types, util, computed state machine…
archandatta Apr 13, 2026
42999af
self review
archandatta Apr 13, 2026
b151535
review: cursor feedback
archandatta Apr 13, 2026
869e368
[kernel-1116] CDP monitor core (#214)
archandatta Apr 13, 2026
150a306
review: update types and sensitive interaction data
archandatta Apr 14, 2026
b77e7e2
feat: add two-layer CDP decode, protocol-faithful types, then monitor…
archandatta Apr 14, 2026
f6258b6
review: clean up monitor health and types
archandatta Apr 22, 2026
915009c
review: add chromium version
archandatta Apr 22, 2026
52c6f87
review: remove dead code
archandatta Apr 22, 2026
277084e
fix injection script
archandatta Apr 22, 2026
635449d
review: remove sensitive data from inject
archandatta Apr 22, 2026
d4585b5
review
archandatta Apr 22, 2026
ca628ef
review: sensitive data audit interaction.js
archandatta Apr 22, 2026
bf8026a
review: reconnect failure leaks goroutines and deadlocks Stop
archandatta Apr 22, 2026
f3c1441
review: add ctx to monitor and update comment
archandatta Apr 23, 2026
b536299
review: lift lifeMu to dispatch level to make ctx handling explicit
archandatta Apr 23, 2026
e977007
review: add readme for cdp monitor
archandatta Apr 23, 2026
e8eb642
feat: capturesession: add Active(), publishLocked helper, session_end…
archandatta Apr 23, 2026
37b79ee
feat: add PublishEvent and StreamEvents handlers
archandatta Apr 23, 2026
ff15e66
review: address feedback
archandatta Apr 24, 2026
6bf578e
review: update comment
archandatta Apr 24, 2026
c266e78
review: add events lifecycle test
archandatta May 1, 2026
3f95cff
review: add documentation
archandatta May 6, 2026
b61d0f1
chore: rename event type constants to drop Type prefix
archandatta May 6, 2026
121e5ae
feat: add S2 durable event storage library
archandatta May 6, 2026
c4a9ae1
feat: wire S2 storage writer into api service
archandatta May 6, 2026
98b54de
review: rename storage fields and clean up writer tests
archandatta May 6, 2026
609bbb1
chore: update headless configs
archandatta May 6, 2026
9d1885b
fix: bound ack goroutine lifetimes and close the wg.Add/Wait race in …
archandatta May 6, 2026
a78dc6b
fix: drain SessionEnded to storage before evicting s2 producer
archandatta May 6, 2026
d86369f
fix: run shutdown phases in parallel and bound storageWriter.Close wi…
archandatta May 6, 2026
36cc25b
fix: suppress spurious storage_error on shutdown and surface ring ove…
archandatta May 6, 2026
9927d41
fix: remove streams:list probe and drop ctx from NewS2Storage; degrad…
archandatta May 6, 2026
0766996
fix: make s2 Append synchronous — wait for ack before returning
archandatta May 6, 2026
93cd63f
review: add missing test coverage for overflow, slow-append shutdown,…
archandatta May 6, 2026
2e4c65c
review: use EventsDropped for ring overflow events and add batcher tu…
archandatta May 6, 2026
503f4bd
review: document nil storageWriter contract and session cleanup ordering
archandatta May 6, 2026
f9f31b3
review: remove unnecessary sessionID local var in StopCaptureSession
archandatta May 6, 2026
6871eac
fix: block EventsStorageError in PublishEvent reserved type check
archandatta May 6, 2026
2e638ae
fix: remove overflow PublishUnfiltered to eliminate cpu-spinning feed…
archandatta May 6, 2026
a54a688
fix: remove unused storageWriter field and parameter from ApiService
archandatta May 6, 2026
8b34d4f
fix: stop capture session before cancelling writer so SessionEnded re…
archandatta May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions images/chromium-headful/run-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ if [[ -n "${PLAYWRIGHT_ENGINE:-}" ]]; then
RUN_ARGS+=( -e PLAYWRIGHT_ENGINE="$PLAYWRIGHT_ENGINE" )
fi

# S2 durable event storage
if [[ -n "${S2_BASIN:-}" ]]; then
RUN_ARGS+=( -e S2_BASIN="$S2_BASIN" )
fi
if [[ -n "${S2_ACCESS_TOKEN:-}" ]]; then
RUN_ARGS+=( -e S2_ACCESS_TOKEN="$S2_ACCESS_TOKEN" )
fi

# WebRTC port mapping
if [[ "${ENABLE_WEBRTC:-}" == "true" ]]; then
echo "Running container with WebRTC"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[program:kernel-images-api]
command=/bin/bash -lc 'mkdir -p "${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" && PORT="${KERNEL_IMAGES_API_PORT:-10001}" FRAME_RATE="${KERNEL_IMAGES_API_FRAME_RATE:-10}" DISPLAY_NUM="${KERNEL_IMAGES_API_DISPLAY_NUM:-${DISPLAY_NUM:-1}}" MAX_SIZE_MB="${KERNEL_IMAGES_API_MAX_SIZE_MB:-500}" OUTPUT_DIR="${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" LOG_CDP_MESSAGES="${LOG_CDP_MESSAGES:-false}" exec /usr/local/bin/kernel-images-api'
command=/bin/bash -lc 'mkdir -p "${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" && PORT="${KERNEL_IMAGES_API_PORT:-10001}" FRAME_RATE="${KERNEL_IMAGES_API_FRAME_RATE:-10}" DISPLAY_NUM="${KERNEL_IMAGES_API_DISPLAY_NUM:-${DISPLAY_NUM:-1}}" MAX_SIZE_MB="${KERNEL_IMAGES_API_MAX_SIZE_MB:-500}" OUTPUT_DIR="${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" LOG_CDP_MESSAGES="${LOG_CDP_MESSAGES:-false}" S2_BASIN="${S2_BASIN:-}" S2_ACCESS_TOKEN="${S2_ACCESS_TOKEN:-}" exec /usr/local/bin/kernel-images-api'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there also be an s2 stream config?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stream names are derived dynamically from the CaptureSessionID at runtime, so one S2 stream is created per capture session within the configured basin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will make it hard for, e.g. the API to know what stream to read when it wants to stream telemetry for a browser. It also is at odds with "one global event stream" that the cdp stuff publishes into. I would make this a constant that is injected on startup

autostart=false
autorestart=true
startsecs=2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[program:kernel-images-api]
command=/bin/bash -lc 'mkdir -p "${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" && PORT="${KERNEL_IMAGES_API_PORT:-10001}" FRAME_RATE="${KERNEL_IMAGES_API_FRAME_RATE:-10}" DISPLAY_NUM="${KERNEL_IMAGES_API_DISPLAY_NUM:-${DISPLAY_NUM:-1}}" MAX_SIZE_MB="${KERNEL_IMAGES_API_MAX_SIZE_MB:-500}" OUTPUT_DIR="${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" LOG_CDP_MESSAGES="${LOG_CDP_MESSAGES:-false}" exec /usr/local/bin/kernel-images-api'
command=/bin/bash -lc 'mkdir -p "${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" && PORT="${KERNEL_IMAGES_API_PORT:-10001}" FRAME_RATE="${KERNEL_IMAGES_API_FRAME_RATE:-10}" DISPLAY_NUM="${KERNEL_IMAGES_API_DISPLAY_NUM:-${DISPLAY_NUM:-1}}" MAX_SIZE_MB="${KERNEL_IMAGES_API_MAX_SIZE_MB:-500}" OUTPUT_DIR="${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" LOG_CDP_MESSAGES="${LOG_CDP_MESSAGES:-false}" S2_BASIN="${S2_BASIN:-}" S2_ACCESS_TOKEN="${S2_ACCESS_TOKEN:-}" exec /usr/local/bin/kernel-images-api'
autostart=false
autorestart=true
startsecs=2
Expand Down
8 changes: 8 additions & 0 deletions images/chromium-headless/run-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ if [[ -n "${PLAYWRIGHT_ENGINE:-}" ]]; then
RUN_ARGS+=( -e PLAYWRIGHT_ENGINE="$PLAYWRIGHT_ENGINE" )
fi

# S2 durable event storage
if [[ -n "${S2_BASIN:-}" ]]; then
RUN_ARGS+=( -e S2_BASIN="$S2_BASIN" )
fi
if [[ -n "${S2_ACCESS_TOKEN:-}" ]]; then
RUN_ARGS+=( -e S2_ACCESS_TOKEN="$S2_ACCESS_TOKEN" )
fi

# If a positional argument is given, use it as the entrypoint
ENTRYPOINT_ARG=()
if [[ $# -ge 1 && -n "$1" ]]; then
Expand Down
18 changes: 7 additions & 11 deletions server/cmd/api/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ 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/events"
Expand Down Expand Up @@ -82,8 +81,7 @@ type ApiService struct {
xvfbResizeMu sync.Mutex

// CDP event pipeline and cdpMonitor.
eventStream *events.EventStream
captureSession *capturesession.CaptureSession
captureSession *events.CaptureSession
cdpMonitor cdpMonitorController
monitorMu sync.Mutex
lifecycleCtx context.Context
Expand All @@ -92,14 +90,14 @@ type ApiService struct {

var _ oapi.StrictServerInterface = (*ApiService)(nil)

// New constructs an ApiService.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

func New(
recordManager recorder.RecordManager,
factory recorder.FFmpegRecorderFactory,
upstreamMgr *devtoolsproxy.UpstreamManager,
stz scaletozero.Controller,
nekoAuthClient *nekoclient.AuthClient,
captureSession *capturesession.CaptureSession,
eventStream *events.EventStream,
captureSession *events.CaptureSession,
displayNum int,
) (*ApiService, error) {
switch {
Expand All @@ -113,24 +111,21 @@ func New(
return nil, fmt.Errorf("nekoAuthClient cannot be nil")
case captureSession == nil:
return nil, fmt.Errorf("captureSession 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, func(ev events.Event) { captureSession.Publish(ev) }, 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),
upstreamMgr: upstreamMgr,
stz: stz,
nekoAuthClient: nekoAuthClient,
policy: &policy.Policy{},
eventStream: eventStream,
captureSession: captureSession,
cdpMonitor: mon,
lifecycleCtx: ctx,
Expand Down Expand Up @@ -358,6 +353,7 @@ func (s *ApiService) Shutdown(ctx context.Context) error {
s.lifecycleCancel()
s.cdpMonitor.Stop()
s.captureSession.Stop()
_ = s.captureSession.Close()
s.monitorMu.Unlock()
return s.recordManager.StopAll(ctx)
}
40 changes: 18 additions & 22 deletions server/cmd/api/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ 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/events"
"github.com/kernel/kernel-images/server/lib/nekoclient"
Expand All @@ -27,7 +26,7 @@ func TestApiService_StartRecording(t *testing.T) {

t.Run("success", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)

resp, err := svc.StartRecording(ctx, oapi.StartRecordingRequestObject{})
Expand All @@ -41,7 +40,7 @@ func TestApiService_StartRecording(t *testing.T) {

t.Run("already recording", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)

// First start should succeed
Expand All @@ -56,7 +55,7 @@ func TestApiService_StartRecording(t *testing.T) {

t.Run("custom ids don't collide", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)

for i := 0; i < 5; i++ {
Expand Down Expand Up @@ -89,7 +88,7 @@ func TestApiService_StopRecording(t *testing.T) {

t.Run("no active recording", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)

resp, err := svc.StopRecording(ctx, oapi.StopRecordingRequestObject{})
Expand All @@ -102,7 +101,7 @@ func TestApiService_StopRecording(t *testing.T) {
rec := &mockRecorder{id: "default", isRecordingFlag: true}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)
resp, err := svc.StopRecording(ctx, oapi.StopRecordingRequestObject{})
require.NoError(t, err)
Expand All @@ -117,7 +116,7 @@ func TestApiService_StopRecording(t *testing.T) {

force := true
req := oapi.StopRecordingRequestObject{Body: &oapi.StopRecordingJSONRequestBody{ForceStop: &force}}
svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)
resp, err := svc.StopRecording(ctx, req)
require.NoError(t, err)
Expand All @@ -131,7 +130,7 @@ func TestApiService_DownloadRecording(t *testing.T) {

t.Run("not found", func(t *testing.T) {
mgr := recorder.NewFFmpegManager()
svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)
resp, err := svc.DownloadRecording(ctx, oapi.DownloadRecordingRequestObject{})
require.NoError(t, err)
Expand All @@ -151,7 +150,7 @@ func TestApiService_DownloadRecording(t *testing.T) {
rec := &mockRecorder{id: "default", isRecordingFlag: true, recordingData: randomBytes(minRecordingSizeInBytes - 1)}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)
// will return a 202 when the recording is too small
resp, err := svc.DownloadRecording(ctx, oapi.DownloadRecordingRequestObject{})
Expand Down Expand Up @@ -181,7 +180,7 @@ func TestApiService_DownloadRecording(t *testing.T) {
rec := &mockRecorder{id: "default", recordingData: data}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)
resp, err := svc.DownloadRecording(ctx, oapi.DownloadRecordingRequestObject{})
require.NoError(t, err)
Expand All @@ -201,7 +200,7 @@ func TestApiService_Shutdown(t *testing.T) {
rec := &mockRecorder{id: "default", isRecordingFlag: true}
require.NoError(t, mgr.RegisterRecorder(ctx, rec), "failed to register recorder")

svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)

require.NoError(t, svc.Shutdown(ctx))
Expand Down Expand Up @@ -305,26 +304,23 @@ func newMockNekoClient(t *testing.T) *nekoclient.AuthClient {
return client
}

func newCaptureSession(t *testing.T) (*capturesession.CaptureSession, *events.EventStream) {
func newCaptureSession(t *testing.T) *events.CaptureSession {
t.Helper()
es, err := events.NewEventStream(events.EventStreamConfig{RingCapacity: 64})
cs, err := events.NewCaptureSession(events.CaptureSessionConfig{
LogDir: t.TempDir(),
RingCapacity: 64,
})
if err != nil {
t.Fatal(err)
}
return capturesession.NewCaptureSession(es), es
}

// newSvc constructs an ApiService with a fresh capture 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)
t.Cleanup(func() { cs.Close() })
return cs
}

func TestApiService_PatchChromiumFlags(t *testing.T) {
ctx := context.Background()
mgr := recorder.NewFFmpegManager()
svc, err := newSvc(t, mgr)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)

// Test with valid flags
Expand Down
20 changes: 11 additions & 9 deletions server/cmd/api/api/capture_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"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"
)
Expand Down Expand Up @@ -88,6 +87,9 @@ func (s *ApiService) StopCaptureSession(_ context.Context, _ oapi.StopCaptureSes
// tear down asynchronously, leaving IsRunning briefly true.
resp := s.buildSessionResponse()
resp.Status = oapi.CaptureSessionStatusStopped
// Session cleanup (Remove on the S2 producer) happens automatically in
// EventsStorageWriter.Run when it processes the SessionEnded event, ensuring
// all pending writes are flushed before the producer is torn down.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment feels out of place. remove or move closer to the things it's talking about?

s.captureSession.Stop()

return oapi.StopCaptureSession200JSONResponse(resp), nil
Expand Down Expand Up @@ -120,26 +122,26 @@ func (s *ApiService) buildSessionResponse() oapi.CaptureSession {
}

// captureConfigFrom converts the optional StartCaptureSessionRequest body
// into a capturesession.CaptureConfig.
func captureConfigFrom(body *oapi.StartCaptureSessionRequest) (capturesession.CaptureConfig, error) {
// into an events.CaptureConfig.
func captureConfigFrom(body *oapi.StartCaptureSessionRequest) (events.CaptureConfig, error) {
if body == nil {
return capturesession.CaptureConfig{}, nil
return events.CaptureConfig{}, nil
}
return captureConfigFromOAPI(body.Config)
}

// captureConfigFromOAPI converts an oapi.CaptureConfig to capturesession.CaptureConfig.
func captureConfigFromOAPI(cfg *oapi.CaptureConfig) (capturesession.CaptureConfig, error) {
// captureConfigFromOAPI converts an oapi.CaptureConfig to events.CaptureConfig.
func captureConfigFromOAPI(cfg *oapi.CaptureConfig) (events.CaptureConfig, error) {
if cfg == nil || cfg.Categories == nil {
return capturesession.CaptureConfig{}, nil
return events.CaptureConfig{}, nil
}
out := capturesession.CaptureConfig{
out := events.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)
return events.CaptureConfig{}, fmt.Errorf("unknown category: %q", c)
}
out.Categories = append(out.Categories, cat)
}
Expand Down
6 changes: 4 additions & 2 deletions server/cmd/api/api/capture_session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,11 @@ func (m *mockRecordManager) ListActiveRecorders(_ context.Context) []recorder.Re
func (m *mockRecordManager) StopAll(_ context.Context) error { return nil }

// newTestService builds an ApiService with minimal dependencies for capture session tests.
// The RemoveSession path (triggered by SessionEnded events via the writer's Run loop) is
// not exercised here — it lives in eventsstorage_writer_test.go.
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)
svc, err := New(mgr, newMockFactory(), newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)
svc.cdpMonitor = &stubCdpMonitor{}
return svc
Expand All @@ -258,3 +259,4 @@ type stubCdpMonitor struct{}
func (s *stubCdpMonitor) Start(_ context.Context) error { return nil }
func (s *stubCdpMonitor) Stop() {}
func (s *stubCdpMonitor) IsRunning() bool { return false }

3 changes: 1 addition & 2 deletions server/cmd/api/api/display_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ 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)
svc, err := New(mgr, factory, newTestUpstreamManager(), scaletozero.NewNoopController(), newMockNekoClient(t), newCaptureSession(t), 0)
require.NoError(t, err)
return svc
}
Expand Down
Loading
Loading