@narumitw/pi-chrome-devtools 0.53.2 → 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 +1 -0
- package/dist/chunks/{browser-settings-menu-IMB6HXSU.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-LLXOCRNF.js → chunk-MZCGM6D3.js} +12 -33
- package/dist/chunks/chunk-MZCGM6D3.js.map +7 -0
- package/dist/chunks/{menu-UJETYMSB.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 +18 -42
- package/dist/index.ts.map +2 -2
- 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 +26 -29
- package/src/lazy-tools.ts +158 -181
- package/src/menu.ts +409 -446
- package/src/render.ts +72 -81
- 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 +242 -271
- 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-IMB6HXSU.js.map +0 -7
- package/dist/chunks/chunk-GTTYS5GT.js.map +0 -7
- package/dist/chunks/chunk-LLXOCRNF.js.map +0 -7
- package/dist/chunks/chunk-SRT3UUIP.js.map +0 -7
- package/dist/chunks/menu-UJETYMSB.js.map +0 -7
- package/dist/chunks/tools-6P7EUX37.js.map +0 -7
package/src/lazy-tools.ts
CHANGED
|
@@ -1,240 +1,217 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defineTool,
|
|
3
|
-
type ExtensionAPI,
|
|
4
|
-
type ExtensionContext,
|
|
5
|
-
} from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import { defineTool, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
6
2
|
import { Type } from "typebox";
|
|
7
3
|
import { webMcpEnabled } from "./runtime.js";
|
|
8
|
-
import {
|
|
9
|
-
CHROME_DEVTOOLS_TOOL_NAMES,
|
|
10
|
-
type ChromeDevToolsToolName,
|
|
11
|
-
isWebMcpToolName,
|
|
12
|
-
} from "./tool-names.js";
|
|
4
|
+
import { CHROME_DEVTOOLS_TOOL_NAMES, type ChromeDevToolsToolName, isWebMcpToolName } from "./tool-names.js";
|
|
13
5
|
|
|
14
6
|
export const CHROME_DEVTOOLS_LOAD_TOOL_NAME = "chrome_devtools_load";
|
|
15
7
|
|
|
16
8
|
const AVAILABLE_TOOLS_STORE = Symbol.for("@narumitw/pi-chrome-devtools.available-tools-store");
|
|
17
9
|
type ChromeDevtoolsGlobal = typeof globalThis & {
|
|
18
|
-
|
|
10
|
+
[AVAILABLE_TOOLS_STORE]?: WeakMap<ExtensionAPI, Set<ChromeDevToolsToolName>>;
|
|
19
11
|
};
|
|
20
12
|
const sharedGlobal = globalThis as ChromeDevtoolsGlobal;
|
|
21
13
|
const existingAvailableToolsStore = sharedGlobal[AVAILABLE_TOOLS_STORE];
|
|
22
|
-
const availableToolsByApi =
|
|
23
|
-
existingAvailableToolsStore ?? new WeakMap<ExtensionAPI, Set<ChromeDevToolsToolName>>();
|
|
14
|
+
const availableToolsByApi = existingAvailableToolsStore ?? new WeakMap<ExtensionAPI, Set<ChromeDevToolsToolName>>();
|
|
24
15
|
if (!existingAvailableToolsStore) sharedGlobal[AVAILABLE_TOOLS_STORE] = availableToolsByApi;
|
|
25
16
|
const lazyExposureByApi = new WeakMap<ExtensionAPI, boolean>();
|
|
26
17
|
const sessionOwnerByApi = new WeakMap<ExtensionAPI, object>();
|
|
27
18
|
|
|
28
19
|
const SEARCH_TEXT: Record<ChromeDevToolsToolName, string> = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
chrome_devtools_webmcp_call_tool:
|
|
37
|
-
"call invoke page provided website webmcp tool confirmation experimental",
|
|
20
|
+
chrome_devtools_list_pages: "list open inspectable chrome browser pages tabs targets",
|
|
21
|
+
chrome_devtools_select_page: "select choose active chrome browser page tab target",
|
|
22
|
+
chrome_devtools_navigate: "navigate open create chrome browser page url website",
|
|
23
|
+
chrome_devtools_evaluate: "evaluate run javascript expression dom inspect chrome browser page",
|
|
24
|
+
chrome_devtools_screenshot: "capture screenshot png image visual chrome browser page",
|
|
25
|
+
chrome_devtools_webmcp_list_tools: "list discover page provided website webmcp tools capabilities experimental",
|
|
26
|
+
chrome_devtools_webmcp_call_tool: "call invoke page provided website webmcp tool confirmation experimental",
|
|
38
27
|
};
|
|
39
28
|
|
|
40
29
|
export function setChromeDevtoolsSessionOwner(pi: ExtensionAPI, owner: object) {
|
|
41
|
-
|
|
30
|
+
sessionOwnerByApi.set(pi, owner);
|
|
42
31
|
}
|
|
43
32
|
|
|
44
33
|
export function initializeAvailableChromeDevtoolsTools(pi: ExtensionAPI) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
if (availableToolsByApi.has(pi)) return;
|
|
35
|
+
const activeTools = new Set(pi.getActiveTools());
|
|
36
|
+
setAvailableTools(
|
|
37
|
+
pi,
|
|
38
|
+
CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => activeTools.has(name)),
|
|
39
|
+
);
|
|
51
40
|
}
|
|
52
41
|
|
|
53
42
|
export function configureChromeDevtoolsToolExposure(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
43
|
+
pi: ExtensionAPI,
|
|
44
|
+
availableTools: readonly ChromeDevToolsToolName[],
|
|
45
|
+
model?: ExtensionContext["model"],
|
|
57
46
|
) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.filter((name) => !CHROME_DEVTOOLS_TOOL_NAMES.includes(name as ChromeDevToolsToolName));
|
|
68
|
-
pi.setActiveTools(
|
|
69
|
-
unique([...nonCapabilityTools, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...exposedTools]),
|
|
70
|
-
);
|
|
47
|
+
setAvailableTools(pi, availableTools);
|
|
48
|
+
const available = effectiveAvailableTools(pi);
|
|
49
|
+
const lazyExposure = supportsNativeDeferredToolLoading(model);
|
|
50
|
+
lazyExposureByApi.set(pi, lazyExposure);
|
|
51
|
+
const exposedTools = lazyExposure ? [] : CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
|
|
52
|
+
const nonCapabilityTools = pi
|
|
53
|
+
.getActiveTools()
|
|
54
|
+
.filter((name) => !CHROME_DEVTOOLS_TOOL_NAMES.includes(name as ChromeDevToolsToolName));
|
|
55
|
+
pi.setActiveTools(unique([...nonCapabilityTools, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...exposedTools]));
|
|
71
56
|
}
|
|
72
57
|
|
|
73
58
|
export function requireEagerChromeDevtoolsToolExposure(pi: ExtensionAPI) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function applyAvailableChromeDevtoolsTools(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
const eagerTools = lazyExposure
|
|
95
|
-
? []
|
|
96
|
-
: CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
|
|
97
|
-
pi.setActiveTools(unique([...active, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...eagerTools]));
|
|
59
|
+
lazyExposureByApi.set(pi, false);
|
|
60
|
+
const active = pi.getActiveTools();
|
|
61
|
+
const available = availableChromeDevtoolsTools(pi);
|
|
62
|
+
pi.setActiveTools(unique([...active, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...available]));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function applyAvailableChromeDevtoolsTools(pi: ExtensionAPI, availableTools: readonly ChromeDevToolsToolName[]) {
|
|
66
|
+
setAvailableTools(pi, availableTools);
|
|
67
|
+
const available = effectiveAvailableTools(pi);
|
|
68
|
+
const lazyExposure = lazyExposureByApi.get(pi) === true;
|
|
69
|
+
const active = pi
|
|
70
|
+
.getActiveTools()
|
|
71
|
+
.filter(
|
|
72
|
+
(name) =>
|
|
73
|
+
!CHROME_DEVTOOLS_TOOL_NAMES.includes(name as ChromeDevToolsToolName) ||
|
|
74
|
+
(lazyExposure && available.has(name as ChromeDevToolsToolName)),
|
|
75
|
+
);
|
|
76
|
+
const eagerTools = lazyExposure ? [] : CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
|
|
77
|
+
pi.setActiveTools(unique([...active, CHROME_DEVTOOLS_LOAD_TOOL_NAME, ...eagerTools]));
|
|
98
78
|
}
|
|
99
79
|
|
|
100
80
|
export function chromeDevtoolsToolExposureMode(pi: ExtensionAPI) {
|
|
101
|
-
|
|
81
|
+
return lazyExposureByApi.get(pi) === true ? "native deferred" : "eager";
|
|
102
82
|
}
|
|
103
83
|
|
|
104
84
|
export function supportsNativeDeferredToolLoading(model: ExtensionContext["model"]): boolean {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
85
|
+
if (!model) return false;
|
|
86
|
+
if (model.api === "anthropic-messages") {
|
|
87
|
+
// Fireworks Messages requires the canonical ToolSearch/tool_search loader name.
|
|
88
|
+
// chrome_devtools_load stays package-specific so independently installed loaders cannot collide.
|
|
89
|
+
if (model.provider === "fireworks") return false;
|
|
90
|
+
const configured = compatBoolean(model.compat, "supportsToolReferences");
|
|
91
|
+
if (configured !== undefined) return configured;
|
|
92
|
+
if (model.provider !== "anthropic" || model.id.includes("haiku")) return false;
|
|
93
|
+
const version = model.id.match(/^claude-(?:opus|sonnet|fable)-(\d+)(?:-(\d+))?(?:-|$)/);
|
|
94
|
+
if (!version) return false;
|
|
95
|
+
const major = Number(version[1]);
|
|
96
|
+
const minor = version[2] && version[2].length < 8 ? Number(version[2]) : 0;
|
|
97
|
+
return major > 4 || (major === 4 && minor >= 5);
|
|
98
|
+
}
|
|
99
|
+
if (model.api === "openai-completions") {
|
|
100
|
+
return compatString(model.compat, "deferredToolsMode") === "kimi";
|
|
101
|
+
}
|
|
102
|
+
if (model.api === "openai-responses" || model.api === "openai-codex-responses") {
|
|
103
|
+
return (
|
|
104
|
+
compatBoolean(model.compat, "supportsAdditionalTools") === true ||
|
|
105
|
+
compatBoolean(model.compat, "supportsToolSearch") === true
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
126
109
|
}
|
|
127
110
|
|
|
128
111
|
export function availableChromeDevtoolsTools(pi: ExtensionAPI) {
|
|
129
|
-
|
|
130
|
-
|
|
112
|
+
const available = effectiveAvailableTools(pi);
|
|
113
|
+
return CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
|
|
131
114
|
}
|
|
132
115
|
|
|
133
116
|
export function configuredChromeDevtoolsTools(pi: ExtensionAPI) {
|
|
134
|
-
|
|
135
|
-
|
|
117
|
+
const available = availableToolsByApi.get(pi) ?? new Set();
|
|
118
|
+
return CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
|
|
136
119
|
}
|
|
137
120
|
|
|
138
121
|
export function createChromeDevtoolsLoadTool(pi: ExtensionAPI) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
187
|
-
function matchChromeDevtoolsTools(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
.filter((match) => match.score > 0)
|
|
204
|
-
.sort((left, right) => right.score - left.score || left.index - right.index)
|
|
205
|
-
.slice(0, limit)
|
|
206
|
-
.map((match) => match.name);
|
|
122
|
+
return defineTool({
|
|
123
|
+
name: CHROME_DEVTOOLS_LOAD_TOOL_NAME,
|
|
124
|
+
label: "Chrome DevTools: Load Tools",
|
|
125
|
+
description:
|
|
126
|
+
"Find and enable Chrome DevTools browser tools relevant to a task. Loaded tools remain available for the session.",
|
|
127
|
+
promptSnippet: "Load Chrome DevTools browser capabilities on demand",
|
|
128
|
+
promptGuidelines: [
|
|
129
|
+
"Use chrome_devtools_load when a task requires inspecting or controlling a Chrome browser and the needed chrome_devtools_* capability is not active.",
|
|
130
|
+
],
|
|
131
|
+
parameters: Type.Object({
|
|
132
|
+
query: Type.String({
|
|
133
|
+
description: "Browser capability or task to find tools for.",
|
|
134
|
+
maxLength: 500,
|
|
135
|
+
}),
|
|
136
|
+
limit: Type.Optional(
|
|
137
|
+
Type.Integer({
|
|
138
|
+
description: "Maximum tools to load. Defaults to 3.",
|
|
139
|
+
minimum: 1,
|
|
140
|
+
maximum: 5,
|
|
141
|
+
}),
|
|
142
|
+
),
|
|
143
|
+
}),
|
|
144
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
145
|
+
signal?.throwIfAborted();
|
|
146
|
+
setChromeDevtoolsSessionOwner(pi, ctx.sessionManager);
|
|
147
|
+
const available = new Set(availableChromeDevtoolsTools(pi));
|
|
148
|
+
const matches = matchChromeDevtoolsTools(params.query, params.limit ?? 3, available);
|
|
149
|
+
const active = pi.getActiveTools();
|
|
150
|
+
const activeSet = new Set(active);
|
|
151
|
+
const added = matches.filter((name) => !activeSet.has(name));
|
|
152
|
+
if (added.length > 0) {
|
|
153
|
+
pi.setActiveTools(unique([...active, ...added]));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const text =
|
|
157
|
+
matches.length === 0
|
|
158
|
+
? "No available Chrome DevTools tools matched the query."
|
|
159
|
+
: added.length > 0
|
|
160
|
+
? `Loaded Chrome DevTools tools: ${added.join(", ")}`
|
|
161
|
+
: `Matching Chrome DevTools tools are already loaded: ${matches.join(", ")}`;
|
|
162
|
+
return {
|
|
163
|
+
content: [{ type: "text" as const, text }],
|
|
164
|
+
details: { matches, added },
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function matchChromeDevtoolsTools(query: string, limit: number, available: ReadonlySet<ChromeDevToolsToolName>) {
|
|
171
|
+
const terms = query
|
|
172
|
+
.toLowerCase()
|
|
173
|
+
.split(/[^a-z0-9]+/)
|
|
174
|
+
.filter((term) => term.length >= 2);
|
|
175
|
+
if (terms.length === 0) return [];
|
|
176
|
+
return CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name))
|
|
177
|
+
.map((name, index) => ({
|
|
178
|
+
name,
|
|
179
|
+
index,
|
|
180
|
+
score: terms.reduce((score, term) => score + (SEARCH_TEXT[name].includes(term) ? 1 : 0), 0),
|
|
181
|
+
}))
|
|
182
|
+
.filter((match) => match.score > 0)
|
|
183
|
+
.sort((left, right) => right.score - left.score || left.index - right.index)
|
|
184
|
+
.slice(0, limit)
|
|
185
|
+
.map((match) => match.name);
|
|
207
186
|
}
|
|
208
187
|
|
|
209
188
|
function setAvailableTools(pi: ExtensionAPI, availableTools: readonly ChromeDevToolsToolName[]) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
189
|
+
const available = new Set(availableTools);
|
|
190
|
+
availableToolsByApi.set(pi, available);
|
|
191
|
+
return available;
|
|
213
192
|
}
|
|
214
193
|
|
|
215
194
|
function effectiveAvailableTools(pi: ExtensionAPI) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
),
|
|
223
|
-
);
|
|
195
|
+
const configured = availableToolsByApi.get(pi) ?? new Set();
|
|
196
|
+
const owner = sessionOwnerByApi.get(pi);
|
|
197
|
+
const enabled = owner !== undefined && webMcpEnabled(owner);
|
|
198
|
+
return new Set(
|
|
199
|
+
CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => configured.has(name) && (enabled || !isWebMcpToolName(name))),
|
|
200
|
+
);
|
|
224
201
|
}
|
|
225
202
|
|
|
226
203
|
function compatBoolean(value: unknown, key: string) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
204
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
205
|
+
const record = value as Record<string, unknown>;
|
|
206
|
+
return typeof record[key] === "boolean" ? record[key] : undefined;
|
|
230
207
|
}
|
|
231
208
|
|
|
232
209
|
function compatString(value: unknown, key: string) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
210
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
211
|
+
const record = value as Record<string, unknown>;
|
|
212
|
+
return typeof record[key] === "string" ? record[key] : undefined;
|
|
236
213
|
}
|
|
237
214
|
|
|
238
215
|
function unique(values: readonly string[]) {
|
|
239
|
-
|
|
216
|
+
return [...new Set(values)];
|
|
240
217
|
}
|