Skip to content

feat(live): human live view via CDP screencast over SSE (C1) - #39

Merged
fusengine merged 1 commit into
mainfrom
feat/c1-live-view
Jun 4, 2026
Merged

feat(live): human live view via CDP screencast over SSE (C1)#39
fusengine merged 1 commit into
mainfrom
feat/c1-live-view

Conversation

@fusengine

Copy link
Copy Markdown
Owner

What

C1 — browser_live_view / browser_live_view_stop (31st/32nd tools). Watch a session's browser in real time from a normal web page — works even headless.

  • src/live/screencast.tspage.context().newCDPSession(page)Page.startScreencast (JPEG); acks each frame with its own sessionId (ack-first, or the stream stalls); re-issues on load (screencast stops on hard nav); returns a stop fn.
  • src/live/sse-server.ts — ephemeral node:http server bound to 127.0.0.1:0, token-gated; /stream = text/event-stream of base64 JPEG frames, / = the viewer. Zero new deps (SSE, no ws).
  • src/live/viewer-html.ts — self-contained <img>/EventSource viewer.
  • src/live/open-url.ts — best-effort OS opener.
  • src/live/manager.ts — one live view per session; replace-on-restart; auto-stop on page.once('close').

Read-only (no click-through) in v1. Returns a 127.0.0.1 URL with an embedded token; auto-opens unless open:false.

Robustness

Two sniper passes. Fixed a real process-crash: broadcast() now guards writableEnded/destroyed and consumes async EPIPE/ECONNRESET in the res.write callback, so a viewer disconnecting mid-stream cannot crash the MCP process. Documented (@remarks) that the screencast does not auto-reattach after a B1 page recovery (call browser_live_view again).

Security

Loopback-only bind, 128-bit random token validated on every request, auto-close on page close / stop / explicit teardown.

Validation

npm run typecheck   # clean
npm test            # 141 pass / 0 fail
node --test live-view.test.ts   # real screencast → SSE → frame asserted, stop idempotent
mcp.test.ts         # 32 tools, no duplicates

Docs: new Live view section in docs/mcp-tools.md + tool count 30→32 across README/docs.

Closes #29

New browser_live_view / browser_live_view_stop tools (31st/32nd). A CDP
screencast (page.context().newCDPSession → Page.startScreencast, ack-per-frame,
re-issued on navigation) streams JPEG frames over an ephemeral, token-gated
127.0.0.1 Server-Sent-Events server to a self-contained <canvas> viewer that
auto-opens in the OS browser. Read-only; works for headless sessions too.
Auto-closes on page close or stop. Zero new dependencies (node:http + SSE).

broadcast() guards writableEnded/destroyed and handles async EPIPE/ECONNRESET
in the write callback so a viewer disconnecting mid-stream can't crash the MCP
process. Real screencast→SSE→frame integration test.

Closes #29
@fusengine
fusengine merged commit d383232 into main Jun 4, 2026
1 check passed
@fusengine
fusengine deleted the feat/c1-live-view branch June 4, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C1 — Human live view (CDP screencast)

1 participant