@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.
@@ -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 = "menu" | "help" | "quickstart" | "status" | "tools" | "enable" | "disable";
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: "tools", label: "tools", description: "Select Chrome DevTools tools" },
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: "Enable all Chrome DevTools tools" },
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: "Disable all Chrome DevTools tools" },
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
- if (settings.kind === "loaded" && settings.settings.tools) {
73
- applyChromeDevtoolsTools(pi, settings.settings.tools);
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(), "enabled all");
152
+ await updateChromeDevtoolsTools(pi, ctx, allChromeDevtoolsTools(), "made all available");
124
153
  return;
125
154
  case "disable":
126
- await updateChromeDevtoolsTools(pi, ctx, [], "disabled all");
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" | "settings" | "help";
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: "Select what Pi may do in Chrome.",
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
- ? "Disable all browser tools…"
89
- : "Enable all browser tools…",
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: "Settings & setup",
107
- description: "Files, sources, trust, and reload steps.",
108
- to: "settings",
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
- `Current: ${current.accepted.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
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 enabled.`,
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 selection";
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
- `Tools: ${snapshot.activeTools.length} of ${CHROME_DEVTOOLS_TOOL_NAMES.length} enabled · ${snapshot.persistenceLabel}`,
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
- `Current accepted: ${current.accepted.size}/${CHROME_DEVTOOLS_TOOL_NAMES.length}`,
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 enabled = CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => current.draft.has(name));
419
- const disabled = CHROME_DEVTOOLS_TOOL_NAMES.filter((name) => !current.draft.has(name));
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
- "Enabled after apply:",
426
- ...(enabled.length > 0
427
- ? enabled.map((name) => ` - ${TOOL_PRESENTATION[name].label} (${name})`)
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
- "Disabled after apply:",
431
- ...(disabled.length > 0
432
- ? disabled.map((name) => ` - ${TOOL_PRESENTATION[name].label} (${name})`)
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
- "The accepted selection is saved for future sessions.",
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
- const active = new Set(pi.getActiveTools());
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 type { EffectiveBrowserSettings } from "./settings.js";
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 = "127.0.0.1";
5
- export const DEFAULT_PORT = 9222;
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 function parseConfiguredPort(value: string | undefined) {
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: configuredHost,
89
- port: configuredPort,
90
- configuredPort,
91
- hostConfigured: process.env.PI_CHROME_DEVTOOLS_HOST !== undefined,
92
- portConfigured: configuredPortOverride !== undefined,
93
- autoLaunchEnabled: process.env.PI_CHROME_DEVTOOLS_AUTO_LAUNCH !== "0",
94
- browserExecutable: process.env.PI_CHROME_DEVTOOLS_BROWSER,
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: process.env.PI_CHROME_DEVTOOLS_BROWSER ? "environment" : "default",
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;