@narumitw/pi-chrome-devtools 0.50.1 → 0.52.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 +107 -61
- package/package.json +1 -1
- package/src/browser-manager.ts +3 -5
- package/src/browser-settings-menu.ts +311 -0
- package/src/chrome-devtools.ts +42 -11
- package/src/lazy-tools.ts +143 -0
- package/src/menu.ts +36 -37
- package/src/runtime.ts +28 -23
- package/src/settings.ts +204 -38
- package/src/tool-selector.ts +45 -31
- package/src/tools.ts +0 -5
package/src/chrome-devtools.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { shutdownManagedBrowser } from "./browser-manager.js";
|
|
3
|
+
import {
|
|
4
|
+
availableChromeDevtoolsTools,
|
|
5
|
+
configureLazyChromeDevtoolsTools,
|
|
6
|
+
createChromeDevtoolsLoadTool,
|
|
7
|
+
initializeAvailableChromeDevtoolsTools,
|
|
8
|
+
} from "./lazy-tools.js";
|
|
3
9
|
import { applyRuntimeBrowserSettings, state } from "./runtime.js";
|
|
4
|
-
import { loadSettings } from "./settings.js";
|
|
10
|
+
import { loadSettings, waitForSettingsWrites } from "./settings.js";
|
|
5
11
|
import {
|
|
6
12
|
allChromeDevtoolsTools,
|
|
7
|
-
applyChromeDevtoolsTools,
|
|
8
13
|
buildCommandGuide,
|
|
9
14
|
buildQuickstartMessage,
|
|
10
15
|
buildToolStatusMessage,
|
|
@@ -20,19 +25,28 @@ import {
|
|
|
20
25
|
selectPageTool,
|
|
21
26
|
} from "./tools.js";
|
|
22
27
|
|
|
23
|
-
type CommandAction =
|
|
28
|
+
type CommandAction =
|
|
29
|
+
| "menu"
|
|
30
|
+
| "help"
|
|
31
|
+
| "quickstart"
|
|
32
|
+
| "status"
|
|
33
|
+
| "settings"
|
|
34
|
+
| "tools"
|
|
35
|
+
| "enable"
|
|
36
|
+
| "disable";
|
|
24
37
|
type CommandContext = ExtensionCommandContext;
|
|
25
38
|
const STATUS_KEY = "chrome-devtools";
|
|
26
39
|
const COMMAND_COMPLETIONS = [
|
|
27
40
|
{ value: "help", label: "help", description: "Show command usage" },
|
|
28
41
|
{ value: "quickstart", label: "quickstart", description: "Show endpoint and launch help" },
|
|
29
42
|
{ value: "status", label: "status", description: "Show tool and settings status" },
|
|
30
|
-
{ value: "
|
|
43
|
+
{ value: "settings", label: "settings", description: "Edit browser connection settings" },
|
|
44
|
+
{ value: "tools", label: "tools", description: "Choose lazy-loadable Chrome DevTools tools" },
|
|
31
45
|
{ value: "toggle", label: "toggle", description: "Alias for tools" },
|
|
32
46
|
{ value: "select", label: "select", description: "Compatibility alias for tools" },
|
|
33
|
-
{ value: "enable", label: "enable", description: "
|
|
47
|
+
{ value: "enable", label: "enable", description: "Make all Chrome DevTools tools available" },
|
|
34
48
|
{ value: "on", label: "on", description: "Compatibility alias for enable" },
|
|
35
|
-
{ value: "disable", label: "disable", description: "
|
|
49
|
+
{ value: "disable", label: "disable", description: "Make all Chrome DevTools tools unavailable" },
|
|
36
50
|
{ value: "off", label: "off", description: "Compatibility alias for disable" },
|
|
37
51
|
];
|
|
38
52
|
export default function chromeDevtools(pi: ExtensionAPI) {
|
|
@@ -41,11 +55,13 @@ export default function chromeDevtools(pi: ExtensionAPI) {
|
|
|
41
55
|
pi.registerTool(navigateTool);
|
|
42
56
|
pi.registerTool(evaluateTool);
|
|
43
57
|
pi.registerTool(screenshotTool);
|
|
58
|
+
pi.registerTool(createChromeDevtoolsLoadTool(pi));
|
|
44
59
|
|
|
45
60
|
pi.registerCommand("chrome-devtools", {
|
|
46
61
|
description: "Open Chrome DevTools help and tool controls",
|
|
47
62
|
getArgumentCompletions: (prefix) => commandCompletions(prefix),
|
|
48
63
|
handler: async (args, ctx) => {
|
|
64
|
+
initializeAvailableChromeDevtoolsTools(pi);
|
|
49
65
|
const generation = state.sessionGeneration;
|
|
50
66
|
await handleChromeDevtoolsCommand(pi, args, ctx, generation);
|
|
51
67
|
},
|
|
@@ -53,6 +69,7 @@ export default function chromeDevtools(pi: ExtensionAPI) {
|
|
|
53
69
|
|
|
54
70
|
pi.on("session_start", async (_event, ctx) => {
|
|
55
71
|
const generation = ++state.sessionGeneration;
|
|
72
|
+
initializeAvailableChromeDevtoolsTools(pi);
|
|
56
73
|
replaceSessionController("Chrome DevTools session replaced");
|
|
57
74
|
state.shuttingDown = false;
|
|
58
75
|
state.settingsNotice = undefined;
|
|
@@ -69,9 +86,11 @@ export default function chromeDevtools(pi: ExtensionAPI) {
|
|
|
69
86
|
for (const warning of settings.warnings) {
|
|
70
87
|
ctx.ui.notify(sanitizeChromeDevtoolsDisplay(warning), "warning");
|
|
71
88
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
const availableTools =
|
|
90
|
+
settings.kind === "loaded" && settings.settings.tools
|
|
91
|
+
? settings.settings.tools
|
|
92
|
+
: availableChromeDevtoolsTools(pi);
|
|
93
|
+
configureLazyChromeDevtoolsTools(pi, availableTools);
|
|
75
94
|
});
|
|
76
95
|
|
|
77
96
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
@@ -80,6 +99,7 @@ export default function chromeDevtools(pi: ExtensionAPI) {
|
|
|
80
99
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
81
100
|
const browserShutdown = shutdownManagedBrowser(undefined, { cancelLaunch: true });
|
|
82
101
|
await waitForChromeDevtoolsSettings();
|
|
102
|
+
await waitForSettingsWrites();
|
|
83
103
|
await browserShutdown;
|
|
84
104
|
});
|
|
85
105
|
}
|
|
@@ -110,6 +130,15 @@ async function handleChromeDevtoolsCommand(
|
|
|
110
130
|
ctx.ui.notify(status, "info");
|
|
111
131
|
return;
|
|
112
132
|
}
|
|
133
|
+
case "settings": {
|
|
134
|
+
if (!ctx.hasUI || (ctx.mode !== "tui" && ctx.mode !== "rpc")) {
|
|
135
|
+
throw new Error("/chrome-devtools settings requires TUI or RPC mode");
|
|
136
|
+
}
|
|
137
|
+
const { showChromeDevtoolsBrowserSettings } = await import("./browser-settings-menu.js");
|
|
138
|
+
if (generation !== state.sessionGeneration) return;
|
|
139
|
+
await showChromeDevtoolsBrowserSettings(ctx, generation);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
113
142
|
case "tools": {
|
|
114
143
|
if (!ctx.hasUI || (ctx.mode !== "tui" && ctx.mode !== "rpc")) {
|
|
115
144
|
throw new Error("/chrome-devtools tools requires TUI or RPC mode");
|
|
@@ -120,10 +149,10 @@ async function handleChromeDevtoolsCommand(
|
|
|
120
149
|
return;
|
|
121
150
|
}
|
|
122
151
|
case "enable":
|
|
123
|
-
await updateChromeDevtoolsTools(pi, ctx, allChromeDevtoolsTools(), "
|
|
152
|
+
await updateChromeDevtoolsTools(pi, ctx, allChromeDevtoolsTools(), "made all available");
|
|
124
153
|
return;
|
|
125
154
|
case "disable":
|
|
126
|
-
await updateChromeDevtoolsTools(pi, ctx, [], "
|
|
155
|
+
await updateChromeDevtoolsTools(pi, ctx, [], "made all unavailable");
|
|
127
156
|
return;
|
|
128
157
|
}
|
|
129
158
|
|
|
@@ -164,6 +193,7 @@ export function parseCommand(args: string): CommandAction | "unknown" {
|
|
|
164
193
|
if (command === "help") return "help";
|
|
165
194
|
if (command === "quickstart") return "quickstart";
|
|
166
195
|
if (command === "status") return "status";
|
|
196
|
+
if (command === "settings") return "settings";
|
|
167
197
|
if (command === "tools" || command === "select" || command === "toggle") return "tools";
|
|
168
198
|
if (command === "enable" || command === "on") return "enable";
|
|
169
199
|
if (command === "disable" || command === "off") return "disable";
|
|
@@ -185,6 +215,7 @@ export {
|
|
|
185
215
|
isLocalDevToolsHost,
|
|
186
216
|
quoteCommandPart,
|
|
187
217
|
} from "./browser-manager.js";
|
|
218
|
+
export { CHROME_DEVTOOLS_LOAD_TOOL_NAME } from "./lazy-tools.js";
|
|
188
219
|
export { parseConfiguredPort } from "./runtime.js";
|
|
189
220
|
export {
|
|
190
221
|
hasParentPathSegment,
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { defineTool, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { CHROME_DEVTOOLS_TOOL_NAMES, type ChromeDevToolsToolName } from "./tool-names.js";
|
|
4
|
+
|
|
5
|
+
export const CHROME_DEVTOOLS_LOAD_TOOL_NAME = "chrome_devtools_load";
|
|
6
|
+
|
|
7
|
+
const AVAILABLE_TOOLS_STORE = Symbol.for("@narumitw/pi-chrome-devtools.available-tools-store");
|
|
8
|
+
type ChromeDevtoolsGlobal = typeof globalThis & {
|
|
9
|
+
[AVAILABLE_TOOLS_STORE]?: WeakMap<ExtensionAPI, Set<ChromeDevToolsToolName>>;
|
|
10
|
+
};
|
|
11
|
+
const sharedGlobal = globalThis as ChromeDevtoolsGlobal;
|
|
12
|
+
const existingAvailableToolsStore = sharedGlobal[AVAILABLE_TOOLS_STORE];
|
|
13
|
+
const availableToolsByApi =
|
|
14
|
+
existingAvailableToolsStore ?? new WeakMap<ExtensionAPI, Set<ChromeDevToolsToolName>>();
|
|
15
|
+
if (!existingAvailableToolsStore) sharedGlobal[AVAILABLE_TOOLS_STORE] = availableToolsByApi;
|
|
16
|
+
|
|
17
|
+
const SEARCH_TEXT: Record<ChromeDevToolsToolName, string> = {
|
|
18
|
+
chrome_devtools_list_pages: "list open inspectable chrome browser pages tabs targets",
|
|
19
|
+
chrome_devtools_select_page: "select choose active chrome browser page tab target",
|
|
20
|
+
chrome_devtools_navigate: "navigate open create chrome browser page url website",
|
|
21
|
+
chrome_devtools_evaluate: "evaluate run javascript expression dom inspect chrome browser page",
|
|
22
|
+
chrome_devtools_screenshot: "capture screenshot png image visual chrome browser page",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function initializeAvailableChromeDevtoolsTools(pi: ExtensionAPI) {
|
|
26
|
+
if (availableToolsByApi.has(pi)) return;
|
|
27
|
+
const activeTools = new Set(pi.getActiveTools());
|
|
28
|
+
setAvailableTools(
|
|
29
|
+
pi,
|
|
30
|
+
CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => activeTools.has(name)),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function configureLazyChromeDevtoolsTools(
|
|
35
|
+
pi: ExtensionAPI,
|
|
36
|
+
availableTools: readonly ChromeDevToolsToolName[],
|
|
37
|
+
) {
|
|
38
|
+
setAvailableTools(pi, availableTools);
|
|
39
|
+
const nonCapabilityTools = pi
|
|
40
|
+
.getActiveTools()
|
|
41
|
+
.filter((name) => !CHROME_DEVTOOLS_TOOL_NAMES.includes(name as ChromeDevToolsToolName));
|
|
42
|
+
pi.setActiveTools(unique([...nonCapabilityTools, CHROME_DEVTOOLS_LOAD_TOOL_NAME]));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function applyAvailableChromeDevtoolsTools(
|
|
46
|
+
pi: ExtensionAPI,
|
|
47
|
+
availableTools: readonly ChromeDevToolsToolName[],
|
|
48
|
+
) {
|
|
49
|
+
const available = setAvailableTools(pi, availableTools);
|
|
50
|
+
const active = pi
|
|
51
|
+
.getActiveTools()
|
|
52
|
+
.filter(
|
|
53
|
+
(name) =>
|
|
54
|
+
!CHROME_DEVTOOLS_TOOL_NAMES.includes(name as ChromeDevToolsToolName) ||
|
|
55
|
+
available.has(name as ChromeDevToolsToolName),
|
|
56
|
+
);
|
|
57
|
+
pi.setActiveTools(unique([...active, CHROME_DEVTOOLS_LOAD_TOOL_NAME]));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function availableChromeDevtoolsTools(pi: ExtensionAPI) {
|
|
61
|
+
const available = availableToolsByApi.get(pi) ?? new Set();
|
|
62
|
+
return CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function createChromeDevtoolsLoadTool(pi: ExtensionAPI) {
|
|
66
|
+
return defineTool({
|
|
67
|
+
name: CHROME_DEVTOOLS_LOAD_TOOL_NAME,
|
|
68
|
+
label: "Chrome DevTools: Load Tools",
|
|
69
|
+
description:
|
|
70
|
+
"Find and enable Chrome DevTools browser tools relevant to a task. Loaded tools remain available for the session.",
|
|
71
|
+
promptSnippet: "Load Chrome DevTools browser capabilities on demand",
|
|
72
|
+
promptGuidelines: [
|
|
73
|
+
"Use chrome_devtools_load when a task requires inspecting or controlling a Chrome browser and the needed chrome_devtools_* capability is not active.",
|
|
74
|
+
],
|
|
75
|
+
parameters: Type.Object({
|
|
76
|
+
query: Type.String({
|
|
77
|
+
description: "Browser capability or task to find tools for.",
|
|
78
|
+
maxLength: 500,
|
|
79
|
+
}),
|
|
80
|
+
limit: Type.Optional(
|
|
81
|
+
Type.Integer({
|
|
82
|
+
description: "Maximum tools to load. Defaults to 3.",
|
|
83
|
+
minimum: 1,
|
|
84
|
+
maximum: 5,
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
}),
|
|
88
|
+
async execute(_toolCallId, params, signal) {
|
|
89
|
+
signal?.throwIfAborted();
|
|
90
|
+
const available = new Set(availableChromeDevtoolsTools(pi));
|
|
91
|
+
const matches = matchChromeDevtoolsTools(params.query, params.limit ?? 3, available);
|
|
92
|
+
const active = pi.getActiveTools();
|
|
93
|
+
const activeSet = new Set(active);
|
|
94
|
+
const added = matches.filter((name) => !activeSet.has(name));
|
|
95
|
+
if (added.length > 0) {
|
|
96
|
+
pi.setActiveTools(unique([...active, ...added]));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const text =
|
|
100
|
+
matches.length === 0
|
|
101
|
+
? "No available Chrome DevTools tools matched the query."
|
|
102
|
+
: added.length > 0
|
|
103
|
+
? `Loaded Chrome DevTools tools: ${added.join(", ")}`
|
|
104
|
+
: `Matching Chrome DevTools tools are already loaded: ${matches.join(", ")}`;
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: "text" as const, text }],
|
|
107
|
+
details: { matches, added },
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function matchChromeDevtoolsTools(
|
|
114
|
+
query: string,
|
|
115
|
+
limit: number,
|
|
116
|
+
available: ReadonlySet<ChromeDevToolsToolName>,
|
|
117
|
+
) {
|
|
118
|
+
const terms = query
|
|
119
|
+
.toLowerCase()
|
|
120
|
+
.split(/[^a-z0-9]+/)
|
|
121
|
+
.filter((term) => term.length >= 2);
|
|
122
|
+
if (terms.length === 0) return [];
|
|
123
|
+
return CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => available.has(name))
|
|
124
|
+
.map((name, index) => ({
|
|
125
|
+
name,
|
|
126
|
+
index,
|
|
127
|
+
score: terms.reduce((score, term) => score + (SEARCH_TEXT[name].includes(term) ? 1 : 0), 0),
|
|
128
|
+
}))
|
|
129
|
+
.filter((match) => match.score > 0)
|
|
130
|
+
.sort((left, right) => right.score - left.score || left.index - right.index)
|
|
131
|
+
.slice(0, limit)
|
|
132
|
+
.map((match) => match.name);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function setAvailableTools(pi: ExtensionAPI, availableTools: readonly ChromeDevToolsToolName[]) {
|
|
136
|
+
const available = new Set(availableTools);
|
|
137
|
+
availableToolsByApi.set(pi, available);
|
|
138
|
+
return available;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function unique(values: readonly string[]) {
|
|
142
|
+
return [...new Set(values)];
|
|
143
|
+
}
|
package/src/menu.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { defineMenu, runMenu, runTask } from "@narumitw/pi-tui-kit";
|
|
3
3
|
import { browserLifecycleState, devToolsEndpoint, launchModeLabel } from "./browser-manager.js";
|
|
4
|
+
import { availableChromeDevtoolsTools } from "./lazy-tools.js";
|
|
4
5
|
import { state } from "./runtime.js";
|
|
5
6
|
import { loadSettings, type SettingsLoadResult } from "./settings.js";
|
|
6
7
|
import { CHROME_DEVTOOLS_TOOL_NAMES, type ChromeDevToolsToolName } from "./tool-names.js";
|
|
7
8
|
import {
|
|
8
9
|
buildBrowserStatusMessage,
|
|
9
10
|
buildCommandGuide,
|
|
10
|
-
buildSettingsSetupMessage,
|
|
11
11
|
sanitizeChromeDevtoolsDisplay,
|
|
12
12
|
setSelectedChromeDevtoolsTools,
|
|
13
13
|
} from "./tool-selector.js";
|
|
14
14
|
|
|
15
15
|
type CommandContext = ExtensionCommandContext;
|
|
16
|
-
type MainScreen = "main" | "browserStatus" | "
|
|
17
|
-
type MainAction = "tools" | "bulk";
|
|
16
|
+
type MainScreen = "main" | "browserStatus" | "help";
|
|
17
|
+
type MainAction = "tools" | "bulk" | "settings";
|
|
18
18
|
type ToolScreen = "tools" | "review";
|
|
19
19
|
type ToolAction = "toggle" | "selectAll" | "selectNone" | "review" | "apply" | "cancel";
|
|
20
20
|
|
|
@@ -75,8 +75,8 @@ export async function showChromeDevtoolsMenu(
|
|
|
75
75
|
items: [
|
|
76
76
|
{
|
|
77
77
|
id: "tools",
|
|
78
|
-
label: "Choose browser tools…",
|
|
79
|
-
description: "
|
|
78
|
+
label: "Choose available browser tools…",
|
|
79
|
+
description: "Choose which capabilities the loader may expose.",
|
|
80
80
|
disabled: Boolean(current.mutationBlockedReason),
|
|
81
81
|
disabledReason: current.mutationBlockedReason,
|
|
82
82
|
action: "tools",
|
|
@@ -85,8 +85,8 @@ export async function showChromeDevtoolsMenu(
|
|
|
85
85
|
id: "bulk",
|
|
86
86
|
label:
|
|
87
87
|
current.activeTools.length === CHROME_DEVTOOLS_TOOL_NAMES.length
|
|
88
|
-
? "
|
|
89
|
-
: "
|
|
88
|
+
? "Make all browser tools unavailable…"
|
|
89
|
+
: "Make all browser tools available…",
|
|
90
90
|
description:
|
|
91
91
|
current.activeTools.length === CHROME_DEVTOOLS_TOOL_NAMES.length
|
|
92
92
|
? "Preview 0 of 5; other active tools stay enabled."
|
|
@@ -103,9 +103,9 @@ export async function showChromeDevtoolsMenu(
|
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
id: "settings",
|
|
106
|
-
label: "
|
|
107
|
-
description: "
|
|
108
|
-
|
|
106
|
+
label: "Browser settings",
|
|
107
|
+
description: "Edit endpoint, auto-launch, and browser executable settings.",
|
|
108
|
+
action: "settings",
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
id: "help",
|
|
@@ -124,14 +124,6 @@ export async function showChromeDevtoolsMenu(
|
|
|
124
124
|
viewportSize: "adaptive",
|
|
125
125
|
hint: "back",
|
|
126
126
|
}),
|
|
127
|
-
settings: () => ({
|
|
128
|
-
kind: "review",
|
|
129
|
-
title: "Settings & setup",
|
|
130
|
-
content: buildSettingsSetupMessage(),
|
|
131
|
-
format: { kind: "text" },
|
|
132
|
-
viewportSize: "adaptive",
|
|
133
|
-
hint: "back",
|
|
134
|
-
}),
|
|
135
127
|
help: () => ({
|
|
136
128
|
kind: "review",
|
|
137
129
|
title: "Chrome DevTools help",
|
|
@@ -162,6 +154,13 @@ export async function showChromeDevtoolsMenu(
|
|
|
162
154
|
if (result?.applied && isCurrent()) updateSnapshotAfterApply(current, result.selectedTools);
|
|
163
155
|
return result?.closeParent ? { kind: "close" } : { kind: "stay" };
|
|
164
156
|
},
|
|
157
|
+
settings: async () => {
|
|
158
|
+
const { showChromeDevtoolsBrowserSettings } = await import("./browser-settings-menu.js");
|
|
159
|
+
if (!isCurrent()) return { kind: "stay" };
|
|
160
|
+
const result = await showChromeDevtoolsBrowserSettings(ctx, generation);
|
|
161
|
+
if (!isCurrent()) return { kind: "stay" };
|
|
162
|
+
return result.closeParent ? { kind: "close" } : { kind: "stay" };
|
|
163
|
+
},
|
|
165
164
|
},
|
|
166
165
|
});
|
|
167
166
|
await runMenu(ctx, menu, {
|
|
@@ -250,8 +249,8 @@ export async function showChromeDevtoolsToolWorkflow(
|
|
|
250
249
|
kind: "review",
|
|
251
250
|
title: "Review tool changes",
|
|
252
251
|
lines: [
|
|
253
|
-
`
|
|
254
|
-
`Proposed: ${current.draft.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
252
|
+
`Currently available: ${current.accepted.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
253
|
+
`Proposed availability: ${current.draft.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
255
254
|
],
|
|
256
255
|
content: buildToolReview(current),
|
|
257
256
|
format: { kind: "text" },
|
|
@@ -307,7 +306,7 @@ export async function showChromeDevtoolsToolWorkflow(
|
|
|
307
306
|
current.draft = new Set(selectedTools);
|
|
308
307
|
current.applied = true;
|
|
309
308
|
ctx.ui.notify(
|
|
310
|
-
`Saved: ${selectedTools.length} of ${CHROME_DEVTOOLS_TOOL_NAMES.length} browser tools
|
|
309
|
+
`Saved: ${selectedTools.length} of ${CHROME_DEVTOOLS_TOOL_NAMES.length} browser tools available to lazy-load.`,
|
|
311
310
|
"info",
|
|
312
311
|
);
|
|
313
312
|
return { kind: "close" };
|
|
@@ -363,7 +362,7 @@ function buildMenuSnapshot(pi: ExtensionAPI, settings: SettingsLoadResult): Menu
|
|
|
363
362
|
? "not saved"
|
|
364
363
|
: arraysEqual(activeTools, persistedTools)
|
|
365
364
|
? "saved"
|
|
366
|
-
: "runtime differs from saved
|
|
365
|
+
: "runtime catalog differs from saved catalog";
|
|
367
366
|
const settingsWarning = [
|
|
368
367
|
...new Set([settings.notice, state.settingsNotice].filter(Boolean)),
|
|
369
368
|
].join("\n");
|
|
@@ -379,7 +378,7 @@ function buildMenuSnapshot(pi: ExtensionAPI, settings: SettingsLoadResult): Menu
|
|
|
379
378
|
|
|
380
379
|
function mainStateLines(snapshot: MenuSnapshot) {
|
|
381
380
|
return sanitizeLines([
|
|
382
|
-
`
|
|
381
|
+
`Lazy catalog: ${snapshot.activeTools.length} of ${CHROME_DEVTOOLS_TOOL_NAMES.length} available · ${snapshot.persistenceLabel}`,
|
|
383
382
|
`Browser: ${browserLifecycleSummary()}`,
|
|
384
383
|
`Endpoint: ${devToolsEndpoint()}`,
|
|
385
384
|
...(snapshot.settingsWarning ? [`Settings warning: ${snapshot.settingsWarning}`] : []),
|
|
@@ -406,7 +405,7 @@ function toolDraftLines(current: ToolWorkflowState) {
|
|
|
406
405
|
return sanitizeLines([`Unavailable: ${current.mutationBlockedReason}`]);
|
|
407
406
|
const changes = symmetricDifferenceSize(current.accepted, current.draft);
|
|
408
407
|
return [
|
|
409
|
-
`
|
|
408
|
+
`Currently available: ${current.accepted.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
410
409
|
changes === 0
|
|
411
410
|
? "No unapplied changes · Escape cancels"
|
|
412
411
|
: `${changes} unapplied ${changes === 1 ? "change" : "changes"} · Escape cancels`,
|
|
@@ -415,25 +414,26 @@ function toolDraftLines(current: ToolWorkflowState) {
|
|
|
415
414
|
}
|
|
416
415
|
|
|
417
416
|
function buildToolReview(current: ToolWorkflowState) {
|
|
418
|
-
const
|
|
419
|
-
const
|
|
417
|
+
const available = CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => current.draft.has(name));
|
|
418
|
+
const unavailable = CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => !current.draft.has(name));
|
|
420
419
|
return sanitizeChromeDevtoolsDisplay(
|
|
421
420
|
[
|
|
422
|
-
`Current browser tools: ${current.accepted.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
423
|
-
`Proposed browser tools: ${current.draft.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
421
|
+
`Current available browser tools: ${current.accepted.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
422
|
+
`Proposed available browser tools: ${current.draft.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
|
|
424
423
|
"",
|
|
425
|
-
"
|
|
426
|
-
...(
|
|
427
|
-
?
|
|
424
|
+
"Available to lazy-load after apply:",
|
|
425
|
+
...(available.length > 0
|
|
426
|
+
? available.map((name) => ` - ${TOOL_PRESENTATION[name].label} (${name})`)
|
|
428
427
|
: [" - none"]),
|
|
429
428
|
"",
|
|
430
|
-
"
|
|
431
|
-
...(
|
|
432
|
-
?
|
|
429
|
+
"Unavailable after apply:",
|
|
430
|
+
...(unavailable.length > 0
|
|
431
|
+
? unavailable.map((name) => ` - ${TOOL_PRESENTATION[name].label} (${name})`)
|
|
433
432
|
: [" - none"]),
|
|
434
433
|
"",
|
|
435
434
|
"Other active Pi tools remain unchanged.",
|
|
436
|
-
"
|
|
435
|
+
"Newly available tools remain deferred until chrome_devtools_load selects them.",
|
|
436
|
+
"The accepted availability policy is saved for future sessions.",
|
|
437
437
|
].join("\n"),
|
|
438
438
|
);
|
|
439
439
|
}
|
|
@@ -447,8 +447,7 @@ function updateSnapshotAfterApply(
|
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
function activeChromeTools(pi: ExtensionAPI) {
|
|
450
|
-
|
|
451
|
-
return CHROME_DEVTOOLS_TOOL_NAMES.filter((toolName) => active.has(toolName));
|
|
450
|
+
return availableChromeDevtoolsTools(pi);
|
|
452
451
|
}
|
|
453
452
|
|
|
454
453
|
function orderedTools(tools: ReadonlySet<ChromeDevToolsToolName>) {
|
package/src/runtime.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { ChildProcess } from "node:child_process";
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_BROWSER_HOST,
|
|
4
|
+
DEFAULT_BROWSER_PORT,
|
|
5
|
+
type EffectiveBrowserSettings,
|
|
6
|
+
parseConfiguredPort,
|
|
7
|
+
} from "./settings.js";
|
|
3
8
|
|
|
4
|
-
export const DEFAULT_HOST =
|
|
5
|
-
export const DEFAULT_PORT =
|
|
9
|
+
export const DEFAULT_HOST = DEFAULT_BROWSER_HOST;
|
|
10
|
+
export const DEFAULT_PORT = DEFAULT_BROWSER_PORT;
|
|
6
11
|
export const DEFAULT_TIMEOUT_MS = 10_000;
|
|
7
12
|
export const DEFAULT_HTTP_TIMEOUT_MS = 1_000;
|
|
8
13
|
export const DEFAULT_ENDPOINT_WAIT_MS = 5_000;
|
|
@@ -26,6 +31,8 @@ export interface ChromeDevToolsState {
|
|
|
26
31
|
hostConfigured: boolean;
|
|
27
32
|
portConfigured: boolean;
|
|
28
33
|
autoLaunchEnabled: boolean;
|
|
34
|
+
endpointSource: EffectiveBrowserSettings["endpointSource"];
|
|
35
|
+
autoLaunchSource: EffectiveBrowserSettings["autoLaunchSource"];
|
|
29
36
|
browserExecutable?: string;
|
|
30
37
|
extensionPaths: string[];
|
|
31
38
|
browserExecutableSource: EffectiveBrowserSettings["executablePathSource"];
|
|
@@ -71,29 +78,19 @@ export interface BrowserCandidate extends BrowserCandidateDefinition {
|
|
|
71
78
|
resolvedExecutable: string;
|
|
72
79
|
}
|
|
73
80
|
|
|
74
|
-
export
|
|
75
|
-
if (value === undefined) return undefined;
|
|
76
|
-
const trimmedValue = value.trim();
|
|
77
|
-
if (!/^\d+$/.test(trimmedValue)) return undefined;
|
|
78
|
-
const port = Number(trimmedValue);
|
|
79
|
-
if (!Number.isInteger(port) || port < 1 || port > 65_535) return undefined;
|
|
80
|
-
return port;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const configuredHost = process.env.PI_CHROME_DEVTOOLS_HOST ?? DEFAULT_HOST;
|
|
84
|
-
const configuredPortOverride = parseConfiguredPort(process.env.PI_CHROME_DEVTOOLS_PORT);
|
|
85
|
-
const configuredPort = configuredPortOverride ?? DEFAULT_PORT;
|
|
81
|
+
export { parseConfiguredPort };
|
|
86
82
|
|
|
87
83
|
export const state: ChromeDevToolsState = {
|
|
88
|
-
host:
|
|
89
|
-
port:
|
|
90
|
-
configuredPort,
|
|
91
|
-
hostConfigured:
|
|
92
|
-
portConfigured:
|
|
93
|
-
autoLaunchEnabled:
|
|
94
|
-
|
|
84
|
+
host: DEFAULT_HOST,
|
|
85
|
+
port: DEFAULT_PORT,
|
|
86
|
+
configuredPort: DEFAULT_PORT,
|
|
87
|
+
hostConfigured: false,
|
|
88
|
+
portConfigured: false,
|
|
89
|
+
autoLaunchEnabled: true,
|
|
90
|
+
endpointSource: "default",
|
|
91
|
+
autoLaunchSource: "default",
|
|
95
92
|
extensionPaths: [],
|
|
96
|
-
browserExecutableSource:
|
|
93
|
+
browserExecutableSource: "default",
|
|
97
94
|
extensionPathsSource: "default",
|
|
98
95
|
projectSettingsTrusted: false,
|
|
99
96
|
shuttingDown: false,
|
|
@@ -106,6 +103,14 @@ export function applyRuntimeBrowserSettings(
|
|
|
106
103
|
paths: { user: string; project?: string },
|
|
107
104
|
projectTrusted: boolean,
|
|
108
105
|
) {
|
|
106
|
+
state.host = browser.host;
|
|
107
|
+
state.port = browser.port;
|
|
108
|
+
state.configuredPort = browser.port;
|
|
109
|
+
state.hostConfigured = browser.hostConfigured;
|
|
110
|
+
state.portConfigured = browser.portConfigured;
|
|
111
|
+
state.autoLaunchEnabled = browser.autoLaunchEnabled;
|
|
112
|
+
state.endpointSource = browser.endpointSource;
|
|
113
|
+
state.autoLaunchSource = browser.autoLaunchSource;
|
|
109
114
|
state.browserExecutable = browser.executablePath;
|
|
110
115
|
state.extensionPaths = [...browser.extensionPaths];
|
|
111
116
|
state.browserExecutableSource = browser.executablePathSource;
|