pi-agent-browser-native 0.2.47 → 0.2.49
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/CHANGELOG.md +63 -19
- package/README.md +52 -19
- package/dist/extensions/agent-browser/index.js +785 -0
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
- package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
- package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
- package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
- package/dist/extensions/agent-browser/lib/config.js +122 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
- package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
- package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
- package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
- package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
- package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
- package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
- package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
- package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
- package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
- package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
- package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
- package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
- package/dist/extensions/agent-browser/lib/playbook.js +121 -0
- package/dist/extensions/agent-browser/lib/process.js +448 -0
- package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
- package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
- package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
- package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
- package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
- package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
- package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
- package/dist/extensions/agent-browser/lib/results/network.js +73 -0
- package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
- package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
- package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
- package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
- package/dist/extensions/agent-browser/lib/results/text.js +40 -0
- package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
- package/dist/extensions/agent-browser/lib/runtime.js +816 -0
- package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
- package/dist/extensions/agent-browser/lib/temp.js +498 -0
- package/dist/extensions/agent-browser/lib/web-search.js +562 -0
- package/docs/ARCHITECTURE.md +10 -10
- package/docs/COMMAND_REFERENCE.md +35 -21
- package/docs/ELECTRON.md +3 -3
- package/docs/RELEASE.md +46 -26
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +35 -106
- package/docs/TOOL_CONTRACT.md +23 -21
- package/package.json +12 -8
- package/scripts/agent-browser-capability-baseline.mjs +6 -3
- package/scripts/config.mjs +8 -2
- package/scripts/doctor.mjs +19 -17
- package/scripts/platform-smoke.mjs +1 -1
- package/extensions/agent-browser/index.ts +0 -952
- package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
- package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
- package/extensions/agent-browser/lib/bash-guard.ts +0 -205
- package/extensions/agent-browser/lib/command-policy.ts +0 -71
- package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
- package/extensions/agent-browser/lib/config-policy.js +0 -690
- package/extensions/agent-browser/lib/config.ts +0 -209
- package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
- package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
- package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
- package/extensions/agent-browser/lib/electron/launch.ts +0 -499
- package/extensions/agent-browser/lib/executable-path.ts +0 -19
- package/extensions/agent-browser/lib/fs-utils.ts +0 -18
- package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
- package/extensions/agent-browser/lib/input-modes/job.ts +0 -451
- package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
- package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
- package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
- package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
- package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
- package/extensions/agent-browser/lib/input-modes.ts +0 -45
- package/extensions/agent-browser/lib/json-schema.ts +0 -73
- package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
- package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
- package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
- package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -257
- package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -912
- package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -512
- package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -1481
- package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
- package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
- package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
- package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
- package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
- package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -252
- package/extensions/agent-browser/lib/playbook.ts +0 -142
- package/extensions/agent-browser/lib/process.ts +0 -516
- package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
- package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
- package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
- package/extensions/agent-browser/lib/results/categories.ts +0 -106
- package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
- package/extensions/agent-browser/lib/results/contracts.ts +0 -241
- package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
- package/extensions/agent-browser/lib/results/envelope.ts +0 -195
- package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
- package/extensions/agent-browser/lib/results/network.ts +0 -78
- package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
- package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
- package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
- package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
- package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
- package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
- package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
- package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
- package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
- package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
- package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
- package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
- package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
- package/extensions/agent-browser/lib/results/presentation.ts +0 -257
- package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
- package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
- package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
- package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
- package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
- package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
- package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
- package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
- package/extensions/agent-browser/lib/results/text.ts +0 -40
- package/extensions/agent-browser/lib/runtime.ts +0 -988
- package/extensions/agent-browser/lib/session-page-state.ts +0 -512
- package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
- package/extensions/agent-browser/lib/temp.ts +0 -577
- package/extensions/agent-browser/lib/web-search.ts +0 -721
- /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
|
@@ -1,690 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
/**
|
|
3
|
-
* Purpose: Canonical pi-agent-browser-native config policy shared by runtime and setup CLI.
|
|
4
|
-
* Responsibilities: Own config paths, provider descriptors, project-local credential safety, layer validation/merge, status projection, and redacted summaries.
|
|
5
|
-
* Scope: Pure configuration policy plus synchronous status loading; secret command execution and browser/web-search runtime calls live elsewhere.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
|
-
import { join, resolve } from "node:path";
|
|
11
|
-
|
|
12
|
-
/** @typedef {"explicit-only" | "authenticated-only" | "always"} BrowserDefaultProfilePolicy */
|
|
13
|
-
/** @typedef {"global" | "project" | "override" | "env-fallback"} AgentBrowserConfigScope */
|
|
14
|
-
/** @typedef {"global" | "project" | "override"} ConfigLayerScope */
|
|
15
|
-
/** @typedef {"literal" | "env" | "command"} CredentialSourceKind */
|
|
16
|
-
/** @typedef {"exa" | "brave"} WebSearchProvider */
|
|
17
|
-
/** @typedef {"exaApiKey" | "braveApiKey"} WebSearchProviderConfigKey */
|
|
18
|
-
/** @typedef {{ provider: WebSearchProvider; apiKeyEnv: string; configKey: WebSearchProviderConfigKey; label: string }} WebSearchProviderDescriptor */
|
|
19
|
-
/** @typedef {{ name: string; policy?: BrowserDefaultProfilePolicy }} BrowserDefaultProfileConfig */
|
|
20
|
-
/** @typedef {{ enabled?: boolean; preferredProvider?: WebSearchProvider; braveApiKey?: string; exaApiKey?: string }} WebSearchConfig */
|
|
21
|
-
/** @typedef {{ defaultProfile?: BrowserDefaultProfileConfig; executablePath?: string; defaultLaunchArgs?: string[] }} BrowserConfig */
|
|
22
|
-
/** @typedef {{ version?: 1; webSearch?: WebSearchConfig; browser?: BrowserConfig }} AgentBrowserConfig */
|
|
23
|
-
/** @typedef {{ config: AgentBrowserConfig; path: string; scope: ConfigLayerScope }} ConfigLayer */
|
|
24
|
-
/** @typedef {{ kind: CredentialSourceKind; provider?: WebSearchProvider; rawValue: string; scope: AgentBrowserConfigScope }} CredentialSource */
|
|
25
|
-
/** @typedef {{ global: string; project: string; override?: string }} AgentBrowserConfigPaths */
|
|
26
|
-
/** @typedef {{ cwd?: string; env?: NodeJS.ProcessEnv; includeProjectConfig?: boolean }} AgentBrowserConfigLoadOptions */
|
|
27
|
-
/** @typedef {{ browserDefaultProfile?: Required<BrowserDefaultProfileConfig>; browserDefaultProfileScope?: ConfigLayerScope; browserExecutablePath?: string; browserExecutablePathScope?: ConfigLayerScope; trustedBrowserDefaultProfile?: Required<BrowserDefaultProfileConfig>; trustedBrowserDefaultProfileScope?: Exclude<ConfigLayerScope, "project">; trustedBrowserExecutablePath?: string; trustedBrowserExecutablePathScope?: Exclude<ConfigLayerScope, "project">; config: AgentBrowserConfig; webSearchCredentialSources: Partial<Record<WebSearchProvider, CredentialSource>>; webSearchEnabled: boolean; webSearchPreferredProvider: WebSearchProvider; errors: string[]; layers: ConfigLayer[]; paths: AgentBrowserConfigPaths; projectConfigIncluded: boolean; warnings: string[] }} AgentBrowserConfigState */
|
|
28
|
-
/** @typedef {{ scope: string; path: string; exists: boolean }} ConfigFileSummary */
|
|
29
|
-
|
|
30
|
-
export const AGENT_BROWSER_CONFIG_ENV = "PI_AGENT_BROWSER_CONFIG";
|
|
31
|
-
export const BRAVE_API_KEY_ENV = "BRAVE_API_KEY";
|
|
32
|
-
export const EXA_API_KEY_ENV = "EXA_API_KEY";
|
|
33
|
-
export const CONFIG_RELATIVE_PATH = /** @type {const} */ ([".pi", "config", "pi-agent-browser-native", "config.json"]);
|
|
34
|
-
export const GLOBAL_CONFIG_RELATIVE_PATH = /** @type {const} */ ([".pi", "config", "pi-agent-browser-native", "config.json"]);
|
|
35
|
-
export const SECRET_COMMAND_TIMEOUT_MS = 15_000;
|
|
36
|
-
|
|
37
|
-
/** @type {Readonly<Record<WebSearchProvider, WebSearchProviderDescriptor>>} */
|
|
38
|
-
export const WEB_SEARCH_PROVIDER_DESCRIPTORS = Object.freeze({
|
|
39
|
-
exa: Object.freeze({
|
|
40
|
-
provider: "exa",
|
|
41
|
-
apiKeyEnv: EXA_API_KEY_ENV,
|
|
42
|
-
configKey: "exaApiKey",
|
|
43
|
-
label: "Exa",
|
|
44
|
-
}),
|
|
45
|
-
brave: Object.freeze({
|
|
46
|
-
provider: "brave",
|
|
47
|
-
apiKeyEnv: BRAVE_API_KEY_ENV,
|
|
48
|
-
configKey: "braveApiKey",
|
|
49
|
-
label: "Brave Search",
|
|
50
|
-
}),
|
|
51
|
-
});
|
|
52
|
-
/** @type {readonly WebSearchProvider[]} */
|
|
53
|
-
export const WEB_SEARCH_PROVIDERS = Object.freeze(["exa", "brave"]);
|
|
54
|
-
/** @type {WebSearchProvider} */
|
|
55
|
-
export const DEFAULT_WEB_SEARCH_PROVIDER = "exa";
|
|
56
|
-
/** @type {Readonly<Record<WebSearchProvider, WebSearchProviderConfigKey>>} */
|
|
57
|
-
export const WEB_SEARCH_PROVIDER_CONFIG_KEYS = Object.freeze({
|
|
58
|
-
exa: WEB_SEARCH_PROVIDER_DESCRIPTORS.exa.configKey,
|
|
59
|
-
brave: WEB_SEARCH_PROVIDER_DESCRIPTORS.brave.configKey,
|
|
60
|
-
});
|
|
61
|
-
/** @type {Readonly<Record<WebSearchProvider, string>>} */
|
|
62
|
-
export const WEB_SEARCH_PROVIDER_ENV_VARS = Object.freeze({
|
|
63
|
-
exa: WEB_SEARCH_PROVIDER_DESCRIPTORS.exa.apiKeyEnv,
|
|
64
|
-
brave: WEB_SEARCH_PROVIDER_DESCRIPTORS.brave.apiKeyEnv,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @param {unknown} value
|
|
69
|
-
* @returns {value is WebSearchProvider}
|
|
70
|
-
*/
|
|
71
|
-
export function isWebSearchProvider(value) {
|
|
72
|
-
return typeof value === "string" && WEB_SEARCH_PROVIDERS.includes(/** @type {WebSearchProvider} */ (value));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @param {WebSearchProvider} provider
|
|
77
|
-
* @returns {WebSearchProviderDescriptor}
|
|
78
|
-
*/
|
|
79
|
-
export function getWebSearchProviderDescriptor(provider) {
|
|
80
|
-
const descriptor = WEB_SEARCH_PROVIDER_DESCRIPTORS[provider];
|
|
81
|
-
if (!descriptor) throw new Error(`Unknown web-search provider: ${String(provider)}`);
|
|
82
|
-
return descriptor;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** @param {WebSearchProvider} provider */
|
|
86
|
-
export function getWebSearchProviderLabel(provider) {
|
|
87
|
-
return getWebSearchProviderDescriptor(provider).label;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** @param {WebSearchProvider} provider */
|
|
91
|
-
export function getWebSearchProviderEnvVar(provider) {
|
|
92
|
-
return getWebSearchProviderDescriptor(provider).apiKeyEnv;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* @param {WebSearchProvider} provider
|
|
97
|
-
* @returns {WebSearchProviderConfigKey}
|
|
98
|
-
*/
|
|
99
|
-
export function getWebSearchProviderConfigKey(provider) {
|
|
100
|
-
return getWebSearchProviderDescriptor(provider).configKey;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** @param {NodeJS.ProcessEnv} [env] */
|
|
104
|
-
export function getGlobalAgentBrowserConfigPath(env = process.env) {
|
|
105
|
-
const home = env.HOME?.trim() || env.USERPROFILE?.trim() || homedir();
|
|
106
|
-
return join(home, ...GLOBAL_CONFIG_RELATIVE_PATH);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** @param {string} [cwd] */
|
|
110
|
-
export function getProjectAgentBrowserConfigPath(cwd = process.cwd()) {
|
|
111
|
-
return resolve(cwd, ...CONFIG_RELATIVE_PATH);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @param {{ cwd?: string; env?: NodeJS.ProcessEnv }} [options]
|
|
116
|
-
* @returns {AgentBrowserConfigPaths}
|
|
117
|
-
*/
|
|
118
|
-
export function getAgentBrowserConfigPaths(options = {}) {
|
|
119
|
-
const env = options.env ?? process.env;
|
|
120
|
-
const override = env[AGENT_BROWSER_CONFIG_ENV]?.trim();
|
|
121
|
-
return {
|
|
122
|
-
global: getGlobalAgentBrowserConfigPath(env),
|
|
123
|
-
project: getProjectAgentBrowserConfigPath(options.cwd),
|
|
124
|
-
...(override ? { override: resolve(override) } : {}),
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* @param {AgentBrowserConfig} base
|
|
130
|
-
* @param {AgentBrowserConfig} override
|
|
131
|
-
* @returns {AgentBrowserConfig}
|
|
132
|
-
*/
|
|
133
|
-
export function mergeAgentBrowserConfig(base, override) {
|
|
134
|
-
return {
|
|
135
|
-
...base,
|
|
136
|
-
...override,
|
|
137
|
-
browser: {
|
|
138
|
-
...(base.browser ?? {}),
|
|
139
|
-
...(override.browser ?? {}),
|
|
140
|
-
defaultProfile: override.browser?.defaultProfile ?? base.browser?.defaultProfile,
|
|
141
|
-
},
|
|
142
|
-
webSearch: {
|
|
143
|
-
...(base.webSearch ?? {}),
|
|
144
|
-
...(override.webSearch ?? {}),
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @param {unknown} value
|
|
151
|
-
* @returns {value is Record<string, unknown>}
|
|
152
|
-
*/
|
|
153
|
-
function isRecord(value) {
|
|
154
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @param {unknown} value
|
|
159
|
-
* @param {string} path
|
|
160
|
-
* @param {string[]} errors
|
|
161
|
-
*/
|
|
162
|
-
function validateString(value, path, errors) {
|
|
163
|
-
if (value === undefined) return undefined;
|
|
164
|
-
if (typeof value !== "string") {
|
|
165
|
-
errors.push(`${path} must be a string.`);
|
|
166
|
-
return undefined;
|
|
167
|
-
}
|
|
168
|
-
return value;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* @param {unknown} value
|
|
173
|
-
* @param {string} path
|
|
174
|
-
* @param {string[]} errors
|
|
175
|
-
* @returns {string[] | undefined}
|
|
176
|
-
*/
|
|
177
|
-
function validateStringArray(value, path, errors) {
|
|
178
|
-
if (value === undefined) return undefined;
|
|
179
|
-
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
180
|
-
errors.push(`${path} must be an array of strings.`);
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
return value;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* @param {unknown} value
|
|
188
|
-
* @param {string} path
|
|
189
|
-
* @param {string[]} errors
|
|
190
|
-
*/
|
|
191
|
-
function validateBoolean(value, path, errors) {
|
|
192
|
-
if (value === undefined) return undefined;
|
|
193
|
-
if (typeof value !== "boolean") {
|
|
194
|
-
errors.push(`${path} must be a boolean.`);
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
return value;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* @param {unknown} value
|
|
202
|
-
* @param {string} path
|
|
203
|
-
* @param {string[]} errors
|
|
204
|
-
* @returns {WebSearchProvider | undefined}
|
|
205
|
-
*/
|
|
206
|
-
export function validateWebSearchProvider(value, path, errors) {
|
|
207
|
-
if (value === undefined) return undefined;
|
|
208
|
-
const provider = validateString(value, path, errors)?.trim();
|
|
209
|
-
if (provider === undefined) return undefined;
|
|
210
|
-
if (!isWebSearchProvider(provider)) {
|
|
211
|
-
errors.push(`${path} must be one of ${WEB_SEARCH_PROVIDERS.join(", ")}.`);
|
|
212
|
-
return undefined;
|
|
213
|
-
}
|
|
214
|
-
return provider;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* @param {unknown} value
|
|
219
|
-
* @param {string} path
|
|
220
|
-
* @param {string[]} errors
|
|
221
|
-
* @returns {BrowserDefaultProfileConfig | undefined}
|
|
222
|
-
*/
|
|
223
|
-
function validateBrowserDefaultProfile(value, path, errors) {
|
|
224
|
-
if (value === undefined) return undefined;
|
|
225
|
-
if (!isRecord(value)) {
|
|
226
|
-
errors.push(`${path} must be an object.`);
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
|
229
|
-
const name = validateString(value.name, `${path}.name`, errors)?.trim();
|
|
230
|
-
if (!name) {
|
|
231
|
-
errors.push(`${path}.name must not be blank.`);
|
|
232
|
-
return undefined;
|
|
233
|
-
}
|
|
234
|
-
const rawPolicy = validateString(value.policy, `${path}.policy`, errors);
|
|
235
|
-
const policy = rawPolicy ?? "authenticated-only";
|
|
236
|
-
if (!["explicit-only", "authenticated-only", "always"].includes(policy)) {
|
|
237
|
-
errors.push(`${path}.policy must be one of explicit-only, authenticated-only, always.`);
|
|
238
|
-
return undefined;
|
|
239
|
-
}
|
|
240
|
-
return { name, policy: /** @type {BrowserDefaultProfilePolicy} */ (policy) };
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/** @param {string} rawValue */
|
|
244
|
-
export function isPlaintextCredentialValue(rawValue) {
|
|
245
|
-
const trimmed = rawValue.trim();
|
|
246
|
-
return Boolean(trimmed) && !trimmed.startsWith("!") && !trimmed.startsWith("$");
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* @param {string} rawValue
|
|
251
|
-
* @param {WebSearchProvider} provider
|
|
252
|
-
*/
|
|
253
|
-
export function isProjectSafeCredentialValueForProvider(rawValue, provider) {
|
|
254
|
-
const envName = getWebSearchProviderEnvVar(provider);
|
|
255
|
-
const trimmed = rawValue.trim();
|
|
256
|
-
return trimmed === `$${envName}` || trimmed === `\${${envName}}`;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* @param {unknown} value
|
|
261
|
-
* @param {string} path
|
|
262
|
-
* @param {ConfigLayerScope} scope
|
|
263
|
-
* @param {string[]} errors
|
|
264
|
-
* @param {string[]} warnings
|
|
265
|
-
* @returns {AgentBrowserConfig | undefined}
|
|
266
|
-
*/
|
|
267
|
-
export function validateAgentBrowserConfig(value, path, scope, errors, warnings) {
|
|
268
|
-
if (!isRecord(value)) {
|
|
269
|
-
errors.push(`${path} must contain a JSON object.`);
|
|
270
|
-
return undefined;
|
|
271
|
-
}
|
|
272
|
-
if (value.version !== undefined && value.version !== 1) {
|
|
273
|
-
errors.push(`${path}.version must be 1 when present.`);
|
|
274
|
-
}
|
|
275
|
-
/** @type {AgentBrowserConfig} */
|
|
276
|
-
const config = value.version === 1 ? { version: 1 } : {};
|
|
277
|
-
|
|
278
|
-
if (value.webSearch !== undefined) {
|
|
279
|
-
if (!isRecord(value.webSearch)) {
|
|
280
|
-
errors.push(`${path}.webSearch must be an object.`);
|
|
281
|
-
} else {
|
|
282
|
-
/** @type {NonNullable<AgentBrowserConfig["webSearch"]>} */
|
|
283
|
-
const webSearch = {};
|
|
284
|
-
const enabled = validateBoolean(value.webSearch.enabled, `${path}.webSearch.enabled`, errors);
|
|
285
|
-
if (enabled !== undefined) webSearch.enabled = enabled;
|
|
286
|
-
const preferredProvider = validateWebSearchProvider(value.webSearch.preferredProvider, `${path}.webSearch.preferredProvider`, errors);
|
|
287
|
-
if (preferredProvider) webSearch.preferredProvider = preferredProvider;
|
|
288
|
-
for (const provider of WEB_SEARCH_PROVIDERS) {
|
|
289
|
-
const descriptor = getWebSearchProviderDescriptor(provider);
|
|
290
|
-
const apiKey = validateString(value.webSearch[descriptor.configKey], `${path}.webSearch.${descriptor.configKey}`, errors);
|
|
291
|
-
if (apiKey !== undefined) {
|
|
292
|
-
webSearch[descriptor.configKey] = apiKey;
|
|
293
|
-
if (scope === "project" && !isProjectSafeCredentialValueForProvider(apiKey, provider)) {
|
|
294
|
-
errors.push(`${path}.webSearch.${descriptor.configKey} must be exactly $${descriptor.apiKeyEnv} or \${${descriptor.apiKeyEnv}} in project-local config; plaintext, custom env aliases, interpolation literals, malformed env references, and command-backed project secrets are not allowed.`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (Object.keys(webSearch).length > 0) config.webSearch = webSearch;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
if (value.browser !== undefined) {
|
|
303
|
-
if (!isRecord(value.browser)) {
|
|
304
|
-
errors.push(`${path}.browser must be an object.`);
|
|
305
|
-
} else {
|
|
306
|
-
config.browser = {};
|
|
307
|
-
const defaultProfile = validateBrowserDefaultProfile(value.browser.defaultProfile, `${path}.browser.defaultProfile`, errors);
|
|
308
|
-
if (defaultProfile) {
|
|
309
|
-
config.browser.defaultProfile = defaultProfile;
|
|
310
|
-
if (scope === "project" && defaultProfile.policy !== "explicit-only") {
|
|
311
|
-
warnings.push(`${path}.browser.defaultProfile is project-local; authenticated/always profile prompt guidance is emitted only from global or override config.`);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
const executablePath = validateString(value.browser.executablePath, `${path}.browser.executablePath`, errors)?.trim();
|
|
315
|
-
if (executablePath) {
|
|
316
|
-
config.browser.executablePath = executablePath;
|
|
317
|
-
if (scope === "project") {
|
|
318
|
-
warnings.push(`${path}.browser.executablePath is project-local; executable launch prompt guidance is emitted only from global or override config.`);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const defaultLaunchArgs = validateStringArray(value.browser.defaultLaunchArgs, `${path}.browser.defaultLaunchArgs`, errors);
|
|
322
|
-
if (defaultLaunchArgs) {
|
|
323
|
-
config.browser.defaultLaunchArgs = defaultLaunchArgs;
|
|
324
|
-
warnings.push(`${path}.browser.defaultLaunchArgs is recorded for future use; current releases do not auto-inject default launch args.`);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
for (const key of Object.keys(value)) {
|
|
330
|
-
if (!["version", "webSearch", "browser"].includes(key)) {
|
|
331
|
-
warnings.push(`${path}.${key} is not a recognized pi-agent-browser-native config field and was ignored.`);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
return config;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* @param {string} raw
|
|
339
|
-
* @param {string} path
|
|
340
|
-
* @param {ConfigLayerScope} scope
|
|
341
|
-
* @param {string[]} errors
|
|
342
|
-
* @param {string[]} warnings
|
|
343
|
-
* @returns {ConfigLayer | undefined}
|
|
344
|
-
*/
|
|
345
|
-
export function parseAgentBrowserConfigLayer(raw, path, scope, errors, warnings) {
|
|
346
|
-
let parsed;
|
|
347
|
-
try {
|
|
348
|
-
parsed = JSON.parse(raw);
|
|
349
|
-
} catch (error) {
|
|
350
|
-
errors.push(`Could not parse ${scope} config ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
351
|
-
return undefined;
|
|
352
|
-
}
|
|
353
|
-
const config = validateAgentBrowserConfig(parsed, path, scope, errors, warnings);
|
|
354
|
-
return config ? { config, path, scope } : undefined;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* @param {string} rawValue
|
|
359
|
-
* @param {AgentBrowserConfigScope} scope
|
|
360
|
-
* @param {WebSearchProvider} [provider]
|
|
361
|
-
* @returns {CredentialSource | undefined}
|
|
362
|
-
*/
|
|
363
|
-
export function classifyCredentialSource(rawValue, scope, provider) {
|
|
364
|
-
const trimmed = rawValue.trim();
|
|
365
|
-
if (!trimmed) return undefined;
|
|
366
|
-
if (trimmed.startsWith("!")) return { kind: "command", provider, rawValue: trimmed, scope };
|
|
367
|
-
if (trimmed.includes("$")) return { kind: "env", provider, rawValue: trimmed, scope };
|
|
368
|
-
return { kind: "literal", provider, rawValue: trimmed, scope };
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* @param {AgentBrowserConfig} config
|
|
373
|
-
* @returns {Required<BrowserDefaultProfileConfig> | undefined}
|
|
374
|
-
*/
|
|
375
|
-
function getBrowserDefaultProfile(config) {
|
|
376
|
-
const profile = config.browser?.defaultProfile;
|
|
377
|
-
if (!profile?.name.trim()) return undefined;
|
|
378
|
-
return { name: profile.name.trim(), policy: profile.policy ?? "authenticated-only" };
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/** @param {AgentBrowserConfig} config */
|
|
382
|
-
function getBrowserExecutablePath(config) {
|
|
383
|
-
const executablePath = config.browser?.executablePath?.trim();
|
|
384
|
-
return executablePath || undefined;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* @param {ConfigLayer[]} layers
|
|
389
|
-
* @returns {ConfigLayerScope | undefined}
|
|
390
|
-
*/
|
|
391
|
-
function getBrowserDefaultProfileScope(layers) {
|
|
392
|
-
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
393
|
-
const layer = layers[index];
|
|
394
|
-
if (layer?.config.browser?.defaultProfile !== undefined) return layer.scope;
|
|
395
|
-
}
|
|
396
|
-
return undefined;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* @param {ConfigLayer[]} layers
|
|
401
|
-
* @returns {ConfigLayerScope | undefined}
|
|
402
|
-
*/
|
|
403
|
-
function getBrowserExecutablePathScope(layers) {
|
|
404
|
-
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
405
|
-
const layer = layers[index];
|
|
406
|
-
if (layer?.config.browser?.executablePath !== undefined) return layer.scope;
|
|
407
|
-
}
|
|
408
|
-
return undefined;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* @param {ConfigLayer[]} layers
|
|
413
|
-
* @returns {{ profile: Required<BrowserDefaultProfileConfig>; scope: Exclude<ConfigLayerScope, "project"> } | undefined}
|
|
414
|
-
*/
|
|
415
|
-
function getTrustedBrowserDefaultProfile(layers) {
|
|
416
|
-
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
417
|
-
const layer = layers[index];
|
|
418
|
-
if (!layer || layer.scope === "project") continue;
|
|
419
|
-
const profile = getBrowserDefaultProfile(layer.config);
|
|
420
|
-
if (profile) return { profile, scope: /** @type {Exclude<ConfigLayerScope, "project">} */ (layer.scope) };
|
|
421
|
-
}
|
|
422
|
-
return undefined;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* @param {ConfigLayer[]} layers
|
|
427
|
-
* @returns {{ executablePath: string; scope: Exclude<ConfigLayerScope, "project"> } | undefined}
|
|
428
|
-
*/
|
|
429
|
-
function getTrustedBrowserExecutablePath(layers) {
|
|
430
|
-
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
431
|
-
const layer = layers[index];
|
|
432
|
-
if (!layer || layer.scope === "project") continue;
|
|
433
|
-
const executablePath = getBrowserExecutablePath(layer.config);
|
|
434
|
-
if (executablePath) return { executablePath, scope: /** @type {Exclude<ConfigLayerScope, "project">} */ (layer.scope) };
|
|
435
|
-
}
|
|
436
|
-
return undefined;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* @param {ConfigLayer[]} layers
|
|
441
|
-
* @param {WebSearchProviderConfigKey} key
|
|
442
|
-
* @returns {AgentBrowserConfigScope}
|
|
443
|
-
*/
|
|
444
|
-
function getWebSearchCredentialScope(layers, key) {
|
|
445
|
-
for (let index = layers.length - 1; index >= 0; index -= 1) {
|
|
446
|
-
const layer = layers[index];
|
|
447
|
-
if (layer?.config.webSearch?.[key] !== undefined) return layer.scope;
|
|
448
|
-
}
|
|
449
|
-
return "global";
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* @param {{ env: NodeJS.ProcessEnv; layers: ConfigLayer[]; mergedConfig: AgentBrowserConfig }} options
|
|
454
|
-
* @returns {Partial<Record<WebSearchProvider, CredentialSource>>}
|
|
455
|
-
*/
|
|
456
|
-
export function buildWebSearchCredentialSources(options) {
|
|
457
|
-
/** @type {Partial<Record<WebSearchProvider, CredentialSource>>} */
|
|
458
|
-
const sources = {};
|
|
459
|
-
for (const provider of WEB_SEARCH_PROVIDERS) {
|
|
460
|
-
const descriptor = getWebSearchProviderDescriptor(provider);
|
|
461
|
-
const apiKey = options.mergedConfig.webSearch?.[descriptor.configKey];
|
|
462
|
-
if (apiKey !== undefined) {
|
|
463
|
-
sources[provider] = classifyCredentialSource(apiKey, getWebSearchCredentialScope(options.layers, descriptor.configKey), provider);
|
|
464
|
-
}
|
|
465
|
-
if (!sources[provider] && options.env[descriptor.apiKeyEnv]?.trim()) {
|
|
466
|
-
sources[provider] = { kind: "literal", provider, rawValue: options.env[descriptor.apiKeyEnv] ?? "", scope: "env-fallback" };
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return sources;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* @param {{ env: NodeJS.ProcessEnv; layers: ConfigLayer[]; mergedConfig: AgentBrowserConfig; paths: AgentBrowserConfigPaths; errors: string[]; warnings: string[]; projectConfigIncluded?: boolean }} options
|
|
474
|
-
* @returns {AgentBrowserConfigState}
|
|
475
|
-
*/
|
|
476
|
-
export function buildAgentBrowserConfigState(options) {
|
|
477
|
-
const webSearchCredentialSources = buildWebSearchCredentialSources(options);
|
|
478
|
-
const trustedBrowserDefaultProfile = getTrustedBrowserDefaultProfile(options.layers);
|
|
479
|
-
const trustedBrowserExecutablePath = getTrustedBrowserExecutablePath(options.layers);
|
|
480
|
-
return {
|
|
481
|
-
browserDefaultProfile: getBrowserDefaultProfile(options.mergedConfig),
|
|
482
|
-
browserDefaultProfileScope: getBrowserDefaultProfileScope(options.layers),
|
|
483
|
-
browserExecutablePath: getBrowserExecutablePath(options.mergedConfig),
|
|
484
|
-
browserExecutablePathScope: getBrowserExecutablePathScope(options.layers),
|
|
485
|
-
trustedBrowserDefaultProfile: trustedBrowserDefaultProfile?.profile,
|
|
486
|
-
trustedBrowserDefaultProfileScope: trustedBrowserDefaultProfile?.scope,
|
|
487
|
-
trustedBrowserExecutablePath: trustedBrowserExecutablePath?.executablePath,
|
|
488
|
-
trustedBrowserExecutablePathScope: trustedBrowserExecutablePath?.scope,
|
|
489
|
-
config: options.mergedConfig,
|
|
490
|
-
webSearchCredentialSources,
|
|
491
|
-
webSearchEnabled: options.mergedConfig.webSearch?.enabled !== false,
|
|
492
|
-
webSearchPreferredProvider: options.mergedConfig.webSearch?.preferredProvider ?? DEFAULT_WEB_SEARCH_PROVIDER,
|
|
493
|
-
errors: options.errors,
|
|
494
|
-
layers: options.layers,
|
|
495
|
-
paths: options.paths,
|
|
496
|
-
projectConfigIncluded: options.projectConfigIncluded ?? options.layers.some((layer) => layer.scope === "project"),
|
|
497
|
-
warnings: options.warnings,
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* @param {string} path
|
|
503
|
-
* @param {ConfigLayerScope} scope
|
|
504
|
-
* @param {string[]} errors
|
|
505
|
-
* @param {string[]} warnings
|
|
506
|
-
* @returns {ConfigLayer | undefined}
|
|
507
|
-
*/
|
|
508
|
-
function readConfigLayerSync(path, scope, errors, warnings) {
|
|
509
|
-
let raw;
|
|
510
|
-
try {
|
|
511
|
-
raw = readFileSync(path, "utf8");
|
|
512
|
-
} catch (error) {
|
|
513
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return undefined;
|
|
514
|
-
errors.push(`Could not read ${scope} config ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
515
|
-
return undefined;
|
|
516
|
-
}
|
|
517
|
-
return parseAgentBrowserConfigLayer(raw, path, scope, errors, warnings);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* @param {AgentBrowserConfigLoadOptions} [options]
|
|
522
|
-
* @returns {AgentBrowserConfigState}
|
|
523
|
-
*/
|
|
524
|
-
export function loadAgentBrowserConfigStateSync(options = {}) {
|
|
525
|
-
const env = options.env ?? process.env;
|
|
526
|
-
const paths = getAgentBrowserConfigPaths({ cwd: options.cwd, env });
|
|
527
|
-
const includeProjectConfig = options.includeProjectConfig !== false;
|
|
528
|
-
/** @type {string[]} */
|
|
529
|
-
const errors = [];
|
|
530
|
-
/** @type {string[]} */
|
|
531
|
-
const warnings = [];
|
|
532
|
-
/** @type {Array<{ path: string; scope: ConfigLayerScope }>} */
|
|
533
|
-
const layerCandidates = [
|
|
534
|
-
{ path: paths.global, scope: "global" },
|
|
535
|
-
...(includeProjectConfig ? [{ path: paths.project, scope: /** @type {ConfigLayerScope} */ ("project") }] : []),
|
|
536
|
-
...(paths.override ? [{ path: paths.override, scope: /** @type {ConfigLayerScope} */ ("override") }] : []),
|
|
537
|
-
];
|
|
538
|
-
/** @type {ConfigLayer[]} */
|
|
539
|
-
const layers = [];
|
|
540
|
-
/** @type {AgentBrowserConfig} */
|
|
541
|
-
let mergedConfig = {};
|
|
542
|
-
for (const candidate of layerCandidates) {
|
|
543
|
-
const layer = readConfigLayerSync(candidate.path, candidate.scope, errors, warnings);
|
|
544
|
-
if (!layer) continue;
|
|
545
|
-
layers.push(layer);
|
|
546
|
-
mergedConfig = mergeAgentBrowserConfig(mergedConfig, layer.config);
|
|
547
|
-
}
|
|
548
|
-
return buildAgentBrowserConfigState({
|
|
549
|
-
env,
|
|
550
|
-
errors,
|
|
551
|
-
layers,
|
|
552
|
-
mergedConfig,
|
|
553
|
-
paths,
|
|
554
|
-
projectConfigIncluded: includeProjectConfig,
|
|
555
|
-
warnings,
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* @param {string} rawValue
|
|
561
|
-
* @param {NodeJS.ProcessEnv} env
|
|
562
|
-
*/
|
|
563
|
-
export function resolveEnvInterpolations(rawValue, env) {
|
|
564
|
-
let output = "";
|
|
565
|
-
for (let index = 0; index < rawValue.length; index += 1) {
|
|
566
|
-
const char = rawValue[index];
|
|
567
|
-
if (char !== "$") {
|
|
568
|
-
output += char;
|
|
569
|
-
continue;
|
|
570
|
-
}
|
|
571
|
-
const next = rawValue[index + 1];
|
|
572
|
-
if (next === "$") {
|
|
573
|
-
output += "$";
|
|
574
|
-
index += 1;
|
|
575
|
-
continue;
|
|
576
|
-
}
|
|
577
|
-
if (next === "!") {
|
|
578
|
-
output += "!";
|
|
579
|
-
index += 1;
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
let name = "";
|
|
583
|
-
if (next === "{") {
|
|
584
|
-
const end = rawValue.indexOf("}", index + 2);
|
|
585
|
-
if (end === -1) return undefined;
|
|
586
|
-
name = rawValue.slice(index + 2, end);
|
|
587
|
-
index = end;
|
|
588
|
-
} else {
|
|
589
|
-
const match = rawValue.slice(index + 1).match(/^([A-Za-z_][A-Za-z0-9_]*)/);
|
|
590
|
-
if (!match) {
|
|
591
|
-
output += "$";
|
|
592
|
-
continue;
|
|
593
|
-
}
|
|
594
|
-
name = match[1] ?? "";
|
|
595
|
-
index += name.length;
|
|
596
|
-
}
|
|
597
|
-
if (!name) return undefined;
|
|
598
|
-
const value = env[name];
|
|
599
|
-
if (value === undefined) return undefined;
|
|
600
|
-
output += value;
|
|
601
|
-
}
|
|
602
|
-
return output;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* @param {AgentBrowserConfigState} state
|
|
607
|
-
* @param {WebSearchProvider | "auto"} [requestedProvider]
|
|
608
|
-
* @returns {WebSearchProvider[]}
|
|
609
|
-
*/
|
|
610
|
-
export function getWebSearchProviderOrder(state, requestedProvider) {
|
|
611
|
-
if (requestedProvider && requestedProvider !== "auto") return [requestedProvider];
|
|
612
|
-
const preferred = state.webSearchPreferredProvider;
|
|
613
|
-
return [preferred, ...WEB_SEARCH_PROVIDERS.filter((provider) => provider !== preferred)];
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* @param {AgentBrowserConfigState} state
|
|
618
|
-
* @param {WebSearchProvider} provider
|
|
619
|
-
*/
|
|
620
|
-
export function getWebSearchCredentialSource(state, provider) {
|
|
621
|
-
return state.webSearchCredentialSources[provider];
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
/**
|
|
625
|
-
* @param {CredentialSource | undefined} source
|
|
626
|
-
* @param {NodeJS.ProcessEnv} env
|
|
627
|
-
*/
|
|
628
|
-
export function hasPotentialCredentialSource(source, env) {
|
|
629
|
-
if (!source) return false;
|
|
630
|
-
if (source.kind === "command") return true;
|
|
631
|
-
if (source.kind === "env") return Boolean(resolveEnvInterpolations(source.rawValue, env)?.trim());
|
|
632
|
-
return Boolean(source.rawValue.trim());
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
* @param {AgentBrowserConfigState} state
|
|
637
|
-
* @param {NodeJS.ProcessEnv} [env]
|
|
638
|
-
*/
|
|
639
|
-
export function canRegisterWebSearchTool(state, env = process.env) {
|
|
640
|
-
if (!state.webSearchEnabled || state.errors.length > 0) return false;
|
|
641
|
-
return WEB_SEARCH_PROVIDERS.some((provider) => hasPotentialCredentialSource(state.webSearchCredentialSources[provider], env));
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
/**
|
|
645
|
-
* @param {CredentialSource | undefined} source
|
|
646
|
-
* @param {WebSearchProvider} [provider]
|
|
647
|
-
*/
|
|
648
|
-
export function getCredentialSourceSummary(source, provider) {
|
|
649
|
-
if (!source) return "not configured";
|
|
650
|
-
if (source.kind === "command") return `configured via command (${source.scope})`;
|
|
651
|
-
if (source.kind === "env") return `configured via environment interpolation (${source.scope})`;
|
|
652
|
-
if (source.scope === "env-fallback") {
|
|
653
|
-
return `configured via ${getWebSearchProviderEnvVar(provider ?? source.provider ?? DEFAULT_WEB_SEARCH_PROVIDER)} environment fallback`;
|
|
654
|
-
}
|
|
655
|
-
return `configured as plaintext ${source.scope} value [redacted]`;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
/** @param {AgentBrowserConfigState} state */
|
|
659
|
-
export function formatBrowserProfileStatus(state) {
|
|
660
|
-
const profile = state.browserDefaultProfile;
|
|
661
|
-
if (!profile) return "not configured";
|
|
662
|
-
const scope = state.browserDefaultProfileScope ?? "unknown";
|
|
663
|
-
const base = `${profile.name} (policy: ${profile.policy}; ${scope})`;
|
|
664
|
-
if (scope !== "project") return base;
|
|
665
|
-
const trustedText = state.trustedBrowserDefaultProfile ? `; trusted guidance: ${state.trustedBrowserDefaultProfile.name} (${state.trustedBrowserDefaultProfileScope})` : "";
|
|
666
|
-
return `${base}; ignored for prompt guidance${trustedText}`;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
/** @param {AgentBrowserConfigState} state */
|
|
670
|
-
export function formatBrowserExecutableStatus(state) {
|
|
671
|
-
const executablePath = state.browserExecutablePath;
|
|
672
|
-
if (!executablePath) return "not configured";
|
|
673
|
-
const scope = state.browserExecutablePathScope ?? "unknown";
|
|
674
|
-
if (scope !== "project") return `${executablePath} (${scope})`;
|
|
675
|
-
const trustedText = state.trustedBrowserExecutablePath ? `; trusted guidance: ${state.trustedBrowserExecutablePath} (${state.trustedBrowserExecutablePathScope})` : "";
|
|
676
|
-
return `${executablePath} (${scope}; ignored for prompt guidance${trustedText})`;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* @param {AgentBrowserConfigState} state
|
|
681
|
-
* @param {(path: string) => boolean} [exists]
|
|
682
|
-
* @returns {ConfigFileSummary[]}
|
|
683
|
-
*/
|
|
684
|
-
export function summarizeConfigFiles(state, exists = existsSync) {
|
|
685
|
-
return [
|
|
686
|
-
["global", state.paths.global],
|
|
687
|
-
["project", state.paths.project],
|
|
688
|
-
...(state.paths.override ? [["override", state.paths.override]] : []),
|
|
689
|
-
].map(([scope, path]) => ({ scope, path, exists: exists(path) }));
|
|
690
|
-
}
|