@narumitw/pi-chrome-devtools 0.53.1 → 0.53.3
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 +89 -230
- package/dist/chunks/{browser-settings-menu-REIU7C3Q.js → browser-settings-menu-SR7KLZT6.js} +16 -42
- package/dist/chunks/browser-settings-menu-SR7KLZT6.js.map +7 -0
- package/dist/chunks/{chunk-GTTYS5GT.js → chunk-4KMN4SFF.js} +2 -2
- package/dist/chunks/chunk-4KMN4SFF.js.map +7 -0
- package/dist/chunks/{chunk-SRT3UUIP.js → chunk-F5VG35UI.js} +23 -74
- package/dist/chunks/chunk-F5VG35UI.js.map +7 -0
- package/dist/chunks/{chunk-YJVYJ2L2.js → chunk-MZCGM6D3.js} +43 -48
- package/dist/chunks/chunk-MZCGM6D3.js.map +7 -0
- package/dist/chunks/{menu-H5HMVGHI.js → menu-YR4VRT7D.js} +10 -23
- package/dist/chunks/menu-YR4VRT7D.js.map +7 -0
- package/dist/chunks/{tools-6P7EUX37.js → tools-6R7Z6HWJ.js} +17 -59
- package/dist/chunks/tools-6R7Z6HWJ.js.map +7 -0
- package/dist/index.ts +23 -48
- package/dist/index.ts.map +2 -2
- package/docs/browser-setup.md +147 -0
- package/package.json +54 -55
- package/src/browser-manager.ts +872 -942
- package/src/browser-settings-menu.ts +315 -350
- package/src/cdp-client.ts +381 -398
- package/src/chrome-devtools.ts +205 -232
- package/src/display.ts +35 -0
- package/src/lazy-tools.ts +158 -181
- package/src/menu.ts +409 -446
- package/src/render.ts +74 -84
- package/src/runtime.ts +137 -140
- package/src/screenshot.ts +170 -186
- package/src/settings.ts +472 -523
- package/src/tool-names.ts +8 -14
- package/src/tool-selector.ts +245 -289
- package/src/tools.ts +218 -226
- package/src/webmcp/discovery.ts +184 -188
- package/src/webmcp/policy.ts +296 -329
- package/src/webmcp/protocol.ts +324 -337
- package/src/webmcp/tools.ts +136 -147
- package/dist/chunks/browser-settings-menu-REIU7C3Q.js.map +0 -7
- package/dist/chunks/chunk-GTTYS5GT.js.map +0 -7
- package/dist/chunks/chunk-SRT3UUIP.js.map +0 -7
- package/dist/chunks/chunk-YJVYJ2L2.js.map +0 -7
- package/dist/chunks/menu-H5HMVGHI.js.map +0 -7
- package/dist/chunks/tools-6P7EUX37.js.map +0 -7
package/src/tools.ts
CHANGED
|
@@ -1,260 +1,252 @@
|
|
|
1
1
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
formatPage,
|
|
5
|
+
getPage,
|
|
6
|
+
listPages,
|
|
7
|
+
resolvePage,
|
|
8
|
+
resolvePageForNavigation,
|
|
9
|
+
setActivePageId,
|
|
10
|
+
textResult,
|
|
11
|
+
withCdp,
|
|
12
12
|
} from "./cdp-client.js";
|
|
13
13
|
import { renderScreenshotResult, renderTextResult, renderToolCall, withStatus } from "./render.js";
|
|
14
14
|
import { formatScreenshotText, saveScreenshot, throwIfAborted } from "./screenshot.js";
|
|
15
15
|
import { CHROME_DEVTOOLS_TOOL_NAMES, WEBMCP_TOOL_NAMES } from "./tool-names.js";
|
|
16
16
|
|
|
17
17
|
export const listPagesTool = defineTool({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
name: CHROME_DEVTOOLS_TOOL_NAMES[0],
|
|
19
|
+
label: "Chrome DevTools: List Pages",
|
|
20
|
+
description: "List Chrome tabs/pages from a running Chrome DevTools Protocol endpoint.",
|
|
21
|
+
parameters: Type.Object({}),
|
|
22
|
+
renderCall: renderToolCall("list pages"),
|
|
23
|
+
renderResult: renderTextResult,
|
|
24
|
+
async execute(_toolCallId, _params, signal, _onUpdate, ctx) {
|
|
25
|
+
return withStatus(ctx, "list pages", async () => {
|
|
26
|
+
const pages = await listPages({ sessionOwner: ctx.sessionManager, signal });
|
|
27
|
+
return textResult(JSON.stringify(pages.map(formatPage), null, 2), { pages });
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
export const selectPageTool = defineTool({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
name: CHROME_DEVTOOLS_TOOL_NAMES[1],
|
|
34
|
+
label: "Chrome DevTools: Select Page",
|
|
35
|
+
description: "Select the active Chrome page for later chrome_devtools_* tool calls.",
|
|
36
|
+
parameters: Type.Object({
|
|
37
|
+
pageId: Type.String({ description: "Page id from chrome_devtools_list_pages." }),
|
|
38
|
+
}),
|
|
39
|
+
renderCall: renderToolCall("select page"),
|
|
40
|
+
renderResult: renderTextResult,
|
|
41
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
42
|
+
return withStatus(ctx, "select page", async () => {
|
|
43
|
+
const page = await getPage(params.pageId, { sessionOwner: ctx.sessionManager, signal });
|
|
44
|
+
setActivePageId(ctx.sessionManager, page.id);
|
|
45
|
+
return textResult(`Selected page ${page.id}: ${page.title}\n${page.url}`, {
|
|
46
|
+
page: formatPage(page),
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
export const navigateTool = defineTool({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return client.send("Page.navigate", { url: params.url });
|
|
74
|
-
});
|
|
53
|
+
name: CHROME_DEVTOOLS_TOOL_NAMES[2],
|
|
54
|
+
label: "Chrome DevTools: Navigate",
|
|
55
|
+
description:
|
|
56
|
+
"Navigate a Chrome page to a URL through Chrome DevTools Protocol, creating a page first if none is available.",
|
|
57
|
+
parameters: Type.Object({
|
|
58
|
+
url: Type.String({ description: "URL to navigate to." }),
|
|
59
|
+
pageId: Type.Optional(Type.String({ description: "Optional page id. Defaults to selected or first page." })),
|
|
60
|
+
}),
|
|
61
|
+
renderCall: renderToolCall("navigate"),
|
|
62
|
+
renderResult: renderTextResult,
|
|
63
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
64
|
+
return withStatus(ctx, "navigate", async () => {
|
|
65
|
+
const { created, page } = await resolvePageForNavigation(params.pageId, {
|
|
66
|
+
sessionOwner: ctx.sessionManager,
|
|
67
|
+
signal,
|
|
68
|
+
});
|
|
69
|
+
const result = await withCdp(page, async (client) => {
|
|
70
|
+
await client.send("Page.enable");
|
|
71
|
+
return client.send("Page.navigate", { url: params.url });
|
|
72
|
+
});
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
setActivePageId(ctx.sessionManager, page.id);
|
|
75
|
+
const action = created ? "Created page and navigated" : "Navigated";
|
|
76
|
+
return textResult(`${action} ${page.id} to ${params.url}`, {
|
|
77
|
+
created,
|
|
78
|
+
page: formatPage(page),
|
|
79
|
+
result,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
},
|
|
85
83
|
});
|
|
86
84
|
|
|
87
85
|
export const evaluateTool = defineTool({
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}),
|
|
114
|
-
);
|
|
86
|
+
name: CHROME_DEVTOOLS_TOOL_NAMES[3],
|
|
87
|
+
label: "Chrome DevTools: Evaluate",
|
|
88
|
+
description: "Evaluate JavaScript in a Chrome page through Chrome DevTools Protocol.",
|
|
89
|
+
parameters: Type.Object({
|
|
90
|
+
expression: Type.String({ description: "JavaScript expression to evaluate." }),
|
|
91
|
+
pageId: Type.Optional(Type.String({ description: "Optional page id. Defaults to selected or first page." })),
|
|
92
|
+
awaitPromise: Type.Optional(
|
|
93
|
+
Type.Boolean({ description: "Whether to await a returned Promise. Defaults to true." }),
|
|
94
|
+
),
|
|
95
|
+
}),
|
|
96
|
+
renderCall: renderToolCall("evaluate"),
|
|
97
|
+
renderResult: renderTextResult,
|
|
98
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
99
|
+
return withStatus(ctx, "evaluate", async () => {
|
|
100
|
+
const page = await resolvePage(params.pageId, {
|
|
101
|
+
sessionOwner: ctx.sessionManager,
|
|
102
|
+
signal,
|
|
103
|
+
});
|
|
104
|
+
const result = await withCdp(page, (client) =>
|
|
105
|
+
client.send("Runtime.evaluate", {
|
|
106
|
+
expression: params.expression,
|
|
107
|
+
awaitPromise: params.awaitPromise ?? true,
|
|
108
|
+
returnByValue: true,
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
setActivePageId(ctx.sessionManager, page.id);
|
|
113
|
+
return textResult(JSON.stringify(result, null, 2), { page: formatPage(page), result });
|
|
114
|
+
});
|
|
115
|
+
},
|
|
120
116
|
});
|
|
121
117
|
|
|
122
118
|
export const webMcpListToolsTool = defineTool({
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
119
|
+
name: WEBMCP_TOOL_NAMES[0],
|
|
120
|
+
label: "Chrome DevTools: List WebMCP Tools (Experimental)",
|
|
121
|
+
description:
|
|
122
|
+
"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.",
|
|
123
|
+
parameters: Type.Object({
|
|
124
|
+
pageId: Type.Optional(
|
|
125
|
+
Type.String({
|
|
126
|
+
description: "Optional page id. Defaults to selected or first page.",
|
|
127
|
+
maxLength: 512,
|
|
128
|
+
}),
|
|
129
|
+
),
|
|
130
|
+
}),
|
|
131
|
+
renderCall: renderToolCall("list WebMCP tools"),
|
|
132
|
+
renderResult: renderTextResult,
|
|
133
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
134
|
+
return withStatus(ctx, "list WebMCP tools", async () => {
|
|
135
|
+
const implementation = await import("./webmcp/tools.js");
|
|
136
|
+
return implementation.executeWebMcpListTool(params, signal, ctx);
|
|
137
|
+
});
|
|
138
|
+
},
|
|
143
139
|
});
|
|
144
140
|
|
|
145
141
|
export const webMcpCallTool = defineTool({
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
142
|
+
name: WEBMCP_TOOL_NAMES[1],
|
|
143
|
+
label: "Chrome DevTools: Call WebMCP Tool (Experimental)",
|
|
144
|
+
description:
|
|
145
|
+
"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.",
|
|
146
|
+
parameters: Type.Object({
|
|
147
|
+
sessionGeneration: Type.String({
|
|
148
|
+
description: "Session generation token returned by chrome_devtools_webmcp_list_tools.",
|
|
149
|
+
maxLength: 100,
|
|
150
|
+
}),
|
|
151
|
+
pageId: Type.String({ description: "Page id returned by the list tool.", maxLength: 512 }),
|
|
152
|
+
documentId: Type.String({
|
|
153
|
+
description: "Document loader id returned by the list tool.",
|
|
154
|
+
maxLength: 512,
|
|
155
|
+
}),
|
|
156
|
+
frameId: Type.String({ description: "Frame id returned by the list tool.", maxLength: 512 }),
|
|
157
|
+
frameOrigin: Type.String({
|
|
158
|
+
description: "Exact frame origin returned by the list tool.",
|
|
159
|
+
maxLength: 2_048,
|
|
160
|
+
}),
|
|
161
|
+
toolName: Type.String({
|
|
162
|
+
description: "Exact tool name returned by the list tool.",
|
|
163
|
+
maxLength: 512,
|
|
164
|
+
}),
|
|
165
|
+
schemaDigest: Type.String({
|
|
166
|
+
description: "Schema and annotation digest returned by the list tool.",
|
|
167
|
+
minLength: 64,
|
|
168
|
+
maxLength: 64,
|
|
169
|
+
pattern: "^[a-f0-9]{64}$",
|
|
170
|
+
}),
|
|
171
|
+
input: Type.Record(Type.String(), Type.Unknown(), {
|
|
172
|
+
description: "JSON object matching the page-provided input schema.",
|
|
173
|
+
}),
|
|
174
|
+
}),
|
|
175
|
+
renderCall: renderToolCall("call WebMCP tool"),
|
|
176
|
+
renderResult: renderTextResult,
|
|
177
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
178
|
+
return withStatus(ctx, "call WebMCP tool", async () => {
|
|
179
|
+
const implementation = await import("./webmcp/tools.js");
|
|
180
|
+
return implementation.executeWebMcpCallTool(params, signal, ctx);
|
|
181
|
+
});
|
|
182
|
+
},
|
|
187
183
|
});
|
|
188
184
|
|
|
189
185
|
export const screenshotTool = defineTool({
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
215
|
-
const result = await withCdp(page, async (client) => {
|
|
216
|
-
throwIfAborted(signal);
|
|
217
|
-
await client.send("Page.enable");
|
|
186
|
+
name: CHROME_DEVTOOLS_TOOL_NAMES[4],
|
|
187
|
+
label: "Chrome DevTools: Screenshot",
|
|
188
|
+
description: "Capture a PNG screenshot from a Chrome page through Chrome DevTools Protocol.",
|
|
189
|
+
parameters: Type.Object({
|
|
190
|
+
pageId: Type.Optional(Type.String({ description: "Optional page id. Defaults to selected or first page." })),
|
|
191
|
+
fullPage: Type.Optional(Type.Boolean({ description: "Capture the full document, not just the viewport." })),
|
|
192
|
+
savePath: Type.Optional(
|
|
193
|
+
Type.String({
|
|
194
|
+
description:
|
|
195
|
+
"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.",
|
|
196
|
+
}),
|
|
197
|
+
),
|
|
198
|
+
}),
|
|
199
|
+
renderCall: renderToolCall("screenshot"),
|
|
200
|
+
renderResult: renderScreenshotResult,
|
|
201
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
202
|
+
return withStatus(ctx, "screenshot", async () => {
|
|
203
|
+
const page = await resolvePage(params.pageId, {
|
|
204
|
+
sessionOwner: ctx.sessionManager,
|
|
205
|
+
signal,
|
|
206
|
+
});
|
|
207
|
+
const result = await withCdp(page, async (client) => {
|
|
208
|
+
throwIfAborted(signal);
|
|
209
|
+
await client.send("Page.enable");
|
|
218
210
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
if (!params.fullPage) {
|
|
212
|
+
return client.send<{ data: string }>("Page.captureScreenshot", { format: "png" });
|
|
213
|
+
}
|
|
222
214
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
215
|
+
const metrics = await client.send<{
|
|
216
|
+
contentSize: { x: number; y: number; width: number; height: number };
|
|
217
|
+
}>("Page.getLayoutMetrics");
|
|
226
218
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
219
|
+
throwIfAborted(signal);
|
|
220
|
+
return client.send<{ data: string }>("Page.captureScreenshot", {
|
|
221
|
+
captureBeyondViewport: true,
|
|
222
|
+
format: "png",
|
|
223
|
+
clip: {
|
|
224
|
+
x: metrics.contentSize.x,
|
|
225
|
+
y: metrics.contentSize.y,
|
|
226
|
+
width: metrics.contentSize.width,
|
|
227
|
+
height: metrics.contentSize.height,
|
|
228
|
+
scale: 1,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
});
|
|
240
232
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
233
|
+
setActivePageId(ctx.sessionManager, page.id);
|
|
234
|
+
const savedScreenshot = await saveScreenshot(result.data, params.savePath, ctx.cwd, signal);
|
|
235
|
+
return {
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: formatScreenshotText(page, savedScreenshot),
|
|
240
|
+
},
|
|
241
|
+
{ type: "image", data: result.data, mimeType: "image/png" },
|
|
242
|
+
],
|
|
243
|
+
details: {
|
|
244
|
+
page: formatPage(page),
|
|
245
|
+
bytes: savedScreenshot.bytes,
|
|
246
|
+
savedPath: savedScreenshot.savedPath,
|
|
247
|
+
isDefaultPath: savedScreenshot.isDefaultPath,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
},
|
|
260
252
|
});
|