cdp-mcp 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +28 -303
  2. package/bin.js +23 -0
  3. package/contract.d.ts +1 -0
  4. package/contract.js +2 -0
  5. package/index.d.ts +1 -0
  6. package/index.js +2 -0
  7. package/package.json +31 -73
  8. package/dist/contract.d.ts +0 -11
  9. package/dist/contract.js +0 -11
  10. package/dist/contract.js.map +0 -1
  11. package/dist/index.d.ts +0 -18
  12. package/dist/index.js +0 -402
  13. package/dist/index.js.map +0 -1
  14. package/dist/locator.d.ts +0 -108
  15. package/dist/locator.js +0 -176
  16. package/dist/locator.js.map +0 -1
  17. package/dist/server.d.ts +0 -2
  18. package/dist/server.js +0 -41
  19. package/dist/server.js.map +0 -1
  20. package/dist/session/browser.d.ts +0 -29
  21. package/dist/session/browser.js +0 -409
  22. package/dist/session/browser.js.map +0 -1
  23. package/dist/session/buffers.d.ts +0 -48
  24. package/dist/session/buffers.js +0 -43
  25. package/dist/session/buffers.js.map +0 -1
  26. package/dist/session/pause.d.ts +0 -21
  27. package/dist/session/pause.js +0 -99
  28. package/dist/session/pause.js.map +0 -1
  29. package/dist/session/state.d.ts +0 -53
  30. package/dist/session/state.js +0 -93
  31. package/dist/session/state.js.map +0 -1
  32. package/dist/sourcemap/loader.d.ts +0 -4
  33. package/dist/sourcemap/loader.js +0 -138
  34. package/dist/sourcemap/loader.js.map +0 -1
  35. package/dist/sourcemap/normalize.d.ts +0 -2
  36. package/dist/sourcemap/normalize.js +0 -59
  37. package/dist/sourcemap/normalize.js.map +0 -1
  38. package/dist/sourcemap/store.d.ts +0 -57
  39. package/dist/sourcemap/store.js +0 -185
  40. package/dist/sourcemap/store.js.map +0 -1
  41. package/dist/tools/_locator_runtime.d.ts +0 -31
  42. package/dist/tools/_locator_runtime.js +0 -243
  43. package/dist/tools/_locator_runtime.js.map +0 -1
  44. package/dist/tools/_register.d.ts +0 -2
  45. package/dist/tools/_register.js +0 -30
  46. package/dist/tools/_register.js.map +0 -1
  47. package/dist/tools/breakpoints.d.ts +0 -4
  48. package/dist/tools/breakpoints.js +0 -164
  49. package/dist/tools/breakpoints.js.map +0 -1
  50. package/dist/tools/console.d.ts +0 -2
  51. package/dist/tools/console.js +0 -48
  52. package/dist/tools/console.js.map +0 -1
  53. package/dist/tools/dom.d.ts +0 -2
  54. package/dist/tools/dom.js +0 -309
  55. package/dist/tools/dom.js.map +0 -1
  56. package/dist/tools/execution.d.ts +0 -29
  57. package/dist/tools/execution.js +0 -89
  58. package/dist/tools/execution.js.map +0 -1
  59. package/dist/tools/forms.d.ts +0 -8
  60. package/dist/tools/forms.js +0 -256
  61. package/dist/tools/forms.js.map +0 -1
  62. package/dist/tools/inspect.d.ts +0 -2
  63. package/dist/tools/inspect.js +0 -178
  64. package/dist/tools/inspect.js.map +0 -1
  65. package/dist/tools/nav.d.ts +0 -2
  66. package/dist/tools/nav.js +0 -136
  67. package/dist/tools/nav.js.map +0 -1
  68. package/dist/tools/network.d.ts +0 -2
  69. package/dist/tools/network.js +0 -137
  70. package/dist/tools/network.js.map +0 -1
  71. package/dist/tools/session.d.ts +0 -2
  72. package/dist/tools/session.js +0 -76
  73. package/dist/tools/session.js.map +0 -1
  74. package/dist/tools/source.d.ts +0 -2
  75. package/dist/tools/source.js +0 -63
  76. package/dist/tools/source.js.map +0 -1
  77. package/dist/tools/storage.d.ts +0 -2
  78. package/dist/tools/storage.js +0 -296
  79. package/dist/tools/storage.js.map +0 -1
  80. package/dist/util/browser-resolve.d.ts +0 -19
  81. package/dist/util/browser-resolve.js +0 -263
  82. package/dist/util/browser-resolve.js.map +0 -1
  83. package/dist/util/errors.d.ts +0 -7
  84. package/dist/util/errors.js +0 -12
  85. package/dist/util/errors.js.map +0 -1
  86. package/dist/util/format.d.ts +0 -20
  87. package/dist/util/format.js +0 -65
  88. package/dist/util/format.js.map +0 -1
  89. package/dist/util/log.d.ts +0 -6
  90. package/dist/util/log.js +0 -34
  91. package/dist/util/log.js.map +0 -1
  92. package/docs/chromium-sandboxing.md +0 -197
  93. package/docs/known-chromium-gaps.md +0 -138
  94. package/docs/launchd-service.md +0 -217
  95. package/docs/local-l3-e2e-setup.md +0 -199
  96. package/docs/systemd-service.md +0 -233
@@ -1,296 +0,0 @@
1
- import { z } from "zod";
2
- import { readFile, writeFile, rename, rm } from "node:fs/promises";
3
- import { randomBytes } from "node:crypto";
4
- import { requireSession } from "../session/state.js";
5
- import { ToolError } from "../util/errors.js";
6
- import { registerJsonTool } from "./_register.js";
7
- /**
8
- * Session-portability tools (issue #12 items 4, 5).
9
- *
10
- * Cookies go through CDP `Network.*` (NOT `document.cookie`) so HttpOnly
11
- * auth/session cookies — the whole reason to resume a session — round-trip.
12
- * localStorage is read/written via `Runtime.evaluate` (the `Storage.*` domain
13
- * is out of scope per AGENTS.md). The on-disk shape is the de-facto Playwright
14
- * `storageState` JSON so a flow can be handed to/from mainstream tooling.
15
- *
16
- * v1 localStorage scope is the **current page origin** only: localStorage is
17
- * origin-partitioned and CDP can only read/write it for a document that is
18
- * actually on that origin. Cookies (the load-bearing part for auth resume) are
19
- * captured/restored for all origins. Multi-origin localStorage restore would
20
- * require navigating to each origin first; origins in the file that don't match
21
- * the current page are reported in `origins_skipped` rather than silently lost.
22
- *
23
- * File read/write mirrors `screenshot path=`: no extra path filter — the
24
- * operator gate is the same `--allow-remote`/non-loopback rule (README §SSE).
25
- */
26
- const SENSITIVE_NAME = /(sess|sid|token|auth|csrf|xsrf|jwt|secret|api[-_]?key)/i;
27
- const sameSiteSchema = z.enum(["Strict", "Lax", "None"]);
28
- const storageStateSchema = z.object({
29
- cookies: z
30
- .array(z.object({
31
- name: z.string(),
32
- value: z.string(),
33
- domain: z.string(),
34
- path: z.string(),
35
- expires: z.number().optional(),
36
- httpOnly: z.boolean().optional(),
37
- secure: z.boolean().optional(),
38
- sameSite: sameSiteSchema.optional(),
39
- }))
40
- .default([]),
41
- origins: z
42
- .array(z.object({
43
- origin: z.string(),
44
- localStorage: z.array(z.object({ name: z.string(), value: z.string() })).default([]),
45
- }))
46
- .default([]),
47
- });
48
- const cookieParamSchema = z.object({
49
- name: z.string(),
50
- value: z.string(),
51
- url: z.string().optional().describe("Cookie URL (provide this OR domain)."),
52
- domain: z.string().optional(),
53
- path: z.string().optional(),
54
- secure: z.boolean().optional(),
55
- httpOnly: z.boolean().optional(),
56
- sameSite: sameSiteSchema.optional(),
57
- expires: z.number().optional().describe("Expiry as seconds since epoch; omit for a session cookie."),
58
- });
59
- /**
60
- * Write `data` to `dest` at mode 0o600, overwrite-safe and TOCTOU-safe.
61
- *
62
- * The storage-state file holds plaintext cookie secrets, so the 0o600
63
- * postcondition must hold even on a shared, world-writable directory. Two traps:
64
- * - Node's writeFile `mode` only applies when it *creates* the file, so writing
65
- * straight to `dest` would leave an existing 0644 file world-readable.
66
- * - A *predictable* temp path lets an attacker pre-create (or symlink) it at
67
- * 0644, so the secret gets written through their file and renamed into place.
68
- *
69
- * So: create a temp with an **unpredictable** same-directory suffix using
70
- * exclusive create (`flag: "wx"` ⇒ O_CREAT|O_EXCL — fails on any pre-existing
71
- * file and refuses to follow a symlink), which makes 0o600 a real creation-time
72
- * guarantee, then atomically rename it over the destination (same filesystem, so
73
- * the secret never exists at the destination path in a half-written/0644 state).
74
- * Retry on the astronomically-unlikely name collision.
75
- */
76
- async function writeSecretFileAtomic(dest, data) {
77
- let lastErr;
78
- for (let attempt = 0; attempt < 5; attempt++) {
79
- const tmp = `${dest}.${randomBytes(8).toString("hex")}.tmp`;
80
- try {
81
- await writeFile(tmp, data, { flag: "wx", mode: 0o600 });
82
- }
83
- catch (e) {
84
- if (e.code === "EEXIST") {
85
- lastErr = e;
86
- continue; // collision (or planted file/symlink) — try a fresh random name
87
- }
88
- throw e; // ENOENT (missing parent dir), EACCES, etc.
89
- }
90
- try {
91
- await rename(tmp, dest);
92
- }
93
- catch (e) {
94
- await rm(tmp, { force: true }).catch(() => { });
95
- throw e;
96
- }
97
- return;
98
- }
99
- throw lastErr;
100
- }
101
- export function registerStorageTools(server) {
102
- registerJsonTool(server, "export_storage_state", "Save the browser session (all cookies including HttpOnly, plus the current page origin's localStorage) to a JSON file in the de-facto Playwright storageState shape, so a flow can be resumed in a fresh session or handed to other tooling. The file preserves full cookie values (it exists to resume auth) — treat it as a secret. File write is gated by the same operator rule as screenshot path= / --allow-remote.", { path: z.string().describe("Absolute path to write the storageState JSON to.") }, async (input) => {
103
- const s = requireSession();
104
- const { cookies } = (await s.client.send("Network.getAllCookies"));
105
- const probe = await s.client.send("Runtime.evaluate", {
106
- expression: READ_ORIGIN_AND_LOCALSTORAGE,
107
- returnByValue: true,
108
- });
109
- const probed = (probe.result?.value ?? { origin: "null", localStorage: [] });
110
- const origins = probed.origin && probed.origin !== "null"
111
- ? [{ origin: probed.origin, localStorage: probed.localStorage }]
112
- : [];
113
- const state = { cookies: cookies.map(cdpCookieToState), origins };
114
- // The file holds full cookie values (incl. HttpOnly auth/session tokens) —
115
- // the description says "treat it as a secret". writeSecretFileAtomic writes
116
- // it 0o600 and overwrite-safe (see the helper for the threat model).
117
- try {
118
- await writeSecretFileAtomic(input.path, JSON.stringify(state, null, 2));
119
- }
120
- catch (e) {
121
- if (e.code === "ENOENT") {
122
- throw new ToolError("not_found", `could not write storage-state file (does the parent directory exist?): ${e.message}`);
123
- }
124
- throw e; // EACCES/EISDIR/etc. surface as internal_error via registerJsonTool
125
- }
126
- return { saved: input.path, cookies: state.cookies.length, origins: origins.length };
127
- });
128
- registerJsonTool(server, "load_storage_state", "Restore a session from a Playwright-shaped storageState JSON file: sets all cookies (no navigation needed), then restores localStorage for the entries whose origin matches the current page. Cookies are added on top of the existing jar (additive, not a clean-context replace), so prefer a fresh session for Playwright-equivalent semantics. Origins that don't match the current page are returned in origins_skipped (restoring them would require navigating there first). File read is gated by the same operator rule as screenshot path= / --allow-remote.", { path: z.string().describe("Absolute path to a storageState JSON file (as written by export_storage_state).") }, async (input) => {
129
- const s = requireSession();
130
- let raw;
131
- try {
132
- raw = await readFile(input.path, "utf8");
133
- }
134
- catch (e) {
135
- if (e.code === "ENOENT") {
136
- throw new ToolError("not_found", `could not read storage-state file: ${e.message}`);
137
- }
138
- throw e; // EACCES/EISDIR/etc. surface as internal_error rather than masquerading as not_found
139
- }
140
- let parsed;
141
- try {
142
- parsed = JSON.parse(raw);
143
- }
144
- catch {
145
- throw new ToolError("invalid_arg", "storage-state file is not valid JSON");
146
- }
147
- const result = storageStateSchema.safeParse(parsed);
148
- if (!result.success) {
149
- throw new ToolError("invalid_arg", `storage-state file is not a valid storageState: ${result.error.message}`);
150
- }
151
- const state = result.data;
152
- if (state.cookies.length > 0) {
153
- await s.client.send("Network.setCookies", { cookies: state.cookies.map(stateCookieToCdp) });
154
- }
155
- const restore = await s.client.send("Runtime.evaluate", {
156
- expression: restoreLocalStorageExpr(state.origins),
157
- returnByValue: true,
158
- });
159
- const restored = (restore.result?.value ?? { restored: [], skipped: state.origins.map((o) => o.origin) });
160
- return {
161
- loaded: input.path,
162
- cookies: state.cookies.length,
163
- origins_restored: restored.restored,
164
- origins_skipped: restored.skipped,
165
- };
166
- });
167
- registerJsonTool(server, "get_cookies", "List cookies (via CDP, so HttpOnly cookies are included) with their flags. For safe printing, the VALUE of likely session/auth cookies is redacted (httpOnly cookies, or names matching sess/sid/token/auth/csrf/jwt/secret/api-key); only value_length is shown for those. Full values are obtainable only through export_storage_state.", {
168
- urls: z
169
- .array(z.string())
170
- .optional()
171
- .describe("Restrict to cookies visible to these URLs; omit for all cookies in the browser."),
172
- }, async (input) => {
173
- const s = requireSession();
174
- const res = (input.urls && input.urls.length > 0
175
- ? await s.client.send("Network.getCookies", { urls: input.urls })
176
- : await s.client.send("Network.getAllCookies"));
177
- return { cookies: res.cookies.map(redactForDisplay) };
178
- });
179
- registerJsonTool(server, "set_cookies", "Set one or more cookies in the browser cookie jar via CDP. Each cookie needs a url OR a domain. For restoring a saved session, prefer load_storage_state; this is the lower-level primitive.", { cookies: z.array(cookieParamSchema).describe("Cookies to set.") }, async (input) => {
180
- const s = requireSession();
181
- if (input.cookies.length === 0)
182
- throw new ToolError("missing_arg", "cookies array is empty");
183
- const missing = input.cookies.find((c) => !c.url && !c.domain);
184
- if (missing)
185
- throw new ToolError("missing_arg", `cookie '${missing.name}' needs a url or domain`);
186
- // Normalize the session-cookie sentinel the same way load_storage_state
187
- // does, so an exported `expires: -1` piped in here isn't sent as a 1969 expiry.
188
- await s.client.send("Network.setCookies", {
189
- cookies: input.cookies.map(withSessionExpiryOmitted),
190
- });
191
- return { set: input.cookies.length };
192
- });
193
- }
194
- function cdpCookieToState(c) {
195
- return {
196
- name: c.name,
197
- value: c.value,
198
- domain: c.domain,
199
- path: c.path,
200
- // CDP uses -1 (or a missing field) for session cookies; Playwright too.
201
- expires: typeof c.expires === "number" ? c.expires : -1,
202
- httpOnly: !!c.httpOnly,
203
- secure: !!c.secure,
204
- // Playwright requires sameSite; CDP may omit it. Lax is the browser default.
205
- sameSite: c.sameSite ?? "Lax",
206
- };
207
- }
208
- /**
209
- * CDP's `Network.setCookies` reads `expires` as an absolute epoch-seconds
210
- * timestamp, so the session-cookie sentinel (`-1`, which `export_storage_state`
211
- * writes) must be dropped rather than forwarded — otherwise the cookie is set
212
- * already-expired (1969) and silently rejected. Shared by `load_storage_state`
213
- * and `set_cookies` so both restore paths normalize identically.
214
- */
215
- function withSessionExpiryOmitted(cookie) {
216
- if (typeof cookie.expires === "number" && cookie.expires >= 0)
217
- return cookie;
218
- const copy = { ...cookie };
219
- delete copy.expires;
220
- return copy;
221
- }
222
- function stateCookieToCdp(c) {
223
- const param = {
224
- name: c.name,
225
- value: c.value,
226
- domain: c.domain,
227
- path: c.path,
228
- secure: !!c.secure,
229
- httpOnly: !!c.httpOnly,
230
- expires: c.expires,
231
- };
232
- if (c.sameSite)
233
- param.sameSite = c.sameSite;
234
- // -1 / undefined means a session cookie — omit expires entirely.
235
- return withSessionExpiryOmitted(param);
236
- }
237
- function redactForDisplay(c) {
238
- const redacted = !!c.httpOnly || SENSITIVE_NAME.test(c.name);
239
- const out = {
240
- name: c.name,
241
- domain: c.domain,
242
- path: c.path,
243
- expires: typeof c.expires === "number" ? c.expires : -1,
244
- httpOnly: !!c.httpOnly,
245
- secure: !!c.secure,
246
- sameSite: c.sameSite ?? null,
247
- redacted,
248
- value_length: (c.value ?? "").length,
249
- };
250
- if (!redacted)
251
- out.value = c.value;
252
- return out;
253
- }
254
- // ---------------------------------------------------------------------------
255
- // In-page expressions
256
- /** Read the current origin and its localStorage as { origin, localStorage:[{name,value}] }. */
257
- const READ_ORIGIN_AND_LOCALSTORAGE = String.raw `(() => {
258
- const items = [];
259
- try {
260
- for (let i = 0; i < localStorage.length; i++) {
261
- const k = localStorage.key(i);
262
- if (k === null) continue;
263
- const v = localStorage.getItem(k);
264
- if (v === null) continue;
265
- items.push({ name: k, value: v });
266
- }
267
- } catch (e) {}
268
- return { origin: location.origin, localStorage: items };
269
- })()`;
270
- /**
271
- * For each origin in the file whose origin matches the current page, write its
272
- * localStorage entries; report which origins were restored vs skipped.
273
- */
274
- function restoreLocalStorageExpr(origins) {
275
- return String.raw `(() => {
276
- const origins = ${JSON.stringify(origins)};
277
- const current = location.origin;
278
- const restored = [];
279
- const skipped = [];
280
- for (const o of origins) {
281
- if (o.origin !== current) {
282
- skipped.push(o.origin);
283
- continue;
284
- }
285
- // If any setItem throws (quota exceeded, storage disabled), report the
286
- // origin as skipped rather than misleadingly claiming it was restored.
287
- let ok = true;
288
- for (const it of o.localStorage) {
289
- try { localStorage.setItem(it.name, it.value); } catch (e) { ok = false; }
290
- }
291
- (ok ? restored : skipped).push(o.origin);
292
- }
293
- return { restored, skipped };
294
- })()`;
295
- }
296
- //# sourceMappingURL=storage.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/tools/storage.ts"],"names":[],"mappings":"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"}
@@ -1,19 +0,0 @@
1
- export type BrowserChoice = "chromium" | "chrome";
2
- export interface ResolvedBrowser {
3
- /** Absolute path to the Chrome/Chromium executable. */
4
- binaryPath: string;
5
- /** Which logical browser this binary represents. */
6
- choice: BrowserChoice;
7
- /** True when the binary is snap-confined (/snap/bin/* on Linux). Triggers
8
- * the user-data-dir workaround in launch_chrome. */
9
- snapConfined: boolean;
10
- /** Diagnostic — which resolution step produced this. */
11
- source: "CDP_TEST_BROWSER_PATH" | "which-chromium" | "playwright-cache" | "chrome-launcher-default";
12
- }
13
- export declare function getBrowserChoice(): BrowserChoice;
14
- export declare function resolveBrowser(choice?: BrowserChoice): ResolvedBrowser;
15
- /** True when the given binary is the snap-marker returned by step 4. */
16
- export declare function isChromeLauncherDefault(b: ResolvedBrowser): boolean;
17
- /** Determine the user-data-dir for snap-confined Chromium. Snap confinement
18
- * rejects /tmp/... paths; only ~/snap/<app>/current/ is writable. */
19
- export declare function snapUserDataDir(binaryPath: string): string;
@@ -1,263 +0,0 @@
1
- // Shared Chromium/Chrome resolver used by BOTH the L3 e2e suite
2
- // (test/e2e/setup/browser-path.ts re-exports from here) AND the L4 eval
3
- // harness (evals/harness/runner.ts imports resolveBrowser() and feeds
4
- // the result into the spawned MCP server via CHROME_PATH). Single
5
- // resolution path so both layers cannot test against different
6
- // protocol versions — the intent already documented in
7
- // docs/test-eval-plan.md §L3 *Production code change required for
8
- // chromePath* and §L3 *CI matrix* ("the same helper is reused by the
9
- // eval harness").
10
- //
11
- // Resolution order (fail-fast — never silently fall back to chrome-
12
- // launcher's auto-detection, because that's how the wrong browser ends
13
- // up running and tests pass against the wrong protocol revision):
14
- // 1. CDP_TEST_BROWSER_PATH env — explicit override, used by CI after
15
- // `npx playwright install --with-deps chromium`. Wins everything.
16
- // 2. `which chromium` (Linux/macOS) / `where chromium.exe` (Windows) —
17
- // local dev path. Catches both /snap/bin/chromium and the apt
18
- // /usr/bin/chromium symlink.
19
- // 3. Playwright bundled cache — ~/.cache/ms-playwright/chromium-*/
20
- // chrome-linux/chrome (Linux), ~/Library/Caches/ms-playwright/...
21
- // (macOS), %LOCALAPPDATA%\ms-playwright\... (Windows). Handy after
22
- // a fresh `npx playwright install`.
23
- // 4. CDP_TEST_BROWSER=chrome — chrome-launcher's default detection.
24
- // 5. Fail with an actionable install hint.
25
- import { execSync } from "node:child_process";
26
- import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
27
- import { homedir, platform } from "node:os";
28
- import { join } from "node:path";
29
- export function getBrowserChoice() {
30
- const env = process.env.CDP_TEST_BROWSER?.toLowerCase();
31
- if (env === "chrome")
32
- return "chrome";
33
- if (env === "chromium" || env === undefined || env === "")
34
- return "chromium";
35
- throw new Error(`CDP_TEST_BROWSER must be 'chromium' or 'chrome' (got '${env}'). Default is 'chromium'.`);
36
- }
37
- export function resolveBrowser(choice = getBrowserChoice()) {
38
- // Step 1 — explicit override wins.
39
- const override = process.env.CDP_TEST_BROWSER_PATH;
40
- if (override) {
41
- if (!existsSync(override)) {
42
- throw new Error(`CDP_TEST_BROWSER_PATH='${override}' does not exist. Unset it or install the binary.`);
43
- }
44
- return {
45
- binaryPath: override,
46
- choice,
47
- snapConfined: override.startsWith("/snap/"),
48
- source: "CDP_TEST_BROWSER_PATH",
49
- };
50
- }
51
- // Step 2 — system-installed chromium (Linux/macOS via `which`, Windows via
52
- // `where`). Skip when choice is 'chrome' — Chrome detection is delegated to
53
- // chrome-launcher's defaults in step 4.
54
- //
55
- // On darwin we additionally skip the deprecated Homebrew cask `chromium`
56
- // wrapper at /opt/homebrew/bin/chromium (and any path whose realpath
57
- // resolves into /opt/homebrew/Caskroom/chromium/). That wrapper points
58
- // at an unsigned .app Gatekeeper rejects as "damaged" — it launches
59
- // successfully but the debug port never opens, so chrome-launcher's
60
- // startup-port poll ECONNREFUSEs. The same gap is precisely what the
61
- // §macOS arm64 entry in docs/known-chromium-gaps.md documents; skipping
62
- // it here is what makes that entry's "resolveBrowser picks it up
63
- // automatically" claim actually true for users who tried the cask first
64
- // before reading the doc. Cask deprecation removal is scheduled
65
- // 2026-09-01; once removed this skip becomes a no-op.
66
- if (choice === "chromium") {
67
- const sys = findOnPath("chromium") ?? findOnPath("chromium-browser");
68
- if (sys && !isBrewCaskChromium(sys)) {
69
- return {
70
- binaryPath: sys,
71
- choice,
72
- snapConfined: sys.startsWith("/snap/"),
73
- source: "which-chromium",
74
- };
75
- }
76
- }
77
- // Step 3 — Playwright's bundled cache. Used in CI after
78
- // `npx playwright install`; also useful locally when the system chromium
79
- // isn't installed.
80
- const pw = findPlaywrightChromium();
81
- if (pw && choice === "chromium") {
82
- return {
83
- binaryPath: pw,
84
- choice,
85
- snapConfined: false,
86
- source: "playwright-cache",
87
- };
88
- }
89
- // Step 4 — Chrome stable, only when explicitly requested. We don't return a
90
- // path here; the caller forwards `undefined` to chrome-launcher, which runs
91
- // its own detection. This is intentionally NOT used as a silent fallback
92
- // for chromium because the protocol revision can diverge.
93
- if (choice === "chrome") {
94
- // chrome-launcher uses its own which() — we just signal it should take
95
- // over by returning the marker path "chrome-launcher-default". Both
96
- // callers strip this back to undefined before forwarding the path: L3
97
- // global setup at test/e2e/setup/global.ts skips `chromePath` entirely,
98
- // and the L4 runner at evals/harness/runner.ts omits CHROME_PATH from
99
- // the subprocess env via isChromeLauncherDefault().
100
- return {
101
- binaryPath: "chrome-launcher-default",
102
- choice,
103
- snapConfined: false,
104
- source: "chrome-launcher-default",
105
- };
106
- }
107
- // Step 5 — actionable failure.
108
- const isArm = process.arch === "arm64";
109
- const installHint = isArm
110
- ? "sudo apt-get install chromium-browser # OR: npx playwright install chromium"
111
- : "sudo snap install chromium OR sudo apt-get install chromium-browser OR npx playwright install chromium";
112
- throw new Error(`Could not resolve a Chromium binary for the e2e suite.\n` +
113
- `Tried (in order): CDP_TEST_BROWSER_PATH env, which chromium, Playwright cache.\n` +
114
- `Install Chromium (${installHint}) or set CDP_TEST_BROWSER_PATH explicitly.`);
115
- }
116
- // Detect the deprecated Homebrew cask `chromium` wrapper on darwin. Returns
117
- // true for both the well-known /opt/homebrew/bin/chromium path and any path
118
- // whose realpath resolves into /opt/homebrew/Caskroom/chromium/ (covers
119
- // future brew layout shifts where the wrapper lives elsewhere but the
120
- // Caskroom prefix stays put). Never true off darwin.
121
- function isBrewCaskChromium(binaryPath) {
122
- if (platform() !== "darwin")
123
- return false;
124
- if (binaryPath === "/opt/homebrew/bin/chromium")
125
- return true;
126
- try {
127
- const resolved = realpathSync(binaryPath);
128
- return resolved.startsWith("/opt/homebrew/Caskroom/chromium/");
129
- }
130
- catch {
131
- return false;
132
- }
133
- }
134
- function findOnPath(cmd) {
135
- try {
136
- const which = platform() === "win32" ? "where" : "which";
137
- const out = execSync(`${which} ${cmd}`, { stdio: ["ignore", "pipe", "ignore"] })
138
- .toString()
139
- .split(/\r?\n/)
140
- .find((line) => line.trim().length > 0);
141
- if (!out)
142
- return null;
143
- const path = out.trim();
144
- return existsSync(path) ? path : null;
145
- }
146
- catch {
147
- return null;
148
- }
149
- }
150
- function findPlaywrightChromium() {
151
- const candidates = playwrightCacheDirs();
152
- for (const dir of candidates) {
153
- if (!existsSync(dir))
154
- continue;
155
- let entries;
156
- try {
157
- entries = readdirSync(dir);
158
- }
159
- catch {
160
- continue;
161
- }
162
- // Filter to chromium-* / chromium-headless-shell-* — pick the highest-
163
- // numbered revision (newest install).
164
- const chromiums = entries
165
- .filter((e) => e.startsWith("chromium-") && !e.startsWith("chromium-headless-shell-"))
166
- .map((e) => ({ name: e, full: join(dir, e) }))
167
- .filter((e) => {
168
- try {
169
- return statSync(e.full).isDirectory();
170
- }
171
- catch {
172
- return false;
173
- }
174
- })
175
- .sort((a, b) => b.name.localeCompare(a.name));
176
- for (const c of chromiums) {
177
- const exe = pickPlaywrightExe(c.full);
178
- if (exe)
179
- return exe;
180
- }
181
- }
182
- return null;
183
- }
184
- function playwrightCacheDirs() {
185
- const home = homedir();
186
- const plat = platform();
187
- if (plat === "linux")
188
- return [join(home, ".cache", "ms-playwright")];
189
- if (plat === "darwin")
190
- return [join(home, "Library", "Caches", "ms-playwright")];
191
- if (plat === "win32") {
192
- const local = process.env.LOCALAPPDATA ?? join(home, "AppData", "Local");
193
- return [join(local, "ms-playwright")];
194
- }
195
- return [join(home, ".cache", "ms-playwright")];
196
- }
197
- function pickPlaywrightExe(chromiumDir) {
198
- // Layout, per Playwright's install layout:
199
- // Linux: chromium-<rev>/chrome-linux/chrome
200
- // macOS: chromium-<rev>/chrome-mac/Chromium.app/Contents/MacOS/Chromium
201
- // Windows: chromium-<rev>/chrome-win/chrome.exe
202
- const plat = platform();
203
- // Playwright's layout varies by version/arch (Chrome-for-Testing renamed
204
- // some folders ~Playwright 1.40+):
205
- // Linux x86_64: chromium-<rev>/chrome-linux/chrome (older)
206
- // chromium-<rev>/chrome-linux64/chrome (CfT, newer)
207
- // Linux ARM64: chromium-<rev>/chrome-linux/chrome (ARM64 unchanged)
208
- // chromium-<rev>/chrome-linux-arm64/chrome (rare variant)
209
- // macOS x86_64: chromium-<rev>/chrome-mac/Chromium.app/... (older)
210
- // chromium-<rev>/chrome-mac-x64/Google Chrome for Testing.app/... (CfT, newer)
211
- // macOS arm64: chromium-<rev>/chrome-mac-arm/Chromium.app/... (older)
212
- // chromium-<rev>/chrome-mac-arm64/Google Chrome for Testing.app/...(CfT, newer)
213
- // Windows: chromium-<rev>/chrome-win/chrome.exe (older)
214
- // chromium-<rev>/chrome-win64/chrome.exe (newer)
215
- // Codex blocker review on PR #11 flagged the missing chrome-linux64
216
- // candidate — modern Playwright on ubuntu-latest landed in CfT layout
217
- // and the resolver step exited 1 before the test runner started.
218
- // The mac-arm64 CfT candidates were added while validating
219
- // set_breakpoint idempotency on darwin-arm64, where Playwright
220
- // v1223+ installs to chrome-mac-arm64/Google Chrome
221
- // for Testing.app/Contents/MacOS/Google Chrome for Testing.
222
- const candidates = plat === "win32"
223
- ? [
224
- join(chromiumDir, "chrome-win64", "chrome.exe"),
225
- join(chromiumDir, "chrome-win", "chrome.exe"),
226
- ]
227
- : plat === "darwin"
228
- ? [
229
- // CfT-renamed layouts first (Playwright 1.40+ / chromium revisions
230
- // ~v1200+). `existsSync` makes the order forgiving — try newer
231
- // first so we don't accidentally pick an older sibling layout
232
- // when both happen to be present.
233
- join(chromiumDir, "chrome-mac-arm64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"),
234
- join(chromiumDir, "chrome-mac-x64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing"),
235
- // Older Chromium.app-branded layouts.
236
- join(chromiumDir, "chrome-mac", "Chromium.app", "Contents", "MacOS", "Chromium"),
237
- join(chromiumDir, "chrome-mac-arm", "Chromium.app", "Contents", "MacOS", "Chromium"),
238
- ]
239
- : [
240
- join(chromiumDir, "chrome-linux", "chrome"),
241
- join(chromiumDir, "chrome-linux64", "chrome"),
242
- join(chromiumDir, "chrome-linux-arm64", "chrome"),
243
- ];
244
- for (const c of candidates) {
245
- if (existsSync(c))
246
- return c;
247
- }
248
- return null;
249
- }
250
- /** True when the given binary is the snap-marker returned by step 4. */
251
- export function isChromeLauncherDefault(b) {
252
- return b.source === "chrome-launcher-default";
253
- }
254
- /** Determine the user-data-dir for snap-confined Chromium. Snap confinement
255
- * rejects /tmp/... paths; only ~/snap/<app>/current/ is writable. */
256
- export function snapUserDataDir(binaryPath) {
257
- // Parse the snap app name out of the binary path. /snap/bin/chromium ->
258
- // 'chromium'; /snap/firefox/current/firefox -> 'firefox'.
259
- const match = binaryPath.match(/\/snap\/(?:bin\/)?([^/]+)/);
260
- const app = match?.[1] ?? "chromium";
261
- return join(homedir(), "snap", app, "current", "cdp-mcp-test-profile");
262
- }
263
- //# sourceMappingURL=browser-resolve.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-resolve.js","sourceRoot":"","sources":["../../src/util/browser-resolve.ts"],"names":[],"mappings":"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"}
@@ -1,7 +0,0 @@
1
- export declare class ToolError extends Error {
2
- code: string;
3
- constructor(code: string, message: string);
4
- }
5
- export declare const noSession: () => ToolError;
6
- export declare const notPaused: () => ToolError;
7
- export declare const alreadySession: () => ToolError;
@@ -1,12 +0,0 @@
1
- export class ToolError extends Error {
2
- code;
3
- constructor(code, message) {
4
- super(message);
5
- this.code = code;
6
- this.name = "ToolError";
7
- }
8
- }
9
- export const noSession = () => new ToolError("no_session", "No browser session. Call launch_chrome or attach_chrome first.");
10
- export const notPaused = () => new ToolError("not_paused", "Operation requires the debugger to be paused. Set a breakpoint and call wait_for_pause.");
11
- export const alreadySession = () => new ToolError("already_session", "A session is already active. Call close_session before opening a new one.");
12
- //# sourceMappingURL=errors.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/util/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAU,SAAQ,KAAK;IACf;IAAnB,YAAmB,IAAY,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,SAAI,GAAJ,IAAI,CAAQ;QAE7B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE,CAC5B,IAAI,SAAS,CACX,YAAY,EACZ,gEAAgE,CACjE,CAAC;AAEJ,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE,CAC5B,IAAI,SAAS,CACX,YAAY,EACZ,yFAAyF,CAC1F,CAAC;AAEJ,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,EAAE,CACjC,IAAI,SAAS,CACX,iBAAiB,EACjB,2EAA2E,CAC5E,CAAC"}