pi-agent-browser-native 0.2.72 → 0.2.75
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 +33 -0
- package/README.md +14 -12
- package/dist/extensions/agent-browser/index.js +148 -16
- package/dist/extensions/agent-browser/lib/argv-grammar.js +122 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +11 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +2 -2
- package/dist/extensions/agent-browser/lib/electron/launch.js +48 -12
- package/dist/extensions/agent-browser/lib/input-modes/params.js +96 -98
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +88 -2
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +22 -0
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +432 -0
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +374 -0
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +367 -0
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +589 -0
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +299 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +35 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +9 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +40 -22
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +15 -6
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +54 -33
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +182 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +629 -430
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +148 -57
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +30 -40
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +102 -19
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +13 -1
- package/dist/extensions/agent-browser/lib/playbook.js +9 -8
- package/dist/extensions/agent-browser/lib/process-identity.js +82 -0
- package/dist/extensions/agent-browser/lib/process.js +271 -34
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +5 -3
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +35 -12
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +42 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +7 -0
- package/dist/extensions/agent-browser/lib/runtime.js +114 -107
- package/dist/extensions/agent-browser/lib/session-page-state.js +48 -17
- package/dist/extensions/agent-browser/lib/temp.js +13 -25
- package/docs/ARCHITECTURE.md +10 -9
- package/docs/COMMAND_REFERENCE.md +43 -23
- package/docs/ELECTRON.md +10 -10
- package/docs/RELEASE.md +3 -2
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +19 -18
- package/docs/TOOL_CONTRACT.md +29 -26
- package/docs/platform-smoke.md +2 -2
- package/package.json +1 -1
- package/platform-smoke.config.mjs +1 -1
- package/scripts/agent-browser-capability-baseline.mjs +11 -3
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Own automatic restore policy for wrapper-managed browser sessions.
|
|
3
|
+
* Responsibilities: Resolve owned session identity, isolate per-call policy, reject incompatible argv/env/config, and persist sticky restore-disable state.
|
|
4
|
+
* Scope: Restore policy only; storage identity and snapshot retention live in focused sibling modules.
|
|
5
|
+
*/
|
|
6
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
|
+
import { chmodSync, lstatSync, readFileSync, unlinkSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { extractCommandTokens, parseCommandInfo } from "./argv-descriptor.js";
|
|
10
|
+
import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, extractRequestedRestoreKey, getAgentBrowserSessionIdentityKey, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
|
|
11
|
+
import { hasLaunchScopedFlagToken, MANAGED_RESTORE_INCOMPATIBLE_BOOLEAN_ENVS, MANAGED_RESTORE_INCOMPATIBLE_ENVS, MANAGED_RESTORE_INCOMPATIBLE_FLAGS, } from "./launch-scoped-flags.js";
|
|
12
|
+
import { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreProtectedStorageEnv, hasManagedSessionRestoreProjectIdentity, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
13
|
+
import { parseUserBatchStdin } from "./orchestration/batch-stdin.js";
|
|
14
|
+
import { writeSecureTempFile } from "./temp.js";
|
|
15
|
+
export { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure } from "./managed-session-storage.js";
|
|
16
|
+
export { pruneOwnedManagedSessionRestoreSnapshots } from "./managed-session-snapshots.js";
|
|
17
|
+
const AGENT_BROWSER_CONFIG_ENV = "AGENT_BROWSER_CONFIG";
|
|
18
|
+
const AGENT_BROWSER_RESTORE_ENV = "AGENT_BROWSER_RESTORE";
|
|
19
|
+
const MANAGED_SESSION_RESTORE_ENV = "PI_AGENT_BROWSER_MANAGED_SESSION_RESTORE";
|
|
20
|
+
export const MANAGED_SESSION_NAME_PREFIX = "piab-";
|
|
21
|
+
const MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT = "{}\n";
|
|
22
|
+
const MANAGED_SESSION_RESTORE_EMPTY_CONFIG_NAME = ".pi-agent-browser-managed-restore-config-v1.json";
|
|
23
|
+
const MANAGED_SESSION_RESTORE_SPAWN_PINNED_ENVS = new Set([AGENT_BROWSER_CONFIG_ENV, AGENT_BROWSER_RESTORE_ENV, "AGENT_BROWSER_NAMESPACE"]);
|
|
24
|
+
let managedSessionRestoreEmptyConfigPath;
|
|
25
|
+
let managedSessionRestoreEmptyConfigPromise;
|
|
26
|
+
function isDisabledEnvFlag(value) {
|
|
27
|
+
if (value === undefined)
|
|
28
|
+
return false;
|
|
29
|
+
return ["0", "false", "no", "off"].includes(value.trim().toLowerCase());
|
|
30
|
+
}
|
|
31
|
+
/** Match upstream env_var_is_truthy exactly: lowercase only, without trimming or accepting "off". */
|
|
32
|
+
function isUpstreamEnvFlagEnabled(value) {
|
|
33
|
+
return value !== undefined && !["", "0", "false", "no"].includes(value.toLowerCase());
|
|
34
|
+
}
|
|
35
|
+
function hasUpstreamEnvValue(env, name) {
|
|
36
|
+
return env?.[name] !== undefined;
|
|
37
|
+
}
|
|
38
|
+
export class ManagedSessionRestoreState {
|
|
39
|
+
#daemonRestoreKeys = new Map();
|
|
40
|
+
#disabled = new Set();
|
|
41
|
+
clear(sessionName, namespace) {
|
|
42
|
+
if (sessionName) {
|
|
43
|
+
const identity = getAgentBrowserSessionIdentityKey(sessionName, namespace);
|
|
44
|
+
this.#daemonRestoreKeys.delete(identity);
|
|
45
|
+
this.#disabled.delete(identity);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.#daemonRestoreKeys.clear();
|
|
49
|
+
this.#disabled.clear();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
disable(sessionName, namespace) {
|
|
53
|
+
if (sessionName)
|
|
54
|
+
this.#disabled.add(getAgentBrowserSessionIdentityKey(sessionName, namespace));
|
|
55
|
+
}
|
|
56
|
+
getDaemonRestoreKey(sessionName, namespace) {
|
|
57
|
+
return typeof sessionName === "string" ? this.#daemonRestoreKeys.get(getAgentBrowserSessionIdentityKey(sessionName, namespace)) : undefined;
|
|
58
|
+
}
|
|
59
|
+
hasDaemonRestoreKey(sessionName, namespace) {
|
|
60
|
+
return typeof sessionName === "string" && this.#daemonRestoreKeys.has(getAgentBrowserSessionIdentityKey(sessionName, namespace));
|
|
61
|
+
}
|
|
62
|
+
forgetDaemonRestoreKey(sessionName, namespace) {
|
|
63
|
+
if (sessionName)
|
|
64
|
+
this.#daemonRestoreKeys.delete(getAgentBrowserSessionIdentityKey(sessionName, namespace));
|
|
65
|
+
}
|
|
66
|
+
isDisabled(sessionName, namespace) {
|
|
67
|
+
return typeof sessionName === "string" && this.#disabled.has(getAgentBrowserSessionIdentityKey(sessionName, namespace));
|
|
68
|
+
}
|
|
69
|
+
recordDaemonRestoreKey(sessionName, namespace, restoreKey) {
|
|
70
|
+
if (sessionName)
|
|
71
|
+
this.#daemonRestoreKeys.set(getAgentBrowserSessionIdentityKey(sessionName, namespace), restoreKey);
|
|
72
|
+
}
|
|
73
|
+
replace(identities, options = {}) {
|
|
74
|
+
if (!options.preserveDaemonRestoreKeys)
|
|
75
|
+
this.#daemonRestoreKeys.clear();
|
|
76
|
+
this.#disabled.clear();
|
|
77
|
+
for (const identity of identities)
|
|
78
|
+
this.disable(identity.sessionName, identity.namespace);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const ownedManagedSessionStorage = new AsyncLocalStorage();
|
|
82
|
+
export async function withOwnedManagedSessionContext(context, run) {
|
|
83
|
+
return await ownedManagedSessionStorage.run(context, run);
|
|
84
|
+
}
|
|
85
|
+
export function resolveOwnedManagedSessionContext(options) {
|
|
86
|
+
const namespace = canonicalizeAgentBrowserNamespace(options.namespace);
|
|
87
|
+
const currentNamespace = canonicalizeAgentBrowserNamespace(options.currentManagedSessionNamespace);
|
|
88
|
+
const recordedNamespace = canonicalizeAgentBrowserNamespace(options.recordedOwnedSession?.namespace);
|
|
89
|
+
if (options.recordedOwnedSession
|
|
90
|
+
&& options.sessionName === options.recordedOwnedSession.sessionName
|
|
91
|
+
&& namespace === recordedNamespace) {
|
|
92
|
+
return { cwd: options.recordedOwnedSession.cwd, namespace, restoreState: options.restoreState, sessionName: options.sessionName };
|
|
93
|
+
}
|
|
94
|
+
if (options.managedSessionName)
|
|
95
|
+
return { cwd: options.cwd, namespace, restoreState: options.restoreState, sessionName: options.managedSessionName };
|
|
96
|
+
if (options.sessionName && options.sessionName === options.currentManagedSessionName && namespace === currentNamespace) {
|
|
97
|
+
return { cwd: options.cwd, namespace, restoreState: options.restoreState, sessionName: options.sessionName };
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
function ownedContextMatches(sessionName, namespace) {
|
|
102
|
+
const owned = ownedManagedSessionStorage.getStore();
|
|
103
|
+
return owned && sessionName === owned.sessionName && canonicalizeAgentBrowserNamespace(namespace) === owned.namespace ? owned : undefined;
|
|
104
|
+
}
|
|
105
|
+
export function isOwnedManagedSessionTarget(args) {
|
|
106
|
+
return ownedContextMatches(extractExplicitSessionName(args), extractExplicitNamespace(args)) !== undefined;
|
|
107
|
+
}
|
|
108
|
+
function pathExistsOrIsUnreadable(path) {
|
|
109
|
+
try {
|
|
110
|
+
lstatSync(path);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return error.code !== "ENOENT";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function hasExplicitConfigArg(args) {
|
|
118
|
+
return scanUpstreamGlobalFlagOccurrences(args, "--config").length > 0;
|
|
119
|
+
}
|
|
120
|
+
function closesBrowserSession(args) {
|
|
121
|
+
return ["close", "exit", "quit"].includes(parseCommandInfo(args).command ?? "");
|
|
122
|
+
}
|
|
123
|
+
export function agentBrowserConfigIsPresent(cwd, parentEnv = process.env, args = [], platform = process.platform) {
|
|
124
|
+
if (hasExplicitConfigArg(args) || hasUpstreamEnvValue(parentEnv, AGENT_BROWSER_CONFIG_ENV))
|
|
125
|
+
return true;
|
|
126
|
+
const paths = [join(cwd, "agent-browser.json")];
|
|
127
|
+
const home = resolveManagedSessionRestoreHome(parentEnv, platform);
|
|
128
|
+
if (home)
|
|
129
|
+
paths.push(join(home, ".agent-browser", "config.json"));
|
|
130
|
+
return paths.some(pathExistsOrIsUnreadable);
|
|
131
|
+
}
|
|
132
|
+
/** Any upstream config disables automatic restore; content inspection would add parser and resource-exhaustion gaps. */
|
|
133
|
+
export function agentBrowserConfigBlocksManagedRestore(cwd, parentEnv = process.env, args = [], platform = process.platform) {
|
|
134
|
+
return !resolveManagedSessionRestoreHome(parentEnv, platform) || agentBrowserConfigIsPresent(cwd, parentEnv, args, platform);
|
|
135
|
+
}
|
|
136
|
+
function omitWrapperInjectedUserAgent(args, enabled) {
|
|
137
|
+
if (!enabled)
|
|
138
|
+
return args;
|
|
139
|
+
const index = args.indexOf("--user-agent");
|
|
140
|
+
return index < 0 ? args : [...args.slice(0, index), ...args.slice(index + 2)];
|
|
141
|
+
}
|
|
142
|
+
function batchHasManagedSessionRestoreConflict(args, stdin) {
|
|
143
|
+
const [command, ...commandArgs] = extractCommandTokens(args);
|
|
144
|
+
if (command !== "batch")
|
|
145
|
+
return false;
|
|
146
|
+
if (commandArgs.some((token) => token !== "--bail"))
|
|
147
|
+
return true;
|
|
148
|
+
const parsed = parseUserBatchStdin(stdin);
|
|
149
|
+
if (parsed.error || !parsed.steps)
|
|
150
|
+
return false;
|
|
151
|
+
return parsed.steps.some((step) => ["connect", "batch"].includes(parseCommandInfo(step).command ?? ""));
|
|
152
|
+
}
|
|
153
|
+
function hasManagedSessionRestoreLaunchConflict(options) {
|
|
154
|
+
const parentEnv = options.parentEnv ?? process.env;
|
|
155
|
+
const effectiveEnv = { ...parentEnv, ...options.env };
|
|
156
|
+
const args = omitWrapperInjectedUserAgent(options.args, options.wrapperInjectedUserAgent);
|
|
157
|
+
if (MANAGED_RESTORE_INCOMPATIBLE_ENVS.some((name) => hasUpstreamEnvValue(effectiveEnv, name)))
|
|
158
|
+
return true;
|
|
159
|
+
if (MANAGED_RESTORE_INCOMPATIBLE_BOOLEAN_ENVS.some((name) => isUpstreamEnvFlagEnabled(effectiveEnv[name])))
|
|
160
|
+
return true;
|
|
161
|
+
if (MANAGED_RESTORE_INCOMPATIBLE_FLAGS.some((flag) => hasLaunchScopedFlagToken(args, flag)))
|
|
162
|
+
return true;
|
|
163
|
+
if (parseCommandInfo(args).command === "connect" || batchHasManagedSessionRestoreConflict(args, options.stdin))
|
|
164
|
+
return true;
|
|
165
|
+
return hasExplicitConfigArg(args) || hasUpstreamEnvValue({ ...parentEnv, ...options.env }, AGENT_BROWSER_CONFIG_ENV);
|
|
166
|
+
}
|
|
167
|
+
function managedSessionRestoreOptedOut(options) {
|
|
168
|
+
const effectiveEnv = { ...(options.parentEnv ?? process.env), ...options.env };
|
|
169
|
+
return isDisabledEnvFlag(effectiveEnv[MANAGED_SESSION_RESTORE_ENV]);
|
|
170
|
+
}
|
|
171
|
+
function isManagedSessionRestoreIncompatible(options) {
|
|
172
|
+
if (hasManagedSessionRestoreLaunchConflict(options))
|
|
173
|
+
return true;
|
|
174
|
+
if (managedSessionRestoreOptedOut(options))
|
|
175
|
+
return false;
|
|
176
|
+
const effectiveEnv = { ...(options.parentEnv ?? process.env), ...options.env };
|
|
177
|
+
const args = omitWrapperInjectedUserAgent(options.args, options.wrapperInjectedUserAgent);
|
|
178
|
+
if (options.cwd && !hasManagedSessionRestoreProjectIdentity(options.cwd))
|
|
179
|
+
return true;
|
|
180
|
+
if (options.cwd && agentBrowserConfigBlocksManagedRestore(options.cwd, effectiveEnv, args))
|
|
181
|
+
return true;
|
|
182
|
+
return !ensureManagedSessionRestoreStorageIsSecure(effectiveEnv, process.platform, extractExplicitNamespace(args));
|
|
183
|
+
}
|
|
184
|
+
function resolveManagedSessionRestorePolicy(options) {
|
|
185
|
+
const parentEnv = options.parentEnv ?? process.env;
|
|
186
|
+
const sessionName = extractExplicitSessionName(options.args);
|
|
187
|
+
const namespace = extractExplicitNamespace(options.args);
|
|
188
|
+
const ownedContext = ownedContextMatches(sessionName, namespace);
|
|
189
|
+
const restoreState = ownedContext?.restoreState ?? options.restoreState;
|
|
190
|
+
const owned = (options.ownedManagedSession || ownedContext !== undefined) && restoreState !== undefined;
|
|
191
|
+
return { namespace, owned, ownedContext, parentEnv, restoreState, sessionName };
|
|
192
|
+
}
|
|
193
|
+
export function getOwnedManagedSessionNamespaceEnv(options) {
|
|
194
|
+
const { namespace, owned, ownedContext } = resolveManagedSessionRestorePolicy(options);
|
|
195
|
+
return owned ? { AGENT_BROWSER_NAMESPACE: ownedContext?.namespace ?? namespace ?? "" } : {};
|
|
196
|
+
}
|
|
197
|
+
export function getOwnedManagedSessionCompatibilityEnv(options) {
|
|
198
|
+
const { owned, ownedContext } = resolveManagedSessionRestorePolicy(options);
|
|
199
|
+
return owned && ownedContext?.compatibilityUserAgent
|
|
200
|
+
? { AGENT_BROWSER_USER_AGENT: ownedContext.compatibilityUserAgent }
|
|
201
|
+
: {};
|
|
202
|
+
}
|
|
203
|
+
export function shouldOmitOwnedManagedSessionRestoreEnv(options) {
|
|
204
|
+
return resolveManagedSessionRestorePolicy(options).owned && closesBrowserSession(options.args);
|
|
205
|
+
}
|
|
206
|
+
export function canonicalizeOwnedManagedSessionCloseArgs(options, force = false) {
|
|
207
|
+
const policy = resolveManagedSessionRestorePolicy(options);
|
|
208
|
+
if (!policy.owned || !closesBrowserSession(options.args))
|
|
209
|
+
return options.args;
|
|
210
|
+
const sessionName = policy.ownedContext?.sessionName ?? policy.sessionName;
|
|
211
|
+
if (!sessionName)
|
|
212
|
+
return options.args;
|
|
213
|
+
const namespace = canonicalizeAgentBrowserNamespace(policy.ownedContext?.namespace ?? policy.namespace) ?? "";
|
|
214
|
+
const command = options.args.at(-1);
|
|
215
|
+
const prefix = options.args.slice(0, -1);
|
|
216
|
+
const safePrefixes = [
|
|
217
|
+
["--session", sessionName],
|
|
218
|
+
["--json", "--session", sessionName],
|
|
219
|
+
["--namespace", namespace, "--session", sessionName],
|
|
220
|
+
["--json", "--namespace", namespace, "--session", sessionName],
|
|
221
|
+
];
|
|
222
|
+
if (!force && command && ["close", "exit", "quit"].includes(command)
|
|
223
|
+
&& safePrefixes.some((candidate) => candidate.length === prefix.length && candidate.every((token, index) => token === prefix[index]))) {
|
|
224
|
+
return options.args;
|
|
225
|
+
}
|
|
226
|
+
return ["--json", "--namespace", namespace, "--session", sessionName, "close"];
|
|
227
|
+
}
|
|
228
|
+
export function cleanupManagedSessionRestoreConfig() {
|
|
229
|
+
if (managedSessionRestoreEmptyConfigPath) {
|
|
230
|
+
try {
|
|
231
|
+
unlinkSync(managedSessionRestoreEmptyConfigPath);
|
|
232
|
+
}
|
|
233
|
+
catch { }
|
|
234
|
+
}
|
|
235
|
+
managedSessionRestoreEmptyConfigPath = undefined;
|
|
236
|
+
managedSessionRestoreEmptyConfigPromise = undefined;
|
|
237
|
+
}
|
|
238
|
+
async function ensureManagedSessionRestoreEmptyConfig(platform) {
|
|
239
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
240
|
+
try {
|
|
241
|
+
managedSessionRestoreEmptyConfigPromise ??= writeSecureTempFile({
|
|
242
|
+
content: MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT,
|
|
243
|
+
prefix: MANAGED_SESSION_RESTORE_EMPTY_CONFIG_NAME.replace(/\.json$/, ""),
|
|
244
|
+
suffix: ".json",
|
|
245
|
+
}).then((path) => {
|
|
246
|
+
if (platform !== "win32")
|
|
247
|
+
chmodSync(path, 0o400);
|
|
248
|
+
managedSessionRestoreEmptyConfigPath = path;
|
|
249
|
+
return path;
|
|
250
|
+
});
|
|
251
|
+
const path = await managedSessionRestoreEmptyConfigPromise;
|
|
252
|
+
let entry = lstatSync(path);
|
|
253
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
254
|
+
throw new Error("Managed restore config is not a regular file.");
|
|
255
|
+
if (platform !== "win32" && (entry.mode & 0o777) !== 0o400) {
|
|
256
|
+
chmodSync(path, 0o400);
|
|
257
|
+
entry = lstatSync(path);
|
|
258
|
+
}
|
|
259
|
+
if (entry.isSymbolicLink() || !entry.isFile() || (platform !== "win32" && (entry.mode & 0o777) !== 0o400))
|
|
260
|
+
throw new Error("Managed restore config permissions are unsafe.");
|
|
261
|
+
if (readFileSync(path, "utf8") !== MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT)
|
|
262
|
+
throw new Error("Managed restore config content changed.");
|
|
263
|
+
return path;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
cleanupManagedSessionRestoreConfig();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
export async function getManagedSessionRestoreConfigEnv(restoreEnv, pinForOwnedClose = false) {
|
|
272
|
+
if (restoreEnv[AGENT_BROWSER_RESTORE_ENV] === undefined && !pinForOwnedClose)
|
|
273
|
+
return {};
|
|
274
|
+
const path = await ensureManagedSessionRestoreEmptyConfig(process.platform);
|
|
275
|
+
return path ? { [AGENT_BROWSER_CONFIG_ENV]: path } : undefined;
|
|
276
|
+
}
|
|
277
|
+
export function getManagedSessionRestoreProtectedEnv(options, restoreEnv) {
|
|
278
|
+
const { ownedContext } = resolveManagedSessionRestorePolicy(options);
|
|
279
|
+
if (restoreEnv[AGENT_BROWSER_RESTORE_ENV] === undefined)
|
|
280
|
+
return {};
|
|
281
|
+
if (ownedContext?.protectedStorageEnv)
|
|
282
|
+
return { ...ownedContext.protectedStorageEnv };
|
|
283
|
+
const effectiveEnv = { ...(options.parentEnv ?? process.env), ...options.env };
|
|
284
|
+
return getManagedSessionRestoreProtectedStorageEnv(true, effectiveEnv);
|
|
285
|
+
}
|
|
286
|
+
export function validateManagedSessionRestoreContextForSpawn(options) {
|
|
287
|
+
const { namespace, ownedContext, parentEnv } = resolveManagedSessionRestorePolicy(options);
|
|
288
|
+
if (closesBrowserSession(options.args) || ownedContext?.restoreDecision !== "enabled")
|
|
289
|
+
return true;
|
|
290
|
+
const ownedCwd = ownedContext.cwd ?? options.cwd;
|
|
291
|
+
if (!ownedContext.restoreKey || createManagedSessionRestoreKey(ownedCwd) !== ownedContext.restoreKey || !hasManagedSessionRestoreProjectIdentity(ownedCwd))
|
|
292
|
+
return false;
|
|
293
|
+
const effectiveEnv = { ...parentEnv, ...options.env };
|
|
294
|
+
if (isDisabledEnvFlag(effectiveEnv[MANAGED_SESSION_RESTORE_ENV]))
|
|
295
|
+
return false;
|
|
296
|
+
if (MANAGED_RESTORE_INCOMPATIBLE_ENVS.some((name) => !MANAGED_SESSION_RESTORE_SPAWN_PINNED_ENVS.has(name) && hasUpstreamEnvValue(effectiveEnv, name)))
|
|
297
|
+
return false;
|
|
298
|
+
if (MANAGED_RESTORE_INCOMPATIBLE_BOOLEAN_ENVS.some((name) => isUpstreamEnvFlagEnabled(effectiveEnv[name])))
|
|
299
|
+
return false;
|
|
300
|
+
if (options.env?.[AGENT_BROWSER_RESTORE_ENV] !== undefined && options.env[AGENT_BROWSER_RESTORE_ENV] !== ownedContext.restoreKey)
|
|
301
|
+
return false;
|
|
302
|
+
return ensureManagedSessionRestoreStorageIsSecure({ ...effectiveEnv, ...ownedContext.protectedStorageEnv }, process.platform, namespace);
|
|
303
|
+
}
|
|
304
|
+
export function getManagedSessionRestoreEnv(options) {
|
|
305
|
+
const { namespace, owned, ownedContext, parentEnv, restoreState, sessionName } = resolveManagedSessionRestorePolicy(options);
|
|
306
|
+
if (!owned || !restoreState || closesBrowserSession(options.args))
|
|
307
|
+
return {};
|
|
308
|
+
if (ownedContext?.restoreDecision) {
|
|
309
|
+
if (ownedContext.restoreDecision !== "enabled" || restoreState.isDisabled(sessionName, namespace) || !sessionName || !ownedContext.restoreKey || !validateManagedSessionRestoreContextForSpawn(options))
|
|
310
|
+
return {};
|
|
311
|
+
return { [AGENT_BROWSER_RESTORE_ENV]: ownedContext.restoreKey };
|
|
312
|
+
}
|
|
313
|
+
const policyOptions = { ...options, parentEnv };
|
|
314
|
+
if (managedSessionRestoreOptedOut(policyOptions) || ownedContext?.restoreSuppressed || isManagedSessionRestoreIncompatible(policyOptions))
|
|
315
|
+
return {};
|
|
316
|
+
if (restoreState.isDisabled(sessionName, namespace) || !sessionName)
|
|
317
|
+
return {};
|
|
318
|
+
return { [AGENT_BROWSER_RESTORE_ENV]: createManagedSessionRestoreKey(options.cwd) };
|
|
319
|
+
}
|
|
320
|
+
/** Commit sticky suppression only after an owned-context subprocess has actually started. */
|
|
321
|
+
export function commitManagedSessionRestoreSuppression(options) {
|
|
322
|
+
const { namespace, owned, ownedContext, parentEnv, restoreState, sessionName } = resolveManagedSessionRestorePolicy(options);
|
|
323
|
+
if (!owned || !restoreState || closesBrowserSession(options.args))
|
|
324
|
+
return;
|
|
325
|
+
if (ownedContext?.restoreDecision) {
|
|
326
|
+
const alreadyDisabled = restoreState.isDisabled(sessionName, namespace);
|
|
327
|
+
if (ownedContext.restoreDecision === "enabled") {
|
|
328
|
+
if (options.ownedManagedSession && !alreadyDisabled && ownedContext.restoreKey) {
|
|
329
|
+
restoreState.recordDaemonRestoreKey(sessionName, namespace, ownedContext.restoreKey);
|
|
330
|
+
}
|
|
331
|
+
else if (options.ownedManagedSession && alreadyDisabled && !restoreState.hasDaemonRestoreKey(sessionName, namespace)) {
|
|
332
|
+
restoreState.recordDaemonRestoreKey(sessionName, namespace, null);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
if (options.ownedManagedSession)
|
|
337
|
+
restoreState.recordDaemonRestoreKey(sessionName, namespace, ownedContext.expectedDaemonRestoreKey ?? null);
|
|
338
|
+
restoreState.disable(sessionName, namespace);
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const policyOptions = { ...options, parentEnv };
|
|
343
|
+
if (managedSessionRestoreOptedOut(policyOptions) || ownedContext?.restoreSuppressed || isManagedSessionRestoreIncompatible(policyOptions))
|
|
344
|
+
restoreState.disable(sessionName, namespace);
|
|
345
|
+
}
|
|
346
|
+
export function buildOwnedManagedSessionRestoreContext(options) {
|
|
347
|
+
const owned = resolveOwnedManagedSessionContext(options);
|
|
348
|
+
if (!owned)
|
|
349
|
+
return undefined;
|
|
350
|
+
const ownedCwd = owned.cwd ?? options.cwd;
|
|
351
|
+
const policyOptions = {
|
|
352
|
+
args: options.args,
|
|
353
|
+
cwd: ownedCwd,
|
|
354
|
+
env: options.env,
|
|
355
|
+
parentEnv: options.parentEnv,
|
|
356
|
+
stdin: options.stdin,
|
|
357
|
+
wrapperInjectedUserAgent: options.wrapperInjectedUserAgent,
|
|
358
|
+
};
|
|
359
|
+
const optedOut = managedSessionRestoreOptedOut(policyOptions);
|
|
360
|
+
const projectIdentityAvailable = !optedOut && hasManagedSessionRestoreProjectIdentity(ownedCwd);
|
|
361
|
+
const incompatible = !optedOut && isManagedSessionRestoreIncompatible(policyOptions);
|
|
362
|
+
const enabled = !optedOut && !incompatible;
|
|
363
|
+
const effectiveEnv = { ...(options.parentEnv ?? process.env), ...options.env };
|
|
364
|
+
const restoreKey = projectIdentityAvailable ? createManagedSessionRestoreKey(ownedCwd) : undefined;
|
|
365
|
+
return {
|
|
366
|
+
...owned,
|
|
367
|
+
compatibilityUserAgent: options.compatibilityUserAgent,
|
|
368
|
+
expectedDaemonRestoreKey: enabled ? restoreKey : extractRequestedRestoreKey(options.args, owned.sessionName, effectiveEnv[AGENT_BROWSER_RESTORE_ENV]),
|
|
369
|
+
protectedStorageEnv: enabled ? getManagedSessionRestoreProtectedStorageEnv(true, effectiveEnv) : undefined,
|
|
370
|
+
restoreDecision: optedOut ? "opted-out" : incompatible ? "incompatible" : "enabled",
|
|
371
|
+
restoreKey,
|
|
372
|
+
restoreSuppressed: optedOut || incompatible,
|
|
373
|
+
};
|
|
374
|
+
}
|