@narumitw/pi-chrome-devtools 0.53.0 → 0.53.2

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 (32) hide show
  1. package/README.md +88 -230
  2. package/dist/chunks/{browser-settings-menu-N5O65I7S.js → browser-settings-menu-IMB6HXSU.js} +5 -3
  3. package/dist/chunks/browser-settings-menu-IMB6HXSU.js.map +7 -0
  4. package/dist/chunks/{chunk-RTECHAEZ.js → chunk-GTTYS5GT.js} +20 -9
  5. package/dist/chunks/chunk-GTTYS5GT.js.map +7 -0
  6. package/dist/chunks/{chunk-B7B63PWB.js → chunk-LLXOCRNF.js} +50 -30
  7. package/dist/chunks/chunk-LLXOCRNF.js.map +7 -0
  8. package/dist/chunks/{chunk-GVBXT3FB.js → chunk-SRT3UUIP.js} +117 -73
  9. package/dist/chunks/chunk-SRT3UUIP.js.map +7 -0
  10. package/dist/chunks/{menu-UBHW6UP2.js → menu-UJETYMSB.js} +4 -4
  11. package/dist/chunks/{tools-V7OR5KJ3.js → tools-6P7EUX37.js} +49 -30
  12. package/dist/chunks/tools-6P7EUX37.js.map +7 -0
  13. package/dist/index.ts +21 -18
  14. package/dist/index.ts.map +2 -2
  15. package/docs/browser-setup.md +147 -0
  16. package/package.json +7 -7
  17. package/src/browser-manager.ts +138 -72
  18. package/src/browser-settings-menu.ts +2 -1
  19. package/src/cdp-client.ts +18 -7
  20. package/src/chrome-devtools.ts +8 -1
  21. package/src/display.ts +38 -0
  22. package/src/render.ts +6 -7
  23. package/src/tool-selector.ts +21 -32
  24. package/src/tools.ts +5 -5
  25. package/src/webmcp/policy.ts +14 -7
  26. package/src/webmcp/protocol.ts +33 -21
  27. package/dist/chunks/browser-settings-menu-N5O65I7S.js.map +0 -7
  28. package/dist/chunks/chunk-B7B63PWB.js.map +0 -7
  29. package/dist/chunks/chunk-GVBXT3FB.js.map +0 -7
  30. package/dist/chunks/chunk-RTECHAEZ.js.map +0 -7
  31. package/dist/chunks/tools-V7OR5KJ3.js.map +0 -7
  32. /package/dist/chunks/{menu-UBHW6UP2.js.map → menu-UJETYMSB.js.map} +0 -0
package/dist/index.ts CHANGED
@@ -6,9 +6,10 @@ import {
6
6
  listPages,
7
7
  resolvePage,
8
8
  resolvePageForNavigation,
9
+ setActivePageId,
9
10
  textResult,
10
11
  withCdp
11
- } from "./chunks/chunk-RTECHAEZ.js";
12
+ } from "./chunks/chunk-GTTYS5GT.js";
12
13
  import {
13
14
  allChromeDevtoolsTools,
14
15
  availableChromeDevtoolsTools,
@@ -24,7 +25,7 @@ import {
24
25
  supportsNativeDeferredToolLoading,
25
26
  updateChromeDevtoolsTools,
26
27
  waitForChromeDevtoolsSettings
27
- } from "./chunks/chunk-B7B63PWB.js";
28
+ } from "./chunks/chunk-LLXOCRNF.js";
28
29
  import {
29
30
  CHROME_DEVTOOLS_TOOL_NAMES,
30
31
  WEBMCP_TOOL_NAMES,
@@ -36,15 +37,17 @@ import {
36
37
  shutdownManagedBrowser,
37
38
  startManagedBrowserSession,
38
39
  state,
40
+ syncManagedBrowserSettings,
39
41
  waitForSettingsWrites,
40
42
  webMcpEnabled
41
- } from "./chunks/chunk-GVBXT3FB.js";
43
+ } from "./chunks/chunk-SRT3UUIP.js";
42
44
 
43
45
  // src/tools.ts
44
46
  import { defineTool } from "@earendil-works/pi-coding-agent";
45
47
  import { Type } from "typebox";
46
48
 
47
49
  // src/render.ts
50
+ import { truncateToWidth } from "@earendil-works/pi-tui";
48
51
  var STATUS_KEY = "chrome-devtools";
49
52
  var activeStatuses = /* @__PURE__ */ new WeakMap();
50
53
  function renderToolCall(action) {
@@ -89,16 +92,14 @@ var PiTextComponent = class {
89
92
  invalidate() {
90
93
  }
91
94
  render(width) {
92
- if (!this.text.trim()) return [];
93
- return this.text.replace(/\t/g, " ").split(/\r?\n/).map((line) => {
94
- const truncatedLine = truncateLine(line, Math.max(1, width));
95
+ const sanitizedText = sanitizeChromeDevtoolsDisplay(this.text);
96
+ if (!sanitizedText.trim()) return [];
97
+ return sanitizedText.replace(/\t/g, " ").split(/\r?\n/).map((line) => {
98
+ const truncatedLine = truncateToWidth(line, Math.max(0, width), "");
95
99
  return this.theme && this.color ? this.theme.fg(this.color, truncatedLine) : truncatedLine;
96
100
  });
97
101
  }
98
102
  };
99
- function truncateLine(line, maxWidth) {
100
- return Array.from(line).slice(0, maxWidth).join("");
101
- }
102
103
  async function withStatus(ctx, status, callback) {
103
104
  const token = Symbol(status);
104
105
  const statuses = activeStatuses.get(ctx.sessionManager) ?? [];
@@ -332,7 +333,7 @@ var selectPageTool = defineTool({
332
333
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
333
334
  return withStatus(ctx, "select page", async () => {
334
335
  const page = await getPage(params.pageId, { sessionOwner: ctx.sessionManager, signal });
335
- state.activePageId = page.id;
336
+ setActivePageId(ctx.sessionManager, page.id);
336
337
  return textResult(`Selected page ${page.id}: ${page.title}
337
338
  ${page.url}`, {
338
339
  page: formatPage(page)
@@ -362,7 +363,7 @@ var navigateTool = defineTool({
362
363
  await client.send("Page.enable");
363
364
  return client.send("Page.navigate", { url: params.url });
364
365
  });
365
- state.activePageId = page.id;
366
+ setActivePageId(ctx.sessionManager, page.id);
366
367
  const action = created ? "Created page and navigated" : "Navigated";
367
368
  return textResult(`${action} ${page.id} to ${params.url}`, {
368
369
  created,
@@ -401,7 +402,7 @@ var evaluateTool = defineTool({
401
402
  returnByValue: true
402
403
  })
403
404
  );
404
- state.activePageId = page.id;
405
+ setActivePageId(ctx.sessionManager, page.id);
405
406
  return textResult(JSON.stringify(result, null, 2), { page: formatPage(page), result });
406
407
  });
407
408
  }
@@ -422,7 +423,7 @@ var webMcpListToolsTool = defineTool({
422
423
  renderResult: renderTextResult,
423
424
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
424
425
  return withStatus(ctx, "list WebMCP tools", async () => {
425
- const implementation = await import("./chunks/tools-V7OR5KJ3.js");
426
+ const implementation = await import("./chunks/tools-6P7EUX37.js");
426
427
  return implementation.executeWebMcpListTool(params, signal, ctx);
427
428
  });
428
429
  }
@@ -464,7 +465,7 @@ var webMcpCallTool = defineTool({
464
465
  renderResult: renderTextResult,
465
466
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
466
467
  return withStatus(ctx, "call WebMCP tool", async () => {
467
- const implementation = await import("./chunks/tools-V7OR5KJ3.js");
468
+ const implementation = await import("./chunks/tools-6P7EUX37.js");
468
469
  return implementation.executeWebMcpCallTool(params, signal, ctx);
469
470
  });
470
471
  }
@@ -514,7 +515,7 @@ var screenshotTool = defineTool({
514
515
  }
515
516
  });
516
517
  });
517
- state.activePageId = page.id;
518
+ setActivePageId(ctx.sessionManager, page.id);
518
519
  const savedScreenshot = await saveScreenshot(result.data, params.savePath, ctx.cwd, signal);
519
520
  return {
520
521
  content: [
@@ -585,12 +586,14 @@ function chromeDevtools(pi) {
585
586
  });
586
587
  startManagedBrowserSession(ctx.sessionManager);
587
588
  if (generation !== state.sessionGeneration) return;
589
+ setActivePageId(ctx.sessionManager, void 0);
588
590
  state.activePageId = void 0;
589
591
  state.lastLaunchAttempt = void 0;
590
592
  const projectTrusted = ctx.isProjectTrusted();
591
593
  const settings = await loadSettings({ cwd: ctx.cwd, projectTrusted });
592
594
  if (generation !== state.sessionGeneration) return;
593
595
  applyRuntimeBrowserSettings(settings.effectiveBrowser, settings.paths, projectTrusted);
596
+ syncManagedBrowserSettings(ctx.sessionManager, settings.effectiveBrowser);
594
597
  applyRuntimeWebMcpSetting(settings.effectiveWebMcpEnabled, ctx.sessionManager);
595
598
  state.settingsNotice = settings.notice;
596
599
  for (const warning of settings.warnings) {
@@ -653,7 +656,7 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
653
656
  if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
654
657
  throw new Error("/chrome-devtools settings requires TUI or RPC mode");
655
658
  }
656
- const { showChromeDevtoolsBrowserSettings } = await import("./chunks/browser-settings-menu-N5O65I7S.js");
659
+ const { showChromeDevtoolsBrowserSettings } = await import("./chunks/browser-settings-menu-IMB6HXSU.js");
657
660
  if (generation !== state.sessionGeneration) return;
658
661
  await showChromeDevtoolsBrowserSettings(pi, ctx, generation);
659
662
  return;
@@ -662,7 +665,7 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
662
665
  if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
663
666
  throw new Error("/chrome-devtools tools requires TUI or RPC mode");
664
667
  }
665
- const { showChromeDevtoolsToolWorkflow } = await import("./chunks/menu-UBHW6UP2.js");
668
+ const { showChromeDevtoolsToolWorkflow } = await import("./chunks/menu-UJETYMSB.js");
666
669
  if (generation !== state.sessionGeneration) return;
667
670
  await showChromeDevtoolsToolWorkflow(pi, ctx, generation);
668
671
  return;
@@ -698,7 +701,7 @@ async function showMenu(pi, ctx, generation) {
698
701
  if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
699
702
  throw new Error("/chrome-devtools menu requires TUI or RPC mode; use a direct subcommand");
700
703
  }
701
- const { showChromeDevtoolsMenu } = await import("./chunks/menu-UBHW6UP2.js");
704
+ const { showChromeDevtoolsMenu } = await import("./chunks/menu-UJETYMSB.js");
702
705
  if (generation !== state.sessionGeneration) return;
703
706
  await showChromeDevtoolsMenu(pi, ctx, generation);
704
707
  }
package/dist/index.ts.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/tools.ts", "../src/render.ts", "../src/screenshot.ts", "../src/chrome-devtools.ts"],
4
- "sourcesContent": ["import { defineTool } from \"@earendil-works/pi-coding-agent\";\nimport { Type } from \"typebox\";\nimport {\n\tformatPage,\n\tgetPage,\n\tlistPages,\n\tresolvePage,\n\tresolvePageForNavigation,\n\ttextResult,\n\twithCdp,\n} from \"./cdp-client.js\";\nimport { renderScreenshotResult, renderTextResult, renderToolCall, withStatus } from \"./render.js\";\nimport { state } from \"./runtime.js\";\nimport { formatScreenshotText, saveScreenshot, throwIfAborted } from \"./screenshot.js\";\nimport { CHROME_DEVTOOLS_TOOL_NAMES, WEBMCP_TOOL_NAMES } from \"./tool-names.js\";\n\nexport const listPagesTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[0],\n\tlabel: \"Chrome DevTools: List Pages\",\n\tdescription: \"List Chrome tabs/pages from a running Chrome DevTools Protocol endpoint.\",\n\tparameters: Type.Object({}),\n\trenderCall: renderToolCall(\"list pages\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, _params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"list pages\", async () => {\n\t\t\tconst pages = await listPages({ sessionOwner: ctx.sessionManager, signal });\n\t\t\treturn textResult(JSON.stringify(pages.map(formatPage), null, 2), { pages });\n\t\t});\n\t},\n});\n\nexport const selectPageTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[1],\n\tlabel: \"Chrome DevTools: Select Page\",\n\tdescription: \"Select the active Chrome page for later chrome_devtools_* tool calls.\",\n\tparameters: Type.Object({\n\t\tpageId: Type.String({ description: \"Page id from chrome_devtools_list_pages.\" }),\n\t}),\n\trenderCall: renderToolCall(\"select page\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"select page\", async () => {\n\t\t\tconst page = await getPage(params.pageId, { sessionOwner: ctx.sessionManager, signal });\n\t\t\tstate.activePageId = page.id;\n\t\t\treturn textResult(`Selected page ${page.id}: ${page.title}\\n${page.url}`, {\n\t\t\t\tpage: formatPage(page),\n\t\t\t});\n\t\t});\n\t},\n});\n\nexport const navigateTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[2],\n\tlabel: \"Chrome DevTools: Navigate\",\n\tdescription:\n\t\t\"Navigate a Chrome page to a URL through Chrome DevTools Protocol, creating a page first if none is available.\",\n\tparameters: Type.Object({\n\t\turl: Type.String({ description: \"URL to navigate to.\" }),\n\t\tpageId: Type.Optional(\n\t\t\tType.String({ description: \"Optional page id. Defaults to selected or first page.\" }),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"navigate\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"navigate\", async () => {\n\t\t\tconst { created, page } = await resolvePageForNavigation(params.pageId, {\n\t\t\t\tsessionOwner: ctx.sessionManager,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tconst result = await withCdp(page, async (client) => {\n\t\t\t\tawait client.send(\"Page.enable\");\n\t\t\t\treturn client.send(\"Page.navigate\", { url: params.url });\n\t\t\t});\n\n\t\t\tstate.activePageId = page.id;\n\t\t\tconst action = created ? \"Created page and navigated\" : \"Navigated\";\n\t\t\treturn textResult(`${action} ${page.id} to ${params.url}`, {\n\t\t\t\tcreated,\n\t\t\t\tpage: formatPage(page),\n\t\t\t\tresult,\n\t\t\t});\n\t\t});\n\t},\n});\n\nexport const evaluateTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[3],\n\tlabel: \"Chrome DevTools: Evaluate\",\n\tdescription: \"Evaluate JavaScript in a Chrome page through Chrome DevTools Protocol.\",\n\tparameters: Type.Object({\n\t\texpression: Type.String({ description: \"JavaScript expression to evaluate.\" }),\n\t\tpageId: Type.Optional(\n\t\t\tType.String({ description: \"Optional page id. Defaults to selected or first page.\" }),\n\t\t),\n\t\tawaitPromise: Type.Optional(\n\t\t\tType.Boolean({ description: \"Whether to await a returned Promise. Defaults to true.\" }),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"evaluate\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"evaluate\", async () => {\n\t\t\tconst page = await resolvePage(params.pageId, {\n\t\t\t\tsessionOwner: ctx.sessionManager,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tconst result = await withCdp(page, (client) =>\n\t\t\t\tclient.send(\"Runtime.evaluate\", {\n\t\t\t\t\texpression: params.expression,\n\t\t\t\t\tawaitPromise: params.awaitPromise ?? true,\n\t\t\t\t\treturnByValue: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tstate.activePageId = page.id;\n\t\t\treturn textResult(JSON.stringify(result, null, 2), { page: formatPage(page), result });\n\t\t});\n\t},\n});\n\nexport const webMcpListToolsTool = defineTool({\n\tname: WEBMCP_TOOL_NAMES[0],\n\tlabel: \"Chrome DevTools: List WebMCP Tools (Experimental)\",\n\tdescription:\n\t\t\"List bounded page-provided WebMCP tool descriptors from the selected Chrome page through the experimental CDP WebMCP domain. Definitions remain in this tool result and are never registered as dynamic Pi tools.\",\n\tparameters: Type.Object({\n\t\tpageId: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Optional page id. Defaults to selected or first page.\",\n\t\t\t\tmaxLength: 512,\n\t\t\t}),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"list WebMCP tools\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"list WebMCP tools\", async () => {\n\t\t\tconst implementation = await import(\"./webmcp/tools.js\");\n\t\t\treturn implementation.executeWebMcpListTool(params, signal, ctx);\n\t\t});\n\t},\n});\n\nexport const webMcpCallTool = defineTool({\n\tname: WEBMCP_TOOL_NAMES[1],\n\tlabel: \"Chrome DevTools: Call WebMCP Tool (Experimental)\",\n\tdescription:\n\t\t\"Call one previously listed page-provided WebMCP tool after exact page, document, frame, origin, schema, annotation, and session revalidation. Every call requires observable user confirmation and output is bounded to Pi's 50 KB or 2,000-line limit.\",\n\tparameters: Type.Object({\n\t\tsessionGeneration: Type.String({\n\t\t\tdescription: \"Session generation token returned by chrome_devtools_webmcp_list_tools.\",\n\t\t\tmaxLength: 100,\n\t\t}),\n\t\tpageId: Type.String({ description: \"Page id returned by the list tool.\", maxLength: 512 }),\n\t\tdocumentId: Type.String({\n\t\t\tdescription: \"Document loader id returned by the list tool.\",\n\t\t\tmaxLength: 512,\n\t\t}),\n\t\tframeId: Type.String({ description: \"Frame id returned by the list tool.\", maxLength: 512 }),\n\t\tframeOrigin: Type.String({\n\t\t\tdescription: \"Exact frame origin returned by the list tool.\",\n\t\t\tmaxLength: 2_048,\n\t\t}),\n\t\ttoolName: Type.String({\n\t\t\tdescription: \"Exact tool name returned by the list tool.\",\n\t\t\tmaxLength: 512,\n\t\t}),\n\t\tschemaDigest: Type.String({\n\t\t\tdescription: \"Schema and annotation digest returned by the list tool.\",\n\t\t\tminLength: 64,\n\t\t\tmaxLength: 64,\n\t\t\tpattern: \"^[a-f0-9]{64}$\",\n\t\t}),\n\t\tinput: Type.Record(Type.String(), Type.Unknown(), {\n\t\t\tdescription: \"JSON object matching the page-provided input schema.\",\n\t\t}),\n\t}),\n\trenderCall: renderToolCall(\"call WebMCP tool\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"call WebMCP tool\", async () => {\n\t\t\tconst implementation = await import(\"./webmcp/tools.js\");\n\t\t\treturn implementation.executeWebMcpCallTool(params, signal, ctx);\n\t\t});\n\t},\n});\n\nexport const screenshotTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[4],\n\tlabel: \"Chrome DevTools: Screenshot\",\n\tdescription: \"Capture a PNG screenshot from a Chrome page through Chrome DevTools Protocol.\",\n\tparameters: Type.Object({\n\t\tpageId: Type.Optional(\n\t\t\tType.String({ description: \"Optional page id. Defaults to selected or first page.\" }),\n\t\t),\n\t\tfullPage: Type.Optional(\n\t\t\tType.Boolean({ description: \"Capture the full document, not just the viewport.\" }),\n\t\t),\n\t\tsavePath: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Screenshot is always saved as a PNG file. Optional output path; omitted defaults to a unique temp file. Relative paths resolve from the current working directory. A single leading @ is stripped to match Pi file-mention paths. Existing regular files are replaced.\",\n\t\t\t}),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"screenshot\"),\n\trenderResult: renderScreenshotResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"screenshot\", async () => {\n\t\t\tconst page = await resolvePage(params.pageId, {\n\t\t\t\tsessionOwner: ctx.sessionManager,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tconst result = await withCdp(page, async (client) => {\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tawait client.send(\"Page.enable\");\n\n\t\t\t\tif (!params.fullPage) {\n\t\t\t\t\treturn client.send<{ data: string }>(\"Page.captureScreenshot\", { format: \"png\" });\n\t\t\t\t}\n\n\t\t\t\tconst metrics = await client.send<{\n\t\t\t\t\tcontentSize: { x: number; y: number; width: number; height: number };\n\t\t\t\t}>(\"Page.getLayoutMetrics\");\n\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\treturn client.send<{ data: string }>(\"Page.captureScreenshot\", {\n\t\t\t\t\tcaptureBeyondViewport: true,\n\t\t\t\t\tformat: \"png\",\n\t\t\t\t\tclip: {\n\t\t\t\t\t\tx: metrics.contentSize.x,\n\t\t\t\t\t\ty: metrics.contentSize.y,\n\t\t\t\t\t\twidth: metrics.contentSize.width,\n\t\t\t\t\t\theight: metrics.contentSize.height,\n\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tstate.activePageId = page.id;\n\t\t\tconst savedScreenshot = await saveScreenshot(result.data, params.savePath, ctx.cwd, signal);\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: formatScreenshotText(page, savedScreenshot),\n\t\t\t\t\t},\n\t\t\t\t\t{ type: \"image\", data: result.data, mimeType: \"image/png\" },\n\t\t\t\t],\n\t\t\t\tdetails: {\n\t\t\t\t\tpage: formatPage(page),\n\t\t\t\t\tbytes: savedScreenshot.bytes,\n\t\t\t\t\tsavedPath: savedScreenshot.savedPath,\n\t\t\t\t\tisDefaultPath: savedScreenshot.isDefaultPath,\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t},\n});\n", "import type { AgentToolResult, ToolRenderResultOptions } from \"@earendil-works/pi-coding-agent\";\n\nconst STATUS_KEY = \"chrome-devtools\";\ninterface StatusContext {\n\tsessionManager: object;\n\tui: { setStatus: (key: string, value: string | undefined) => void };\n}\ninterface RenderTheme {\n\tbold(text: string): string;\n\tfg(color: string, text: string): string;\n}\nconst activeStatuses = new WeakMap<object, Array<{ status: string; token: symbol }>>();\n\ninterface RenderComponent {\n\tinvalidate(): void;\n\trender(width: number): string[];\n}\n\nexport function renderToolCall(action: string) {\n\treturn () => new PiTextComponent(`Chrome DevTools: ${action}`);\n}\n\nexport function renderTextResult(\n\tresult: AgentToolResult<unknown>,\n\toptions: ToolRenderResultOptions,\n\ttheme: RenderTheme,\n) {\n\tconst output = formatCollapsibleOutput(textContent(result), options);\n\treturn new PiTextComponent(output.text, theme, output.color);\n}\n\nexport function renderScreenshotResult(\n\tresult: AgentToolResult<unknown>,\n\toptions: ToolRenderResultOptions,\n\ttheme: RenderTheme,\n): RenderComponent {\n\tconst output = formatCollapsibleOutput(screenshotTextContent(result), options);\n\treturn new PiTextComponent(output.text, theme, output.color);\n}\n\nfunction textContent(result: AgentToolResult<unknown>) {\n\treturn result.content\n\t\t.flatMap((content) => (content.type === \"text\" ? [content.text] : []))\n\t\t.join(\"\\n\");\n}\n\nfunction screenshotTextContent(result: AgentToolResult<unknown>) {\n\tconst text = textContent(result);\n\tif (text.trim()) return text;\n\n\tconst details = result.details as { savedPath?: unknown; bytes?: unknown } | undefined;\n\tif (typeof details?.savedPath !== \"string\") return text;\n\tconst bytes = typeof details.bytes === \"number\" ? ` (${details.bytes} bytes)` : \"\";\n\treturn `Saved screenshot to ${details.savedPath}${bytes}`;\n}\n\nfunction formatCollapsibleOutput(\n\ttext: string,\n\toptions: ToolRenderResultOptions,\n): { text: string; color?: string } {\n\tif (options.isPartial) return { text: \"Running...\", color: \"warning\" };\n\tif (options.expanded) return { text, color: \"toolOutput\" };\n\n\treturn { text: \"\" };\n}\n\nclass PiTextComponent implements RenderComponent {\n\tprivate text: string;\n\tprivate readonly theme?: RenderTheme;\n\tprivate readonly color?: string;\n\n\tconstructor(text = \"\", theme?: RenderTheme, color?: string) {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t\tthis.color = color;\n\t}\n\n\tsetText(text: string) {\n\t\tthis.text = text;\n\t}\n\n\tinvalidate() {\n\t\t// Stateless renderer: no cached layout to invalidate.\n\t}\n\n\trender(width: number) {\n\t\tif (!this.text.trim()) return [];\n\t\treturn this.text\n\t\t\t.replace(/\\t/g, \" \")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((line) => {\n\t\t\t\tconst truncatedLine = truncateLine(line, Math.max(1, width));\n\t\t\t\treturn this.theme && this.color ? this.theme.fg(this.color, truncatedLine) : truncatedLine;\n\t\t\t});\n\t}\n}\n\nfunction truncateLine(line: string, maxWidth: number) {\n\treturn Array.from(line).slice(0, maxWidth).join(\"\");\n}\n\nexport async function withStatus<T>(\n\tctx: StatusContext,\n\tstatus: string,\n\tcallback: () => Promise<T>,\n) {\n\tconst token = Symbol(status);\n\tconst statuses = activeStatuses.get(ctx.sessionManager) ?? [];\n\tstatuses.push({ status, token });\n\tactiveStatuses.set(ctx.sessionManager, statuses);\n\tctx.ui.setStatus(STATUS_KEY, status);\n\ttry {\n\t\treturn await callback();\n\t} finally {\n\t\tconst index = statuses.findIndex((entry) => entry.token === token);\n\t\tif (index >= 0) statuses.splice(index, 1);\n\t\tconst remaining = statuses.at(-1)?.status;\n\t\tif (statuses.length === 0) activeStatuses.delete(ctx.sessionManager);\n\t\tctx.ui.setStatus(STATUS_KEY, remaining);\n\t}\n}\n", "import { randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, realpath, rename, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { withFileMutationQueue } from \"@earendil-works/pi-coding-agent\";\nimport type { DevToolsPage } from \"./runtime.js\";\n\nfunction unique<T>(values: T[]) {\n\treturn Array.from(new Set(values));\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n\treturn error instanceof Error && \"code\" in error;\n}\n\nexport interface ResolvedScreenshotPath {\n\tpath: string;\n\tallowedRoots: string[];\n\tisDefault: boolean;\n}\nexport interface ScreenshotSaveResult {\n\tsavedPath: string;\n\tbytes: number;\n\tisDefaultPath: boolean;\n}\n\nexport async function saveScreenshot(\n\tbase64Png: string,\n\tsavePath: string | undefined,\n\tcwd: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ScreenshotSaveResult> {\n\tconst resolvedPath = resolveScreenshotPath(savePath, cwd);\n\tconst pngBytes = Buffer.from(base64Png, \"base64\");\n\n\tawait withFileMutationQueue(resolvedPath.path, async () => {\n\t\tthrowIfAborted(signal);\n\t\tawait ensureSafeScreenshotParent(resolvedPath);\n\t\tawait assertSafeScreenshotTargetPath(resolvedPath);\n\t\tawait writeScreenshotFileSafely(resolvedPath, pngBytes, signal);\n\t});\n\n\treturn {\n\t\tsavedPath: resolvedPath.path,\n\t\tbytes: pngBytes.byteLength,\n\t\tisDefaultPath: resolvedPath.isDefault,\n\t};\n}\n\nexport function resolveScreenshotPath(\n\tsavePath: string | undefined,\n\tcwd: string,\n): ResolvedScreenshotPath {\n\tconst cwdRoot = resolve(cwd);\n\tconst tempRoot = resolve(tmpdir());\n\n\tif (savePath === undefined) {\n\t\treturn {\n\t\t\tpath: join(tempRoot, `pi-chrome-devtools-screenshot-${randomUUID()}.png`),\n\t\t\tallowedRoots: [tempRoot],\n\t\t\tisDefault: true,\n\t\t};\n\t}\n\n\tconst normalizedPath = stripLeadingAtPath(savePath);\n\tif (!normalizedPath.trim()) {\n\t\tthrow new Error(\"Screenshot savePath must not be empty.\");\n\t}\n\tif (normalizedPath.includes(\"\\0\")) {\n\t\tthrow new Error(\"Screenshot savePath must not contain NUL bytes.\");\n\t}\n\tif (hasParentPathSegment(normalizedPath)) {\n\t\tthrow new Error(\"Screenshot savePath must not contain '..' path segments.\");\n\t}\n\n\tconst isAbsolutePath = isAbsolute(normalizedPath);\n\tconst path = isAbsolutePath ? resolve(normalizedPath) : resolve(cwdRoot, normalizedPath);\n\tconst allowedRoots = isAbsolutePath ? unique([cwdRoot, tempRoot]) : [cwdRoot];\n\tif (!allowedRoots.some((root) => isPathInsideRoot(path, root))) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath must be relative to the current working directory, or an absolute path inside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n\n\treturn { path, allowedRoots, isDefault: false };\n}\n\nfunction stripLeadingAtPath(path: string) {\n\treturn path.startsWith(\"@\") ? path.slice(1) : path;\n}\n\nexport function hasParentPathSegment(path: string) {\n\treturn path.split(/[\\\\/]+/).some((part) => part === \"..\");\n}\n\nasync function ensureSafeScreenshotParent(resolvedPath: ResolvedScreenshotPath) {\n\tconst parentPath = dirname(resolvedPath.path);\n\tconst rootPath = selectAllowedRoot(parentPath, resolvedPath.allowedRoots);\n\tif (!rootPath) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath parent must stay inside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n\n\tconst realRootPath = await realpath(rootPath);\n\tlet currentPath = rootPath;\n\tconst parentSegments = relative(rootPath, parentPath)\n\t\t.split(/[\\\\/]+/)\n\t\t.filter((part) => part.length > 0);\n\n\tfor (const segment of parentSegments) {\n\t\tcurrentPath = join(currentPath, segment);\n\t\tawait ensureSafeDirectorySegment(currentPath, realRootPath);\n\t}\n}\n\nexport function selectAllowedRoot(path: string, roots: readonly string[]) {\n\tconst matchingRoots = roots.filter((root) => isPathInsideRoot(path, root));\n\tmatchingRoots.sort(\n\t\t(left, right) =>\n\t\t\tnormalizePathForComparison(resolve(right)).length -\n\t\t\tnormalizePathForComparison(resolve(left)).length,\n\t);\n\treturn matchingRoots[0];\n}\n\nasync function ensureSafeDirectorySegment(path: string, realRootPath: string) {\n\tconst existingDirectory = await lstat(path).catch(async (error: unknown) => {\n\t\tif (!isNodeError(error) || error.code !== \"ENOENT\") throw error;\n\t\tawait mkdir(path).catch((mkdirError: unknown) => {\n\t\t\tif (!isNodeError(mkdirError) || mkdirError.code !== \"EEXIST\") throw mkdirError;\n\t\t});\n\t\treturn lstat(path);\n\t});\n\n\tif (existingDirectory.isSymbolicLink()) {\n\t\tthrow new Error(\"Screenshot savePath parent directories must not contain symbolic links.\");\n\t}\n\tif (!existingDirectory.isDirectory()) {\n\t\tthrow new Error(\"Screenshot savePath parent must be a directory.\");\n\t}\n\tawait assertPathWithinRealRoot(path, realRootPath);\n}\n\nasync function assertSafeScreenshotTargetPath(resolvedPath: ResolvedScreenshotPath) {\n\tconst existingTarget = await lstat(resolvedPath.path).catch((error: unknown) => {\n\t\tif (isNodeError(error) && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t});\n\tif (existingTarget?.isSymbolicLink()) {\n\t\tthrow new Error(\"Screenshot savePath must not point to a symbolic link.\");\n\t}\n\tif (existingTarget?.isDirectory()) {\n\t\tthrow new Error(\"Screenshot savePath must point to a file, not a directory.\");\n\t}\n\tif (existingTarget && !existingTarget.isFile()) {\n\t\tthrow new Error(\"Screenshot savePath may only replace regular files.\");\n\t}\n\n\tconst realAllowedRoots = await Promise.all(resolvedPath.allowedRoots.map(realpathOrResolvedPath));\n\tconst realParent = await realpath(dirname(resolvedPath.path));\n\tconst realTargetPath = join(realParent, basename(resolvedPath.path));\n\tif (!realAllowedRoots.some((root) => isPathInsideRoot(realTargetPath, root))) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath resolves outside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n}\n\nasync function assertPathWithinRealRoot(path: string, realRootPath: string) {\n\tconst realPath = await realpath(path);\n\tif (!isPathInsideRoot(realPath, realRootPath)) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath parent resolves outside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n}\n\nasync function writeScreenshotFileSafely(\n\tresolvedPath: ResolvedScreenshotPath,\n\tpngBytes: Buffer,\n\tsignal: AbortSignal | undefined,\n) {\n\tconst tempFile = join(\n\t\tdirname(resolvedPath.path),\n\t\t`.${basename(resolvedPath.path)}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`,\n\t);\n\ttry {\n\t\tawait writeFile(tempFile, pngBytes, { flag: \"wx\", signal });\n\t\tthrowIfAborted(signal);\n\t\tawait replaceScreenshotFile(resolvedPath, tempFile, signal);\n\t} catch (error) {\n\t\tawait rm(tempFile, { force: true }).catch(() => undefined);\n\t\tthrow error;\n\t}\n}\n\nasync function replaceScreenshotFile(\n\tresolvedPath: ResolvedScreenshotPath,\n\ttempFile: string,\n\tsignal: AbortSignal | undefined,\n) {\n\ttry {\n\t\tawait rename(tempFile, resolvedPath.path);\n\t\treturn;\n\t} catch (error) {\n\t\tif (!shouldRetryRenameAfterRemovingDestination(error)) throw error;\n\t}\n\n\t// Some Windows filesystems reject renaming over an existing file. Revalidate before\n\t// removing the destination so the fallback still refuses directories and symlinks.\n\tawait assertSafeScreenshotTargetPath(resolvedPath);\n\tthrowIfAborted(signal);\n\tawait rm(resolvedPath.path, { force: true });\n\tawait rename(tempFile, resolvedPath.path);\n}\n\nfunction shouldRetryRenameAfterRemovingDestination(error: unknown) {\n\treturn (\n\t\tprocess.platform === \"win32\" &&\n\t\tisNodeError(error) &&\n\t\t[\"EACCES\", \"EEXIST\", \"EPERM\"].includes(error.code ?? \"\")\n\t);\n}\n\nasync function realpathOrResolvedPath(path: string) {\n\treturn realpath(path).catch(() => resolve(path));\n}\n\nexport function isPathInsideRoot(path: string, root: string) {\n\tconst normalizedPath = normalizePathForComparison(resolve(path));\n\tconst normalizedRoot = normalizePathForComparison(resolve(root));\n\tif (normalizedPath === normalizedRoot) return true;\n\tconst rootWithSeparator = normalizedRoot.endsWith(sep)\n\t\t? normalizedRoot\n\t\t: `${normalizedRoot}${sep}`;\n\treturn normalizedPath.startsWith(rootWithSeparator);\n}\n\nfunction normalizePathForComparison(path: string) {\n\treturn process.platform === \"win32\" ? path.toLowerCase() : path;\n}\n\nexport function throwIfAborted(signal: AbortSignal | undefined) {\n\tif (!signal?.aborted) return;\n\tthrow signal.reason instanceof Error ? signal.reason : new Error(\"Screenshot capture cancelled.\");\n}\n\nexport function formatScreenshotText(page: DevToolsPage, screenshot: ScreenshotSaveResult) {\n\tconst pathLabel = screenshot.isDefaultPath ? \"Saved to temp file\" : \"Saved to\";\n\treturn [\n\t\t`Captured PNG screenshot from ${page.title || page.url || page.id}.`,\n\t\t`${pathLabel}: ${screenshot.savedPath}`,\n\t\t`Bytes: ${screenshot.bytes}`,\n\t\t`Use read({ path: ${JSON.stringify(screenshot.savedPath)} }) to inspect the saved screenshot if inline image content is not available.`,\n\t].join(\"\\n\");\n}\n", "import type { ExtensionAPI, ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport { shutdownManagedBrowser, startManagedBrowserSession } from \"./browser-manager.js\";\nimport {\n\tavailableChromeDevtoolsTools,\n\tconfigureChromeDevtoolsToolExposure,\n\tcreateChromeDevtoolsLoadTool,\n\tinitializeAvailableChromeDevtoolsTools,\n\trequireEagerChromeDevtoolsToolExposure,\n\tsetChromeDevtoolsSessionOwner,\n\tsupportsNativeDeferredToolLoading,\n} from \"./lazy-tools.js\";\nimport {\n\tapplyRuntimeBrowserSettings,\n\tapplyRuntimeWebMcpSetting,\n\tinvalidateWebMcpOperations,\n\tsetWebMcpSessionOwner,\n\tstate,\n\twebMcpEnabled,\n} from \"./runtime.js\";\nimport { loadSettings, waitForSettingsWrites } from \"./settings.js\";\nimport {\n\tallChromeDevtoolsTools,\n\tbuildCommandGuide,\n\tbuildQuickstartMessage,\n\tbuildToolStatusMessage,\n\tsanitizeChromeDevtoolsDisplay,\n\tupdateChromeDevtoolsTools,\n\twaitForChromeDevtoolsSettings,\n} from \"./tool-selector.js\";\nimport {\n\tevaluateTool,\n\tlistPagesTool,\n\tnavigateTool,\n\tscreenshotTool,\n\tselectPageTool,\n\twebMcpCallTool,\n\twebMcpListToolsTool,\n} from \"./tools.js\";\n\ntype CommandAction =\n\t| \"menu\"\n\t| \"help\"\n\t| \"quickstart\"\n\t| \"status\"\n\t| \"settings\"\n\t| \"tools\"\n\t| \"enable\"\n\t| \"disable\";\ntype CommandContext = ExtensionCommandContext;\nconst STATUS_KEY = \"chrome-devtools\";\nconst COMMAND_COMPLETIONS = [\n\t{ value: \"help\", label: \"help\", description: \"Show command usage\" },\n\t{ value: \"quickstart\", label: \"quickstart\", description: \"Show endpoint and launch help\" },\n\t{ value: \"status\", label: \"status\", description: \"Show tool and settings status\" },\n\t{ value: \"settings\", label: \"settings\", description: \"Edit browser connection settings\" },\n\t{ value: \"tools\", label: \"tools\", description: \"Choose available Chrome DevTools tools\" },\n\t{ value: \"toggle\", label: \"toggle\", description: \"Alias for tools\" },\n\t{ value: \"select\", label: \"select\", description: \"Compatibility alias for tools\" },\n\t{ value: \"enable\", label: \"enable\", description: \"Make all Chrome DevTools tools available\" },\n\t{ value: \"on\", label: \"on\", description: \"Compatibility alias for enable\" },\n\t{ value: \"disable\", label: \"disable\", description: \"Make all Chrome DevTools tools unavailable\" },\n\t{ value: \"off\", label: \"off\", description: \"Compatibility alias for disable\" },\n];\nexport default function chromeDevtools(pi: ExtensionAPI) {\n\tpi.registerTool(listPagesTool);\n\tpi.registerTool(selectPageTool);\n\tpi.registerTool(navigateTool);\n\tpi.registerTool(evaluateTool);\n\tpi.registerTool(screenshotTool);\n\tpi.registerTool(webMcpListToolsTool);\n\tpi.registerTool(webMcpCallTool);\n\tpi.registerTool(createChromeDevtoolsLoadTool(pi));\n\n\tpi.registerCommand(\"chrome-devtools\", {\n\t\tdescription: \"Open Chrome DevTools help and tool controls\",\n\t\tgetArgumentCompletions: (prefix) => commandCompletions(prefix),\n\t\thandler: async (args, ctx) => {\n\t\t\tsetChromeDevtoolsSessionOwner(pi, ctx.sessionManager);\n\t\t\tinitializeAvailableChromeDevtoolsTools(pi);\n\t\t\tconst generation = state.sessionGeneration;\n\t\t\tawait handleChromeDevtoolsCommand(pi, args, ctx, generation);\n\t\t},\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tconst generation = ++state.sessionGeneration;\n\t\tsetChromeDevtoolsSessionOwner(pi, ctx.sessionManager);\n\t\tinitializeAvailableChromeDevtoolsTools(pi);\n\t\tsetWebMcpSessionOwner(ctx.sessionManager);\n\t\treplaceSessionController(\"Chrome DevTools session replaced\");\n\t\tinvalidateWebMcpOperations(ctx.sessionManager, \"Chrome DevTools session replaced\");\n\t\tstate.shuttingDown = false;\n\t\tstate.settingsNotice = undefined;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tawait shutdownManagedBrowser(undefined, {\n\t\t\tcancelLaunch: true,\n\t\t\towner: ctx.sessionManager,\n\t\t});\n\t\tstartManagedBrowserSession(ctx.sessionManager);\n\t\tif (generation !== state.sessionGeneration) return;\n\t\tstate.activePageId = undefined;\n\t\tstate.lastLaunchAttempt = undefined;\n\t\tconst projectTrusted = ctx.isProjectTrusted();\n\t\tconst settings = await loadSettings({ cwd: ctx.cwd, projectTrusted });\n\t\tif (generation !== state.sessionGeneration) return;\n\t\tapplyRuntimeBrowserSettings(settings.effectiveBrowser, settings.paths, projectTrusted);\n\t\tapplyRuntimeWebMcpSetting(settings.effectiveWebMcpEnabled, ctx.sessionManager);\n\t\tstate.settingsNotice = settings.notice;\n\t\tfor (const warning of settings.warnings) {\n\t\t\tctx.ui.notify(sanitizeChromeDevtoolsDisplay(warning), \"warning\");\n\t\t}\n\t\tif (webMcpEnabled(ctx.sessionManager)) {\n\t\t\tctx.ui.notify(\n\t\t\t\t\"Experimental WebMCP is enabled. Page-provided tools use the visible browser session and require confirmation for every call.\",\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t}\n\t\tconst availableTools =\n\t\t\tsettings.kind === \"loaded\" && settings.settings.tools\n\t\t\t\t? settings.settings.tools\n\t\t\t\t: availableChromeDevtoolsTools(pi);\n\t\tconfigureChromeDevtoolsToolExposure(pi, availableTools, ctx.model);\n\t});\n\n\tpi.on(\"model_select\", (event, ctx) => {\n\t\tinvalidateWebMcpOperations(\n\t\t\tctx.sessionManager,\n\t\t\t\"Chrome DevTools model and tool exposure changed\",\n\t\t);\n\t\tif (!supportsNativeDeferredToolLoading(event.model)) {\n\t\t\trequireEagerChromeDevtoolsToolExposure(pi);\n\t\t}\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tstate.sessionGeneration += 1;\n\t\treplaceSessionController(\"Chrome DevTools session shut down\");\n\t\tinvalidateWebMcpOperations(ctx.sessionManager, \"Chrome DevTools session shut down\");\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tconst browserShutdown = shutdownManagedBrowser(undefined, {\n\t\t\tcancelLaunch: true,\n\t\t\towner: ctx.sessionManager,\n\t\t});\n\t\tawait waitForChromeDevtoolsSettings();\n\t\tawait waitForSettingsWrites();\n\t\tawait browserShutdown;\n\t});\n}\n\nasync function handleChromeDevtoolsCommand(\n\tpi: ExtensionAPI,\n\targs: string,\n\tctx: CommandContext,\n\tgeneration: number,\n) {\n\tconst command = parseCommand(args);\n\tswitch (command) {\n\t\tcase \"menu\":\n\t\t\tawait showMenu(pi, ctx, generation);\n\t\t\treturn;\n\t\tcase \"help\":\n\t\t\trequireObservableUi(ctx, \"help\");\n\t\t\tctx.ui.notify(buildCommandGuide(ctx.sessionManager), \"info\");\n\t\t\treturn;\n\t\tcase \"quickstart\":\n\t\t\trequireObservableUi(ctx, \"quickstart\");\n\t\t\tctx.ui.notify(buildQuickstartMessage(ctx.sessionManager), \"info\");\n\t\t\treturn;\n\t\tcase \"status\": {\n\t\t\trequireObservableUi(ctx, \"status\");\n\t\t\tconst status = await buildToolStatusMessage(pi, ctx.sessionManager);\n\t\t\tif (generation !== state.sessionGeneration) return;\n\t\t\tctx.ui.notify(status, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"settings\": {\n\t\t\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\t\t\tthrow new Error(\"/chrome-devtools settings requires TUI or RPC mode\");\n\t\t\t}\n\t\t\tconst { showChromeDevtoolsBrowserSettings } = await import(\"./browser-settings-menu.js\");\n\t\t\tif (generation !== state.sessionGeneration) return;\n\t\t\tawait showChromeDevtoolsBrowserSettings(pi, ctx, generation);\n\t\t\treturn;\n\t\t}\n\t\tcase \"tools\": {\n\t\t\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\t\t\tthrow new Error(\"/chrome-devtools tools requires TUI or RPC mode\");\n\t\t\t}\n\t\t\tconst { showChromeDevtoolsToolWorkflow } = await import(\"./menu.js\");\n\t\t\tif (generation !== state.sessionGeneration) return;\n\t\t\tawait showChromeDevtoolsToolWorkflow(pi, ctx, generation);\n\t\t\treturn;\n\t\t}\n\t\tcase \"enable\":\n\t\t\tawait updateChromeDevtoolsTools(\n\t\t\t\tpi,\n\t\t\t\tctx,\n\t\t\t\tallChromeDevtoolsTools(ctx.sessionManager),\n\t\t\t\t\"made all available\",\n\t\t\t);\n\t\t\treturn;\n\t\tcase \"disable\":\n\t\t\tawait updateChromeDevtoolsTools(pi, ctx, [], \"made all unavailable\");\n\t\t\treturn;\n\t}\n\n\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\tthrow new Error(`Unknown /chrome-devtools command: ${args.trim()}`);\n\t}\n\tctx.ui.notify(\n\t\t`Unknown /chrome-devtools command: ${args.trim()}\n\n${buildCommandGuide(ctx.sessionManager)}`,\n\t\t\"warning\",\n\t);\n}\n\nfunction requireObservableUi(ctx: CommandContext, route: string) {\n\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\tthrow new Error(`/chrome-devtools ${route} requires TUI or RPC mode`);\n\t}\n}\n\nasync function showMenu(pi: ExtensionAPI, ctx: CommandContext, generation: number) {\n\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\tthrow new Error(\"/chrome-devtools menu requires TUI or RPC mode; use a direct subcommand\");\n\t}\n\tconst { showChromeDevtoolsMenu } = await import(\"./menu.js\");\n\tif (generation !== state.sessionGeneration) return;\n\tawait showChromeDevtoolsMenu(pi, ctx, generation);\n}\n\nfunction replaceSessionController(reason: string) {\n\tstate.sessionController.abort(new DOMException(reason, \"AbortError\"));\n\tstate.sessionController = new AbortController();\n}\n\nexport function parseCommand(args: string): CommandAction | \"unknown\" {\n\tconst command = args.trim().toLowerCase();\n\tif (!command) return \"menu\";\n\tif (command === \"help\") return \"help\";\n\tif (command === \"quickstart\") return \"quickstart\";\n\tif (command === \"status\") return \"status\";\n\tif (command === \"settings\") return \"settings\";\n\tif (command === \"tools\" || command === \"select\" || command === \"toggle\") return \"tools\";\n\tif (command === \"enable\" || command === \"on\") return \"enable\";\n\tif (command === \"disable\" || command === \"off\") return \"disable\";\n\treturn \"unknown\";\n}\n\nexport function commandCompletions(prefix: string) {\n\tconst normalized = prefix.trimStart().toLowerCase();\n\tif (/\\s/.test(normalized)) return null;\n\n\tconst matches = COMMAND_COMPLETIONS.filter((completion) =>\n\t\tcompletion.value.startsWith(normalized),\n\t);\n\treturn matches.length > 0 ? matches : null;\n}\n\nexport {\n\tformatHostForUrl,\n\tisLocalDevToolsHost,\n\tquoteCommandPart,\n} from \"./browser-manager.js\";\nexport { CHROME_DEVTOOLS_LOAD_TOOL_NAME } from \"./lazy-tools.js\";\nexport { parseConfiguredPort } from \"./runtime.js\";\nexport {\n\thasParentPathSegment,\n\tisPathInsideRoot,\n\tresolveScreenshotPath,\n\tselectAllowedRoot,\n} from \"./screenshot.js\";\nexport { normalizeChromeDevtoolsSettings } from \"./settings.js\";\nexport {\n\torderedChromeDevtoolsTools,\n\tsanitizeChromeDevtoolsDisplay,\n} from \"./tool-selector.js\";\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { defineTool } from \"@earendil-works/pi-coding-agent\";\nimport { Type } from \"typebox\";\nimport {\n\tformatPage,\n\tgetPage,\n\tlistPages,\n\tresolvePage,\n\tresolvePageForNavigation,\n\tsetActivePageId,\n\ttextResult,\n\twithCdp,\n} from \"./cdp-client.js\";\nimport { renderScreenshotResult, renderTextResult, renderToolCall, withStatus } from \"./render.js\";\nimport { formatScreenshotText, saveScreenshot, throwIfAborted } from \"./screenshot.js\";\nimport { CHROME_DEVTOOLS_TOOL_NAMES, WEBMCP_TOOL_NAMES } from \"./tool-names.js\";\n\nexport const listPagesTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[0],\n\tlabel: \"Chrome DevTools: List Pages\",\n\tdescription: \"List Chrome tabs/pages from a running Chrome DevTools Protocol endpoint.\",\n\tparameters: Type.Object({}),\n\trenderCall: renderToolCall(\"list pages\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, _params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"list pages\", async () => {\n\t\t\tconst pages = await listPages({ sessionOwner: ctx.sessionManager, signal });\n\t\t\treturn textResult(JSON.stringify(pages.map(formatPage), null, 2), { pages });\n\t\t});\n\t},\n});\n\nexport const selectPageTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[1],\n\tlabel: \"Chrome DevTools: Select Page\",\n\tdescription: \"Select the active Chrome page for later chrome_devtools_* tool calls.\",\n\tparameters: Type.Object({\n\t\tpageId: Type.String({ description: \"Page id from chrome_devtools_list_pages.\" }),\n\t}),\n\trenderCall: renderToolCall(\"select page\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"select page\", async () => {\n\t\t\tconst page = await getPage(params.pageId, { sessionOwner: ctx.sessionManager, signal });\n\t\t\tsetActivePageId(ctx.sessionManager, page.id);\n\t\t\treturn textResult(`Selected page ${page.id}: ${page.title}\\n${page.url}`, {\n\t\t\t\tpage: formatPage(page),\n\t\t\t});\n\t\t});\n\t},\n});\n\nexport const navigateTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[2],\n\tlabel: \"Chrome DevTools: Navigate\",\n\tdescription:\n\t\t\"Navigate a Chrome page to a URL through Chrome DevTools Protocol, creating a page first if none is available.\",\n\tparameters: Type.Object({\n\t\turl: Type.String({ description: \"URL to navigate to.\" }),\n\t\tpageId: Type.Optional(\n\t\t\tType.String({ description: \"Optional page id. Defaults to selected or first page.\" }),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"navigate\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"navigate\", async () => {\n\t\t\tconst { created, page } = await resolvePageForNavigation(params.pageId, {\n\t\t\t\tsessionOwner: ctx.sessionManager,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tconst result = await withCdp(page, async (client) => {\n\t\t\t\tawait client.send(\"Page.enable\");\n\t\t\t\treturn client.send(\"Page.navigate\", { url: params.url });\n\t\t\t});\n\n\t\t\tsetActivePageId(ctx.sessionManager, page.id);\n\t\t\tconst action = created ? \"Created page and navigated\" : \"Navigated\";\n\t\t\treturn textResult(`${action} ${page.id} to ${params.url}`, {\n\t\t\t\tcreated,\n\t\t\t\tpage: formatPage(page),\n\t\t\t\tresult,\n\t\t\t});\n\t\t});\n\t},\n});\n\nexport const evaluateTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[3],\n\tlabel: \"Chrome DevTools: Evaluate\",\n\tdescription: \"Evaluate JavaScript in a Chrome page through Chrome DevTools Protocol.\",\n\tparameters: Type.Object({\n\t\texpression: Type.String({ description: \"JavaScript expression to evaluate.\" }),\n\t\tpageId: Type.Optional(\n\t\t\tType.String({ description: \"Optional page id. Defaults to selected or first page.\" }),\n\t\t),\n\t\tawaitPromise: Type.Optional(\n\t\t\tType.Boolean({ description: \"Whether to await a returned Promise. Defaults to true.\" }),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"evaluate\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"evaluate\", async () => {\n\t\t\tconst page = await resolvePage(params.pageId, {\n\t\t\t\tsessionOwner: ctx.sessionManager,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tconst result = await withCdp(page, (client) =>\n\t\t\t\tclient.send(\"Runtime.evaluate\", {\n\t\t\t\t\texpression: params.expression,\n\t\t\t\t\tawaitPromise: params.awaitPromise ?? true,\n\t\t\t\t\treturnByValue: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tsetActivePageId(ctx.sessionManager, page.id);\n\t\t\treturn textResult(JSON.stringify(result, null, 2), { page: formatPage(page), result });\n\t\t});\n\t},\n});\n\nexport const webMcpListToolsTool = defineTool({\n\tname: WEBMCP_TOOL_NAMES[0],\n\tlabel: \"Chrome DevTools: List WebMCP Tools (Experimental)\",\n\tdescription:\n\t\t\"List bounded page-provided WebMCP tool descriptors from the selected Chrome page through the experimental CDP WebMCP domain. Definitions remain in this tool result and are never registered as dynamic Pi tools.\",\n\tparameters: Type.Object({\n\t\tpageId: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Optional page id. Defaults to selected or first page.\",\n\t\t\t\tmaxLength: 512,\n\t\t\t}),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"list WebMCP tools\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"list WebMCP tools\", async () => {\n\t\t\tconst implementation = await import(\"./webmcp/tools.js\");\n\t\t\treturn implementation.executeWebMcpListTool(params, signal, ctx);\n\t\t});\n\t},\n});\n\nexport const webMcpCallTool = defineTool({\n\tname: WEBMCP_TOOL_NAMES[1],\n\tlabel: \"Chrome DevTools: Call WebMCP Tool (Experimental)\",\n\tdescription:\n\t\t\"Call one previously listed page-provided WebMCP tool after exact page, document, frame, origin, schema, annotation, and session revalidation. Every call requires observable user confirmation and output is bounded to Pi's 50 KB or 2,000-line limit.\",\n\tparameters: Type.Object({\n\t\tsessionGeneration: Type.String({\n\t\t\tdescription: \"Session generation token returned by chrome_devtools_webmcp_list_tools.\",\n\t\t\tmaxLength: 100,\n\t\t}),\n\t\tpageId: Type.String({ description: \"Page id returned by the list tool.\", maxLength: 512 }),\n\t\tdocumentId: Type.String({\n\t\t\tdescription: \"Document loader id returned by the list tool.\",\n\t\t\tmaxLength: 512,\n\t\t}),\n\t\tframeId: Type.String({ description: \"Frame id returned by the list tool.\", maxLength: 512 }),\n\t\tframeOrigin: Type.String({\n\t\t\tdescription: \"Exact frame origin returned by the list tool.\",\n\t\t\tmaxLength: 2_048,\n\t\t}),\n\t\ttoolName: Type.String({\n\t\t\tdescription: \"Exact tool name returned by the list tool.\",\n\t\t\tmaxLength: 512,\n\t\t}),\n\t\tschemaDigest: Type.String({\n\t\t\tdescription: \"Schema and annotation digest returned by the list tool.\",\n\t\t\tminLength: 64,\n\t\t\tmaxLength: 64,\n\t\t\tpattern: \"^[a-f0-9]{64}$\",\n\t\t}),\n\t\tinput: Type.Record(Type.String(), Type.Unknown(), {\n\t\t\tdescription: \"JSON object matching the page-provided input schema.\",\n\t\t}),\n\t}),\n\trenderCall: renderToolCall(\"call WebMCP tool\"),\n\trenderResult: renderTextResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"call WebMCP tool\", async () => {\n\t\t\tconst implementation = await import(\"./webmcp/tools.js\");\n\t\t\treturn implementation.executeWebMcpCallTool(params, signal, ctx);\n\t\t});\n\t},\n});\n\nexport const screenshotTool = defineTool({\n\tname: CHROME_DEVTOOLS_TOOL_NAMES[4],\n\tlabel: \"Chrome DevTools: Screenshot\",\n\tdescription: \"Capture a PNG screenshot from a Chrome page through Chrome DevTools Protocol.\",\n\tparameters: Type.Object({\n\t\tpageId: Type.Optional(\n\t\t\tType.String({ description: \"Optional page id. Defaults to selected or first page.\" }),\n\t\t),\n\t\tfullPage: Type.Optional(\n\t\t\tType.Boolean({ description: \"Capture the full document, not just the viewport.\" }),\n\t\t),\n\t\tsavePath: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Screenshot is always saved as a PNG file. Optional output path; omitted defaults to a unique temp file. Relative paths resolve from the current working directory. A single leading @ is stripped to match Pi file-mention paths. Existing regular files are replaced.\",\n\t\t\t}),\n\t\t),\n\t}),\n\trenderCall: renderToolCall(\"screenshot\"),\n\trenderResult: renderScreenshotResult,\n\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\treturn withStatus(ctx, \"screenshot\", async () => {\n\t\t\tconst page = await resolvePage(params.pageId, {\n\t\t\t\tsessionOwner: ctx.sessionManager,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t\tconst result = await withCdp(page, async (client) => {\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tawait client.send(\"Page.enable\");\n\n\t\t\t\tif (!params.fullPage) {\n\t\t\t\t\treturn client.send<{ data: string }>(\"Page.captureScreenshot\", { format: \"png\" });\n\t\t\t\t}\n\n\t\t\t\tconst metrics = await client.send<{\n\t\t\t\t\tcontentSize: { x: number; y: number; width: number; height: number };\n\t\t\t\t}>(\"Page.getLayoutMetrics\");\n\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\treturn client.send<{ data: string }>(\"Page.captureScreenshot\", {\n\t\t\t\t\tcaptureBeyondViewport: true,\n\t\t\t\t\tformat: \"png\",\n\t\t\t\t\tclip: {\n\t\t\t\t\t\tx: metrics.contentSize.x,\n\t\t\t\t\t\ty: metrics.contentSize.y,\n\t\t\t\t\t\twidth: metrics.contentSize.width,\n\t\t\t\t\t\theight: metrics.contentSize.height,\n\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tsetActivePageId(ctx.sessionManager, page.id);\n\t\t\tconst savedScreenshot = await saveScreenshot(result.data, params.savePath, ctx.cwd, signal);\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: formatScreenshotText(page, savedScreenshot),\n\t\t\t\t\t},\n\t\t\t\t\t{ type: \"image\", data: result.data, mimeType: \"image/png\" },\n\t\t\t\t],\n\t\t\t\tdetails: {\n\t\t\t\t\tpage: formatPage(page),\n\t\t\t\t\tbytes: savedScreenshot.bytes,\n\t\t\t\t\tsavedPath: savedScreenshot.savedPath,\n\t\t\t\t\tisDefaultPath: savedScreenshot.isDefaultPath,\n\t\t\t\t},\n\t\t\t};\n\t\t});\n\t},\n});\n", "import type { AgentToolResult, ToolRenderResultOptions } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { sanitizeChromeDevtoolsDisplay } from \"./display.js\";\n\nconst STATUS_KEY = \"chrome-devtools\";\ninterface StatusContext {\n\tsessionManager: object;\n\tui: { setStatus: (key: string, value: string | undefined) => void };\n}\ninterface RenderTheme {\n\tbold(text: string): string;\n\tfg(color: string, text: string): string;\n}\nconst activeStatuses = new WeakMap<object, Array<{ status: string; token: symbol }>>();\n\ninterface RenderComponent {\n\tinvalidate(): void;\n\trender(width: number): string[];\n}\n\nexport function renderToolCall(action: string) {\n\treturn () => new PiTextComponent(`Chrome DevTools: ${action}`);\n}\n\nexport function renderTextResult(\n\tresult: AgentToolResult<unknown>,\n\toptions: ToolRenderResultOptions,\n\ttheme: RenderTheme,\n) {\n\tconst output = formatCollapsibleOutput(textContent(result), options);\n\treturn new PiTextComponent(output.text, theme, output.color);\n}\n\nexport function renderScreenshotResult(\n\tresult: AgentToolResult<unknown>,\n\toptions: ToolRenderResultOptions,\n\ttheme: RenderTheme,\n): RenderComponent {\n\tconst output = formatCollapsibleOutput(screenshotTextContent(result), options);\n\treturn new PiTextComponent(output.text, theme, output.color);\n}\n\nfunction textContent(result: AgentToolResult<unknown>) {\n\treturn result.content\n\t\t.flatMap((content) => (content.type === \"text\" ? [content.text] : []))\n\t\t.join(\"\\n\");\n}\n\nfunction screenshotTextContent(result: AgentToolResult<unknown>) {\n\tconst text = textContent(result);\n\tif (text.trim()) return text;\n\n\tconst details = result.details as { savedPath?: unknown; bytes?: unknown } | undefined;\n\tif (typeof details?.savedPath !== \"string\") return text;\n\tconst bytes = typeof details.bytes === \"number\" ? ` (${details.bytes} bytes)` : \"\";\n\treturn `Saved screenshot to ${details.savedPath}${bytes}`;\n}\n\nfunction formatCollapsibleOutput(\n\ttext: string,\n\toptions: ToolRenderResultOptions,\n): { text: string; color?: string } {\n\tif (options.isPartial) return { text: \"Running...\", color: \"warning\" };\n\tif (options.expanded) return { text, color: \"toolOutput\" };\n\n\treturn { text: \"\" };\n}\n\nclass PiTextComponent implements RenderComponent {\n\tprivate text: string;\n\tprivate readonly theme?: RenderTheme;\n\tprivate readonly color?: string;\n\n\tconstructor(text = \"\", theme?: RenderTheme, color?: string) {\n\t\tthis.text = text;\n\t\tthis.theme = theme;\n\t\tthis.color = color;\n\t}\n\n\tsetText(text: string) {\n\t\tthis.text = text;\n\t}\n\n\tinvalidate() {\n\t\t// Stateless renderer: no cached layout to invalidate.\n\t}\n\n\trender(width: number) {\n\t\tconst sanitizedText = sanitizeChromeDevtoolsDisplay(this.text);\n\t\tif (!sanitizedText.trim()) return [];\n\t\treturn sanitizedText\n\t\t\t.replace(/\\t/g, \" \")\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((line) => {\n\t\t\t\tconst truncatedLine = truncateToWidth(line, Math.max(0, width), \"\");\n\t\t\t\treturn this.theme && this.color ? this.theme.fg(this.color, truncatedLine) : truncatedLine;\n\t\t\t});\n\t}\n}\n\nexport async function withStatus<T>(\n\tctx: StatusContext,\n\tstatus: string,\n\tcallback: () => Promise<T>,\n) {\n\tconst token = Symbol(status);\n\tconst statuses = activeStatuses.get(ctx.sessionManager) ?? [];\n\tstatuses.push({ status, token });\n\tactiveStatuses.set(ctx.sessionManager, statuses);\n\tctx.ui.setStatus(STATUS_KEY, status);\n\ttry {\n\t\treturn await callback();\n\t} finally {\n\t\tconst index = statuses.findIndex((entry) => entry.token === token);\n\t\tif (index >= 0) statuses.splice(index, 1);\n\t\tconst remaining = statuses.at(-1)?.status;\n\t\tif (statuses.length === 0) activeStatuses.delete(ctx.sessionManager);\n\t\tctx.ui.setStatus(STATUS_KEY, remaining);\n\t}\n}\n", "import { randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, realpath, rename, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { withFileMutationQueue } from \"@earendil-works/pi-coding-agent\";\nimport type { DevToolsPage } from \"./runtime.js\";\n\nfunction unique<T>(values: T[]) {\n\treturn Array.from(new Set(values));\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n\treturn error instanceof Error && \"code\" in error;\n}\n\nexport interface ResolvedScreenshotPath {\n\tpath: string;\n\tallowedRoots: string[];\n\tisDefault: boolean;\n}\nexport interface ScreenshotSaveResult {\n\tsavedPath: string;\n\tbytes: number;\n\tisDefaultPath: boolean;\n}\n\nexport async function saveScreenshot(\n\tbase64Png: string,\n\tsavePath: string | undefined,\n\tcwd: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ScreenshotSaveResult> {\n\tconst resolvedPath = resolveScreenshotPath(savePath, cwd);\n\tconst pngBytes = Buffer.from(base64Png, \"base64\");\n\n\tawait withFileMutationQueue(resolvedPath.path, async () => {\n\t\tthrowIfAborted(signal);\n\t\tawait ensureSafeScreenshotParent(resolvedPath);\n\t\tawait assertSafeScreenshotTargetPath(resolvedPath);\n\t\tawait writeScreenshotFileSafely(resolvedPath, pngBytes, signal);\n\t});\n\n\treturn {\n\t\tsavedPath: resolvedPath.path,\n\t\tbytes: pngBytes.byteLength,\n\t\tisDefaultPath: resolvedPath.isDefault,\n\t};\n}\n\nexport function resolveScreenshotPath(\n\tsavePath: string | undefined,\n\tcwd: string,\n): ResolvedScreenshotPath {\n\tconst cwdRoot = resolve(cwd);\n\tconst tempRoot = resolve(tmpdir());\n\n\tif (savePath === undefined) {\n\t\treturn {\n\t\t\tpath: join(tempRoot, `pi-chrome-devtools-screenshot-${randomUUID()}.png`),\n\t\t\tallowedRoots: [tempRoot],\n\t\t\tisDefault: true,\n\t\t};\n\t}\n\n\tconst normalizedPath = stripLeadingAtPath(savePath);\n\tif (!normalizedPath.trim()) {\n\t\tthrow new Error(\"Screenshot savePath must not be empty.\");\n\t}\n\tif (normalizedPath.includes(\"\\0\")) {\n\t\tthrow new Error(\"Screenshot savePath must not contain NUL bytes.\");\n\t}\n\tif (hasParentPathSegment(normalizedPath)) {\n\t\tthrow new Error(\"Screenshot savePath must not contain '..' path segments.\");\n\t}\n\n\tconst isAbsolutePath = isAbsolute(normalizedPath);\n\tconst path = isAbsolutePath ? resolve(normalizedPath) : resolve(cwdRoot, normalizedPath);\n\tconst allowedRoots = isAbsolutePath ? unique([cwdRoot, tempRoot]) : [cwdRoot];\n\tif (!allowedRoots.some((root) => isPathInsideRoot(path, root))) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath must be relative to the current working directory, or an absolute path inside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n\n\treturn { path, allowedRoots, isDefault: false };\n}\n\nfunction stripLeadingAtPath(path: string) {\n\treturn path.startsWith(\"@\") ? path.slice(1) : path;\n}\n\nexport function hasParentPathSegment(path: string) {\n\treturn path.split(/[\\\\/]+/).some((part) => part === \"..\");\n}\n\nasync function ensureSafeScreenshotParent(resolvedPath: ResolvedScreenshotPath) {\n\tconst parentPath = dirname(resolvedPath.path);\n\tconst rootPath = selectAllowedRoot(parentPath, resolvedPath.allowedRoots);\n\tif (!rootPath) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath parent must stay inside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n\n\tconst realRootPath = await realpath(rootPath);\n\tlet currentPath = rootPath;\n\tconst parentSegments = relative(rootPath, parentPath)\n\t\t.split(/[\\\\/]+/)\n\t\t.filter((part) => part.length > 0);\n\n\tfor (const segment of parentSegments) {\n\t\tcurrentPath = join(currentPath, segment);\n\t\tawait ensureSafeDirectorySegment(currentPath, realRootPath);\n\t}\n}\n\nexport function selectAllowedRoot(path: string, roots: readonly string[]) {\n\tconst matchingRoots = roots.filter((root) => isPathInsideRoot(path, root));\n\tmatchingRoots.sort(\n\t\t(left, right) =>\n\t\t\tnormalizePathForComparison(resolve(right)).length -\n\t\t\tnormalizePathForComparison(resolve(left)).length,\n\t);\n\treturn matchingRoots[0];\n}\n\nasync function ensureSafeDirectorySegment(path: string, realRootPath: string) {\n\tconst existingDirectory = await lstat(path).catch(async (error: unknown) => {\n\t\tif (!isNodeError(error) || error.code !== \"ENOENT\") throw error;\n\t\tawait mkdir(path).catch((mkdirError: unknown) => {\n\t\t\tif (!isNodeError(mkdirError) || mkdirError.code !== \"EEXIST\") throw mkdirError;\n\t\t});\n\t\treturn lstat(path);\n\t});\n\n\tif (existingDirectory.isSymbolicLink()) {\n\t\tthrow new Error(\"Screenshot savePath parent directories must not contain symbolic links.\");\n\t}\n\tif (!existingDirectory.isDirectory()) {\n\t\tthrow new Error(\"Screenshot savePath parent must be a directory.\");\n\t}\n\tawait assertPathWithinRealRoot(path, realRootPath);\n}\n\nasync function assertSafeScreenshotTargetPath(resolvedPath: ResolvedScreenshotPath) {\n\tconst existingTarget = await lstat(resolvedPath.path).catch((error: unknown) => {\n\t\tif (isNodeError(error) && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t});\n\tif (existingTarget?.isSymbolicLink()) {\n\t\tthrow new Error(\"Screenshot savePath must not point to a symbolic link.\");\n\t}\n\tif (existingTarget?.isDirectory()) {\n\t\tthrow new Error(\"Screenshot savePath must point to a file, not a directory.\");\n\t}\n\tif (existingTarget && !existingTarget.isFile()) {\n\t\tthrow new Error(\"Screenshot savePath may only replace regular files.\");\n\t}\n\n\tconst realAllowedRoots = await Promise.all(resolvedPath.allowedRoots.map(realpathOrResolvedPath));\n\tconst realParent = await realpath(dirname(resolvedPath.path));\n\tconst realTargetPath = join(realParent, basename(resolvedPath.path));\n\tif (!realAllowedRoots.some((root) => isPathInsideRoot(realTargetPath, root))) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath resolves outside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n}\n\nasync function assertPathWithinRealRoot(path: string, realRootPath: string) {\n\tconst realPath = await realpath(path);\n\tif (!isPathInsideRoot(realPath, realRootPath)) {\n\t\tthrow new Error(\n\t\t\t\"Screenshot savePath parent resolves outside the current working directory or OS temp directory.\",\n\t\t);\n\t}\n}\n\nasync function writeScreenshotFileSafely(\n\tresolvedPath: ResolvedScreenshotPath,\n\tpngBytes: Buffer,\n\tsignal: AbortSignal | undefined,\n) {\n\tconst tempFile = join(\n\t\tdirname(resolvedPath.path),\n\t\t`.${basename(resolvedPath.path)}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`,\n\t);\n\ttry {\n\t\tawait writeFile(tempFile, pngBytes, { flag: \"wx\", signal });\n\t\tthrowIfAborted(signal);\n\t\tawait replaceScreenshotFile(resolvedPath, tempFile, signal);\n\t} catch (error) {\n\t\tawait rm(tempFile, { force: true }).catch(() => undefined);\n\t\tthrow error;\n\t}\n}\n\nasync function replaceScreenshotFile(\n\tresolvedPath: ResolvedScreenshotPath,\n\ttempFile: string,\n\tsignal: AbortSignal | undefined,\n) {\n\ttry {\n\t\tawait rename(tempFile, resolvedPath.path);\n\t\treturn;\n\t} catch (error) {\n\t\tif (!shouldRetryRenameAfterRemovingDestination(error)) throw error;\n\t}\n\n\t// Some Windows filesystems reject renaming over an existing file. Revalidate before\n\t// removing the destination so the fallback still refuses directories and symlinks.\n\tawait assertSafeScreenshotTargetPath(resolvedPath);\n\tthrowIfAborted(signal);\n\tawait rm(resolvedPath.path, { force: true });\n\tawait rename(tempFile, resolvedPath.path);\n}\n\nfunction shouldRetryRenameAfterRemovingDestination(error: unknown) {\n\treturn (\n\t\tprocess.platform === \"win32\" &&\n\t\tisNodeError(error) &&\n\t\t[\"EACCES\", \"EEXIST\", \"EPERM\"].includes(error.code ?? \"\")\n\t);\n}\n\nasync function realpathOrResolvedPath(path: string) {\n\treturn realpath(path).catch(() => resolve(path));\n}\n\nexport function isPathInsideRoot(path: string, root: string) {\n\tconst normalizedPath = normalizePathForComparison(resolve(path));\n\tconst normalizedRoot = normalizePathForComparison(resolve(root));\n\tif (normalizedPath === normalizedRoot) return true;\n\tconst rootWithSeparator = normalizedRoot.endsWith(sep)\n\t\t? normalizedRoot\n\t\t: `${normalizedRoot}${sep}`;\n\treturn normalizedPath.startsWith(rootWithSeparator);\n}\n\nfunction normalizePathForComparison(path: string) {\n\treturn process.platform === \"win32\" ? path.toLowerCase() : path;\n}\n\nexport function throwIfAborted(signal: AbortSignal | undefined) {\n\tif (!signal?.aborted) return;\n\tthrow signal.reason instanceof Error ? signal.reason : new Error(\"Screenshot capture cancelled.\");\n}\n\nexport function formatScreenshotText(page: DevToolsPage, screenshot: ScreenshotSaveResult) {\n\tconst pathLabel = screenshot.isDefaultPath ? \"Saved to temp file\" : \"Saved to\";\n\treturn [\n\t\t`Captured PNG screenshot from ${page.title || page.url || page.id}.`,\n\t\t`${pathLabel}: ${screenshot.savedPath}`,\n\t\t`Bytes: ${screenshot.bytes}`,\n\t\t`Use read({ path: ${JSON.stringify(screenshot.savedPath)} }) to inspect the saved screenshot if inline image content is not available.`,\n\t].join(\"\\n\");\n}\n", "import type { ExtensionAPI, ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport {\n\tshutdownManagedBrowser,\n\tstartManagedBrowserSession,\n\tsyncManagedBrowserSettings,\n} from \"./browser-manager.js\";\nimport { setActivePageId } from \"./cdp-client.js\";\nimport {\n\tavailableChromeDevtoolsTools,\n\tconfigureChromeDevtoolsToolExposure,\n\tcreateChromeDevtoolsLoadTool,\n\tinitializeAvailableChromeDevtoolsTools,\n\trequireEagerChromeDevtoolsToolExposure,\n\tsetChromeDevtoolsSessionOwner,\n\tsupportsNativeDeferredToolLoading,\n} from \"./lazy-tools.js\";\nimport {\n\tapplyRuntimeBrowserSettings,\n\tapplyRuntimeWebMcpSetting,\n\tinvalidateWebMcpOperations,\n\tsetWebMcpSessionOwner,\n\tstate,\n\twebMcpEnabled,\n} from \"./runtime.js\";\nimport { loadSettings, waitForSettingsWrites } from \"./settings.js\";\nimport {\n\tallChromeDevtoolsTools,\n\tbuildCommandGuide,\n\tbuildQuickstartMessage,\n\tbuildToolStatusMessage,\n\tsanitizeChromeDevtoolsDisplay,\n\tupdateChromeDevtoolsTools,\n\twaitForChromeDevtoolsSettings,\n} from \"./tool-selector.js\";\nimport {\n\tevaluateTool,\n\tlistPagesTool,\n\tnavigateTool,\n\tscreenshotTool,\n\tselectPageTool,\n\twebMcpCallTool,\n\twebMcpListToolsTool,\n} from \"./tools.js\";\n\ntype CommandAction =\n\t| \"menu\"\n\t| \"help\"\n\t| \"quickstart\"\n\t| \"status\"\n\t| \"settings\"\n\t| \"tools\"\n\t| \"enable\"\n\t| \"disable\";\ntype CommandContext = ExtensionCommandContext;\nconst STATUS_KEY = \"chrome-devtools\";\nconst COMMAND_COMPLETIONS = [\n\t{ value: \"help\", label: \"help\", description: \"Show command usage\" },\n\t{ value: \"quickstart\", label: \"quickstart\", description: \"Show endpoint and launch help\" },\n\t{ value: \"status\", label: \"status\", description: \"Show tool and settings status\" },\n\t{ value: \"settings\", label: \"settings\", description: \"Edit browser connection settings\" },\n\t{ value: \"tools\", label: \"tools\", description: \"Choose available Chrome DevTools tools\" },\n\t{ value: \"toggle\", label: \"toggle\", description: \"Alias for tools\" },\n\t{ value: \"select\", label: \"select\", description: \"Compatibility alias for tools\" },\n\t{ value: \"enable\", label: \"enable\", description: \"Make all Chrome DevTools tools available\" },\n\t{ value: \"on\", label: \"on\", description: \"Compatibility alias for enable\" },\n\t{ value: \"disable\", label: \"disable\", description: \"Make all Chrome DevTools tools unavailable\" },\n\t{ value: \"off\", label: \"off\", description: \"Compatibility alias for disable\" },\n];\nexport default function chromeDevtools(pi: ExtensionAPI) {\n\tpi.registerTool(listPagesTool);\n\tpi.registerTool(selectPageTool);\n\tpi.registerTool(navigateTool);\n\tpi.registerTool(evaluateTool);\n\tpi.registerTool(screenshotTool);\n\tpi.registerTool(webMcpListToolsTool);\n\tpi.registerTool(webMcpCallTool);\n\tpi.registerTool(createChromeDevtoolsLoadTool(pi));\n\n\tpi.registerCommand(\"chrome-devtools\", {\n\t\tdescription: \"Open Chrome DevTools help and tool controls\",\n\t\tgetArgumentCompletions: (prefix) => commandCompletions(prefix),\n\t\thandler: async (args, ctx) => {\n\t\t\tsetChromeDevtoolsSessionOwner(pi, ctx.sessionManager);\n\t\t\tinitializeAvailableChromeDevtoolsTools(pi);\n\t\t\tconst generation = state.sessionGeneration;\n\t\t\tawait handleChromeDevtoolsCommand(pi, args, ctx, generation);\n\t\t},\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tconst generation = ++state.sessionGeneration;\n\t\tsetChromeDevtoolsSessionOwner(pi, ctx.sessionManager);\n\t\tinitializeAvailableChromeDevtoolsTools(pi);\n\t\tsetWebMcpSessionOwner(ctx.sessionManager);\n\t\treplaceSessionController(\"Chrome DevTools session replaced\");\n\t\tinvalidateWebMcpOperations(ctx.sessionManager, \"Chrome DevTools session replaced\");\n\t\tstate.shuttingDown = false;\n\t\tstate.settingsNotice = undefined;\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tawait shutdownManagedBrowser(undefined, {\n\t\t\tcancelLaunch: true,\n\t\t\towner: ctx.sessionManager,\n\t\t});\n\t\tstartManagedBrowserSession(ctx.sessionManager);\n\t\tif (generation !== state.sessionGeneration) return;\n\t\tsetActivePageId(ctx.sessionManager, undefined);\n\t\tstate.activePageId = undefined;\n\t\tstate.lastLaunchAttempt = undefined;\n\t\tconst projectTrusted = ctx.isProjectTrusted();\n\t\tconst settings = await loadSettings({ cwd: ctx.cwd, projectTrusted });\n\t\tif (generation !== state.sessionGeneration) return;\n\t\tapplyRuntimeBrowserSettings(settings.effectiveBrowser, settings.paths, projectTrusted);\n\t\tsyncManagedBrowserSettings(ctx.sessionManager, settings.effectiveBrowser);\n\t\tapplyRuntimeWebMcpSetting(settings.effectiveWebMcpEnabled, ctx.sessionManager);\n\t\tstate.settingsNotice = settings.notice;\n\t\tfor (const warning of settings.warnings) {\n\t\t\tctx.ui.notify(sanitizeChromeDevtoolsDisplay(warning), \"warning\");\n\t\t}\n\t\tif (webMcpEnabled(ctx.sessionManager)) {\n\t\t\tctx.ui.notify(\n\t\t\t\t\"Experimental WebMCP is enabled. Page-provided tools use the visible browser session and require confirmation for every call.\",\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t}\n\t\tconst availableTools =\n\t\t\tsettings.kind === \"loaded\" && settings.settings.tools\n\t\t\t\t? settings.settings.tools\n\t\t\t\t: availableChromeDevtoolsTools(pi);\n\t\tconfigureChromeDevtoolsToolExposure(pi, availableTools, ctx.model);\n\t});\n\n\tpi.on(\"model_select\", (event, ctx) => {\n\t\tinvalidateWebMcpOperations(\n\t\t\tctx.sessionManager,\n\t\t\t\"Chrome DevTools model and tool exposure changed\",\n\t\t);\n\t\tif (!supportsNativeDeferredToolLoading(event.model)) {\n\t\t\trequireEagerChromeDevtoolsToolExposure(pi);\n\t\t}\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tstate.sessionGeneration += 1;\n\t\treplaceSessionController(\"Chrome DevTools session shut down\");\n\t\tinvalidateWebMcpOperations(ctx.sessionManager, \"Chrome DevTools session shut down\");\n\t\tctx.ui.setStatus(STATUS_KEY, undefined);\n\t\tconst browserShutdown = shutdownManagedBrowser(undefined, {\n\t\t\tcancelLaunch: true,\n\t\t\towner: ctx.sessionManager,\n\t\t});\n\t\tawait waitForChromeDevtoolsSettings();\n\t\tawait waitForSettingsWrites();\n\t\tawait browserShutdown;\n\t});\n}\n\nasync function handleChromeDevtoolsCommand(\n\tpi: ExtensionAPI,\n\targs: string,\n\tctx: CommandContext,\n\tgeneration: number,\n) {\n\tconst command = parseCommand(args);\n\tswitch (command) {\n\t\tcase \"menu\":\n\t\t\tawait showMenu(pi, ctx, generation);\n\t\t\treturn;\n\t\tcase \"help\":\n\t\t\trequireObservableUi(ctx, \"help\");\n\t\t\tctx.ui.notify(buildCommandGuide(ctx.sessionManager), \"info\");\n\t\t\treturn;\n\t\tcase \"quickstart\":\n\t\t\trequireObservableUi(ctx, \"quickstart\");\n\t\t\tctx.ui.notify(buildQuickstartMessage(ctx.sessionManager), \"info\");\n\t\t\treturn;\n\t\tcase \"status\": {\n\t\t\trequireObservableUi(ctx, \"status\");\n\t\t\tconst status = await buildToolStatusMessage(pi, ctx.sessionManager);\n\t\t\tif (generation !== state.sessionGeneration) return;\n\t\t\tctx.ui.notify(status, \"info\");\n\t\t\treturn;\n\t\t}\n\t\tcase \"settings\": {\n\t\t\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\t\t\tthrow new Error(\"/chrome-devtools settings requires TUI or RPC mode\");\n\t\t\t}\n\t\t\tconst { showChromeDevtoolsBrowserSettings } = await import(\"./browser-settings-menu.js\");\n\t\t\tif (generation !== state.sessionGeneration) return;\n\t\t\tawait showChromeDevtoolsBrowserSettings(pi, ctx, generation);\n\t\t\treturn;\n\t\t}\n\t\tcase \"tools\": {\n\t\t\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\t\t\tthrow new Error(\"/chrome-devtools tools requires TUI or RPC mode\");\n\t\t\t}\n\t\t\tconst { showChromeDevtoolsToolWorkflow } = await import(\"./menu.js\");\n\t\t\tif (generation !== state.sessionGeneration) return;\n\t\t\tawait showChromeDevtoolsToolWorkflow(pi, ctx, generation);\n\t\t\treturn;\n\t\t}\n\t\tcase \"enable\":\n\t\t\tawait updateChromeDevtoolsTools(\n\t\t\t\tpi,\n\t\t\t\tctx,\n\t\t\t\tallChromeDevtoolsTools(ctx.sessionManager),\n\t\t\t\t\"made all available\",\n\t\t\t);\n\t\t\treturn;\n\t\tcase \"disable\":\n\t\t\tawait updateChromeDevtoolsTools(pi, ctx, [], \"made all unavailable\");\n\t\t\treturn;\n\t}\n\n\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\tthrow new Error(`Unknown /chrome-devtools command: ${args.trim()}`);\n\t}\n\tctx.ui.notify(\n\t\t`Unknown /chrome-devtools command: ${args.trim()}\n\n${buildCommandGuide(ctx.sessionManager)}`,\n\t\t\"warning\",\n\t);\n}\n\nfunction requireObservableUi(ctx: CommandContext, route: string) {\n\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\tthrow new Error(`/chrome-devtools ${route} requires TUI or RPC mode`);\n\t}\n}\n\nasync function showMenu(pi: ExtensionAPI, ctx: CommandContext, generation: number) {\n\tif (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\")) {\n\t\tthrow new Error(\"/chrome-devtools menu requires TUI or RPC mode; use a direct subcommand\");\n\t}\n\tconst { showChromeDevtoolsMenu } = await import(\"./menu.js\");\n\tif (generation !== state.sessionGeneration) return;\n\tawait showChromeDevtoolsMenu(pi, ctx, generation);\n}\n\nfunction replaceSessionController(reason: string) {\n\tstate.sessionController.abort(new DOMException(reason, \"AbortError\"));\n\tstate.sessionController = new AbortController();\n}\n\nexport function parseCommand(args: string): CommandAction | \"unknown\" {\n\tconst command = args.trim().toLowerCase();\n\tif (!command) return \"menu\";\n\tif (command === \"help\") return \"help\";\n\tif (command === \"quickstart\") return \"quickstart\";\n\tif (command === \"status\") return \"status\";\n\tif (command === \"settings\") return \"settings\";\n\tif (command === \"tools\" || command === \"select\" || command === \"toggle\") return \"tools\";\n\tif (command === \"enable\" || command === \"on\") return \"enable\";\n\tif (command === \"disable\" || command === \"off\") return \"disable\";\n\treturn \"unknown\";\n}\n\nexport function commandCompletions(prefix: string) {\n\tconst normalized = prefix.trimStart().toLowerCase();\n\tif (/\\s/.test(normalized)) return null;\n\n\tconst matches = COMMAND_COMPLETIONS.filter((completion) =>\n\t\tcompletion.value.startsWith(normalized),\n\t);\n\treturn matches.length > 0 ? matches : null;\n}\n\nexport {\n\tformatHostForUrl,\n\tisLocalDevToolsHost,\n\tquoteCommandPart,\n} from \"./browser-manager.js\";\nexport { CHROME_DEVTOOLS_LOAD_TOOL_NAME } from \"./lazy-tools.js\";\nexport { parseConfiguredPort } from \"./runtime.js\";\nexport {\n\thasParentPathSegment,\n\tisPathInsideRoot,\n\tresolveScreenshotPath,\n\tselectAllowedRoot,\n} from \"./screenshot.js\";\nexport { normalizeChromeDevtoolsSettings } from \"./settings.js\";\nexport {\n\torderedChromeDevtoolsTools,\n\tsanitizeChromeDevtoolsDisplay,\n} from \"./tool-selector.js\";\n"],
5
+ "mappings": "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",
6
6
  "names": ["STATUS_KEY"]
7
7
  }
@@ -0,0 +1,147 @@
1
+ # Pi Chrome DevTools browser setup reference
2
+
3
+ [Back to README](../README.md)
4
+
5
+ - [Browser connection](#-browser-setup)
6
+ - [Unpacked extensions](#unpacked-extensions)
7
+ - [Experimental WebMCP](#experimental-webmcp)
8
+ - [Deprecated overrides and manual endpoints](#deprecated-environment-overrides-and-manual-endpoints)
9
+
10
+ ## 🌐 Browser setup
11
+
12
+ Without unpacked extensions, the extension first tries `browser.endpoint`, defaulting to `http://127.0.0.1:9222`.
13
+ If that endpoint is unavailable and `browser.autoLaunch` is `true`, it lazily launches an extension-owned browser with an isolated temporary profile and retries the CDP request.
14
+ The extension reuses existing endpoints and never terminates them.
15
+
16
+ Configure the canonical user file at `${PI_CODING_AGENT_DIR:-~/.pi/agent}/pi-chrome-devtools.json`:
17
+
18
+ ```json
19
+ {
20
+ "browser": {
21
+ "endpoint": "http://127.0.0.1:9222",
22
+ "autoLaunch": true,
23
+ "executablePath": "/absolute/path/to/chromium"
24
+ }
25
+ }
26
+ ```
27
+
28
+ `browser.endpoint` must be an HTTP origin with an explicit port and no credentials, path, query, or fragment.
29
+ Omitting it keeps attach-first behavior on `127.0.0.1:9222` and lets a managed launch use Chrome's dynamic DevTools port mode (`--remote-debugging-port=0`).
30
+ Saving an explicit endpoint pins managed launches to that port.
31
+ `browser.autoLaunch` defaults to `true`.
32
+ `browser.executablePath` is optional and must be an absolute path; when absent, normal browser discovery applies.
33
+
34
+ The configured endpoint must expose the standard CDP HTTP discovery routes such as `/json/version` and `/json/list`.
35
+ Chrome's newer built-in permission flow can listen on port `9222` while returning `404` from those routes; setting the same HTTP origin does not by itself make that flow compatible.
36
+
37
+ ### Unpacked extensions
38
+
39
+ > [!WARNING]
40
+ > An unpacked extension executes privileged browser code.
41
+ > Load only code you trust.
42
+ > Project settings are honored only when Pi reports the project as trusted.
43
+
44
+ Add trusted unpacked-extension paths to the same canonical user file:
45
+
46
+ ```json
47
+ {
48
+ "browser": {
49
+ "executablePath": "/absolute/path/to/chrome-for-testing",
50
+ "extensionPaths": [
51
+ "/absolute/path/to/unpacked-extension-one",
52
+ "/absolute/path/to/unpacked-extension-two"
53
+ ]
54
+ }
55
+ }
56
+ ```
57
+
58
+ Every user-file path must be absolute.
59
+ Each extension path must resolve to a directory containing a valid `manifest.json` and cannot contain a comma because Chrome uses commas to separate multiple startup paths.
60
+ For extension-configured sessions, `executablePath` must identify Chrome for Testing or Chromium.
61
+ Branded Google Chrome is rejected because tested releases can silently ignore unpacked-extension startup flags.
62
+
63
+ A trusted project can replace the user extension list in `<workspace>/.pi/pi-chrome-devtools.json`.
64
+ Relative paths resolve from the workspace (`ctx.cwd`):
65
+
66
+ ```json
67
+ {
68
+ "browser": {
69
+ "extensionPaths": ["./extension"]
70
+ }
71
+ }
72
+ ```
73
+
74
+ Project `extensionPaths` replace, rather than append to, the user array.
75
+ A project file cannot override `browser.endpoint`, `browser.autoLaunch`, or `browser.executablePath`; browser connection settings remain machine-owned user configuration.
76
+ Effective precedence is defaults, user settings, trusted project extension paths, then deprecated environment overrides.
77
+ No new environment variable is required.
78
+
79
+ When `extensionPaths` is non-empty, the extension skips attach-first behavior and starts an isolated, extension-owned managed browser with `--disable-extensions-except` and `--load-extension`.
80
+ It fails before spawning when the endpoint is remote, auto-launch is disabled, an explicit port is occupied, the executable is missing, or the browser product is unsupported.
81
+ It never adds extensions to, modifies, restarts, or closes an external browser.
82
+
83
+ Settings are loaded on session start.
84
+ After editing JSON, use `/reload` or replace the session; the old managed browser is closed before the new configuration is applied.
85
+ Missing files preserve the existing no-extension behavior.
86
+ Invalid JSON, invalid browser values, and missing manifests are left unchanged and ignored with an actionable warning.
87
+
88
+ ### Experimental WebMCP
89
+
90
+ > [!WARNING]
91
+ > WebMCP support is experimental, disabled by default, and subject to Chrome protocol changes.
92
+ > Page-provided tools operate the visible page with its current authentication, entitlement, and UI state.
93
+ > Every call requires observable confirmation, including tools that claim to be read-only.
94
+
95
+ Enable WebMCP only in the canonical user settings file:
96
+
97
+ ```json
98
+ {
99
+ "webmcp": {
100
+ "enabled": true
101
+ }
102
+ }
103
+ ```
104
+
105
+ A project `pi-chrome-devtools.json` cannot enable WebMCP or weaken confirmation policy.
106
+ The settings menu labels WebMCP as experimental, persists the user-owned boolean atomically, and aborts active WebMCP work before disablement or browser replacement.
107
+ The two gateway tools remain unavailable while WebMCP is disabled, even if an older `tools` array contains their names.
108
+ After enabling the gate, choose whether each gateway is available through `/chrome-devtools tools` or the main menu.
109
+
110
+ WebMCP requires a Chrome build whose `/json/protocol` exposes the experimental `WebMCP` domain.
111
+ Origin-trial sites must meet Chrome's Origin Trial, origin isolation, Permissions Policy, authentication, and browser feature requirements.
112
+ For local pages outside an Origin Trial, open `chrome://flags/#enable-webmcp-testing`, enable **WebMCP for testing**, and relaunch a compatible Chrome build.
113
+ The extension does not automatically add testing flags to a managed browser.
114
+
115
+ `chrome_devtools_webmcp_list_tools` opens an operation-scoped page CDP session, lists frame-aware page tools, and returns bounded metadata with a deterministic schema-and-annotation digest.
116
+ `chrome_devtools_webmcp_call_tool` re-discovers the exact page, document loader, frame, origin, tool, schema, annotations, and session generation before invocation and again after confirmation.
117
+ The call gateway rejects print and JSON modes because they cannot provide observable confirmation.
118
+ TUI and RPC modes use Pi's standard confirmation dialog.
119
+ Cancellation is forwarded to Chrome through `WebMCP.cancelInvocation` after Chrome returns an invocation ID.
120
+
121
+ Page URLs, origins, names, descriptions, schemas, errors, and outputs are untrusted.
122
+ Displayed text strips terminal controls and bidirectional overrides, accepted JSON has explicit byte, depth, and collection limits, and model-visible output is capped at 50 KB or 2,000 lines.
123
+ CDP text messages are capped at 8 MB before JSON parsing.
124
+ Page-controlled `pattern` and `patternProperties` schemas and schemas exceeding 128 combinator or dependent-schema branches are rejected because they cannot be evaluated safely on Pi's main thread.
125
+ Annotations are descriptive hints only and never authorize a call.
126
+ WebMCP is not backend MCP, generic browser automation, or a way to bypass browser policy.
127
+
128
+ If listing reports that the WebMCP domain is unavailable, update Chrome and verify `/json/protocol` contains `WebMCP`.
129
+ If a page lists no tools, verify that the site participates in the Origin Trial or that **WebMCP for testing** is enabled for local development.
130
+ If a call reports a stale identity, list tools again after navigation, reload, frame changes, tool registration changes, settings changes, or browser replacement.
131
+ Attached everyday browser profiles receive a stronger confirmation warning than isolated managed profiles.
132
+
133
+ ### Deprecated environment overrides and manual endpoints
134
+
135
+ The existing `PI_CHROME_DEVTOOLS_HOST`, `PI_CHROME_DEVTOOLS_PORT`, `PI_CHROME_DEVTOOLS_AUTO_LAUNCH`, and `PI_CHROME_DEVTOOLS_BROWSER` variables remain temporary compatibility overrides.
136
+ They still take precedence over JSON, but every session that sees one emits a deprecation warning.
137
+ Move their values to `browser.endpoint`, `browser.autoLaunch`, and `browser.executablePath`; the variables will be removed in a future version.
138
+
139
+ Without unpacked extensions, browser discovery still checks platform-specific Chrome, Chromium, Brave, and Microsoft Edge candidates.
140
+ Manual launch remains available when no unpacked extensions are configured:
141
+
142
+ ```bash
143
+ google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/pi-chrome-devtools
144
+ ```
145
+
146
+ On session shutdown, the extension terminates only browser processes it started and best-effort removes their temporary profiles.
147
+ It never closes user-started browsers or remote endpoints.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-chrome-devtools",
3
- "version": "0.53.0",
3
+ "version": "0.53.2",
4
4
  "description": "Pi extension that exposes Chrome DevTools Protocol tools.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,6 +17,7 @@
17
17
  "files": [
18
18
  "src",
19
19
  "dist",
20
+ "docs",
20
21
  "README.md",
21
22
  "LICENSE"
22
23
  ],
@@ -25,9 +26,6 @@
25
26
  "./dist/index.ts"
26
27
  ]
27
28
  },
28
- "piExtension": {
29
- "lifecycle": "stable"
30
- },
31
29
  "scripts": {
32
30
  "build": "node scripts/build-runtime.mjs",
33
31
  "check": "npm run build && biome check . && npm run typecheck",
@@ -37,13 +35,15 @@
37
35
  },
38
36
  "peerDependencies": {
39
37
  "@earendil-works/pi-coding-agent": "*",
38
+ "@earendil-works/pi-tui": "*",
40
39
  "typebox": "*"
41
40
  },
42
41
  "devDependencies": {
43
- "@biomejs/biome": "2.5.10",
44
- "@earendil-works/pi-coding-agent": "0.84.3",
42
+ "@biomejs/biome": "2.5.11",
43
+ "@earendil-works/pi-coding-agent": "0.85.0",
44
+ "@earendil-works/pi-tui": "0.85.0",
45
45
  "esbuild": "0.28.2",
46
- "typebox": "1.3.18",
46
+ "typebox": "1.3.25",
47
47
  "typescript": "7.0.2"
48
48
  },
49
49
  "dependencies": {