Files
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
..

dev-browser benchmarks

Measures the numbers from docs/design-decisions.md §8 against an isolated DEV_BROWSER_HOME temp dir and a warm headless daemon. Everything is cleaned up at the end (daemon stopped, temp home removed), also on error.

bun run bench/run.ts [--check] [--bin PATH] [--runs N]
  • Uses dist/dev-browser when it exists (bun run build), else bun run src/cli/main.ts (slower cold start and per-call client time). --bin overrides. The chosen binary is printed first; --bin dev forces the bun run path.
  • --runs N is the sample count per item (default 15); the table shows medians.
  • --check exits 1 if any measured item is over its target. Items that are skipped because the API is not implemented yet count as pass.
item what is timed target
1+1 wall time of one dev-browser --headless -e '1+1' process 25 ms
getPage+title wall time, getPage("bench") + title() 40 ms
evaluate(()=>1) wall time, getPage + one evaluate 40 ms (informal)
snapshot small page.snapshot() timed inside the script, ~3 KB page, ~60 elements 30 ms
snapshot large same, SERP-like page (~1500 nodes, 300 links, 100 buttons) 150 ms
shot viewport page.shot() timed inside the script 120 ms
per-call overhead (50x await page.evaluate(()=>1) in a script / 50) minus the same loop via raw puppeteer.connect to the daemon's Chrome 0.1 ms
cold start stop daemon, remove socket, time the first 1+1 (daemon spawn + Chrome launch), median of 3 1500 ms

Fixtures live in bench/fixtures/ (small.ts, serp.ts) and are served by a local Bun.serve on 127.0.0.1. bench/fixtures/fixtures.test.ts pins their size (bun test bench/fixtures).

In-process micro-harness

bun run bench/inproc.ts [--runs N]

Calls runScript from src/daemon/run.ts directly with a fake emit and a BrowserManager: no socket, no client process. Compare its 1+1 median with the 1+1 row of bench/run.ts to see how much of the end-to-end time is the client (process start + socket round trip) vs the daemon.

CI

CI runs bun run build && bun run bench/run.ts --check on Linux (headless Chrome, --no-sandbox is applied automatically when the sandbox is unavailable). Medians are noisy on shared runners; bump --runs rather than the targets if it flakes.