mirror of
https://github.com/SawyerHood/dev-browser.git
synced 2026-09-20 13:44:44 +08:00
16dec89cfe
* 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>
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
/**
|
|
* Socket source: raw CDP JSON lines over a Unix socket. We stand up a proxy
|
|
* that bridges lines <-> Chrome's devtools websocket, the way a host app
|
|
* (e.g. bb) would expose its own Chromium, then drive it with --connect unix:.
|
|
*/
|
|
import { test, expect, afterAll, beforeAll } from "bun:test";
|
|
import * as net from "node:net";
|
|
import * as fs from "node:fs";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import { getBrowser, closeBrowser } from "./helpers/browser.ts";
|
|
import { makeCliEnv, type CliEnv } from "./helpers/cli.ts";
|
|
|
|
let cli: CliEnv;
|
|
let proxy: net.Server;
|
|
let sockPath: string;
|
|
|
|
beforeAll(async () => {
|
|
cli = makeCliEnv("dev-browser-sock-");
|
|
const browser = await getBrowser();
|
|
const wsUrl = browser.wsEndpoint();
|
|
sockPath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "dev-browser-cdp-")), "cdp.sock");
|
|
proxy = net.createServer((sock) => {
|
|
const ws = new WebSocket(wsUrl);
|
|
let buf = "";
|
|
const pending: string[] = [];
|
|
ws.onopen = () => {
|
|
for (const m of pending) ws.send(m);
|
|
pending.length = 0;
|
|
};
|
|
ws.onmessage = (ev) => {
|
|
sock.write(String(ev.data) + "\n");
|
|
};
|
|
ws.onclose = () => sock.end();
|
|
sock.setEncoding("utf8");
|
|
sock.on("data", (chunk: string) => {
|
|
buf += chunk;
|
|
let i: number;
|
|
while ((i = buf.indexOf("\n")) >= 0) {
|
|
const line = buf.slice(0, i);
|
|
buf = buf.slice(i + 1);
|
|
if (!line) continue;
|
|
if (ws.readyState === WebSocket.OPEN) ws.send(line);
|
|
else pending.push(line);
|
|
}
|
|
});
|
|
sock.on("close", () => ws.close());
|
|
sock.on("error", () => ws.close());
|
|
});
|
|
await new Promise<void>((r) => proxy.listen(sockPath, r));
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await cli.cleanup();
|
|
proxy.close();
|
|
await closeBrowser();
|
|
});
|
|
|
|
test("--connect unix:/path drives a browser over raw CDP lines", async () => {
|
|
const r = await cli.run([
|
|
"--connect",
|
|
`unix:${sockPath}`,
|
|
"-e",
|
|
'const p = await browser.getPage("sock"); await p.setContent("<title>via socket</title>"); await p.title()',
|
|
]);
|
|
expect(r.stderr).toBe("");
|
|
expect(r.stdout.trim()).toBe("via socket");
|
|
expect(r.code).toBe(0);
|
|
const b = await cli.run(["browsers"]);
|
|
expect(b.stdout).toContain(`socket:unix:${sockPath}`);
|
|
});
|