pi-agent-browser-native 0.2.48 → 0.2.50

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.
Files changed (189) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +21 -11
  3. package/dist/extensions/agent-browser/index.js +808 -0
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
  6. package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
  7. package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
  8. package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
  9. package/dist/extensions/agent-browser/lib/config-policy.js +669 -0
  10. package/dist/extensions/agent-browser/lib/config.js +122 -0
  11. package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
  12. package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
  13. package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
  14. package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
  15. package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
  16. package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
  17. package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
  18. package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
  19. package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
  20. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
  21. package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
  22. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
  23. package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
  24. package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
  25. package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
  26. package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
  27. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
  28. package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
  29. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
  30. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
  31. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
  32. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
  40. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
  41. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
  42. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
  43. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
  44. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
  45. package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
  46. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
  47. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
  48. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
  49. package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
  50. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
  51. package/dist/extensions/agent-browser/lib/playbook.js +121 -0
  52. package/dist/extensions/agent-browser/lib/process.js +363 -0
  53. package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
  54. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
  55. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
  56. package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
  57. package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
  58. package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
  59. package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
  60. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
  61. package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
  62. package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
  63. package/dist/extensions/agent-browser/lib/results/network.js +73 -0
  64. package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
  65. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
  66. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
  67. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
  68. package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
  69. package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
  70. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +956 -0
  71. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
  72. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
  73. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
  74. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
  75. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
  76. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
  77. package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
  78. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
  79. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
  80. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
  81. package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
  82. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
  83. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
  84. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
  85. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
  86. package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
  87. package/dist/extensions/agent-browser/lib/results/text.js +40 -0
  88. package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
  89. package/dist/extensions/agent-browser/lib/runtime.js +855 -0
  90. package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
  91. package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
  92. package/dist/extensions/agent-browser/lib/temp.js +498 -0
  93. package/dist/extensions/agent-browser/lib/web-search.js +562 -0
  94. package/docs/ARCHITECTURE.md +5 -5
  95. package/docs/COMMAND_REFERENCE.md +4 -4
  96. package/docs/RELEASE.md +22 -11
  97. package/docs/REQUIREMENTS.md +1 -1
  98. package/docs/SUPPORT_MATRIX.md +5 -4
  99. package/docs/TOOL_CONTRACT.md +1 -1
  100. package/package.json +9 -5
  101. package/scripts/config.mjs +14 -20
  102. package/scripts/doctor.mjs +8 -7
  103. package/extensions/agent-browser/index.ts +0 -961
  104. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  105. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  106. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  107. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  108. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  109. package/extensions/agent-browser/lib/config-policy.js +0 -690
  110. package/extensions/agent-browser/lib/config.ts +0 -211
  111. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  112. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  113. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  114. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  115. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  116. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  117. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  118. package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
  119. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  120. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  121. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  122. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  123. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  124. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  125. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  126. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  127. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  128. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  129. package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
  130. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
  131. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
  132. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
  133. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  134. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
  135. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
  136. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
  137. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
  138. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
  139. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
  140. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
  141. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  142. package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
  143. package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
  144. package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
  145. package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
  146. package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
  147. package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
  148. package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -267
  149. package/extensions/agent-browser/lib/playbook.ts +0 -142
  150. package/extensions/agent-browser/lib/process.ts +0 -516
  151. package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
  152. package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
  153. package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
  154. package/extensions/agent-browser/lib/results/categories.ts +0 -106
  155. package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
  156. package/extensions/agent-browser/lib/results/contracts.ts +0 -241
  157. package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
  158. package/extensions/agent-browser/lib/results/envelope.ts +0 -195
  159. package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
  160. package/extensions/agent-browser/lib/results/network.ts +0 -78
  161. package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
  162. package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
  163. package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
  164. package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
  165. package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
  166. package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
  167. package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
  168. package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
  169. package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
  170. package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
  171. package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
  172. package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
  173. package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
  174. package/extensions/agent-browser/lib/results/presentation.ts +0 -257
  175. package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
  176. package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
  177. package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
  178. package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
  179. package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
  180. package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
  181. package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
  182. package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
  183. package/extensions/agent-browser/lib/results/text.ts +0 -40
  184. package/extensions/agent-browser/lib/runtime.ts +0 -988
  185. package/extensions/agent-browser/lib/session-page-state.ts +0 -512
  186. package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
  187. package/extensions/agent-browser/lib/temp.ts +0 -577
  188. package/extensions/agent-browser/lib/web-search.ts +0 -728
  189. /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
@@ -1,211 +0,0 @@
1
- /**
2
- * Purpose: Load pi-agent-browser-native package configuration from Pi-scoped global, project, or explicit paths.
3
- * Responsibilities: Resolve config layers, resolve secrets without exposing values, and provide redacted status for tools/CLIs.
4
- * Scope: Package-owned configuration only; canonical config policy lives in config-policy.js, browser command execution and web-search API calls live in focused modules.
5
- * Invariants/Assumptions: Raw project-local plaintext credentials are unsafe and rejected by the shared config policy; command credentials are resolved lazily at execution time.
6
- */
7
-
8
- import { exec as execCallback } from "node:child_process";
9
- import { readFile } from "node:fs/promises";
10
- import { promisify } from "node:util";
11
-
12
- import {
13
- SECRET_COMMAND_TIMEOUT_MS,
14
- buildAgentBrowserConfigState,
15
- getAgentBrowserConfigPaths,
16
- getWebSearchCredentialSource,
17
- getWebSearchProviderOrder,
18
- loadAgentBrowserConfigStateSync,
19
- mergeAgentBrowserConfig,
20
- parseAgentBrowserConfigLayer,
21
- resolveEnvInterpolations,
22
- } from "./config-policy.js";
23
- import type {
24
- AgentBrowserConfig,
25
- AgentBrowserConfigLoadOptions,
26
- AgentBrowserConfigScope,
27
- AgentBrowserConfigState,
28
- BrowserDefaultProfileConfig,
29
- BrowserDefaultProfilePolicy,
30
- ConfigLayer,
31
- CredentialSource,
32
- CredentialSourceKind,
33
- WebSearchProvider,
34
- } from "./config-policy.js";
35
-
36
- export {
37
- AGENT_BROWSER_CONFIG_ENV,
38
- BRAVE_API_KEY_ENV,
39
- CONFIG_RELATIVE_PATH,
40
- DEFAULT_WEB_SEARCH_PROVIDER,
41
- EXA_API_KEY_ENV,
42
- GLOBAL_CONFIG_RELATIVE_PATH,
43
- SECRET_COMMAND_TIMEOUT_MS,
44
- WEB_SEARCH_PROVIDER_CONFIG_KEYS,
45
- WEB_SEARCH_PROVIDER_DESCRIPTORS,
46
- WEB_SEARCH_PROVIDER_ENV_VARS,
47
- WEB_SEARCH_PROVIDERS,
48
- buildAgentBrowserConfigState,
49
- buildWebSearchCredentialSources,
50
- canRegisterWebSearchTool,
51
- classifyCredentialSource,
52
- formatBrowserExecutableStatus,
53
- formatBrowserProfileStatus,
54
- getAgentBrowserConfigPaths,
55
- getCredentialSourceSummary,
56
- getGlobalAgentBrowserConfigPath,
57
- getProjectAgentBrowserConfigPath,
58
- getWebSearchCredentialSource,
59
- getWebSearchProviderConfigKey,
60
- getWebSearchProviderDescriptor,
61
- getWebSearchProviderEnvVar,
62
- getWebSearchProviderLabel,
63
- getWebSearchProviderOrder,
64
- hasPotentialCredentialSource,
65
- isPlaintextCredentialValue,
66
- isProjectSafeCredentialValueForProvider,
67
- isWebSearchProvider,
68
- loadAgentBrowserConfigStateSync,
69
- mergeAgentBrowserConfig,
70
- parseAgentBrowserConfigLayer,
71
- resolveEnvInterpolations,
72
- summarizeConfigFiles,
73
- validateAgentBrowserConfig,
74
- validateWebSearchProvider,
75
- } from "./config-policy.js";
76
- export type {
77
- AgentBrowserConfig,
78
- AgentBrowserConfigLoadOptions,
79
- AgentBrowserConfigScope,
80
- AgentBrowserConfigState,
81
- BrowserDefaultProfileConfig,
82
- BrowserDefaultProfilePolicy,
83
- ConfigLayer,
84
- CredentialSource,
85
- CredentialSourceKind,
86
- WebSearchProvider,
87
- WebSearchProviderDescriptor,
88
- } from "./config-policy.js";
89
-
90
- const exec = promisify(execCallback);
91
-
92
- export interface ResolvedCredential {
93
- source: CredentialSource;
94
- value: string;
95
- }
96
-
97
- async function readConfigLayer(path: string, scope: ConfigLayer["scope"], errors: string[], warnings: string[]): Promise<ConfigLayer | undefined> {
98
- let raw: string;
99
- try {
100
- raw = await readFile(path, "utf8");
101
- } catch (error) {
102
- if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
103
- return undefined;
104
- }
105
- errors.push(`Could not read ${scope} config ${path}: ${error instanceof Error ? error.message : String(error)}`);
106
- return undefined;
107
- }
108
- return parseAgentBrowserConfigLayer(raw, path, scope, errors, warnings);
109
- }
110
-
111
- export async function loadAgentBrowserConfig(options: AgentBrowserConfigLoadOptions = {}): Promise<AgentBrowserConfigState> {
112
- const env = options.env ?? process.env;
113
- const paths = getAgentBrowserConfigPaths({ cwd: options.cwd, env });
114
- const includeProjectConfig = options.includeProjectConfig !== false;
115
- const errors: string[] = [];
116
- const warnings: string[] = [];
117
- const layerCandidates = [
118
- { path: paths.global, scope: "global" as const },
119
- ...(includeProjectConfig ? [{ path: paths.project, scope: "project" as const }] : []),
120
- ...(paths.override ? [{ path: paths.override, scope: "override" as const }] : []),
121
- ];
122
- const layers: ConfigLayer[] = [];
123
- let mergedConfig: AgentBrowserConfig = {};
124
- for (const candidate of layerCandidates) {
125
- const layer = await readConfigLayer(candidate.path, candidate.scope, errors, warnings);
126
- if (!layer) continue;
127
- layers.push(layer);
128
- mergedConfig = mergeAgentBrowserConfig(mergedConfig, layer.config);
129
- }
130
- return buildAgentBrowserConfigState({
131
- env,
132
- errors,
133
- layers,
134
- mergedConfig,
135
- paths,
136
- projectConfigIncluded: includeProjectConfig,
137
- warnings,
138
- });
139
- }
140
-
141
- export function loadAgentBrowserConfigSync(options: AgentBrowserConfigLoadOptions = {}): AgentBrowserConfigState {
142
- return loadAgentBrowserConfigStateSync(options);
143
- }
144
-
145
- async function resolveCommandCredential(rawValue: string, signal?: AbortSignal): Promise<string | undefined> {
146
- const command = rawValue.slice(1).trim();
147
- if (!command) return undefined;
148
- try {
149
- const result = await exec(command, {
150
- signal,
151
- timeout: SECRET_COMMAND_TIMEOUT_MS,
152
- maxBuffer: 1024 * 1024,
153
- });
154
- const value = result.stdout.trim();
155
- return value.length > 0 ? value : undefined;
156
- } catch (error) {
157
- if (signal?.aborted) throw error;
158
- throw new Error("Credential command failed without exposing command output. Check pi-agent-browser-config web-search status and the configured secret manager command.");
159
- }
160
- }
161
-
162
- export async function resolveCredentialSource(
163
- source: CredentialSource | undefined,
164
- options: { env?: NodeJS.ProcessEnv; signal?: AbortSignal } = {},
165
- ): Promise<ResolvedCredential | undefined> {
166
- if (!source) return undefined;
167
- let value: string | undefined;
168
- if (source.kind === "command") {
169
- value = await resolveCommandCredential(source.rawValue, options.signal);
170
- } else if (source.kind === "env") {
171
- value = resolveEnvInterpolations(source.rawValue, options.env ?? process.env)?.trim();
172
- } else {
173
- value = source.rawValue.trim();
174
- }
175
- return value ? { source, value } : undefined;
176
- }
177
-
178
- export async function resolveWebSearchCredential(
179
- state: AgentBrowserConfigState,
180
- provider: WebSearchProvider,
181
- options: { env?: NodeJS.ProcessEnv; signal?: AbortSignal } = {},
182
- ): Promise<ResolvedCredential | undefined> {
183
- if (!state.webSearchEnabled || state.errors.length > 0) return undefined;
184
- return resolveCredentialSource(getWebSearchCredentialSource(state, provider), options);
185
- }
186
-
187
- export async function resolvePreferredWebSearchCredential(
188
- state: AgentBrowserConfigState,
189
- options: { env?: NodeJS.ProcessEnv; provider?: WebSearchProvider | "auto"; signal?: AbortSignal } = {},
190
- ): Promise<{ provider: WebSearchProvider; credential: ResolvedCredential } | undefined> {
191
- if (!state.webSearchEnabled || state.errors.length > 0) return undefined;
192
- for (const provider of getWebSearchProviderOrder(state, options.provider)) {
193
- const credential = await resolveWebSearchCredential(state, provider, options);
194
- if (credential) return { provider, credential };
195
- }
196
- return undefined;
197
- }
198
-
199
- export async function hasResolvableCredentialSource(
200
- state: AgentBrowserConfigState,
201
- options: { env?: NodeJS.ProcessEnv } = {},
202
- ): Promise<boolean> {
203
- if (!state.webSearchEnabled || state.errors.length > 0) return false;
204
- for (const provider of getWebSearchProviderOrder(state)) {
205
- const source = getWebSearchCredentialSource(state, provider);
206
- if (!source) continue;
207
- if (source.kind === "command") return true;
208
- if ((await resolveCredentialSource(source, options))?.value) return true;
209
- }
210
- return false;
211
- }
@@ -1,69 +0,0 @@
1
- /**
2
- * Purpose: Parse and fetch Chrome DevTools Protocol metadata for wrapper-owned Electron launches.
3
- * Responsibilities: Normalize CDP version/target JSON and perform bounded localhost CDP JSON fetches.
4
- * Scope: Tiny Electron CDP boundary helpers only; launch, status, cleanup, and target selection stay in their owning modules.
5
- * Usage: Imported by Electron launch and cleanup paths when polling `/json/version` and `/json/list`.
6
- * Invariants/Assumptions: Malformed or unavailable CDP endpoints return undefined/empty metadata rather than throwing, matching prior caller behavior.
7
- */
8
-
9
- import { isRecord } from "../parsing.js";
10
-
11
- const ELECTRON_CDP_FETCH_TIMEOUT_MS = 1_000;
12
-
13
- export interface ElectronCdpVersion {
14
- browser?: string;
15
- protocolVersion?: string;
16
- userAgent?: string;
17
- v8Version?: string;
18
- webKitVersion?: string;
19
- webSocketDebuggerUrl?: string;
20
- }
21
-
22
- export interface ElectronCdpTarget {
23
- id?: string;
24
- title?: string;
25
- type?: string;
26
- url?: string;
27
- webSocketDebuggerUrl?: string;
28
- }
29
-
30
- function asString(value: unknown): string | undefined {
31
- return typeof value === "string" && value.trim().length > 0 ? value : undefined;
32
- }
33
-
34
- export function parseCdpVersion(value: unknown): ElectronCdpVersion | undefined {
35
- if (!isRecord(value)) return undefined;
36
- return {
37
- browser: asString(value.Browser) ?? asString(value.browser),
38
- protocolVersion: asString(value["Protocol-Version"]) ?? asString(value.protocolVersion),
39
- userAgent: asString(value["User-Agent"]) ?? asString(value.userAgent),
40
- v8Version: asString(value["V8-Version"]) ?? asString(value.v8Version),
41
- webKitVersion: asString(value["WebKit-Version"]) ?? asString(value.webKitVersion),
42
- webSocketDebuggerUrl: asString(value.webSocketDebuggerUrl),
43
- };
44
- }
45
-
46
- export function parseCdpTargets(value: unknown): ElectronCdpTarget[] {
47
- if (!Array.isArray(value)) return [];
48
- return value.filter(isRecord).map((target) => ({
49
- id: asString(target.id),
50
- title: asString(target.title),
51
- type: asString(target.type),
52
- url: asString(target.url),
53
- webSocketDebuggerUrl: asString(target.webSocketDebuggerUrl),
54
- }));
55
- }
56
-
57
- export async function fetchCdpJson(url: string): Promise<unknown | undefined> {
58
- const controller = new AbortController();
59
- const timeout = setTimeout(() => controller.abort(), ELECTRON_CDP_FETCH_TIMEOUT_MS);
60
- try {
61
- const response = await fetch(url, { signal: controller.signal });
62
- if (!response.ok) return undefined;
63
- return await response.json() as unknown;
64
- } catch {
65
- return undefined;
66
- } finally {
67
- clearTimeout(timeout);
68
- }
69
- }
@@ -1,235 +0,0 @@
1
- /**
2
- * Purpose: Inspect and clean wrapper-owned Electron launch resources.
3
- * Responsibilities: Report CDP liveness/targets without mutation and remove only tracked process/profile resources during explicit or shutdown cleanup.
4
- * Scope: Host-side Electron status and resource cleanup only; upstream managed-session close remains in the extension entrypoint.
5
- * Usage: Called by electron.status, electron.cleanup, and session_shutdown handling.
6
- * Invariants/Assumptions: Cleanup operates only on launch records produced by this wrapper and prefers partial cleanup reporting over killing or deleting untracked resources.
7
- */
8
-
9
- import { execFile, type ChildProcess } from "node:child_process";
10
- import { rm } from "node:fs/promises";
11
- import { promisify } from "node:util";
12
-
13
- import { fetchCdpJson, parseCdpTargets, parseCdpVersion } from "./cdp.js";
14
- import { ELECTRON_PROFILE_DIR_PREFIX, type ElectronCdpTarget, type ElectronCdpVersion, type ElectronLaunchRecord } from "./launch.js";
15
- import { pathExists } from "../fs-utils.js";
16
- import { getSecureTempChildDirectoryValidationError } from "../temp.js";
17
-
18
- const ELECTRON_CLEANUP_DEFAULT_TIMEOUT_MS = 5_000;
19
- const ELECTRON_CLEANUP_POLL_INTERVAL_MS = 100;
20
- const RESTORED_PROCESS_COMMAND_TIMEOUT_MS = 1_000;
21
- const execFileAsync = promisify(execFile);
22
-
23
- export interface ElectronLaunchStatus {
24
- cleanupState: ElectronLaunchRecord["cleanupState"];
25
- launchId: string;
26
- pid?: number;
27
- pidAlive?: boolean;
28
- port: number;
29
- portAlive: boolean;
30
- targets: ElectronCdpTarget[];
31
- version?: ElectronCdpVersion;
32
- }
33
-
34
- export interface ElectronCleanupStep {
35
- error?: string;
36
- resource: "debug-port" | "managed-session" | "process" | "user-data-dir";
37
- sessionName?: string;
38
- state: "already-gone" | "failed" | "removed" | "skipped";
39
- }
40
-
41
- export interface ElectronCleanupResult {
42
- launchId: string;
43
- partial: boolean;
44
- record: ElectronLaunchRecord;
45
- remainingResources: string[];
46
- steps: ElectronCleanupStep[];
47
- summary: string;
48
- }
49
-
50
- function sleep(ms: number): Promise<void> {
51
- return new Promise((resolve) => setTimeout(resolve, ms));
52
- }
53
-
54
- function isPidAlive(pid: number | undefined): boolean | undefined {
55
- if (!pid || !Number.isSafeInteger(pid) || pid <= 0) return undefined;
56
- try {
57
- process.kill(pid, 0);
58
- return true;
59
- } catch (error) {
60
- const code = (error as NodeJS.ErrnoException).code;
61
- return code === "EPERM" ? true : false;
62
- }
63
- }
64
-
65
- async function isPortAlive(port: number): Promise<{ targets: ElectronCdpTarget[]; version?: ElectronCdpVersion }> {
66
- const version = parseCdpVersion(await fetchCdpJson(`http://127.0.0.1:${port}/json/version`));
67
- if (!version) return { targets: [] };
68
- const targets = parseCdpTargets(await fetchCdpJson(`http://127.0.0.1:${port}/json/list`));
69
- return { targets, version };
70
- }
71
-
72
- export async function inspectElectronLaunchStatus(record: ElectronLaunchRecord): Promise<ElectronLaunchStatus> {
73
- const cdp = await isPortAlive(record.port);
74
- return {
75
- cleanupState: record.cleanupState,
76
- launchId: record.launchId,
77
- pid: record.pid,
78
- pidAlive: isPidAlive(record.pid),
79
- port: record.port,
80
- portAlive: cdp.version !== undefined,
81
- targets: cdp.targets,
82
- version: cdp.version,
83
- };
84
- }
85
-
86
- async function waitForProcessExit(child: ChildProcess | undefined, pid: number | undefined, deadlineMs: number): Promise<boolean> {
87
- while (Date.now() <= deadlineMs) {
88
- if (child && (child.exitCode !== null || child.signalCode !== null)) return true;
89
- if (isPidAlive(pid) === false) return true;
90
- await sleep(ELECTRON_CLEANUP_POLL_INTERVAL_MS);
91
- }
92
- return isPidAlive(pid) === false;
93
- }
94
-
95
- async function readPidCommandLine(pid: number | undefined): Promise<string | undefined> {
96
- if (!pid || !Number.isSafeInteger(pid) || pid <= 0) return undefined;
97
- try {
98
- const { stdout } = await execFileAsync("ps", ["-ww", "-p", String(pid), "-o", "command="], {
99
- timeout: RESTORED_PROCESS_COMMAND_TIMEOUT_MS,
100
- });
101
- return stdout.trim() || undefined;
102
- } catch {
103
- return undefined;
104
- }
105
- }
106
-
107
- function restoredLaunchCommandMatchesRecord(record: ElectronLaunchRecord, commandLine: string | undefined): boolean {
108
- return commandLine?.includes(`--user-data-dir=${record.userDataDir}`) === true;
109
- }
110
-
111
- async function getRestoredProcessVerificationError(record: ElectronLaunchRecord): Promise<string | undefined> {
112
- const commandLine = await readPidCommandLine(record.pid);
113
- if (!commandLine) {
114
- return `PID ${record.pid} is alive, but this session has no tracked child handle and its command line could not be inspected; refusing to signal a restored PID that may have been reused.`;
115
- }
116
- if (!restoredLaunchCommandMatchesRecord(record, commandLine)) {
117
- return `PID ${record.pid} is alive, but this session has no tracked child handle and its command line does not include wrapper-owned user data dir ${record.userDataDir}; refusing to signal a restored PID that may have been reused.`;
118
- }
119
- return undefined;
120
- }
121
-
122
- function killPid(pid: number | undefined, signal: NodeJS.Signals): boolean {
123
- if (!pid || !Number.isSafeInteger(pid) || pid <= 0) return false;
124
- try {
125
- process.kill(pid, signal);
126
- return true;
127
- } catch {
128
- return false;
129
- }
130
- }
131
-
132
- function killProcessGroup(processGroupId: number | undefined, signal: NodeJS.Signals): boolean {
133
- if (process.platform === "win32" || !processGroupId || !Number.isSafeInteger(processGroupId) || processGroupId <= 0) return false;
134
- try {
135
- process.kill(-processGroupId, signal);
136
- return true;
137
- } catch {
138
- return false;
139
- }
140
- }
141
-
142
- function signalRestoredLaunchProcess(record: ElectronLaunchRecord, signal: NodeJS.Signals): boolean {
143
- return killProcessGroup(record.processGroupId, signal) || killPid(record.pid, signal);
144
- }
145
-
146
- async function cleanupProcess(record: ElectronLaunchRecord, child: ChildProcess | undefined, deadlineMs: number): Promise<ElectronCleanupStep> {
147
- if (!record.pid) return { resource: "process", state: "skipped" };
148
- if (isPidAlive(record.pid) === false) return { resource: "process", state: "already-gone" };
149
- if (!child) {
150
- const verificationError = await getRestoredProcessVerificationError(record);
151
- if (verificationError) return { error: verificationError, resource: "process", state: "failed" };
152
- if (!signalRestoredLaunchProcess(record, "SIGTERM")) {
153
- return { error: `PID ${record.pid} matched wrapper launch metadata but could not be signaled.`, resource: "process", state: "failed" };
154
- }
155
- if (await waitForProcessExit(undefined, record.pid, deadlineMs)) return { resource: "process", state: "removed" };
156
- signalRestoredLaunchProcess(record, "SIGKILL");
157
- if (await waitForProcessExit(undefined, record.pid, Date.now() + 1_000)) return { resource: "process", state: "removed" };
158
- return { error: `PID ${record.pid} remained alive after SIGTERM/SIGKILL.`, resource: "process", state: "failed" };
159
- }
160
- if (child.exitCode === null && child.signalCode === null) child.kill("SIGTERM");
161
- else killPid(record.pid, "SIGTERM");
162
- if (await waitForProcessExit(child, record.pid, deadlineMs)) return { resource: "process", state: "removed" };
163
- if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
164
- else killPid(record.pid, "SIGKILL");
165
- if (await waitForProcessExit(child, record.pid, Date.now() + 1_000)) return { resource: "process", state: "removed" };
166
- return { error: `PID ${record.pid} remained alive after SIGTERM/SIGKILL.`, resource: "process", state: "failed" };
167
- }
168
-
169
- async function cleanupUserDataDir(record: ElectronLaunchRecord): Promise<ElectronCleanupStep> {
170
- if (!record.userDataDir) return { resource: "user-data-dir", state: "skipped" };
171
- if (!await pathExists(record.userDataDir)) return { resource: "user-data-dir", state: "already-gone" };
172
- const validationError = await getSecureTempChildDirectoryValidationError(record.userDataDir, ELECTRON_PROFILE_DIR_PREFIX);
173
- if (validationError) return { error: validationError, resource: "user-data-dir", state: "failed" };
174
- try {
175
- await rm(record.userDataDir, { force: true, recursive: true });
176
- return { resource: "user-data-dir", state: await pathExists(record.userDataDir) ? "failed" : "removed" };
177
- } catch (error) {
178
- return { error: error instanceof Error ? error.message : String(error), resource: "user-data-dir", state: "failed" };
179
- }
180
- }
181
-
182
- function shouldSkipUserDataDirCleanup(processStep: ElectronCleanupStep, debugPortStep: ElectronCleanupStep): string | undefined {
183
- if (processStep.state === "failed") return `Skipped because process cleanup failed: ${processStep.error ?? "process state could not be verified"}.`;
184
- if (debugPortStep.state === "failed") return `Skipped because debug port cleanup is incomplete: ${debugPortStep.error ?? "debug port still responds"}.`;
185
- return undefined;
186
- }
187
-
188
- async function cleanupDebugPort(record: ElectronLaunchRecord): Promise<ElectronCleanupStep> {
189
- const cdp = await isPortAlive(record.port);
190
- return cdp.version ? { resource: "debug-port", state: "failed", error: `/json/version still responds on port ${record.port}.` } : { resource: "debug-port", state: "already-gone" };
191
- }
192
-
193
- function summarizeCleanup(launchId: string, steps: ElectronCleanupStep[]): { partial: boolean; remainingResources: string[]; summary: string } {
194
- const remainingResources = steps
195
- .filter((step) => step.state === "failed" || (step.resource === "user-data-dir" && step.state === "skipped" && step.error))
196
- .map((step) => step.resource);
197
- const partial = remainingResources.length > 0;
198
- return {
199
- partial,
200
- remainingResources,
201
- summary: partial
202
- ? `Electron cleanup for ${launchId} is partial; remaining resources: ${remainingResources.join(", ")}.`
203
- : `Electron cleanup for ${launchId} completed.`,
204
- };
205
- }
206
-
207
- export async function cleanupElectronLaunchResources(options: {
208
- child?: ChildProcess;
209
- record: ElectronLaunchRecord;
210
- timeoutMs?: number;
211
- }): Promise<ElectronCleanupResult> {
212
- const timeoutMs = Number.isSafeInteger(options.timeoutMs) && (options.timeoutMs ?? 0) > 0
213
- ? options.timeoutMs as number
214
- : ELECTRON_CLEANUP_DEFAULT_TIMEOUT_MS;
215
- const deadlineMs = Date.now() + timeoutMs;
216
- const processStep = await cleanupProcess(options.record, options.child, deadlineMs);
217
- const debugPortStep = await cleanupDebugPort(options.record);
218
- const userDataDirSkipReason = shouldSkipUserDataDirCleanup(processStep, debugPortStep);
219
- const userDataDirStep = userDataDirSkipReason
220
- ? { error: userDataDirSkipReason, resource: "user-data-dir" as const, state: "skipped" as const }
221
- : await cleanupUserDataDir(options.record);
222
- const steps = [processStep, debugPortStep, userDataDirStep];
223
- const summary = summarizeCleanup(options.record.launchId, steps);
224
- return {
225
- launchId: options.record.launchId,
226
- partial: summary.partial,
227
- record: {
228
- ...options.record,
229
- cleanupState: summary.partial ? "partial" : "cleaned",
230
- },
231
- remainingResources: summary.remainingResources,
232
- steps,
233
- summary: summary.summary,
234
- };
235
- }