@narumitw/pi-chrome-devtools 0.52.3 → 0.53.1

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 (35) hide show
  1. package/README.md +62 -9
  2. package/dist/chunks/{browser-settings-menu-VWCNG4HB.js → browser-settings-menu-REIU7C3Q.js} +83 -7
  3. package/dist/chunks/browser-settings-menu-REIU7C3Q.js.map +7 -0
  4. package/dist/chunks/chunk-GTTYS5GT.js +384 -0
  5. package/dist/chunks/chunk-GTTYS5GT.js.map +7 -0
  6. package/dist/chunks/{chunk-RH6KESL3.js → chunk-SRT3UUIP.js} +473 -574
  7. package/dist/chunks/chunk-SRT3UUIP.js.map +7 -0
  8. package/dist/chunks/chunk-YJVYJ2L2.js +469 -0
  9. package/dist/chunks/chunk-YJVYJ2L2.js.map +7 -0
  10. package/dist/chunks/{menu-PLD3S3EO.js → menu-H5HMVGHI.js} +72 -39
  11. package/dist/chunks/menu-H5HMVGHI.js.map +7 -0
  12. package/dist/chunks/tools-6P7EUX37.js +964 -0
  13. package/dist/chunks/tools-6P7EUX37.js.map +7 -0
  14. package/dist/index.ts +166 -199
  15. package/dist/index.ts.map +4 -4
  16. package/package.json +4 -3
  17. package/src/browser-manager.ts +408 -169
  18. package/src/browser-settings-menu.ts +100 -5
  19. package/src/cdp-client.ts +347 -90
  20. package/src/chrome-devtools.ts +58 -11
  21. package/src/lazy-tools.ts +37 -4
  22. package/src/menu.ts +80 -39
  23. package/src/render.ts +12 -1
  24. package/src/runtime.ts +92 -0
  25. package/src/settings.ts +47 -10
  26. package/src/tool-names.ts +16 -1
  27. package/src/tool-selector.ts +85 -44
  28. package/src/tools.ts +91 -15
  29. package/src/webmcp/discovery.ts +235 -0
  30. package/src/webmcp/policy.ts +409 -0
  31. package/src/webmcp/protocol.ts +426 -0
  32. package/src/webmcp/tools.ts +190 -0
  33. package/dist/chunks/browser-settings-menu-VWCNG4HB.js.map +0 -7
  34. package/dist/chunks/chunk-RH6KESL3.js.map +0 -7
  35. package/dist/chunks/menu-PLD3S3EO.js.map +0 -7
package/dist/index.ts CHANGED
@@ -1,199 +1,54 @@
1
1
  // @generated by scripts/build-runtime.mjs; do not edit.
2
2
  // @ts-nocheck -- the generated entry uses a .ts extension for Pi's Jiti loader.
3
3
  import {
4
- CHROME_DEVTOOLS_TOOL_NAMES,
5
- DEFAULT_ENDPOINT_WAIT_MS,
6
- DEFAULT_TIMEOUT_MS,
4
+ formatPage,
5
+ getPage,
6
+ listPages,
7
+ resolvePage,
8
+ resolvePageForNavigation,
9
+ setActivePageId,
10
+ textResult,
11
+ withCdp
12
+ } from "./chunks/chunk-GTTYS5GT.js";
13
+ import {
7
14
  allChromeDevtoolsTools,
8
- applyRuntimeBrowserSettings,
9
15
  availableChromeDevtoolsTools,
10
16
  buildCommandGuide,
11
17
  buildQuickstartMessage,
12
18
  buildToolStatusMessage,
13
19
  configureChromeDevtoolsToolExposure,
14
20
  createChromeDevtoolsLoadTool,
15
- devToolsEndpoint,
16
- ensureDevToolsEndpoint,
17
- fetchDevToolsJson,
18
- formatPageListItem,
19
21
  initializeAvailableChromeDevtoolsTools,
20
- launchHint,
21
- loadSettings,
22
22
  requireEagerChromeDevtoolsToolExposure,
23
23
  sanitizeChromeDevtoolsDisplay,
24
- shutdownManagedBrowser,
25
- state,
24
+ setChromeDevtoolsSessionOwner,
26
25
  supportsNativeDeferredToolLoading,
27
26
  updateChromeDevtoolsTools,
28
- waitForChromeDevtoolsSettings,
27
+ waitForChromeDevtoolsSettings
28
+ } from "./chunks/chunk-YJVYJ2L2.js";
29
+ import {
30
+ CHROME_DEVTOOLS_TOOL_NAMES,
31
+ WEBMCP_TOOL_NAMES,
32
+ applyRuntimeBrowserSettings,
33
+ applyRuntimeWebMcpSetting,
34
+ invalidateWebMcpOperations,
35
+ loadSettings,
36
+ setWebMcpSessionOwner,
37
+ shutdownManagedBrowser,
38
+ startManagedBrowserSession,
39
+ state,
40
+ syncManagedBrowserSettings,
29
41
  waitForSettingsWrites,
30
- withEndpointRetry
31
- } from "./chunks/chunk-RH6KESL3.js";
42
+ webMcpEnabled
43
+ } from "./chunks/chunk-SRT3UUIP.js";
32
44
 
33
45
  // src/tools.ts
34
46
  import { defineTool } from "@earendil-works/pi-coding-agent";
35
47
  import { Type } from "typebox";
36
48
 
37
- // src/cdp-client.ts
38
- async function listPages(options = {}) {
39
- const waitMs = options.waitMs ?? DEFAULT_ENDPOINT_WAIT_MS;
40
- await ensureDevToolsEndpoint(waitMs);
41
- return withEndpointRetry(async () => {
42
- const pages = await fetchDevToolsJson("/json/list");
43
- return pages.filter((page) => page.type === "page" && page.webSocketDebuggerUrl);
44
- }, waitMs);
45
- }
46
- async function getPage(pageId) {
47
- const pages = await listPages();
48
- return requirePage(pageId, pages);
49
- }
50
- async function resolvePage(pageId) {
51
- const pages = await listPages();
52
- if (pageId) return requirePage(pageId, pages);
53
- const page = resolveDefaultPage(pages);
54
- if (!page) {
55
- throw new Error(
56
- [
57
- `No Chrome pages found at ${devToolsEndpoint()}.`,
58
- "Use chrome_devtools_navigate with a URL to create a page, or open a Chrome tab manually.",
59
- launchHint()
60
- ].join("\n")
61
- );
62
- }
63
- return page;
64
- }
65
- async function resolvePageForNavigation(pageId) {
66
- const pages = await listPages();
67
- if (pageId) return { created: false, page: requirePage(pageId, pages) };
68
- const page = resolveDefaultPage(pages);
69
- if (page) return { created: false, page };
70
- return { created: true, page: await createPage("about:blank") };
71
- }
72
- function resolveDefaultPage(pages) {
73
- if (!state.activePageId) return pages[0];
74
- const activePage = pages.find((candidate) => candidate.id === state.activePageId);
75
- if (activePage) return activePage;
76
- state.activePageId = void 0;
77
- return pages[0];
78
- }
79
- function requirePage(pageId, pages) {
80
- const page = pages.find((candidate) => candidate.id === pageId);
81
- if (page) return page;
82
- const availablePages = pages.map(formatPageListItem).join("\n");
83
- throw new Error(
84
- [
85
- `Chrome DevTools page not found: ${pageId}.`,
86
- availablePages ? `Available pages:
87
- ${availablePages}` : "No inspectable Chrome pages are currently available."
88
- ].join("\n")
89
- );
90
- }
91
- async function createPage(url, options = {}) {
92
- const waitMs = options.waitMs ?? DEFAULT_ENDPOINT_WAIT_MS;
93
- await ensureDevToolsEndpoint(waitMs);
94
- const page = await withEndpointRetry(
95
- () => fetchDevToolsJson(`/json/new?${encodeURIComponent(url)}`, {
96
- method: "PUT"
97
- }),
98
- waitMs
99
- );
100
- if (page.type !== "page" || !page.webSocketDebuggerUrl) {
101
- throw new Error("Chrome DevTools created a target that is not an inspectable page.");
102
- }
103
- return page;
104
- }
105
- function formatPage(page) {
106
- return {
107
- id: page.id,
108
- type: page.type,
109
- title: page.title,
110
- url: page.url
111
- };
112
- }
113
- function textResult(text, details) {
114
- return {
115
- content: [{ type: "text", text }],
116
- details
117
- };
118
- }
119
- async function withCdp(page, callback) {
120
- if (!page.webSocketDebuggerUrl) throw new Error(`Page has no webSocketDebuggerUrl: ${page.id}`);
121
- const client = await CdpClient.connect(page.webSocketDebuggerUrl);
122
- try {
123
- return await callback(client);
124
- } finally {
125
- client.close();
126
- }
127
- }
128
- var CdpClient = class _CdpClient {
129
- #nextId = 1;
130
- #pending = /* @__PURE__ */ new Map();
131
- socket;
132
- constructor(socket) {
133
- this.socket = socket;
134
- socket.addEventListener("message", (event) => {
135
- const response = JSON.parse(String(event.data));
136
- if (typeof response.id !== "number") return;
137
- const pending = this.#pending.get(response.id);
138
- if (!pending) return;
139
- clearTimeout(pending.timeout);
140
- this.#pending.delete(response.id);
141
- if (response.error) {
142
- pending.reject(new Error(`CDP error ${response.error.code}: ${response.error.message}`));
143
- } else {
144
- pending.resolve(response.result);
145
- }
146
- });
147
- socket.addEventListener("close", () => {
148
- this.rejectAll(new Error("Chrome DevTools WebSocket closed"));
149
- });
150
- socket.addEventListener("error", () => {
151
- this.rejectAll(new Error("Chrome DevTools WebSocket error"));
152
- });
153
- }
154
- static connect(url) {
155
- return new Promise((resolve2, reject) => {
156
- const socket = new WebSocket(url);
157
- const timeout = setTimeout(() => {
158
- socket.close();
159
- reject(new Error(`Timed out connecting to Chrome DevTools WebSocket: ${url}`));
160
- }, DEFAULT_TIMEOUT_MS);
161
- socket.addEventListener("open", () => {
162
- clearTimeout(timeout);
163
- resolve2(new _CdpClient(socket));
164
- });
165
- socket.addEventListener("error", () => {
166
- clearTimeout(timeout);
167
- reject(new Error(`Failed to connect to Chrome DevTools WebSocket: ${url}`));
168
- });
169
- });
170
- }
171
- send(method, params) {
172
- const id = this.#nextId;
173
- this.#nextId += 1;
174
- return new Promise((resolve2, reject) => {
175
- const timeout = setTimeout(() => {
176
- this.#pending.delete(id);
177
- reject(new Error(`Timed out waiting for CDP response: ${method}`));
178
- }, DEFAULT_TIMEOUT_MS);
179
- this.#pending.set(id, { resolve: resolve2, reject, timeout });
180
- this.socket.send(JSON.stringify({ id, method, params: params ?? {} }));
181
- });
182
- }
183
- close() {
184
- this.socket.close();
185
- }
186
- rejectAll(error) {
187
- for (const [id, pending] of this.#pending) {
188
- clearTimeout(pending.timeout);
189
- pending.reject(error);
190
- this.#pending.delete(id);
191
- }
192
- }
193
- };
194
-
195
49
  // src/render.ts
196
50
  var STATUS_KEY = "chrome-devtools";
51
+ var activeStatuses = /* @__PURE__ */ new WeakMap();
197
52
  function renderToolCall(action) {
198
53
  return () => new PiTextComponent(`Chrome DevTools: ${action}`);
199
54
  }
@@ -247,11 +102,19 @@ function truncateLine(line, maxWidth) {
247
102
  return Array.from(line).slice(0, maxWidth).join("");
248
103
  }
249
104
  async function withStatus(ctx, status, callback) {
105
+ const token = Symbol(status);
106
+ const statuses = activeStatuses.get(ctx.sessionManager) ?? [];
107
+ statuses.push({ status, token });
108
+ activeStatuses.set(ctx.sessionManager, statuses);
250
109
  ctx.ui.setStatus(STATUS_KEY, status);
251
110
  try {
252
111
  return await callback();
253
112
  } finally {
254
- ctx.ui.setStatus(STATUS_KEY, void 0);
113
+ const index = statuses.findIndex((entry) => entry.token === token);
114
+ if (index >= 0) statuses.splice(index, 1);
115
+ const remaining = statuses.at(-1)?.status;
116
+ if (statuses.length === 0) activeStatuses.delete(ctx.sessionManager);
117
+ ctx.ui.setStatus(STATUS_KEY, remaining);
255
118
  }
256
119
  }
257
120
 
@@ -452,9 +315,9 @@ var listPagesTool = defineTool({
452
315
  parameters: Type.Object({}),
453
316
  renderCall: renderToolCall("list pages"),
454
317
  renderResult: renderTextResult,
455
- async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
318
+ async execute(_toolCallId, _params, signal, _onUpdate, ctx) {
456
319
  return withStatus(ctx, "list pages", async () => {
457
- const pages = await listPages();
320
+ const pages = await listPages({ sessionOwner: ctx.sessionManager, signal });
458
321
  return textResult(JSON.stringify(pages.map(formatPage), null, 2), { pages });
459
322
  });
460
323
  }
@@ -468,10 +331,10 @@ var selectPageTool = defineTool({
468
331
  }),
469
332
  renderCall: renderToolCall("select page"),
470
333
  renderResult: renderTextResult,
471
- async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
334
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
472
335
  return withStatus(ctx, "select page", async () => {
473
- const page = await getPage(params.pageId);
474
- state.activePageId = page.id;
336
+ const page = await getPage(params.pageId, { sessionOwner: ctx.sessionManager, signal });
337
+ setActivePageId(ctx.sessionManager, page.id);
475
338
  return textResult(`Selected page ${page.id}: ${page.title}
476
339
  ${page.url}`, {
477
340
  page: formatPage(page)
@@ -491,14 +354,17 @@ var navigateTool = defineTool({
491
354
  }),
492
355
  renderCall: renderToolCall("navigate"),
493
356
  renderResult: renderTextResult,
494
- async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
357
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
495
358
  return withStatus(ctx, "navigate", async () => {
496
- const { created, page } = await resolvePageForNavigation(params.pageId);
359
+ const { created, page } = await resolvePageForNavigation(params.pageId, {
360
+ sessionOwner: ctx.sessionManager,
361
+ signal
362
+ });
497
363
  const result = await withCdp(page, async (client) => {
498
364
  await client.send("Page.enable");
499
365
  return client.send("Page.navigate", { url: params.url });
500
366
  });
501
- state.activePageId = page.id;
367
+ setActivePageId(ctx.sessionManager, page.id);
502
368
  const action = created ? "Created page and navigated" : "Navigated";
503
369
  return textResult(`${action} ${page.id} to ${params.url}`, {
504
370
  created,
@@ -523,9 +389,12 @@ var evaluateTool = defineTool({
523
389
  }),
524
390
  renderCall: renderToolCall("evaluate"),
525
391
  renderResult: renderTextResult,
526
- async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
392
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
527
393
  return withStatus(ctx, "evaluate", async () => {
528
- const page = await resolvePage(params.pageId);
394
+ const page = await resolvePage(params.pageId, {
395
+ sessionOwner: ctx.sessionManager,
396
+ signal
397
+ });
529
398
  const result = await withCdp(
530
399
  page,
531
400
  (client) => client.send("Runtime.evaluate", {
@@ -534,11 +403,74 @@ var evaluateTool = defineTool({
534
403
  returnByValue: true
535
404
  })
536
405
  );
537
- state.activePageId = page.id;
406
+ setActivePageId(ctx.sessionManager, page.id);
538
407
  return textResult(JSON.stringify(result, null, 2), { page: formatPage(page), result });
539
408
  });
540
409
  }
541
410
  });
411
+ var webMcpListToolsTool = defineTool({
412
+ name: WEBMCP_TOOL_NAMES[0],
413
+ label: "Chrome DevTools: List WebMCP Tools (Experimental)",
414
+ description: "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.",
415
+ parameters: Type.Object({
416
+ pageId: Type.Optional(
417
+ Type.String({
418
+ description: "Optional page id. Defaults to selected or first page.",
419
+ maxLength: 512
420
+ })
421
+ )
422
+ }),
423
+ renderCall: renderToolCall("list WebMCP tools"),
424
+ renderResult: renderTextResult,
425
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
426
+ return withStatus(ctx, "list WebMCP tools", async () => {
427
+ const implementation = await import("./chunks/tools-6P7EUX37.js");
428
+ return implementation.executeWebMcpListTool(params, signal, ctx);
429
+ });
430
+ }
431
+ });
432
+ var webMcpCallTool = defineTool({
433
+ name: WEBMCP_TOOL_NAMES[1],
434
+ label: "Chrome DevTools: Call WebMCP Tool (Experimental)",
435
+ description: "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.",
436
+ parameters: Type.Object({
437
+ sessionGeneration: Type.String({
438
+ description: "Session generation token returned by chrome_devtools_webmcp_list_tools.",
439
+ maxLength: 100
440
+ }),
441
+ pageId: Type.String({ description: "Page id returned by the list tool.", maxLength: 512 }),
442
+ documentId: Type.String({
443
+ description: "Document loader id returned by the list tool.",
444
+ maxLength: 512
445
+ }),
446
+ frameId: Type.String({ description: "Frame id returned by the list tool.", maxLength: 512 }),
447
+ frameOrigin: Type.String({
448
+ description: "Exact frame origin returned by the list tool.",
449
+ maxLength: 2048
450
+ }),
451
+ toolName: Type.String({
452
+ description: "Exact tool name returned by the list tool.",
453
+ maxLength: 512
454
+ }),
455
+ schemaDigest: Type.String({
456
+ description: "Schema and annotation digest returned by the list tool.",
457
+ minLength: 64,
458
+ maxLength: 64,
459
+ pattern: "^[a-f0-9]{64}$"
460
+ }),
461
+ input: Type.Record(Type.String(), Type.Unknown(), {
462
+ description: "JSON object matching the page-provided input schema."
463
+ })
464
+ }),
465
+ renderCall: renderToolCall("call WebMCP tool"),
466
+ renderResult: renderTextResult,
467
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
468
+ return withStatus(ctx, "call WebMCP tool", async () => {
469
+ const implementation = await import("./chunks/tools-6P7EUX37.js");
470
+ return implementation.executeWebMcpCallTool(params, signal, ctx);
471
+ });
472
+ }
473
+ });
542
474
  var screenshotTool = defineTool({
543
475
  name: CHROME_DEVTOOLS_TOOL_NAMES[4],
544
476
  label: "Chrome DevTools: Screenshot",
@@ -560,7 +492,10 @@ var screenshotTool = defineTool({
560
492
  renderResult: renderScreenshotResult,
561
493
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
562
494
  return withStatus(ctx, "screenshot", async () => {
563
- const page = await resolvePage(params.pageId);
495
+ const page = await resolvePage(params.pageId, {
496
+ sessionOwner: ctx.sessionManager,
497
+ signal
498
+ });
564
499
  const result = await withCdp(page, async (client) => {
565
500
  throwIfAborted(signal);
566
501
  await client.send("Page.enable");
@@ -581,7 +516,7 @@ var screenshotTool = defineTool({
581
516
  }
582
517
  });
583
518
  });
584
- state.activePageId = page.id;
519
+ setActivePageId(ctx.sessionManager, page.id);
585
520
  const savedScreenshot = await saveScreenshot(result.data, params.savePath, ctx.cwd, signal);
586
521
  return {
587
522
  content: [
@@ -623,11 +558,14 @@ function chromeDevtools(pi) {
623
558
  pi.registerTool(navigateTool);
624
559
  pi.registerTool(evaluateTool);
625
560
  pi.registerTool(screenshotTool);
561
+ pi.registerTool(webMcpListToolsTool);
562
+ pi.registerTool(webMcpCallTool);
626
563
  pi.registerTool(createChromeDevtoolsLoadTool(pi));
627
564
  pi.registerCommand("chrome-devtools", {
628
565
  description: "Open Chrome DevTools help and tool controls",
629
566
  getArgumentCompletions: (prefix) => commandCompletions(prefix),
630
567
  handler: async (args, ctx) => {
568
+ setChromeDevtoolsSessionOwner(pi, ctx.sessionManager);
631
569
  initializeAvailableChromeDevtoolsTools(pi);
632
570
  const generation = state.sessionGeneration;
633
571
  await handleChromeDevtoolsCommand(pi, args, ctx, generation);
@@ -635,27 +573,47 @@ function chromeDevtools(pi) {
635
573
  });
636
574
  pi.on("session_start", async (_event, ctx) => {
637
575
  const generation = ++state.sessionGeneration;
576
+ setChromeDevtoolsSessionOwner(pi, ctx.sessionManager);
638
577
  initializeAvailableChromeDevtoolsTools(pi);
578
+ setWebMcpSessionOwner(ctx.sessionManager);
639
579
  replaceSessionController("Chrome DevTools session replaced");
580
+ invalidateWebMcpOperations(ctx.sessionManager, "Chrome DevTools session replaced");
640
581
  state.shuttingDown = false;
641
582
  state.settingsNotice = void 0;
642
583
  ctx.ui.setStatus(STATUS_KEY2, void 0);
643
- await shutdownManagedBrowser();
584
+ await shutdownManagedBrowser(void 0, {
585
+ cancelLaunch: true,
586
+ owner: ctx.sessionManager
587
+ });
588
+ startManagedBrowserSession(ctx.sessionManager);
644
589
  if (generation !== state.sessionGeneration) return;
590
+ setActivePageId(ctx.sessionManager, void 0);
645
591
  state.activePageId = void 0;
646
592
  state.lastLaunchAttempt = void 0;
647
593
  const projectTrusted = ctx.isProjectTrusted();
648
594
  const settings = await loadSettings({ cwd: ctx.cwd, projectTrusted });
649
595
  if (generation !== state.sessionGeneration) return;
650
596
  applyRuntimeBrowserSettings(settings.effectiveBrowser, settings.paths, projectTrusted);
597
+ syncManagedBrowserSettings(ctx.sessionManager, settings.effectiveBrowser);
598
+ applyRuntimeWebMcpSetting(settings.effectiveWebMcpEnabled, ctx.sessionManager);
651
599
  state.settingsNotice = settings.notice;
652
600
  for (const warning of settings.warnings) {
653
601
  ctx.ui.notify(sanitizeChromeDevtoolsDisplay(warning), "warning");
654
602
  }
603
+ if (webMcpEnabled(ctx.sessionManager)) {
604
+ ctx.ui.notify(
605
+ "Experimental WebMCP is enabled. Page-provided tools use the visible browser session and require confirmation for every call.",
606
+ "warning"
607
+ );
608
+ }
655
609
  const availableTools = settings.kind === "loaded" && settings.settings.tools ? settings.settings.tools : availableChromeDevtoolsTools(pi);
656
610
  configureChromeDevtoolsToolExposure(pi, availableTools, ctx.model);
657
611
  });
658
- pi.on("model_select", (event) => {
612
+ pi.on("model_select", (event, ctx) => {
613
+ invalidateWebMcpOperations(
614
+ ctx.sessionManager,
615
+ "Chrome DevTools model and tool exposure changed"
616
+ );
659
617
  if (!supportsNativeDeferredToolLoading(event.model)) {
660
618
  requireEagerChromeDevtoolsToolExposure(pi);
661
619
  }
@@ -663,8 +621,12 @@ function chromeDevtools(pi) {
663
621
  pi.on("session_shutdown", async (_event, ctx) => {
664
622
  state.sessionGeneration += 1;
665
623
  replaceSessionController("Chrome DevTools session shut down");
624
+ invalidateWebMcpOperations(ctx.sessionManager, "Chrome DevTools session shut down");
666
625
  ctx.ui.setStatus(STATUS_KEY2, void 0);
667
- const browserShutdown = shutdownManagedBrowser(void 0, { cancelLaunch: true });
626
+ const browserShutdown = shutdownManagedBrowser(void 0, {
627
+ cancelLaunch: true,
628
+ owner: ctx.sessionManager
629
+ });
668
630
  await waitForChromeDevtoolsSettings();
669
631
  await waitForSettingsWrites();
670
632
  await browserShutdown;
@@ -678,15 +640,15 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
678
640
  return;
679
641
  case "help":
680
642
  requireObservableUi(ctx, "help");
681
- ctx.ui.notify(buildCommandGuide(), "info");
643
+ ctx.ui.notify(buildCommandGuide(ctx.sessionManager), "info");
682
644
  return;
683
645
  case "quickstart":
684
646
  requireObservableUi(ctx, "quickstart");
685
- ctx.ui.notify(buildQuickstartMessage(), "info");
647
+ ctx.ui.notify(buildQuickstartMessage(ctx.sessionManager), "info");
686
648
  return;
687
649
  case "status": {
688
650
  requireObservableUi(ctx, "status");
689
- const status = await buildToolStatusMessage(pi);
651
+ const status = await buildToolStatusMessage(pi, ctx.sessionManager);
690
652
  if (generation !== state.sessionGeneration) return;
691
653
  ctx.ui.notify(status, "info");
692
654
  return;
@@ -695,22 +657,27 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
695
657
  if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
696
658
  throw new Error("/chrome-devtools settings requires TUI or RPC mode");
697
659
  }
698
- const { showChromeDevtoolsBrowserSettings } = await import("./chunks/browser-settings-menu-VWCNG4HB.js");
660
+ const { showChromeDevtoolsBrowserSettings } = await import("./chunks/browser-settings-menu-REIU7C3Q.js");
699
661
  if (generation !== state.sessionGeneration) return;
700
- await showChromeDevtoolsBrowserSettings(ctx, generation);
662
+ await showChromeDevtoolsBrowserSettings(pi, ctx, generation);
701
663
  return;
702
664
  }
703
665
  case "tools": {
704
666
  if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
705
667
  throw new Error("/chrome-devtools tools requires TUI or RPC mode");
706
668
  }
707
- const { showChromeDevtoolsToolWorkflow } = await import("./chunks/menu-PLD3S3EO.js");
669
+ const { showChromeDevtoolsToolWorkflow } = await import("./chunks/menu-H5HMVGHI.js");
708
670
  if (generation !== state.sessionGeneration) return;
709
671
  await showChromeDevtoolsToolWorkflow(pi, ctx, generation);
710
672
  return;
711
673
  }
712
674
  case "enable":
713
- await updateChromeDevtoolsTools(pi, ctx, allChromeDevtoolsTools(), "made all available");
675
+ await updateChromeDevtoolsTools(
676
+ pi,
677
+ ctx,
678
+ allChromeDevtoolsTools(ctx.sessionManager),
679
+ "made all available"
680
+ );
714
681
  return;
715
682
  case "disable":
716
683
  await updateChromeDevtoolsTools(pi, ctx, [], "made all unavailable");
@@ -722,7 +689,7 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
722
689
  ctx.ui.notify(
723
690
  `Unknown /chrome-devtools command: ${args.trim()}
724
691
 
725
- ${buildCommandGuide()}`,
692
+ ${buildCommandGuide(ctx.sessionManager)}`,
726
693
  "warning"
727
694
  );
728
695
  }
@@ -735,7 +702,7 @@ async function showMenu(pi, ctx, generation) {
735
702
  if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
736
703
  throw new Error("/chrome-devtools menu requires TUI or RPC mode; use a direct subcommand");
737
704
  }
738
- const { showChromeDevtoolsMenu } = await import("./chunks/menu-PLD3S3EO.js");
705
+ const { showChromeDevtoolsMenu } = await import("./chunks/menu-H5HMVGHI.js");
739
706
  if (generation !== state.sessionGeneration) return;
740
707
  await showChromeDevtoolsMenu(pi, ctx, generation);
741
708
  }