pi-agent-browser-native 0.3.0 → 0.6.5
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 +265 -0
- package/README.md +130 -54
- package/dist/extensions/agent-browser/index.js +781 -169
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +35 -3
- package/dist/extensions/agent-browser/lib/argv-grammar.js +50 -2
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +71 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +5 -8
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +53 -12
- package/dist/extensions/agent-browser/lib/config-policy.js +25 -1
- package/dist/extensions/agent-browser/lib/config.js +1 -1
- package/dist/extensions/agent-browser/lib/input-modes/job.js +61 -13
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +2 -2
- package/dist/extensions/agent-browser/lib/input-modes/params.js +23 -24
- package/dist/extensions/agent-browser/lib/input-modes/script.js +462 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +51 -12
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +26 -4
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +6 -139
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +26 -116
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +2 -4
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +54 -25
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +26 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +110 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +54 -48
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +71 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +6 -7
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +119 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +7 -6
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +152 -64
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +244 -102
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +63 -37
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +20 -21
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +36 -18
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +41 -21
- package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +299 -0
- package/dist/extensions/agent-browser/lib/page-target-validation.js +270 -0
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +32 -10
- package/dist/extensions/agent-browser/lib/playbook.js +29 -25
- package/dist/extensions/agent-browser/lib/process-environment.js +14 -0
- package/dist/extensions/agent-browser/lib/process-identity.js +5 -12
- package/dist/extensions/agent-browser/lib/process.js +130 -104
- package/dist/extensions/agent-browser/lib/recording-reservations.js +116 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +63 -6
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +62 -4
- package/dist/extensions/agent-browser/lib/results/categories.js +6 -1
- package/dist/extensions/agent-browser/lib/results/next-actions.js +19 -5
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +85 -38
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +86 -18
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +38 -2
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +18 -17
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +38 -20
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +60 -15
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +1 -10
- package/dist/extensions/agent-browser/lib/results/presentation.js +36 -6
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +3 -1
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +9 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +54 -11
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +13 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +2 -1
- package/dist/extensions/agent-browser/lib/results/snapshot.js +4 -4
- package/dist/extensions/agent-browser/lib/runtime.js +186 -108
- package/dist/extensions/agent-browser/lib/session-page-state.js +71 -10
- package/dist/extensions/agent-browser/lib/temp.js +1 -2
- package/dist/extensions/agent-browser/lib/upstream-version.js +14 -0
- package/dist/extensions/agent-browser/lib/web-search.js +108 -24
- package/dist/extensions/agent-browser/script-worker.js +169 -0
- package/dist/scripts/agent-browser-target.mjs +21 -0
- package/docs/ARCHITECTURE.md +57 -34
- package/docs/COMMAND_REFERENCE.md +255 -68
- package/docs/ELECTRON.md +2 -2
- package/docs/RELEASE.md +12 -10
- package/docs/REQUIREMENTS.md +11 -8
- package/docs/SUPPORT_MATRIX.md +36 -24
- package/docs/TOOL_CONTRACT.md +169 -95
- package/package.json +3 -1
- package/platform-smoke.config.mjs +2 -2
- package/scripts/agent-browser-capability-baseline.mjs +87 -9
- package/scripts/agent-browser-target.mjs +21 -0
- package/scripts/build.mjs +41 -0
- package/scripts/config.mjs +1 -0
- package/scripts/doctor.mjs +16 -9
- package/scripts/platform-smoke/browser-dogfood-windows.ps1 +9 -3
- package/scripts/platform-smoke/targets.mjs +12 -6
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -20
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +0 -583
- package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -78
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -37
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { findCommandStartIndex } from "./argv-descriptor.js";
|
|
2
|
-
import { isBooleanFlagEnabled
|
|
2
|
+
import { isBooleanFlagEnabled } from "./argv-grammar.js";
|
|
3
3
|
export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
|
|
4
4
|
{
|
|
5
5
|
flag: "--auto-connect",
|
|
@@ -17,6 +17,14 @@ export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
|
|
|
17
17
|
flag: "--cdp",
|
|
18
18
|
reason: "selects the browser/CDP endpoint used when an upstream session is launched",
|
|
19
19
|
},
|
|
20
|
+
{
|
|
21
|
+
flag: "--ca-cert",
|
|
22
|
+
reason: "selects isolated Chromium CA trust and may relaunch the browser",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
flag: "--no-ca-cert",
|
|
26
|
+
reason: "clears retained isolated Chromium CA trust and may relaunch the browser",
|
|
27
|
+
},
|
|
20
28
|
{
|
|
21
29
|
flag: "--enable",
|
|
22
30
|
reason: "selects built-in page init scripts before the upstream browser session is launched",
|
|
@@ -29,6 +37,10 @@ export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
|
|
|
29
37
|
flag: "--webgpu",
|
|
30
38
|
reason: "selects the platform-specific WebGPU browser launch preset",
|
|
31
39
|
},
|
|
40
|
+
{
|
|
41
|
+
flag: "--no-webmcp",
|
|
42
|
+
reason: "selects whether locally launched Chrome enables experimental WebMCP support",
|
|
43
|
+
},
|
|
32
44
|
{
|
|
33
45
|
flag: "--init-script",
|
|
34
46
|
reason: "registers page init scripts before the upstream browser session is launched",
|
|
@@ -37,6 +49,14 @@ export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
|
|
|
37
49
|
flag: "--idle-timeout",
|
|
38
50
|
reason: "configures background browser lifecycle for the launched session",
|
|
39
51
|
},
|
|
52
|
+
{
|
|
53
|
+
flag: "--args",
|
|
54
|
+
reason: "selects raw Chrome arguments for the browser launch",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
flag: "--user-agent",
|
|
58
|
+
reason: "selects the browser user agent at launch time",
|
|
59
|
+
},
|
|
40
60
|
{
|
|
41
61
|
flag: "--headed",
|
|
42
62
|
reason: "selects whether the launched browser has a visible window",
|
|
@@ -112,6 +132,7 @@ export const MANAGED_RESTORE_INCOMPATIBLE_FLAGS = [
|
|
|
112
132
|
"--user-agent",
|
|
113
133
|
"--proxy",
|
|
114
134
|
"--proxy-bypass",
|
|
135
|
+
"--ca-cert",
|
|
115
136
|
"--ignore-https-errors",
|
|
116
137
|
"--allow-file-access",
|
|
117
138
|
"--webgpu",
|
|
@@ -140,6 +161,7 @@ export const MANAGED_RESTORE_INCOMPATIBLE_ENVS = [
|
|
|
140
161
|
"AGENT_BROWSER_USER_AGENT",
|
|
141
162
|
"AGENT_BROWSER_PROXY",
|
|
142
163
|
"AGENT_BROWSER_PROXY_BYPASS",
|
|
164
|
+
"AGENT_BROWSER_CA_CERT",
|
|
143
165
|
"AGENT_BROWSER_PLUGINS",
|
|
144
166
|
"AGENT_BROWSER_IOS_DEVICE",
|
|
145
167
|
"AGENT_BROWSER_IOS_UDID",
|
|
@@ -162,12 +184,12 @@ export function hasLaunchScopedFlagToken(args, flag) {
|
|
|
162
184
|
const commandStartIndex = findCommandStartIndex(args);
|
|
163
185
|
const command = commandStartIndex === undefined ? undefined : args[commandStartIndex];
|
|
164
186
|
return args.some((token, index) => {
|
|
165
|
-
if (token
|
|
187
|
+
if (token.startsWith(`${flag}=`))
|
|
188
|
+
return flag === "--restore";
|
|
189
|
+
if (token !== flag)
|
|
166
190
|
return false;
|
|
167
191
|
if (flag === "--auto-connect")
|
|
168
192
|
return isBooleanFlagEnabled(args, flag);
|
|
169
|
-
if (flag === "--restore" && token === "--restore" && optionalGlobalValueFlagConsumesNext(flag, args[index + 1]))
|
|
170
|
-
return true;
|
|
171
193
|
if (flag === "--state" && command === "wait" && commandStartIndex !== undefined && index > commandStartIndex)
|
|
172
194
|
return false;
|
|
173
195
|
return true;
|
|
@@ -12,7 +12,9 @@ const LOCK_TICKET_FILE = "ticket.json";
|
|
|
12
12
|
function getCoordinationDirectory(platform = process.platform) {
|
|
13
13
|
if (platform !== "win32") {
|
|
14
14
|
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
15
|
-
return
|
|
15
|
+
return platform === "android"
|
|
16
|
+
? join(tmpdir(), `pi-agent-browser-policy${uid === undefined ? "" : `-${uid}`}`)
|
|
17
|
+
: `/tmp/pi-agent-browser-policy${uid === undefined ? "" : `-${uid}`}`;
|
|
16
18
|
}
|
|
17
19
|
const user = process.env.USERNAME ?? process.env.USER ?? "unknown";
|
|
18
20
|
const suffix = createHash("sha256").update(user).digest("hex").slice(0, 12);
|
|
@@ -48,9 +50,6 @@ function getPolicyLockDigest(sessionName, namespace) {
|
|
|
48
50
|
export function getManagedSessionPolicyLockPath(sessionName, namespace) {
|
|
49
51
|
return join(getCoordinationDirectory(), `.pi-agent-browser-policy-${getPolicyLockDigest(sessionName, namespace)}.lock-v3`);
|
|
50
52
|
}
|
|
51
|
-
export function getLegacyManagedSessionPolicyLockPath(sessionName, namespace) {
|
|
52
|
-
return join(getCoordinationDirectory(), `.pi-agent-browser-policy-${getPolicyLockDigest(sessionName, namespace)}.lock-v2`);
|
|
53
|
-
}
|
|
54
53
|
function parseOwner(content) {
|
|
55
54
|
if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
|
|
56
55
|
return undefined;
|
|
@@ -67,22 +66,6 @@ function parseOwner(content) {
|
|
|
67
66
|
return undefined;
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
|
-
function parseLegacyBridgeOwner(content) {
|
|
71
|
-
if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
|
|
72
|
-
return undefined;
|
|
73
|
-
try {
|
|
74
|
-
const parsed = JSON.parse(content);
|
|
75
|
-
return (parsed.version === 2 || parsed.version === 3)
|
|
76
|
-
&& Number.isSafeInteger(parsed.pid) && (parsed.pid ?? 0) > 0
|
|
77
|
-
&& typeof parsed.startIdentity === "string" && parsed.startIdentity.length > 0
|
|
78
|
-
&& typeof parsed.token === "string" && parsed.token.length > 0
|
|
79
|
-
? parsed
|
|
80
|
-
: undefined;
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
69
|
function parseTicket(content, token) {
|
|
87
70
|
if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
|
|
88
71
|
return undefined;
|
|
@@ -137,26 +120,6 @@ async function readClaim(path) {
|
|
|
137
120
|
return undefined;
|
|
138
121
|
}
|
|
139
122
|
}
|
|
140
|
-
async function readLegacyBridgeOwner(path) {
|
|
141
|
-
try {
|
|
142
|
-
const directory = await lstat(path);
|
|
143
|
-
const ownerPath = join(path, LOCK_OWNER_FILE);
|
|
144
|
-
const ownerEntry = await lstat(ownerPath);
|
|
145
|
-
if (!directory.isDirectory() || directory.isSymbolicLink() || !ownerEntry.isFile() || ownerEntry.isSymbolicLink())
|
|
146
|
-
return undefined;
|
|
147
|
-
if (ownerEntry.size > POLICY_LOCK_MAX_BYTES)
|
|
148
|
-
return undefined;
|
|
149
|
-
if (process.platform !== "win32") {
|
|
150
|
-
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
151
|
-
if (uid === undefined || directory.uid !== uid || ownerEntry.uid !== uid || (directory.mode & 0o077) !== 0 || (ownerEntry.mode & 0o177) !== 0)
|
|
152
|
-
return undefined;
|
|
153
|
-
}
|
|
154
|
-
return parseLegacyBridgeOwner(await readFile(ownerPath, "utf8"));
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
return undefined;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
123
|
async function readClaims(basePath) {
|
|
161
124
|
const directory = dirname(basePath);
|
|
162
125
|
const prefix = `${basename(basePath)}.claim-`;
|
|
@@ -223,28 +186,6 @@ async function removeClaimOwnedBy(path, token) {
|
|
|
223
186
|
await rm(movedPath, { force: true, recursive: true });
|
|
224
187
|
return true;
|
|
225
188
|
}
|
|
226
|
-
async function removeLegacyBridgeOwnedBy(path, token) {
|
|
227
|
-
const current = await readLegacyBridgeOwner(path);
|
|
228
|
-
if (current?.version !== 3 || current.token !== token)
|
|
229
|
-
return false;
|
|
230
|
-
const movedPath = join(dirname(path), `.pi-agent-browser-policy-bridge-remove-${token}-${randomUUID()}`);
|
|
231
|
-
try {
|
|
232
|
-
await rename(path, movedPath);
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
return error.code === "ENOENT";
|
|
236
|
-
}
|
|
237
|
-
const moved = await readLegacyBridgeOwner(movedPath);
|
|
238
|
-
if (moved?.version !== 3 || moved.token !== token) {
|
|
239
|
-
try {
|
|
240
|
-
await rename(movedPath, path);
|
|
241
|
-
}
|
|
242
|
-
catch { }
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
await rm(movedPath, { force: true, recursive: true });
|
|
246
|
-
return true;
|
|
247
|
-
}
|
|
248
189
|
async function cleanDeadPolicyArtifacts(directory) {
|
|
249
190
|
let names;
|
|
250
191
|
try {
|
|
@@ -254,72 +195,13 @@ async function cleanDeadPolicyArtifacts(directory) {
|
|
|
254
195
|
return;
|
|
255
196
|
}
|
|
256
197
|
for (const name of names.filter((candidate) => candidate.startsWith(".pi-agent-browser-policy-remove-")
|
|
257
|
-
|| candidate.startsWith(".pi-agent-browser-policy-bridge-remove-")
|
|
258
|
-
|| candidate.includes(".lock-v2.bridge-candidate-")
|
|
259
|
-
|| candidate.includes(".lock-v2.candidate-")
|
|
260
198
|
|| candidate.includes(".lock-v3.candidate-"))) {
|
|
261
199
|
const path = join(directory, name);
|
|
262
|
-
const
|
|
263
|
-
if (
|
|
200
|
+
const claim = await readClaim(path);
|
|
201
|
+
if (claim && await ownerAlive(claim.owner) === false)
|
|
264
202
|
await rm(path, { force: true, recursive: true }).catch(() => undefined);
|
|
265
203
|
}
|
|
266
204
|
}
|
|
267
|
-
async function hasLegacyV2Contender(path) {
|
|
268
|
-
try {
|
|
269
|
-
const prefix = `${basename(path)}.candidate-`;
|
|
270
|
-
return (await readdir(dirname(path))).some((name) => name.startsWith(prefix));
|
|
271
|
-
}
|
|
272
|
-
catch {
|
|
273
|
-
return undefined;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
async function acquireLegacyPolicyBridge(options) {
|
|
277
|
-
const path = getLegacyManagedSessionPolicyLockPath(options.sessionName, options.namespace);
|
|
278
|
-
const candidatePath = `${path}.bridge-candidate-${options.owner.token}`;
|
|
279
|
-
try {
|
|
280
|
-
await mkdir(candidatePath, { mode: 0o700 });
|
|
281
|
-
await writeFile(join(candidatePath, LOCK_OWNER_FILE), JSON.stringify(options.owner), { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
282
|
-
while (!options.signal?.aborted) {
|
|
283
|
-
const legacyContender = await hasLegacyV2Contender(path);
|
|
284
|
-
if (legacyContender === undefined)
|
|
285
|
-
return undefined;
|
|
286
|
-
if (legacyContender) {
|
|
287
|
-
if (Date.now() >= options.deadline)
|
|
288
|
-
return undefined;
|
|
289
|
-
await waitForRetry(options.signal);
|
|
290
|
-
continue;
|
|
291
|
-
}
|
|
292
|
-
try {
|
|
293
|
-
await rename(candidatePath, path);
|
|
294
|
-
const installed = await readLegacyBridgeOwner(path);
|
|
295
|
-
return installed?.version === 3 && installed.token === options.owner.token
|
|
296
|
-
? { release: async () => { await removeLegacyBridgeOwnedBy(path, options.owner.token); } }
|
|
297
|
-
: undefined;
|
|
298
|
-
}
|
|
299
|
-
catch (error) {
|
|
300
|
-
if (!["EACCES", "EEXIST", "ENOTEMPTY", "EPERM"].includes(error.code ?? ""))
|
|
301
|
-
return undefined;
|
|
302
|
-
}
|
|
303
|
-
const observed = await readLegacyBridgeOwner(path);
|
|
304
|
-
if (!observed)
|
|
305
|
-
return undefined;
|
|
306
|
-
if (observed.version === 3 && await ownerAlive(observed) === false) {
|
|
307
|
-
if (await removeLegacyBridgeOwnedBy(path, observed.token))
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
if (Date.now() >= options.deadline)
|
|
311
|
-
return undefined;
|
|
312
|
-
await waitForRetry(options.signal);
|
|
313
|
-
}
|
|
314
|
-
return undefined;
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
return undefined;
|
|
318
|
-
}
|
|
319
|
-
finally {
|
|
320
|
-
await rm(candidatePath, { force: true, recursive: true }).catch(() => undefined);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
205
|
function claimPrecedes(left, right) {
|
|
324
206
|
if (left.ticket === null)
|
|
325
207
|
return true;
|
|
@@ -356,7 +238,6 @@ export async function acquireManagedSessionPolicyLock(options) {
|
|
|
356
238
|
const candidatePath = `${basePath}.candidate-${token}`;
|
|
357
239
|
const claimPath = `${basePath}.claim-${token}`;
|
|
358
240
|
let claimPublished = false;
|
|
359
|
-
let legacyBridge;
|
|
360
241
|
let lockAcquired = false;
|
|
361
242
|
try {
|
|
362
243
|
await mkdir(candidatePath, { mode: 0o700 });
|
|
@@ -395,20 +276,8 @@ export async function acquireManagedSessionPolicyLock(options) {
|
|
|
395
276
|
}
|
|
396
277
|
if (!blocked) {
|
|
397
278
|
await cleanDeadPolicyArtifacts(directory);
|
|
398
|
-
legacyBridge = await acquireLegacyPolicyBridge({
|
|
399
|
-
deadline,
|
|
400
|
-
owner,
|
|
401
|
-
signal: options.signal,
|
|
402
|
-
sessionName: options.sessionName,
|
|
403
|
-
namespace: options.namespace,
|
|
404
|
-
});
|
|
405
|
-
if (!legacyBridge)
|
|
406
|
-
return undefined;
|
|
407
279
|
lockAcquired = true;
|
|
408
|
-
return { release: async () => {
|
|
409
|
-
await legacyBridge?.release();
|
|
410
|
-
await removeClaimOwnedBy(claimPath, token);
|
|
411
|
-
} };
|
|
280
|
+
return { release: async () => { await removeClaimOwnedBy(claimPath, token); } };
|
|
412
281
|
}
|
|
413
282
|
if (Date.now() >= deadline)
|
|
414
283
|
return undefined;
|
|
@@ -421,8 +290,6 @@ export async function acquireManagedSessionPolicyLock(options) {
|
|
|
421
290
|
}
|
|
422
291
|
finally {
|
|
423
292
|
await rm(candidatePath, { force: true, recursive: true }).catch(() => undefined);
|
|
424
|
-
if (!lockAcquired)
|
|
425
|
-
await legacyBridge?.release();
|
|
426
293
|
if (claimPublished && !lockAcquired)
|
|
427
294
|
await removeClaimOwnedBy(claimPath, token);
|
|
428
295
|
}
|
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import {
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { extractCommandTokens, parseCommandInfo } from "./argv-descriptor.js";
|
|
2
|
+
import { extractUpstreamCommandTokens, parseCommandInfo } from "./argv-descriptor.js";
|
|
5
3
|
import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, extractRequestedRestoreKey, getAgentBrowserSessionIdentityKey, isUpstreamEnvFlagEnabled, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
|
|
6
4
|
import { hasLaunchScopedFlagToken, MANAGED_RESTORE_INCOMPATIBLE_BOOLEAN_ENVS, MANAGED_RESTORE_INCOMPATIBLE_ENVS, MANAGED_RESTORE_INCOMPATIBLE_FLAGS, } from "./launch-scoped-flags.js";
|
|
7
|
-
import { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreProtectedStorageEnv, hasManagedSessionRestoreProjectIdentity, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
5
|
+
import { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreScope, getManagedSessionRestoreProtectedStorageEnv, hasManagedSessionRestoreProjectIdentity, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
8
6
|
import { parseUserBatchStdin } from "./orchestration/batch-stdin.js";
|
|
9
|
-
import {
|
|
10
|
-
export { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure } from "./managed-session-storage.js";
|
|
7
|
+
import { getAgentBrowserProcessEnvironment } from "./process-environment.js";
|
|
8
|
+
export { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreScope } from "./managed-session-storage.js";
|
|
11
9
|
export { pruneOwnedManagedSessionRestoreSnapshots } from "./managed-session-snapshots.js";
|
|
12
10
|
const AGENT_BROWSER_CONFIG_ENV = "AGENT_BROWSER_CONFIG";
|
|
13
11
|
const AGENT_BROWSER_RESTORE_ENV = "AGENT_BROWSER_RESTORE";
|
|
14
12
|
const MANAGED_SESSION_RESTORE_ENV = "PI_AGENT_BROWSER_MANAGED_SESSION_RESTORE";
|
|
15
13
|
export const MANAGED_SESSION_NAME_PREFIX = "piab-";
|
|
16
|
-
const MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT = "{}\n";
|
|
17
|
-
const MANAGED_SESSION_RESTORE_EMPTY_CONFIG_NAME = ".pi-agent-browser-managed-restore-config-v1.json";
|
|
18
14
|
const MANAGED_SESSION_RESTORE_SPAWN_PINNED_ENVS = new Set([AGENT_BROWSER_CONFIG_ENV, AGENT_BROWSER_RESTORE_ENV, "AGENT_BROWSER_NAMESPACE"]);
|
|
19
|
-
let managedSessionRestoreEmptyConfigPath;
|
|
20
|
-
let managedSessionRestoreEmptyConfigPromise;
|
|
21
15
|
function isDisabledEnvFlag(value) {
|
|
22
16
|
if (value === undefined)
|
|
23
17
|
return false;
|
|
@@ -61,7 +55,7 @@ export class ManagedSessionRestoreState {
|
|
|
61
55
|
if (sessionName)
|
|
62
56
|
this.#daemonRestoreKeys.set(getAgentBrowserSessionIdentityKey(sessionName, namespace), restoreKey);
|
|
63
57
|
}
|
|
64
|
-
replace(identities, options = {}) {
|
|
58
|
+
replace(identities = [], options = {}) {
|
|
65
59
|
if (!options.preserveDaemonRestoreKeys)
|
|
66
60
|
this.#daemonRestoreKeys.clear();
|
|
67
61
|
this.#disabled.clear();
|
|
@@ -73,6 +67,9 @@ const ownedManagedSessionStorage = new AsyncLocalStorage();
|
|
|
73
67
|
export async function withOwnedManagedSessionContext(context, run) {
|
|
74
68
|
return await ownedManagedSessionStorage.run(context, run);
|
|
75
69
|
}
|
|
70
|
+
export function getOwnedManagedSessionRestoreKey() {
|
|
71
|
+
return ownedManagedSessionStorage.getStore()?.restoreKey;
|
|
72
|
+
}
|
|
76
73
|
export function resolveOwnedManagedSessionContext(options) {
|
|
77
74
|
const namespace = canonicalizeAgentBrowserNamespace(options.namespace);
|
|
78
75
|
const currentNamespace = canonicalizeAgentBrowserNamespace(options.currentManagedSessionNamespace);
|
|
@@ -96,33 +93,18 @@ function ownedContextMatches(sessionName, namespace) {
|
|
|
96
93
|
export function isOwnedManagedSessionTarget(args) {
|
|
97
94
|
return ownedContextMatches(extractExplicitSessionName(args), extractExplicitNamespace(args)) !== undefined;
|
|
98
95
|
}
|
|
99
|
-
function pathExistsOrIsUnreadable(path) {
|
|
100
|
-
try {
|
|
101
|
-
lstatSync(path);
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
return error.code !== "ENOENT";
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
96
|
function hasExplicitConfigArg(args) {
|
|
109
97
|
return scanUpstreamGlobalFlagOccurrences(args, "--config").length > 0;
|
|
110
98
|
}
|
|
111
99
|
function closesBrowserSession(args) {
|
|
112
100
|
return ["close", "exit", "quit"].includes(parseCommandInfo(args).command ?? "");
|
|
113
101
|
}
|
|
114
|
-
export function
|
|
115
|
-
|
|
116
|
-
return true;
|
|
117
|
-
const paths = [join(cwd, "agent-browser.json")];
|
|
118
|
-
const home = resolveManagedSessionRestoreHome(parentEnv, platform);
|
|
119
|
-
if (home)
|
|
120
|
-
paths.push(join(home, ".agent-browser", "config.json"));
|
|
121
|
-
return paths.some(pathExistsOrIsUnreadable);
|
|
102
|
+
export function agentBrowserExplicitConfigIsPresent(parentEnv = getAgentBrowserProcessEnvironment(), args = []) {
|
|
103
|
+
return hasExplicitConfigArg(args) || hasUpstreamEnvValue(parentEnv, AGENT_BROWSER_CONFIG_ENV);
|
|
122
104
|
}
|
|
123
|
-
/**
|
|
124
|
-
export function agentBrowserConfigBlocksManagedRestore(
|
|
125
|
-
return !resolveManagedSessionRestoreHome(parentEnv, platform) ||
|
|
105
|
+
/** Caller-selected upstream config disables the wrapper's automatic restore injection without blocking that config. */
|
|
106
|
+
export function agentBrowserConfigBlocksManagedRestore(_cwd, parentEnv = getAgentBrowserProcessEnvironment(), args = [], platform = process.platform) {
|
|
107
|
+
return !resolveManagedSessionRestoreHome(parentEnv, platform) || agentBrowserExplicitConfigIsPresent(parentEnv, args);
|
|
126
108
|
}
|
|
127
109
|
function omitWrapperInjectedUserAgent(args, enabled) {
|
|
128
110
|
if (!enabled)
|
|
@@ -131,7 +113,7 @@ function omitWrapperInjectedUserAgent(args, enabled) {
|
|
|
131
113
|
return index < 0 ? args : [...args.slice(0, index), ...args.slice(index + 2)];
|
|
132
114
|
}
|
|
133
115
|
function batchHasManagedSessionRestoreConflict(args, stdin) {
|
|
134
|
-
const [command, ...commandArgs] =
|
|
116
|
+
const [command, ...commandArgs] = extractUpstreamCommandTokens(args);
|
|
135
117
|
if (command !== "batch")
|
|
136
118
|
return false;
|
|
137
119
|
if (commandArgs.some((token) => token !== "--bail"))
|
|
@@ -142,7 +124,7 @@ function batchHasManagedSessionRestoreConflict(args, stdin) {
|
|
|
142
124
|
return parsed.steps.some((step) => ["connect", "batch"].includes(parseCommandInfo(step).command ?? ""));
|
|
143
125
|
}
|
|
144
126
|
function hasManagedSessionRestoreLaunchConflict(options) {
|
|
145
|
-
const parentEnv = options.parentEnv ??
|
|
127
|
+
const parentEnv = options.parentEnv ?? getAgentBrowserProcessEnvironment();
|
|
146
128
|
const effectiveEnv = { ...parentEnv, ...options.env };
|
|
147
129
|
const args = omitWrapperInjectedUserAgent(options.args, options.wrapperInjectedUserAgent);
|
|
148
130
|
if (MANAGED_RESTORE_INCOMPATIBLE_ENVS.some((name) => hasUpstreamEnvValue(effectiveEnv, name)))
|
|
@@ -156,7 +138,7 @@ function hasManagedSessionRestoreLaunchConflict(options) {
|
|
|
156
138
|
return hasExplicitConfigArg(args) || hasUpstreamEnvValue({ ...parentEnv, ...options.env }, AGENT_BROWSER_CONFIG_ENV);
|
|
157
139
|
}
|
|
158
140
|
function managedSessionRestoreOptedOut(options) {
|
|
159
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
141
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
160
142
|
return isDisabledEnvFlag(effectiveEnv[MANAGED_SESSION_RESTORE_ENV]);
|
|
161
143
|
}
|
|
162
144
|
function isManagedSessionRestoreIncompatible(options) {
|
|
@@ -164,7 +146,7 @@ function isManagedSessionRestoreIncompatible(options) {
|
|
|
164
146
|
return true;
|
|
165
147
|
if (managedSessionRestoreOptedOut(options))
|
|
166
148
|
return false;
|
|
167
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
149
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
168
150
|
const args = omitWrapperInjectedUserAgent(options.args, options.wrapperInjectedUserAgent);
|
|
169
151
|
if (options.cwd && !hasManagedSessionRestoreProjectIdentity(options.cwd))
|
|
170
152
|
return true;
|
|
@@ -173,7 +155,7 @@ function isManagedSessionRestoreIncompatible(options) {
|
|
|
173
155
|
return !ensureManagedSessionRestoreStorageIsSecure(effectiveEnv, process.platform, extractExplicitNamespace(args));
|
|
174
156
|
}
|
|
175
157
|
function resolveManagedSessionRestorePolicy(options) {
|
|
176
|
-
const parentEnv = options.parentEnv ??
|
|
158
|
+
const parentEnv = options.parentEnv ?? getAgentBrowserProcessEnvironment();
|
|
177
159
|
const sessionName = extractExplicitSessionName(options.args);
|
|
178
160
|
const namespace = extractExplicitNamespace(options.args);
|
|
179
161
|
const ownedContext = ownedContextMatches(sessionName, namespace);
|
|
@@ -204,7 +186,7 @@ export function getOwnedManagedSessionCompatibilityEnv(options) {
|
|
|
204
186
|
const { owned, ownedContext } = resolveManagedSessionRestorePolicy(options);
|
|
205
187
|
if (!owned || !ownedContext)
|
|
206
188
|
return {};
|
|
207
|
-
const parentEnv = options.parentEnv ??
|
|
189
|
+
const parentEnv = options.parentEnv ?? getAgentBrowserProcessEnvironment();
|
|
208
190
|
const callEnv = options.env ?? {};
|
|
209
191
|
const explicitRawInterval = Object.hasOwn(callEnv, "AGENT_BROWSER_AUTOSAVE_INTERVAL_MS")
|
|
210
192
|
? callEnv.AGENT_BROWSER_AUTOSAVE_INTERVAL_MS
|
|
@@ -215,87 +197,13 @@ export function getOwnedManagedSessionCompatibilityEnv(options) {
|
|
|
215
197
|
...(ownedContext.headedManagedAutosaveInterval !== undefined && !explicitIntervalMatches ? { AGENT_BROWSER_AUTOSAVE_INTERVAL_MS: ownedContext.headedManagedAutosaveInterval } : {}),
|
|
216
198
|
};
|
|
217
199
|
}
|
|
218
|
-
export function shouldOmitOwnedManagedSessionRestoreEnv(options) {
|
|
219
|
-
return resolveManagedSessionRestorePolicy(options).owned && closesBrowserSession(options.args);
|
|
220
|
-
}
|
|
221
|
-
export function canonicalizeOwnedManagedSessionCloseArgs(options, force = false) {
|
|
222
|
-
const policy = resolveManagedSessionRestorePolicy(options);
|
|
223
|
-
if (!policy.owned || !closesBrowserSession(options.args))
|
|
224
|
-
return options.args;
|
|
225
|
-
const sessionName = policy.ownedContext?.sessionName ?? policy.sessionName;
|
|
226
|
-
if (!sessionName)
|
|
227
|
-
return options.args;
|
|
228
|
-
const namespace = canonicalizeAgentBrowserNamespace(policy.ownedContext?.namespace ?? policy.namespace) ?? "";
|
|
229
|
-
const command = options.args.at(-1);
|
|
230
|
-
const prefix = options.args.slice(0, -1);
|
|
231
|
-
const safePrefixes = [
|
|
232
|
-
["--session", sessionName],
|
|
233
|
-
["--json", "--session", sessionName],
|
|
234
|
-
["--namespace", namespace, "--session", sessionName],
|
|
235
|
-
["--json", "--namespace", namespace, "--session", sessionName],
|
|
236
|
-
];
|
|
237
|
-
if (!force && command && ["close", "exit", "quit"].includes(command)
|
|
238
|
-
&& safePrefixes.some((candidate) => candidate.length === prefix.length && candidate.every((token, index) => token === prefix[index]))) {
|
|
239
|
-
return options.args;
|
|
240
|
-
}
|
|
241
|
-
return ["--json", "--namespace", namespace, "--session", sessionName, "close"];
|
|
242
|
-
}
|
|
243
|
-
export function cleanupManagedSessionRestoreConfig() {
|
|
244
|
-
if (managedSessionRestoreEmptyConfigPath) {
|
|
245
|
-
try {
|
|
246
|
-
unlinkSync(managedSessionRestoreEmptyConfigPath);
|
|
247
|
-
}
|
|
248
|
-
catch { }
|
|
249
|
-
}
|
|
250
|
-
managedSessionRestoreEmptyConfigPath = undefined;
|
|
251
|
-
managedSessionRestoreEmptyConfigPromise = undefined;
|
|
252
|
-
}
|
|
253
|
-
async function ensureManagedSessionRestoreEmptyConfig(platform) {
|
|
254
|
-
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
255
|
-
try {
|
|
256
|
-
managedSessionRestoreEmptyConfigPromise ??= writeSecureTempFile({
|
|
257
|
-
content: MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT,
|
|
258
|
-
prefix: MANAGED_SESSION_RESTORE_EMPTY_CONFIG_NAME.replace(/\.json$/, ""),
|
|
259
|
-
suffix: ".json",
|
|
260
|
-
}).then((path) => {
|
|
261
|
-
if (platform !== "win32")
|
|
262
|
-
chmodSync(path, 0o400);
|
|
263
|
-
managedSessionRestoreEmptyConfigPath = path;
|
|
264
|
-
return path;
|
|
265
|
-
});
|
|
266
|
-
const path = await managedSessionRestoreEmptyConfigPromise;
|
|
267
|
-
let entry = lstatSync(path);
|
|
268
|
-
if (entry.isSymbolicLink() || !entry.isFile())
|
|
269
|
-
throw new Error("Managed restore config is not a regular file.");
|
|
270
|
-
if (platform !== "win32" && (entry.mode & 0o777) !== 0o400) {
|
|
271
|
-
chmodSync(path, 0o400);
|
|
272
|
-
entry = lstatSync(path);
|
|
273
|
-
}
|
|
274
|
-
if (entry.isSymbolicLink() || !entry.isFile() || (platform !== "win32" && (entry.mode & 0o777) !== 0o400))
|
|
275
|
-
throw new Error("Managed restore config permissions are unsafe.");
|
|
276
|
-
if (readFileSync(path, "utf8") !== MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT)
|
|
277
|
-
throw new Error("Managed restore config content changed.");
|
|
278
|
-
return path;
|
|
279
|
-
}
|
|
280
|
-
catch {
|
|
281
|
-
cleanupManagedSessionRestoreConfig();
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return undefined;
|
|
285
|
-
}
|
|
286
|
-
export async function getManagedSessionRestoreConfigEnv(restoreEnv, pinForOwnedClose = false) {
|
|
287
|
-
if (restoreEnv[AGENT_BROWSER_RESTORE_ENV] === undefined && !pinForOwnedClose)
|
|
288
|
-
return {};
|
|
289
|
-
const path = await ensureManagedSessionRestoreEmptyConfig(process.platform);
|
|
290
|
-
return path ? { [AGENT_BROWSER_CONFIG_ENV]: path } : undefined;
|
|
291
|
-
}
|
|
292
200
|
export function getManagedSessionRestoreProtectedEnv(options, restoreEnv) {
|
|
293
201
|
const { ownedContext } = resolveManagedSessionRestorePolicy(options);
|
|
294
202
|
if (restoreEnv[AGENT_BROWSER_RESTORE_ENV] === undefined)
|
|
295
203
|
return {};
|
|
296
204
|
if (ownedContext?.protectedStorageEnv)
|
|
297
205
|
return { ...ownedContext.protectedStorageEnv };
|
|
298
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
206
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
299
207
|
return getManagedSessionRestoreProtectedStorageEnv(true, effectiveEnv);
|
|
300
208
|
}
|
|
301
209
|
export function validateManagedSessionRestoreContextForSpawn(options) {
|
|
@@ -303,7 +211,7 @@ export function validateManagedSessionRestoreContextForSpawn(options) {
|
|
|
303
211
|
if (closesBrowserSession(options.args) || ownedContext?.restoreDecision !== "enabled")
|
|
304
212
|
return true;
|
|
305
213
|
const ownedCwd = ownedContext.cwd ?? options.cwd;
|
|
306
|
-
if (!ownedContext.restoreKey || createManagedSessionRestoreKey(ownedCwd) !== ownedContext.restoreKey || !hasManagedSessionRestoreProjectIdentity(ownedCwd))
|
|
214
|
+
if (!ownedContext.restoreKey || !ownedContext.restoreScope || createManagedSessionRestoreKey(ownedCwd, ownedContext.restoreScope) !== ownedContext.restoreKey || !hasManagedSessionRestoreProjectIdentity(ownedCwd))
|
|
307
215
|
return false;
|
|
308
216
|
const effectiveEnv = { ...parentEnv, ...options.env };
|
|
309
217
|
if (isDisabledEnvFlag(effectiveEnv[MANAGED_SESSION_RESTORE_ENV]))
|
|
@@ -330,7 +238,7 @@ export function getManagedSessionRestoreEnv(options) {
|
|
|
330
238
|
return {};
|
|
331
239
|
if (restoreState.isDisabled(sessionName, namespace) || !sessionName)
|
|
332
240
|
return {};
|
|
333
|
-
return { [AGENT_BROWSER_RESTORE_ENV]: createManagedSessionRestoreKey(options.cwd) };
|
|
241
|
+
return { [AGENT_BROWSER_RESTORE_ENV]: createManagedSessionRestoreKey(options.cwd, getManagedSessionRestoreScope(sessionName)) };
|
|
334
242
|
}
|
|
335
243
|
/** Commit sticky suppression only after an owned-context subprocess has actually started. */
|
|
336
244
|
export function commitManagedSessionRestoreSuppression(options) {
|
|
@@ -375,8 +283,9 @@ export function buildOwnedManagedSessionRestoreContext(options) {
|
|
|
375
283
|
const projectIdentityAvailable = !optedOut && hasManagedSessionRestoreProjectIdentity(ownedCwd);
|
|
376
284
|
const incompatible = !optedOut && isManagedSessionRestoreIncompatible(policyOptions);
|
|
377
285
|
const enabled = !optedOut && !incompatible;
|
|
378
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
379
|
-
const
|
|
286
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
287
|
+
const restoreScope = getManagedSessionRestoreScope(owned.sessionName);
|
|
288
|
+
const restoreKey = projectIdentityAvailable ? createManagedSessionRestoreKey(ownedCwd, restoreScope) : undefined;
|
|
380
289
|
return {
|
|
381
290
|
...owned,
|
|
382
291
|
compatibilityUserAgent: options.compatibilityUserAgent,
|
|
@@ -386,6 +295,7 @@ export function buildOwnedManagedSessionRestoreContext(options) {
|
|
|
386
295
|
protectedStorageEnv: enabled ? getManagedSessionRestoreProtectedStorageEnv(true, effectiveEnv) : undefined,
|
|
387
296
|
restoreDecision: optedOut ? "opted-out" : incompatible ? "incompatible" : "enabled",
|
|
388
297
|
restoreKey,
|
|
298
|
+
restoreScope,
|
|
389
299
|
restoreSuppressed: optedOut || incompatible,
|
|
390
300
|
};
|
|
391
301
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { chmodSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
4
|
-
import { createManagedSessionRestoreKey, directoryContainsSymlink, ensureManagedSessionRestoreStorageIsSecure, ensureOwnerOnlyDirectory, getManagedRestoreSessionsDirectory,
|
|
4
|
+
import { createManagedSessionRestoreKey, directoryContainsSymlink, ensureManagedSessionRestoreStorageIsSecure, hasManagedSessionRestoreProjectIdentity, ensureOwnerOnlyDirectory, getManagedRestoreSessionsDirectory, isManagedSessionRestoreKey, resolveManagedSessionRestoreCheckoutRoot, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
5
5
|
const OWNED_RESTORE_SNAPSHOT_FAMILIES_TO_KEEP = 2;
|
|
6
6
|
const OWNED_RESTORE_SNAPSHOT_MAX_RECORDS = 256;
|
|
7
7
|
const OWNED_RESTORE_SNAPSHOT_RECORD_MAX_BYTES = 16 * 1_024;
|
|
@@ -344,9 +344,7 @@ export function pruneOwnedManagedSessionRestoreSnapshots(options) {
|
|
|
344
344
|
if (!changed)
|
|
345
345
|
break;
|
|
346
346
|
}
|
|
347
|
-
const protectedRestoreKey =
|
|
348
|
-
? createManagedSessionRestoreKey(options.cwd)
|
|
349
|
-
: restoreKey;
|
|
347
|
+
const protectedRestoreKey = restoreKey;
|
|
350
348
|
if (!lineage)
|
|
351
349
|
return removed;
|
|
352
350
|
removed += pruneExpiredOtherRestoreKeys({
|