@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.
- package/README.md +62 -9
- package/dist/chunks/{browser-settings-menu-VWCNG4HB.js → browser-settings-menu-REIU7C3Q.js} +83 -7
- package/dist/chunks/browser-settings-menu-REIU7C3Q.js.map +7 -0
- package/dist/chunks/chunk-GTTYS5GT.js +384 -0
- package/dist/chunks/chunk-GTTYS5GT.js.map +7 -0
- package/dist/chunks/{chunk-RH6KESL3.js → chunk-SRT3UUIP.js} +473 -574
- package/dist/chunks/chunk-SRT3UUIP.js.map +7 -0
- package/dist/chunks/chunk-YJVYJ2L2.js +469 -0
- package/dist/chunks/chunk-YJVYJ2L2.js.map +7 -0
- package/dist/chunks/{menu-PLD3S3EO.js → menu-H5HMVGHI.js} +72 -39
- package/dist/chunks/menu-H5HMVGHI.js.map +7 -0
- package/dist/chunks/tools-6P7EUX37.js +964 -0
- package/dist/chunks/tools-6P7EUX37.js.map +7 -0
- package/dist/index.ts +166 -199
- package/dist/index.ts.map +4 -4
- package/package.json +4 -3
- package/src/browser-manager.ts +408 -169
- package/src/browser-settings-menu.ts +100 -5
- package/src/cdp-client.ts +347 -90
- package/src/chrome-devtools.ts +58 -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 +85 -44
- package/src/tools.ts +91 -15
- package/src/webmcp/discovery.ts +235 -0
- package/src/webmcp/policy.ts +409 -0
- package/src/webmcp/protocol.ts +426 -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,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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
} from "./chunks/chunk-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
334
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
472
335
|
return withStatus(ctx, "select page", async () => {
|
|
473
|
-
const page = await getPage(params.pageId);
|
|
474
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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, {
|
|
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-
|
|
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-
|
|
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(
|
|
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-
|
|
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
|
}
|