Files
sawyerhood__dev-browser/docs/bb-integration.md
Sawyer Hood 16dec89cfe feat!: ship doobie as dev-browser 1.0 (#133)
* doobie: skeleton — client, daemon, vm runner, page helpers, build

Co-Authored-By: Claude <noreply@anthropic.com>

* doobie: snapshot engine, waitForLoad, docs, tests, bench, Bun-native fast client

Co-Authored-By: Claude <noreply@anthropic.com>

* doobie: fix round 1 from adversarial review (transport backpressure, profiles per mode, page mutex, bringToFront, zombie runs, shot DPR, dialogs, snapshot names/frames/boxes, docs)

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: handoff notes

Co-Authored-By: Claude <noreply@anthropic.com>

* launch: mark clean exit to skip session restore, cache --no-sandbox; untrack build artifact

Co-Authored-By: Claude <noreply@anthropic.com>

* snapshot: drop [cursor=pointer] on inherently interactive roles, no row content names (HN interactive 29k -> 21k chars)

Co-Authored-By: Claude <noreply@anthropic.com>

* ci: bench tolerance on shared runners; release: npm publish only with NPM_TOKEN

Co-Authored-By: Claude <noreply@anthropic.com>

* mcp: stdio MCP server over the daemon frames (doobie mcp); ci: explicit test timeout

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: bb integration note (socket source contract)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix round 2: front lock (no stale cache), run gate covers handles/frames/popups, --connect extends only touched tabs, doobie chrome verifies launch, self-healing shim + devDependencies, snapshot name fallbacks/pointer inheritance/shadow refs, docs

Co-Authored-By: Claude <noreply@anthropic.com>

* daemon: drain active requests before exit; tests: realpath-safe and node-optional packaging tests; chrome: share sandbox helpers

Co-Authored-By: Claude <noreply@anthropic.com>

* relative file paths resolve against the caller's cwd; readFile("downloads/<name>"); TimeoutError for ref waits

Co-Authored-By: Claude <noreply@anthropic.com>

* runtime: share host Error constructors with the script realm

Co-Authored-By: Claude <noreply@anthropic.com>

* shim: sh/JS polyglot so bun-only machines run it; v0.1.1

Co-Authored-By: Claude <noreply@anthropic.com>

* launch: automation profile prefs (leak-detection dialog off, no password/autofill UI) — fixes dead input after logins in new headless

Co-Authored-By: Claude <noreply@anthropic.com>

* v0.1.2

Co-Authored-By: Claude <noreply@anthropic.com>

* client: retry when racing a shutting-down daemon (flaky stop/status on CI)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat!: make doobie the dev-browser 1.0 runtime

* ci: use Node 24 GitHub actions

* fix: isolate authenticated CDP sessions

* fix: close run gate escape paths

* fix: preserve UTF-8 across protocol chunks

* test: stabilize browser context gate coverage

* test: isolate browser context gate coverage

---------

Co-authored-by: Sawyer Hood <kirbyhood@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-03 17:21:40 -07:00

2.2 KiB

Driving a host-owned Chromium (bb in-app browser) with dev-browser

dev-browser can attach to any Chromium that a host application owns, without a devtools port, through the socket source:

dev-browser --connect unix:/path/to/cdp.sock -e '...'

Contract (host side)

  • Listen on a Unix domain socket (or a Windows named pipe, pipe:NAME).
  • Each connection is one browser-level CDP session: every line the client sends is one JSON CDP message ({"id":1,"method":"Target.getTargets"}), every line the host sends back is one JSON CDP message (responses and events), exactly as they would travel over ws://.../devtools/browser/<id>.
  • Newline-delimited JSON; no framing beyond \n. Messages may be large (screenshots); do not cap lines below ~50 MB.
  • The host must support the browser-level domains Puppeteer uses on connect: Target.setDiscoverTargets, Target.setAutoAttach (flatten mode), Target.attachToTarget, Target.getTargets, Target.createTarget (for browser.newPage()), Target.closeTarget, Browser.getVersion, and it must multiplex page sessions with sessionId like Chrome does. The simplest implementation is a proxy to the real devtools websocket of the embedded Chromium (see test/socket-source.test.ts for a 40-line proxy).
  • For Electron WebContentsView (bb), the intended path is webContents.debugger.attach("1.3") per view plus a small browser-level target table that maps each view to a targetId and answers the Target.* calls above, forwarding page-level messages to the right webContents.debugger. Only expose the views you want the agent to drive (never the trusted UI renderer).

Behavior on the dev-browser side

  • The browser key is socket:unix:/path; named pages live under that key in ~/.dev-browser/v1/pages/.
  • Nothing is launched or killed; dev-browser stop socket:unix:/path only disconnects.
  • All page helpers (snapshot, ref/, shot, waitForLoad, fill) work unchanged because they only need a Puppeteer Page.
  • Downloads and the idle reaper are not configured for socket browsers.

Verified

test/socket-source.test.ts stands up a Unix-socket ↔ devtools-websocket proxy in front of a headless Chrome and drives it with --connect unix:....