1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
import { readFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
import { connect as netConnect } from "node:net";
import { type Result, err, ok } from "../util/result";
import { type CdpError, cdpError } from "./errors";
const PORT_PROBE_DEADLINE_MS = 30_000;
const PORT_PROBE_INTERVAL_MS = 1_000;
const profileDirs = (): ReadonlyArray<string> => {
const home = homedir();
return [
join(home, "Library/Application Support/Google/Chrome"),
join(home, "Library/Application Support/Microsoft Edge"),
join(home, "Library/Application Support/Microsoft Edge Beta"),
join(home, "Library/Application Support/Microsoft Edge Dev"),
join(home, "Library/Application Support/Microsoft Edge Canary"),
join(home, ".config/google-chrome"),
join(home, ".config/chromium"),
join(home, ".config/chromium-browser"),
join(home, ".config/microsoft-edge"),
join(home, ".config/microsoft-edge-beta"),
join(home, ".config/microsoft-edge-dev"),
join(home, ".var/app/org.chromium.Chromium/config/chromium"),
join(home, ".var/app/com.google.Chrome/config/google-chrome"),
join(home, ".var/app/com.brave.Browser/config/BraveSoftware/Brave-Browser"),
join(home, ".var/app/com.microsoft.Edge/config/microsoft-edge"),
join(home, "AppData/Local/Google/Chrome/User Data"),
join(home, "AppData/Local/Chromium/User Data"),
join(home, "AppData/Local/Microsoft/Edge/User Data"),
join(home, "AppData/Local/Microsoft/Edge Beta/User Data"),
join(home, "AppData/Local/Microsoft/Edge Dev/User Data"),
join(home, "AppData/Local/Microsoft/Edge SxS/User Data"),
];
};
const probePort = (port: number): Promise<Result<void, CdpError>> =>
new Promise((resolve) => {
const sock = netConnect({ host: "127.0.0.1", port });
let settled = false;
const finish = (r: Result<void, CdpError>): void => {
if (settled) return;
settled = true;
sock.setTimeout(0);
sock.destroy();
resolve(r);
};
sock.setTimeout(1000, () => finish(err(cdpError("discovery_failed", "probe timeout"))));
sock.once("error", (e) => finish(err(cdpError("discovery_failed", e.message))));
sock.once("connect", () => finish(ok(undefined)));
});
const waitForPort = async (port: number): Promise<Result<void, CdpError>> => {
const end = Date.now() + PORT_PROBE_DEADLINE_MS;
let lastMessage = "unknown";
while (Date.now() < end) {
const probe = await probePort(port);
if (probe.success) return probe;
lastMessage = probe.error.message;
await new Promise((r) => setTimeout(r, PORT_PROBE_INTERVAL_MS));
}
return err(cdpError(
"discovery_failed",
`Chrome's remote-debugging page is open, but DevTools is not live yet on 127.0.0.1:${port} — if Chrome opened a profile picker, choose your normal profile first, then tick the checkbox and click Allow if shown (last error: ${lastMessage})`,
));
};
export const discoverWsUrl = async (): Promise<Result<string, CdpError>> => {
const dirs = profileDirs();
for (const base of dirs) {
const portFile = join(base, "DevToolsActivePort");
let raw: string;
try {
raw = await readFile(portFile, "utf8");
} catch (e) {
// Node fs errors carry .code; see ErrnoException
const code = (e as NodeJS.ErrnoException).code;
if (code === "ENOENT" || code === undefined) continue;
return err(cdpError("discovery_failed", `failed to read ${portFile}: ${e instanceof Error ? e.message : String(e)}`));
}
const lines = raw.trim().split("\n");
if (lines.length < 2) continue;
const port = lines[0]?.trim();
const path = lines[1]?.trim();
if (!port || !path) continue;
const ready = await waitForPort(Number(port));
if (!ready.success) return ready;
return ok(`ws://127.0.0.1:${port}${path}`);
}
return err(cdpError(
"discovery_failed",
`DevToolsActivePort not found in ${dirs.join(", ")} — open chrome://inspect/#remote-debugging in your browser, tick the checkbox, click Allow, then retry. Or set BU_CDP_WS to a remote browser endpoint.`,
));
};
|