@narumitw/pi-chrome-devtools 0.52.3 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -9
- package/dist/chunks/{browser-settings-menu-VWCNG4HB.js → browser-settings-menu-N5O65I7S.js} +81 -7
- package/dist/chunks/browser-settings-menu-N5O65I7S.js.map +7 -0
- package/dist/chunks/chunk-B7B63PWB.js +465 -0
- package/dist/chunks/chunk-B7B63PWB.js.map +7 -0
- package/dist/chunks/{chunk-RH6KESL3.js → chunk-GVBXT3FB.js} +369 -514
- package/dist/chunks/chunk-GVBXT3FB.js.map +7 -0
- package/dist/chunks/chunk-RTECHAEZ.js +373 -0
- package/dist/chunks/chunk-RTECHAEZ.js.map +7 -0
- package/dist/chunks/{menu-PLD3S3EO.js → menu-UBHW6UP2.js} +72 -39
- package/dist/chunks/menu-UBHW6UP2.js.map +7 -0
- package/dist/chunks/tools-V7OR5KJ3.js +945 -0
- package/dist/chunks/tools-V7OR5KJ3.js.map +7 -0
- package/dist/index.ts +158 -195
- package/dist/index.ts.map +4 -4
- package/package.json +4 -3
- package/src/browser-manager.ts +282 -109
- package/src/browser-settings-menu.ts +98 -4
- package/src/cdp-client.ts +329 -83
- package/src/chrome-devtools.ts +51 -11
- package/src/lazy-tools.ts +37 -4
- package/src/menu.ts +80 -39
- package/src/render.ts +12 -1
- package/src/runtime.ts +92 -0
- package/src/settings.ts +47 -10
- package/src/tool-names.ts +16 -1
- package/src/tool-selector.ts +67 -30
- package/src/tools.ts +86 -10
- package/src/webmcp/discovery.ts +235 -0
- package/src/webmcp/policy.ts +402 -0
- package/src/webmcp/protocol.ts +414 -0
- package/src/webmcp/tools.ts +190 -0
- package/dist/chunks/browser-settings-menu-VWCNG4HB.js.map +0 -7
- package/dist/chunks/chunk-RH6KESL3.js.map +0 -7
- package/dist/chunks/menu-PLD3S3EO.js.map +0 -7
package/dist/index.ts
CHANGED
|
@@ -1,199 +1,52 @@
|
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
formatPage,
|
|
5
|
+
getPage,
|
|
6
|
+
listPages,
|
|
7
|
+
resolvePage,
|
|
8
|
+
resolvePageForNavigation,
|
|
9
|
+
textResult,
|
|
10
|
+
withCdp
|
|
11
|
+
} from "./chunks/chunk-RTECHAEZ.js";
|
|
12
|
+
import {
|
|
7
13
|
allChromeDevtoolsTools,
|
|
8
|
-
applyRuntimeBrowserSettings,
|
|
9
14
|
availableChromeDevtoolsTools,
|
|
10
15
|
buildCommandGuide,
|
|
11
16
|
buildQuickstartMessage,
|
|
12
17
|
buildToolStatusMessage,
|
|
13
18
|
configureChromeDevtoolsToolExposure,
|
|
14
19
|
createChromeDevtoolsLoadTool,
|
|
15
|
-
devToolsEndpoint,
|
|
16
|
-
ensureDevToolsEndpoint,
|
|
17
|
-
fetchDevToolsJson,
|
|
18
|
-
formatPageListItem,
|
|
19
20
|
initializeAvailableChromeDevtoolsTools,
|
|
20
|
-
launchHint,
|
|
21
|
-
loadSettings,
|
|
22
21
|
requireEagerChromeDevtoolsToolExposure,
|
|
23
22
|
sanitizeChromeDevtoolsDisplay,
|
|
24
|
-
|
|
25
|
-
state,
|
|
23
|
+
setChromeDevtoolsSessionOwner,
|
|
26
24
|
supportsNativeDeferredToolLoading,
|
|
27
25
|
updateChromeDevtoolsTools,
|
|
28
|
-
waitForChromeDevtoolsSettings
|
|
26
|
+
waitForChromeDevtoolsSettings
|
|
27
|
+
} from "./chunks/chunk-B7B63PWB.js";
|
|
28
|
+
import {
|
|
29
|
+
CHROME_DEVTOOLS_TOOL_NAMES,
|
|
30
|
+
WEBMCP_TOOL_NAMES,
|
|
31
|
+
applyRuntimeBrowserSettings,
|
|
32
|
+
applyRuntimeWebMcpSetting,
|
|
33
|
+
invalidateWebMcpOperations,
|
|
34
|
+
loadSettings,
|
|
35
|
+
setWebMcpSessionOwner,
|
|
36
|
+
shutdownManagedBrowser,
|
|
37
|
+
startManagedBrowserSession,
|
|
38
|
+
state,
|
|
29
39
|
waitForSettingsWrites,
|
|
30
|
-
|
|
31
|
-
} from "./chunks/chunk-
|
|
40
|
+
webMcpEnabled
|
|
41
|
+
} from "./chunks/chunk-GVBXT3FB.js";
|
|
32
42
|
|
|
33
43
|
// src/tools.ts
|
|
34
44
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
35
45
|
import { Type } from "typebox";
|
|
36
46
|
|
|
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
47
|
// src/render.ts
|
|
196
48
|
var STATUS_KEY = "chrome-devtools";
|
|
49
|
+
var activeStatuses = /* @__PURE__ */ new WeakMap();
|
|
197
50
|
function renderToolCall(action) {
|
|
198
51
|
return () => new PiTextComponent(`Chrome DevTools: ${action}`);
|
|
199
52
|
}
|
|
@@ -247,11 +100,19 @@ function truncateLine(line, maxWidth) {
|
|
|
247
100
|
return Array.from(line).slice(0, maxWidth).join("");
|
|
248
101
|
}
|
|
249
102
|
async function withStatus(ctx, status, callback) {
|
|
103
|
+
const token = Symbol(status);
|
|
104
|
+
const statuses = activeStatuses.get(ctx.sessionManager) ?? [];
|
|
105
|
+
statuses.push({ status, token });
|
|
106
|
+
activeStatuses.set(ctx.sessionManager, statuses);
|
|
250
107
|
ctx.ui.setStatus(STATUS_KEY, status);
|
|
251
108
|
try {
|
|
252
109
|
return await callback();
|
|
253
110
|
} finally {
|
|
254
|
-
|
|
111
|
+
const index = statuses.findIndex((entry) => entry.token === token);
|
|
112
|
+
if (index >= 0) statuses.splice(index, 1);
|
|
113
|
+
const remaining = statuses.at(-1)?.status;
|
|
114
|
+
if (statuses.length === 0) activeStatuses.delete(ctx.sessionManager);
|
|
115
|
+
ctx.ui.setStatus(STATUS_KEY, remaining);
|
|
255
116
|
}
|
|
256
117
|
}
|
|
257
118
|
|
|
@@ -452,9 +313,9 @@ var listPagesTool = defineTool({
|
|
|
452
313
|
parameters: Type.Object({}),
|
|
453
314
|
renderCall: renderToolCall("list pages"),
|
|
454
315
|
renderResult: renderTextResult,
|
|
455
|
-
async execute(_toolCallId, _params,
|
|
316
|
+
async execute(_toolCallId, _params, signal, _onUpdate, ctx) {
|
|
456
317
|
return withStatus(ctx, "list pages", async () => {
|
|
457
|
-
const pages = await listPages();
|
|
318
|
+
const pages = await listPages({ sessionOwner: ctx.sessionManager, signal });
|
|
458
319
|
return textResult(JSON.stringify(pages.map(formatPage), null, 2), { pages });
|
|
459
320
|
});
|
|
460
321
|
}
|
|
@@ -468,9 +329,9 @@ var selectPageTool = defineTool({
|
|
|
468
329
|
}),
|
|
469
330
|
renderCall: renderToolCall("select page"),
|
|
470
331
|
renderResult: renderTextResult,
|
|
471
|
-
async execute(_toolCallId, params,
|
|
332
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
472
333
|
return withStatus(ctx, "select page", async () => {
|
|
473
|
-
const page = await getPage(params.pageId);
|
|
334
|
+
const page = await getPage(params.pageId, { sessionOwner: ctx.sessionManager, signal });
|
|
474
335
|
state.activePageId = page.id;
|
|
475
336
|
return textResult(`Selected page ${page.id}: ${page.title}
|
|
476
337
|
${page.url}`, {
|
|
@@ -491,9 +352,12 @@ var navigateTool = defineTool({
|
|
|
491
352
|
}),
|
|
492
353
|
renderCall: renderToolCall("navigate"),
|
|
493
354
|
renderResult: renderTextResult,
|
|
494
|
-
async execute(_toolCallId, params,
|
|
355
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
495
356
|
return withStatus(ctx, "navigate", async () => {
|
|
496
|
-
const { created, page } = await resolvePageForNavigation(params.pageId
|
|
357
|
+
const { created, page } = await resolvePageForNavigation(params.pageId, {
|
|
358
|
+
sessionOwner: ctx.sessionManager,
|
|
359
|
+
signal
|
|
360
|
+
});
|
|
497
361
|
const result = await withCdp(page, async (client) => {
|
|
498
362
|
await client.send("Page.enable");
|
|
499
363
|
return client.send("Page.navigate", { url: params.url });
|
|
@@ -523,9 +387,12 @@ var evaluateTool = defineTool({
|
|
|
523
387
|
}),
|
|
524
388
|
renderCall: renderToolCall("evaluate"),
|
|
525
389
|
renderResult: renderTextResult,
|
|
526
|
-
async execute(_toolCallId, params,
|
|
390
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
527
391
|
return withStatus(ctx, "evaluate", async () => {
|
|
528
|
-
const page = await resolvePage(params.pageId
|
|
392
|
+
const page = await resolvePage(params.pageId, {
|
|
393
|
+
sessionOwner: ctx.sessionManager,
|
|
394
|
+
signal
|
|
395
|
+
});
|
|
529
396
|
const result = await withCdp(
|
|
530
397
|
page,
|
|
531
398
|
(client) => client.send("Runtime.evaluate", {
|
|
@@ -539,6 +406,69 @@ var evaluateTool = defineTool({
|
|
|
539
406
|
});
|
|
540
407
|
}
|
|
541
408
|
});
|
|
409
|
+
var webMcpListToolsTool = defineTool({
|
|
410
|
+
name: WEBMCP_TOOL_NAMES[0],
|
|
411
|
+
label: "Chrome DevTools: List WebMCP Tools (Experimental)",
|
|
412
|
+
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.",
|
|
413
|
+
parameters: Type.Object({
|
|
414
|
+
pageId: Type.Optional(
|
|
415
|
+
Type.String({
|
|
416
|
+
description: "Optional page id. Defaults to selected or first page.",
|
|
417
|
+
maxLength: 512
|
|
418
|
+
})
|
|
419
|
+
)
|
|
420
|
+
}),
|
|
421
|
+
renderCall: renderToolCall("list WebMCP tools"),
|
|
422
|
+
renderResult: renderTextResult,
|
|
423
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
424
|
+
return withStatus(ctx, "list WebMCP tools", async () => {
|
|
425
|
+
const implementation = await import("./chunks/tools-V7OR5KJ3.js");
|
|
426
|
+
return implementation.executeWebMcpListTool(params, signal, ctx);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
var webMcpCallTool = defineTool({
|
|
431
|
+
name: WEBMCP_TOOL_NAMES[1],
|
|
432
|
+
label: "Chrome DevTools: Call WebMCP Tool (Experimental)",
|
|
433
|
+
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.",
|
|
434
|
+
parameters: Type.Object({
|
|
435
|
+
sessionGeneration: Type.String({
|
|
436
|
+
description: "Session generation token returned by chrome_devtools_webmcp_list_tools.",
|
|
437
|
+
maxLength: 100
|
|
438
|
+
}),
|
|
439
|
+
pageId: Type.String({ description: "Page id returned by the list tool.", maxLength: 512 }),
|
|
440
|
+
documentId: Type.String({
|
|
441
|
+
description: "Document loader id returned by the list tool.",
|
|
442
|
+
maxLength: 512
|
|
443
|
+
}),
|
|
444
|
+
frameId: Type.String({ description: "Frame id returned by the list tool.", maxLength: 512 }),
|
|
445
|
+
frameOrigin: Type.String({
|
|
446
|
+
description: "Exact frame origin returned by the list tool.",
|
|
447
|
+
maxLength: 2048
|
|
448
|
+
}),
|
|
449
|
+
toolName: Type.String({
|
|
450
|
+
description: "Exact tool name returned by the list tool.",
|
|
451
|
+
maxLength: 512
|
|
452
|
+
}),
|
|
453
|
+
schemaDigest: Type.String({
|
|
454
|
+
description: "Schema and annotation digest returned by the list tool.",
|
|
455
|
+
minLength: 64,
|
|
456
|
+
maxLength: 64,
|
|
457
|
+
pattern: "^[a-f0-9]{64}$"
|
|
458
|
+
}),
|
|
459
|
+
input: Type.Record(Type.String(), Type.Unknown(), {
|
|
460
|
+
description: "JSON object matching the page-provided input schema."
|
|
461
|
+
})
|
|
462
|
+
}),
|
|
463
|
+
renderCall: renderToolCall("call WebMCP tool"),
|
|
464
|
+
renderResult: renderTextResult,
|
|
465
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
466
|
+
return withStatus(ctx, "call WebMCP tool", async () => {
|
|
467
|
+
const implementation = await import("./chunks/tools-V7OR5KJ3.js");
|
|
468
|
+
return implementation.executeWebMcpCallTool(params, signal, ctx);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
});
|
|
542
472
|
var screenshotTool = defineTool({
|
|
543
473
|
name: CHROME_DEVTOOLS_TOOL_NAMES[4],
|
|
544
474
|
label: "Chrome DevTools: Screenshot",
|
|
@@ -560,7 +490,10 @@ var screenshotTool = defineTool({
|
|
|
560
490
|
renderResult: renderScreenshotResult,
|
|
561
491
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
562
492
|
return withStatus(ctx, "screenshot", async () => {
|
|
563
|
-
const page = await resolvePage(params.pageId
|
|
493
|
+
const page = await resolvePage(params.pageId, {
|
|
494
|
+
sessionOwner: ctx.sessionManager,
|
|
495
|
+
signal
|
|
496
|
+
});
|
|
564
497
|
const result = await withCdp(page, async (client) => {
|
|
565
498
|
throwIfAborted(signal);
|
|
566
499
|
await client.send("Page.enable");
|
|
@@ -623,11 +556,14 @@ function chromeDevtools(pi) {
|
|
|
623
556
|
pi.registerTool(navigateTool);
|
|
624
557
|
pi.registerTool(evaluateTool);
|
|
625
558
|
pi.registerTool(screenshotTool);
|
|
559
|
+
pi.registerTool(webMcpListToolsTool);
|
|
560
|
+
pi.registerTool(webMcpCallTool);
|
|
626
561
|
pi.registerTool(createChromeDevtoolsLoadTool(pi));
|
|
627
562
|
pi.registerCommand("chrome-devtools", {
|
|
628
563
|
description: "Open Chrome DevTools help and tool controls",
|
|
629
564
|
getArgumentCompletions: (prefix) => commandCompletions(prefix),
|
|
630
565
|
handler: async (args, ctx) => {
|
|
566
|
+
setChromeDevtoolsSessionOwner(pi, ctx.sessionManager);
|
|
631
567
|
initializeAvailableChromeDevtoolsTools(pi);
|
|
632
568
|
const generation = state.sessionGeneration;
|
|
633
569
|
await handleChromeDevtoolsCommand(pi, args, ctx, generation);
|
|
@@ -635,12 +571,19 @@ function chromeDevtools(pi) {
|
|
|
635
571
|
});
|
|
636
572
|
pi.on("session_start", async (_event, ctx) => {
|
|
637
573
|
const generation = ++state.sessionGeneration;
|
|
574
|
+
setChromeDevtoolsSessionOwner(pi, ctx.sessionManager);
|
|
638
575
|
initializeAvailableChromeDevtoolsTools(pi);
|
|
576
|
+
setWebMcpSessionOwner(ctx.sessionManager);
|
|
639
577
|
replaceSessionController("Chrome DevTools session replaced");
|
|
578
|
+
invalidateWebMcpOperations(ctx.sessionManager, "Chrome DevTools session replaced");
|
|
640
579
|
state.shuttingDown = false;
|
|
641
580
|
state.settingsNotice = void 0;
|
|
642
581
|
ctx.ui.setStatus(STATUS_KEY2, void 0);
|
|
643
|
-
await shutdownManagedBrowser(
|
|
582
|
+
await shutdownManagedBrowser(void 0, {
|
|
583
|
+
cancelLaunch: true,
|
|
584
|
+
owner: ctx.sessionManager
|
|
585
|
+
});
|
|
586
|
+
startManagedBrowserSession(ctx.sessionManager);
|
|
644
587
|
if (generation !== state.sessionGeneration) return;
|
|
645
588
|
state.activePageId = void 0;
|
|
646
589
|
state.lastLaunchAttempt = void 0;
|
|
@@ -648,14 +591,25 @@ function chromeDevtools(pi) {
|
|
|
648
591
|
const settings = await loadSettings({ cwd: ctx.cwd, projectTrusted });
|
|
649
592
|
if (generation !== state.sessionGeneration) return;
|
|
650
593
|
applyRuntimeBrowserSettings(settings.effectiveBrowser, settings.paths, projectTrusted);
|
|
594
|
+
applyRuntimeWebMcpSetting(settings.effectiveWebMcpEnabled, ctx.sessionManager);
|
|
651
595
|
state.settingsNotice = settings.notice;
|
|
652
596
|
for (const warning of settings.warnings) {
|
|
653
597
|
ctx.ui.notify(sanitizeChromeDevtoolsDisplay(warning), "warning");
|
|
654
598
|
}
|
|
599
|
+
if (webMcpEnabled(ctx.sessionManager)) {
|
|
600
|
+
ctx.ui.notify(
|
|
601
|
+
"Experimental WebMCP is enabled. Page-provided tools use the visible browser session and require confirmation for every call.",
|
|
602
|
+
"warning"
|
|
603
|
+
);
|
|
604
|
+
}
|
|
655
605
|
const availableTools = settings.kind === "loaded" && settings.settings.tools ? settings.settings.tools : availableChromeDevtoolsTools(pi);
|
|
656
606
|
configureChromeDevtoolsToolExposure(pi, availableTools, ctx.model);
|
|
657
607
|
});
|
|
658
|
-
pi.on("model_select", (event) => {
|
|
608
|
+
pi.on("model_select", (event, ctx) => {
|
|
609
|
+
invalidateWebMcpOperations(
|
|
610
|
+
ctx.sessionManager,
|
|
611
|
+
"Chrome DevTools model and tool exposure changed"
|
|
612
|
+
);
|
|
659
613
|
if (!supportsNativeDeferredToolLoading(event.model)) {
|
|
660
614
|
requireEagerChromeDevtoolsToolExposure(pi);
|
|
661
615
|
}
|
|
@@ -663,8 +617,12 @@ function chromeDevtools(pi) {
|
|
|
663
617
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
664
618
|
state.sessionGeneration += 1;
|
|
665
619
|
replaceSessionController("Chrome DevTools session shut down");
|
|
620
|
+
invalidateWebMcpOperations(ctx.sessionManager, "Chrome DevTools session shut down");
|
|
666
621
|
ctx.ui.setStatus(STATUS_KEY2, void 0);
|
|
667
|
-
const browserShutdown = shutdownManagedBrowser(void 0, {
|
|
622
|
+
const browserShutdown = shutdownManagedBrowser(void 0, {
|
|
623
|
+
cancelLaunch: true,
|
|
624
|
+
owner: ctx.sessionManager
|
|
625
|
+
});
|
|
668
626
|
await waitForChromeDevtoolsSettings();
|
|
669
627
|
await waitForSettingsWrites();
|
|
670
628
|
await browserShutdown;
|
|
@@ -678,15 +636,15 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
|
|
|
678
636
|
return;
|
|
679
637
|
case "help":
|
|
680
638
|
requireObservableUi(ctx, "help");
|
|
681
|
-
ctx.ui.notify(buildCommandGuide(), "info");
|
|
639
|
+
ctx.ui.notify(buildCommandGuide(ctx.sessionManager), "info");
|
|
682
640
|
return;
|
|
683
641
|
case "quickstart":
|
|
684
642
|
requireObservableUi(ctx, "quickstart");
|
|
685
|
-
ctx.ui.notify(buildQuickstartMessage(), "info");
|
|
643
|
+
ctx.ui.notify(buildQuickstartMessage(ctx.sessionManager), "info");
|
|
686
644
|
return;
|
|
687
645
|
case "status": {
|
|
688
646
|
requireObservableUi(ctx, "status");
|
|
689
|
-
const status = await buildToolStatusMessage(pi);
|
|
647
|
+
const status = await buildToolStatusMessage(pi, ctx.sessionManager);
|
|
690
648
|
if (generation !== state.sessionGeneration) return;
|
|
691
649
|
ctx.ui.notify(status, "info");
|
|
692
650
|
return;
|
|
@@ -695,22 +653,27 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
|
|
|
695
653
|
if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
|
|
696
654
|
throw new Error("/chrome-devtools settings requires TUI or RPC mode");
|
|
697
655
|
}
|
|
698
|
-
const { showChromeDevtoolsBrowserSettings } = await import("./chunks/browser-settings-menu-
|
|
656
|
+
const { showChromeDevtoolsBrowserSettings } = await import("./chunks/browser-settings-menu-N5O65I7S.js");
|
|
699
657
|
if (generation !== state.sessionGeneration) return;
|
|
700
|
-
await showChromeDevtoolsBrowserSettings(ctx, generation);
|
|
658
|
+
await showChromeDevtoolsBrowserSettings(pi, ctx, generation);
|
|
701
659
|
return;
|
|
702
660
|
}
|
|
703
661
|
case "tools": {
|
|
704
662
|
if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
|
|
705
663
|
throw new Error("/chrome-devtools tools requires TUI or RPC mode");
|
|
706
664
|
}
|
|
707
|
-
const { showChromeDevtoolsToolWorkflow } = await import("./chunks/menu-
|
|
665
|
+
const { showChromeDevtoolsToolWorkflow } = await import("./chunks/menu-UBHW6UP2.js");
|
|
708
666
|
if (generation !== state.sessionGeneration) return;
|
|
709
667
|
await showChromeDevtoolsToolWorkflow(pi, ctx, generation);
|
|
710
668
|
return;
|
|
711
669
|
}
|
|
712
670
|
case "enable":
|
|
713
|
-
await updateChromeDevtoolsTools(
|
|
671
|
+
await updateChromeDevtoolsTools(
|
|
672
|
+
pi,
|
|
673
|
+
ctx,
|
|
674
|
+
allChromeDevtoolsTools(ctx.sessionManager),
|
|
675
|
+
"made all available"
|
|
676
|
+
);
|
|
714
677
|
return;
|
|
715
678
|
case "disable":
|
|
716
679
|
await updateChromeDevtoolsTools(pi, ctx, [], "made all unavailable");
|
|
@@ -722,7 +685,7 @@ async function handleChromeDevtoolsCommand(pi, args, ctx, generation) {
|
|
|
722
685
|
ctx.ui.notify(
|
|
723
686
|
`Unknown /chrome-devtools command: ${args.trim()}
|
|
724
687
|
|
|
725
|
-
${buildCommandGuide()}`,
|
|
688
|
+
${buildCommandGuide(ctx.sessionManager)}`,
|
|
726
689
|
"warning"
|
|
727
690
|
);
|
|
728
691
|
}
|
|
@@ -735,7 +698,7 @@ async function showMenu(pi, ctx, generation) {
|
|
|
735
698
|
if (!ctx.hasUI || ctx.mode !== "tui" && ctx.mode !== "rpc") {
|
|
736
699
|
throw new Error("/chrome-devtools menu requires TUI or RPC mode; use a direct subcommand");
|
|
737
700
|
}
|
|
738
|
-
const { showChromeDevtoolsMenu } = await import("./chunks/menu-
|
|
701
|
+
const { showChromeDevtoolsMenu } = await import("./chunks/menu-UBHW6UP2.js");
|
|
739
702
|
if (generation !== state.sessionGeneration) return;
|
|
740
703
|
await showChromeDevtoolsMenu(pi, ctx, generation);
|
|
741
704
|
}
|