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

73 lines
3.3 KiB
JavaScript
Executable File

#!/bin/sh
':' //; exec "$(command -v node || command -v bun)" "$0" "$@"
// ^ sh/JS polyglot: runs under node when present, else bun (bun-only machines
// that installed with `bun add -g`, where the postinstall was blocked).
// npm shim. postinstall downloads the native binary next to this file as
// bin/dev-browser-bin and, for global installs, re-points the global `dev-browser`
// symlink at the binary so Node is not on the hot path. This shim is the
// fallback: it runs the binary, downloads it first if the install step was
// skipped (bun / pnpm / --ignore-scripts / offline at install time), or runs
// the TypeScript entry with bun in a git checkout.
"use strict";
const { spawnSync } = require("node:child_process");
const fs = require("node:fs");
const dl = require("../scripts/download-binary.cjs");
const args = process.argv.slice(2);
function runBinary() {
const bin = dl.binPath;
const r = spawnSync(bin, args, { stdio: "inherit" });
if (r.status === null) {
// Could not run (musl vs glibc, CPU without AVX2, noexec mount, truncated download) or died by signal.
const why = r.error ? r.error.message : r.signal ? `killed by ${r.signal}` : "unknown error";
console.error(`dev-browser: could not run the prebuilt binary ${bin}: ${why}`);
console.error(
"dev-browser: hints: check `file " + bin + "` matches this OS/CPU (glibc x64/arm64 and macOS builds only; no musl/baseline yet);\n" +
" if the download was truncated, delete it and rerun `dev-browser` (it re-downloads) or `npm rebuild -g dev-browser`;\n" +
" on a noexec mount or unsupported CPU: git clone https://github.com/" + dl.REPO + " && bun run build" +
(fs.existsSync(dl.devEntry) ? "\n (dev checkout: remove " + bin + " and the shim runs " + dl.devEntry + " with bun)." : "."),
);
process.exit(1);
}
process.exit(r.status);
}
function runDev() {
const main = dl.devEntry;
const r = spawnSync("bun", [main, ...args], { stdio: "inherit" });
if (r.error && r.error.code === "ENOENT") {
console.error(`dev-browser: no binary at ${dl.binPath} and bun not found to run ${main}. Install bun or run \`bun run build\`.`);
process.exit(1);
}
if (r.status === null) {
console.error(`dev-browser: bun failed to run ${main}: ${r.error ? r.error.message : r.signal ? `killed by ${r.signal}` : "unknown error"}`);
process.exit(1);
}
process.exit(r.status);
}
if (fs.existsSync(dl.binPath)) runBinary();
else if (fs.existsSync(dl.devEntry)) runDev();
else if (process.env.DEV_BROWSER_SKIP_DOWNLOAD) {
console.error(`dev-browser: native binary missing at ${dl.binPath} and DEV_BROWSER_SKIP_DOWNLOAD is set, so it will not be downloaded.`);
console.error(dl.manualHint());
process.exit(1);
} else {
// Self-heal: the install script did not run (or failed). Fetch the binary now, then run it.
const plat = dl.platformAsset();
if (plat.error) {
console.error(`dev-browser: ${plat.error}.`);
process.exit(1);
}
console.error(`dev-browser: downloading binary v${dl.VERSION}...`);
dl.downloadBinary()
.then(() => runBinary())
.catch((err) => {
console.error(`dev-browser: could not download the binary (${err.message}).`);
console.error("dev-browser: the binary is fetched on first run; if you are offline, retry when connected, or:");
console.error(dl.manualHint());
process.exit(1);
});
}