@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/tool-selector.ts
CHANGED
|
@@ -1,380 +1,336 @@
|
|
|
1
|
-
import { stripVTControlCharacters } from "node:util";
|
|
2
1
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
browserCandidateHint,
|
|
4
|
+
browserLifecycleState,
|
|
5
|
+
browserSettingsForOwner,
|
|
6
|
+
devToolsEndpoint,
|
|
7
|
+
endpointConfigHint,
|
|
8
|
+
endpointSourceLabel,
|
|
9
|
+
launchAttemptLines,
|
|
10
|
+
launchHint,
|
|
11
|
+
launchModeLabel,
|
|
12
|
+
managedBrowserForOwner,
|
|
14
13
|
} from "./browser-manager.js";
|
|
14
|
+
import { sanitizeChromeDevtoolsDisplay } from "./display.js";
|
|
15
15
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
applyAvailableChromeDevtoolsTools,
|
|
17
|
+
availableChromeDevtoolsTools,
|
|
18
|
+
CHROME_DEVTOOLS_LOAD_TOOL_NAME,
|
|
19
|
+
chromeDevtoolsToolExposureMode,
|
|
20
20
|
} from "./lazy-tools.js";
|
|
21
21
|
import { invalidateWebMcpOperations, state, webMcpEnabled } from "./runtime.js";
|
|
22
22
|
import { loadSettings, saveSettings, settingsFilePath } from "./settings.js";
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
CHROME_DEVTOOLS_TOOL_NAMES,
|
|
25
|
+
type ChromeDevToolsToolName,
|
|
26
|
+
CORE_CHROME_DEVTOOLS_TOOL_NAMES,
|
|
27
|
+
isWebMcpToolName,
|
|
28
28
|
} from "./tool-names.js";
|
|
29
29
|
|
|
30
|
+
export { sanitizeChromeDevtoolsDisplay };
|
|
31
|
+
|
|
30
32
|
type CommandContext = ExtensionCommandContext;
|
|
31
33
|
|
|
32
34
|
function unique<T>(values: T[]) {
|
|
33
|
-
|
|
35
|
+
return Array.from(new Set(values));
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
function formatError(error: unknown) {
|
|
37
|
-
|
|
39
|
+
return error instanceof Error ? error.message : String(error);
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
interface ToolStatusSummary {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
availabilityStatus: "enabled" | "disabled" | "partial";
|
|
44
|
+
availableChromeToolCount: number;
|
|
45
|
+
loadedChromeToolCount: number;
|
|
46
|
+
activeNonChromeToolCount: number;
|
|
47
|
+
capabilityCount: number;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
type ToolSelectionSaveResult = "saved" | "active-tools-changed" | "failed";
|
|
49
51
|
|
|
50
52
|
export async function updateChromeDevtoolsTools(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
pi: ExtensionAPI,
|
|
54
|
+
ctx: CommandContext,
|
|
55
|
+
selectedTools: readonly ChromeDevToolsToolName[],
|
|
56
|
+
action: string,
|
|
55
57
|
) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
const generation = state.sessionGeneration;
|
|
59
|
+
const result = await transactSelectedTools(pi, ctx, selectedTools, generation);
|
|
60
|
+
if (result !== "saved" || generation !== state.sessionGeneration) return;
|
|
61
|
+
const status = await buildToolStatusMessage(pi, ctx.sessionManager);
|
|
62
|
+
if (generation !== state.sessionGeneration) return;
|
|
63
|
+
ctx.ui.notify(`Chrome DevTools tool catalog ${action}.\n\n${status}`, "info");
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
export async function setSelectedChromeDevtoolsTools(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
pi: ExtensionAPI,
|
|
68
|
+
ctx: CommandContext,
|
|
69
|
+
selectedTools: readonly ChromeDevToolsToolName[],
|
|
70
|
+
expectedActiveTools: readonly ChromeDevToolsToolName[],
|
|
69
71
|
): Promise<ToolSelectionSaveResult> {
|
|
70
|
-
|
|
71
|
-
pi,
|
|
72
|
-
ctx,
|
|
73
|
-
selectedTools,
|
|
74
|
-
state.sessionGeneration,
|
|
75
|
-
expectedActiveTools,
|
|
76
|
-
);
|
|
72
|
+
return transactSelectedTools(pi, ctx, selectedTools, state.sessionGeneration, expectedActiveTools);
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
let toolTransactionQueue = Promise.resolve();
|
|
80
76
|
|
|
81
77
|
export async function waitForChromeDevtoolsSettings(): Promise<void> {
|
|
82
|
-
|
|
78
|
+
await toolTransactionQueue;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
81
|
function transactSelectedTools(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
pi: ExtensionAPI,
|
|
83
|
+
ctx: CommandContext,
|
|
84
|
+
selectedTools: readonly ChromeDevToolsToolName[],
|
|
85
|
+
expectedGeneration: number,
|
|
86
|
+
expectedActiveTools?: readonly ChromeDevToolsToolName[],
|
|
91
87
|
): Promise<ToolSelectionSaveResult> {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
const operation = toolTransactionQueue.then(() =>
|
|
89
|
+
transactSelectedToolsNow(pi, ctx, selectedTools, expectedGeneration, expectedActiveTools),
|
|
90
|
+
);
|
|
91
|
+
toolTransactionQueue = operation.then(
|
|
92
|
+
() => undefined,
|
|
93
|
+
() => undefined,
|
|
94
|
+
);
|
|
95
|
+
return operation;
|
|
100
96
|
}
|
|
101
97
|
|
|
102
98
|
async function transactSelectedToolsNow(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
pi: ExtensionAPI,
|
|
100
|
+
ctx: CommandContext,
|
|
101
|
+
selectedTools: readonly ChromeDevToolsToolName[],
|
|
102
|
+
expectedGeneration: number,
|
|
103
|
+
expectedActiveTools?: readonly ChromeDevToolsToolName[],
|
|
108
104
|
): Promise<ToolSelectionSaveResult> {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
),
|
|
156
|
-
"warning",
|
|
157
|
-
);
|
|
158
|
-
return "failed";
|
|
159
|
-
}
|
|
105
|
+
if (expectedGeneration !== state.sessionGeneration) return "failed";
|
|
106
|
+
if (expectedActiveTools && !arraysEqual(availableChromeDevtoolsTools(pi), expectedActiveTools)) {
|
|
107
|
+
ctx.ui.notify(
|
|
108
|
+
"Browser tool selection changed while review was open. Review the current state, then apply again.",
|
|
109
|
+
"warning",
|
|
110
|
+
);
|
|
111
|
+
return "active-tools-changed";
|
|
112
|
+
}
|
|
113
|
+
const previousActiveTools = pi.getActiveTools();
|
|
114
|
+
const previousAvailableTools = availableChromeDevtoolsTools(pi);
|
|
115
|
+
try {
|
|
116
|
+
const previousWebMcpTools = previousAvailableTools.filter(isWebMcpToolName);
|
|
117
|
+
const selectedWebMcpTools = selectedTools.filter(isWebMcpToolName);
|
|
118
|
+
if (!arraysEqual(previousWebMcpTools, selectedWebMcpTools)) {
|
|
119
|
+
invalidateWebMcpOperations(ctx.sessionManager, "Chrome DevTools WebMCP gateway availability changed");
|
|
120
|
+
}
|
|
121
|
+
applyChromeDevtoolsTools(pi, selectedTools);
|
|
122
|
+
await persistSettings(selectedTools);
|
|
123
|
+
return expectedGeneration === state.sessionGeneration ? "saved" : "failed";
|
|
124
|
+
} catch (error) {
|
|
125
|
+
let rollbackError: unknown;
|
|
126
|
+
try {
|
|
127
|
+
applyAvailableChromeDevtoolsTools(pi, previousAvailableTools);
|
|
128
|
+
const currentNonChromeTools = pi
|
|
129
|
+
.getActiveTools()
|
|
130
|
+
.filter((name) => !CHROME_DEVTOOLS_TOOL_NAMES.includes(name as ChromeDevToolsToolName));
|
|
131
|
+
const previousLoadedChromeTools = previousActiveTools.filter((name) =>
|
|
132
|
+
CHROME_DEVTOOLS_TOOL_NAMES.includes(name as ChromeDevToolsToolName),
|
|
133
|
+
);
|
|
134
|
+
const restoredChromeTools =
|
|
135
|
+
chromeDevtoolsToolExposureMode(pi) === "eager" ? previousAvailableTools : previousLoadedChromeTools;
|
|
136
|
+
pi.setActiveTools(unique([...currentNonChromeTools, ...restoredChromeTools]));
|
|
137
|
+
} catch (caught) {
|
|
138
|
+
rollbackError = caught;
|
|
139
|
+
}
|
|
140
|
+
if (expectedGeneration !== state.sessionGeneration) return "failed";
|
|
141
|
+
ctx.ui.notify(
|
|
142
|
+
sanitizeChromeDevtoolsDisplay(
|
|
143
|
+
rollbackError
|
|
144
|
+
? `Chrome DevTools settings save failed: ${formatError(error)}; active-tool rollback failed: ${formatError(rollbackError)}`
|
|
145
|
+
: `Chrome DevTools settings save failed; active tools restored: ${formatError(error)}`,
|
|
146
|
+
),
|
|
147
|
+
"warning",
|
|
148
|
+
);
|
|
149
|
+
return "failed";
|
|
150
|
+
}
|
|
160
151
|
}
|
|
161
152
|
|
|
162
153
|
function arraysEqual<T>(left: readonly T[], right: readonly T[]) {
|
|
163
|
-
|
|
154
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
164
155
|
}
|
|
165
156
|
|
|
166
|
-
export function applyChromeDevtoolsTools(
|
|
167
|
-
|
|
168
|
-
selectedTools: readonly ChromeDevToolsToolName[],
|
|
169
|
-
) {
|
|
170
|
-
applyAvailableChromeDevtoolsTools(pi, selectedTools);
|
|
157
|
+
export function applyChromeDevtoolsTools(pi: ExtensionAPI, selectedTools: readonly ChromeDevToolsToolName[]) {
|
|
158
|
+
applyAvailableChromeDevtoolsTools(pi, selectedTools);
|
|
171
159
|
}
|
|
172
160
|
|
|
173
161
|
function getToolStatusSummary(pi: ExtensionAPI, owner: object): ToolStatusSummary {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const availabilityStatus =
|
|
185
|
-
availableChromeToolCount === capabilityCount
|
|
186
|
-
? "enabled"
|
|
187
|
-
: availableChromeToolCount === 0
|
|
188
|
-
? "disabled"
|
|
189
|
-
: "partial";
|
|
162
|
+
const chromeToolNames = new Set<string>(CHROME_DEVTOOLS_TOOL_NAMES);
|
|
163
|
+
const activeToolNames = new Set(pi.getActiveTools());
|
|
164
|
+
const loadedChromeToolCount = CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => activeToolNames.has(name)).length;
|
|
165
|
+
const availableChromeToolCount = availableChromeDevtoolsTools(pi).length;
|
|
166
|
+
const activeNonChromeToolCount = Array.from(activeToolNames).filter(
|
|
167
|
+
(name) => !chromeToolNames.has(name) && name !== CHROME_DEVTOOLS_LOAD_TOOL_NAME,
|
|
168
|
+
).length;
|
|
169
|
+
const capabilityCount = effectiveCatalog(owner).length;
|
|
170
|
+
const availabilityStatus =
|
|
171
|
+
availableChromeToolCount === capabilityCount ? "enabled" : availableChromeToolCount === 0 ? "disabled" : "partial";
|
|
190
172
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
173
|
+
return {
|
|
174
|
+
availabilityStatus,
|
|
175
|
+
availableChromeToolCount,
|
|
176
|
+
loadedChromeToolCount,
|
|
177
|
+
activeNonChromeToolCount,
|
|
178
|
+
capabilityCount,
|
|
179
|
+
};
|
|
198
180
|
}
|
|
199
181
|
|
|
200
182
|
export async function buildToolStatusMessage(pi: ExtensionAPI, owner: object) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
183
|
+
const summary = getToolStatusSummary(pi, owner);
|
|
184
|
+
const persistedSetting = await persistedSettingLabel();
|
|
185
|
+
return sanitizeChromeDevtoolsDisplay(
|
|
186
|
+
[
|
|
187
|
+
`Chrome DevTools tools available: ${formatRuntimeStatus(summary)}`,
|
|
188
|
+
`Tool exposure: ${chromeDevtoolsToolExposureMode(pi)}`,
|
|
189
|
+
`Loaded capability tools this session: ${summary.loadedChromeToolCount}/${summary.capabilityCount}`,
|
|
190
|
+
`WebMCP: ${webMcpEnabled(owner) ? "enabled · experimental · confirmation required for every call" : "disabled · experimental"}`,
|
|
191
|
+
`Loader: ${pi.getActiveTools().includes(CHROME_DEVTOOLS_LOAD_TOOL_NAME) ? "active" : "inactive"}`,
|
|
192
|
+
`Persisted tool catalog: ${persistedSetting}`,
|
|
193
|
+
...browserSettingsStatusLines(owner),
|
|
194
|
+
...(state.settingsNotice ? [`Settings note: ${state.settingsNotice}`] : []),
|
|
195
|
+
`Other active tools preserved: ${summary.activeNonChromeToolCount}`,
|
|
196
|
+
`Endpoint: ${devToolsEndpoint(owner)}`,
|
|
197
|
+
`Endpoint source: ${endpointSourceLabel(owner)}`,
|
|
198
|
+
`Launch mode: ${launchModeLabel(owner)}`,
|
|
199
|
+
...launchAttemptLines(owner),
|
|
200
|
+
].join("\n"),
|
|
201
|
+
);
|
|
220
202
|
}
|
|
221
203
|
|
|
222
204
|
export function buildQuickstartMessage(owner: object) {
|
|
223
|
-
|
|
205
|
+
return buildSettingsSetupMessage(owner);
|
|
224
206
|
}
|
|
225
207
|
|
|
226
208
|
export function buildBrowserStatusMessage(owner?: object) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
].join("\n"),
|
|
261
|
-
);
|
|
209
|
+
const browser = browserSettingsForOwner(owner);
|
|
210
|
+
const lifecycle = browserLifecycleState(owner);
|
|
211
|
+
const webMcpIsEnabled = owner ? webMcpEnabled(owner) : false;
|
|
212
|
+
const browserState =
|
|
213
|
+
lifecycle === "starting"
|
|
214
|
+
? "starting managed browser"
|
|
215
|
+
: lifecycle === "running"
|
|
216
|
+
? "managed browser running"
|
|
217
|
+
: lifecycle === "exited"
|
|
218
|
+
? "managed browser exited"
|
|
219
|
+
: lifecycle === "failed"
|
|
220
|
+
? "last launch failed"
|
|
221
|
+
: "not started; connection has not been checked";
|
|
222
|
+
const needsRecovery = lifecycle === "exited" || lifecycle === "failed";
|
|
223
|
+
return sanitizeChromeDevtoolsDisplay(
|
|
224
|
+
[
|
|
225
|
+
`Browser: ${browserState}`,
|
|
226
|
+
"Viewing this status does not probe the endpoint or launch Chrome.",
|
|
227
|
+
`Endpoint: ${devToolsEndpoint(owner)}`,
|
|
228
|
+
`Endpoint source: ${endpointSourceLabel(owner)}`,
|
|
229
|
+
`Launch mode: ${launchModeLabel(owner)}`,
|
|
230
|
+
`Unpacked extensions: ${browser.extensionPaths.length} (${browser.extensionPathsSource})`,
|
|
231
|
+
`WebMCP: ${webMcpIsEnabled ? "enabled · experimental" : "disabled · experimental"}`,
|
|
232
|
+
...(webMcpIsEnabled && !managedBrowserForOwner(owner)?.ready
|
|
233
|
+
? ["WebMCP warning: attached browser profiles may contain everyday authenticated sessions and sensitive state."]
|
|
234
|
+
: []),
|
|
235
|
+
...(browser.extensionPaths.length > 0
|
|
236
|
+
? ["Unpacked extensions execute trusted browser code in an isolated managed browser."]
|
|
237
|
+
: []),
|
|
238
|
+
...launchAttemptLines(owner),
|
|
239
|
+
...(needsRecovery ? [launchHint(owner), endpointConfigHint()] : []),
|
|
240
|
+
].join("\n"),
|
|
241
|
+
);
|
|
262
242
|
}
|
|
263
243
|
|
|
264
244
|
export function buildSettingsSetupMessage(owner: object) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export function sanitizeChromeDevtoolsDisplay(value: string, maxCharacters = 50_000) {
|
|
280
|
-
const withoutBidi = stripVTControlCharacters(value).replace(
|
|
281
|
-
/[\u202a-\u202e\u2066-\u2069]/gu,
|
|
282
|
-
"�",
|
|
283
|
-
);
|
|
284
|
-
const sanitized = Array.from(withoutBidi, (character) => {
|
|
285
|
-
const codePoint = character.codePointAt(0) ?? 0;
|
|
286
|
-
const unsafeControl =
|
|
287
|
-
(codePoint >= 0 && codePoint <= 8) ||
|
|
288
|
-
(codePoint >= 11 && codePoint <= 31) ||
|
|
289
|
-
(codePoint >= 127 && codePoint <= 159);
|
|
290
|
-
return unsafeControl ? "�" : character;
|
|
291
|
-
}).join("");
|
|
292
|
-
if (sanitized.length <= maxCharacters) return sanitized;
|
|
293
|
-
return `${sanitized.slice(0, Math.max(0, maxCharacters - 1))}…`;
|
|
245
|
+
return sanitizeChromeDevtoolsDisplay(
|
|
246
|
+
[
|
|
247
|
+
`Chrome DevTools endpoint: ${devToolsEndpoint(owner)}`,
|
|
248
|
+
`Endpoint source: ${endpointSourceLabel(owner)}`,
|
|
249
|
+
`Launch mode: ${launchModeLabel(owner)}`,
|
|
250
|
+
...browserSettingsStatusLines(owner),
|
|
251
|
+
launchHint(owner),
|
|
252
|
+
browserCandidateHint(owner),
|
|
253
|
+
...launchAttemptLines(owner),
|
|
254
|
+
endpointConfigHint(),
|
|
255
|
+
].join("\n"),
|
|
256
|
+
);
|
|
294
257
|
}
|
|
295
258
|
|
|
296
259
|
function browserSettingsStatusLines(owner: object) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
"Unpacked extensions require Chrome for Testing or Chromium and execute trusted browser code.",
|
|
319
|
-
]
|
|
320
|
-
: []),
|
|
321
|
-
];
|
|
260
|
+
const browser = browserSettingsForOwner(owner);
|
|
261
|
+
const extensionPaths = browser.extensionPaths;
|
|
262
|
+
const extensionLines =
|
|
263
|
+
extensionPaths.length > 0 ? extensionPaths.map((extensionPath) => ` - ${extensionPath}`) : [" - none"];
|
|
264
|
+
return [
|
|
265
|
+
`Settings file: ${state.settingsFilePath ?? settingsFilePath()} (user)`,
|
|
266
|
+
...(state.projectSettingsFilePath
|
|
267
|
+
? [
|
|
268
|
+
`Project settings: ${state.projectSettingsFilePath} (${state.projectSettingsTrusted ? "trusted" : "untrusted; ignored"})`,
|
|
269
|
+
]
|
|
270
|
+
: []),
|
|
271
|
+
`Auto-launch: ${browser.autoLaunchEnabled ? "on" : "off"} (${browser.autoLaunchSource})`,
|
|
272
|
+
`Browser executable: ${browser.executablePath ?? "automatic discovery"} (${browser.executablePathSource})`,
|
|
273
|
+
`Unpacked extensions (${browser.extensionPathsSource}):`,
|
|
274
|
+
...extensionLines,
|
|
275
|
+
`WebMCP: ${webMcpEnabled(owner) ? "enabled · experimental · every call requires confirmation" : "disabled · experimental"}`,
|
|
276
|
+
"Confirmed menu settings apply before the next browser connection; manual JSON edits require /reload or session replacement.",
|
|
277
|
+
...(extensionPaths.length > 0
|
|
278
|
+
? ["Unpacked extensions require Chrome for Testing or Chromium and execute trusted browser code."]
|
|
279
|
+
: []),
|
|
280
|
+
];
|
|
322
281
|
}
|
|
323
282
|
|
|
324
283
|
export function buildCommandGuide(owner: object) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
284
|
+
return [
|
|
285
|
+
"Chrome DevTools commands:",
|
|
286
|
+
`WebMCP: ${webMcpEnabled(owner) ? "enabled" : "disabled"} · experimental · every page-provided call requires confirmation`,
|
|
287
|
+
"/chrome-devtools — open this menu",
|
|
288
|
+
"/chrome-devtools help — show command usage",
|
|
289
|
+
"/chrome-devtools quickstart — show endpoint and launch help",
|
|
290
|
+
"/chrome-devtools status — show tool and settings status",
|
|
291
|
+
"/chrome-devtools settings — edit browser connection settings",
|
|
292
|
+
"/chrome-devtools tools — choose available Chrome DevTools tools",
|
|
293
|
+
"/chrome-devtools toggle|select — compatibility aliases for tools",
|
|
294
|
+
"/chrome-devtools enable|on — make all Chrome DevTools tools available",
|
|
295
|
+
"/chrome-devtools disable|off — make all Chrome DevTools capability tools unavailable",
|
|
296
|
+
].join("\n");
|
|
338
297
|
}
|
|
339
298
|
|
|
340
299
|
export function allChromeDevtoolsTools(owner: object) {
|
|
341
|
-
|
|
300
|
+
return effectiveCatalog(owner);
|
|
342
301
|
}
|
|
343
302
|
|
|
344
303
|
export function orderedChromeDevtoolsTools(selectedTools: ReadonlySet<ChromeDevToolsToolName>) {
|
|
345
|
-
|
|
304
|
+
return CHROME_DEVTOOLS_TOOL_NAMES.filter((toolName) => selectedTools.has(toolName));
|
|
346
305
|
}
|
|
347
306
|
|
|
348
307
|
function formatRuntimeStatus(summary: ToolStatusSummary) {
|
|
349
|
-
|
|
308
|
+
return `${summary.availabilityStatus} (${summary.availableChromeToolCount}/${summary.capabilityCount} available)`;
|
|
350
309
|
}
|
|
351
310
|
|
|
352
311
|
async function persistedSettingLabel() {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
312
|
+
const settings = await loadSettings();
|
|
313
|
+
if (settings.kind === "loaded" && settings.settings.tools) {
|
|
314
|
+
return formatPersistedSelection(settings.settings.tools);
|
|
315
|
+
}
|
|
316
|
+
if (settings.kind === "invalid") {
|
|
317
|
+
return `none; current active-tool policy preserved (invalid settings ignored: ${settings.reason})`;
|
|
318
|
+
}
|
|
319
|
+
return "none; current active-tool policy preserved";
|
|
361
320
|
}
|
|
362
321
|
|
|
363
322
|
function formatPersistedSelection(tools: readonly ChromeDevToolsToolName[]) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
return `${tools.length}/${CHROME_DEVTOOLS_TOOL_NAMES.length} selected: ${tools.join(", ")}`;
|
|
323
|
+
if (tools.length === CHROME_DEVTOOLS_TOOL_NAMES.length) {
|
|
324
|
+
return `all available (${tools.length}/${CHROME_DEVTOOLS_TOOL_NAMES.length} selected)`;
|
|
325
|
+
}
|
|
326
|
+
if (tools.length === 0) return `all unavailable (0/${CHROME_DEVTOOLS_TOOL_NAMES.length} selected)`;
|
|
327
|
+
return `${tools.length}/${CHROME_DEVTOOLS_TOOL_NAMES.length} selected: ${tools.join(", ")}`;
|
|
370
328
|
}
|
|
371
329
|
|
|
372
330
|
function effectiveCatalog(owner: object): ChromeDevToolsToolName[] {
|
|
373
|
-
|
|
374
|
-
? [...CHROME_DEVTOOLS_TOOL_NAMES]
|
|
375
|
-
: [...CORE_CHROME_DEVTOOLS_TOOL_NAMES];
|
|
331
|
+
return webMcpEnabled(owner) ? [...CHROME_DEVTOOLS_TOOL_NAMES] : [...CORE_CHROME_DEVTOOLS_TOOL_NAMES];
|
|
376
332
|
}
|
|
377
333
|
|
|
378
334
|
async function persistSettings(selectedTools: readonly ChromeDevToolsToolName[]) {
|
|
379
|
-
|
|
335
|
+
await saveSettings({ tools: [...selectedTools], updatedAt: Date.now() });
|
|
380
336
|
}
|