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,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Retain only wrapper-owned managed-restore snapshots after successful close operations.
|
|
3
|
+
* Responsibilities: Persist atomic per-snapshot ownership records, self-heal malformed records, and apply per-key plus superseded-generation retention without deleting unproven paths.
|
|
4
|
+
* Scope: Snapshot ownership and pruning only; restore eligibility and daemon policy live in sibling modules.
|
|
5
|
+
*/
|
|
6
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
7
|
+
import { chmodSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
9
|
+
import { createManagedSessionRestoreKey, directoryContainsSymlink, ensureManagedSessionRestoreStorageIsSecure, ensureOwnerOnlyDirectory, getManagedRestoreSessionsDirectory, hasManagedSessionRestoreProjectIdentity, isManagedSessionRestoreKey, resolveManagedSessionRestoreCheckoutRoot, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
10
|
+
const OWNED_RESTORE_SNAPSHOT_FAMILIES_TO_KEEP = 2;
|
|
11
|
+
const OWNED_RESTORE_SNAPSHOT_MAX_RECORDS = 256;
|
|
12
|
+
const OWNED_RESTORE_SNAPSHOT_RECORD_MAX_BYTES = 16 * 1_024;
|
|
13
|
+
const OWNED_RESTORE_SNAPSHOT_MANIFEST_PREFIX = ".pi-agent-browser-owned-snapshots-v2";
|
|
14
|
+
const OWNED_RESTORE_SNAPSHOT_LINEAGE_DIRECTORY = ".checkout-lineage-v1";
|
|
15
|
+
const OWNED_RESTORE_SNAPSHOT_TEMP_MAX_AGE_MS = 30_000;
|
|
16
|
+
const OWNED_RESTORE_SNAPSHOT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1_000;
|
|
17
|
+
function pathExistsOrIsUnreadable(path) {
|
|
18
|
+
try {
|
|
19
|
+
lstatSync(path);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return error.code !== "ENOENT";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function validateOwnedSnapshotPath(options) {
|
|
27
|
+
if (!isAbsolute(options.path))
|
|
28
|
+
return undefined;
|
|
29
|
+
let path;
|
|
30
|
+
try {
|
|
31
|
+
path = realpathSync(options.path);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const directory = getManagedRestoreSessionsDirectory(options.home, options.namespace);
|
|
37
|
+
const name = basename(path);
|
|
38
|
+
if (dirname(path) !== directory || !name.startsWith(`${options.restoreKey}-`))
|
|
39
|
+
return undefined;
|
|
40
|
+
if (!/\.json(?:\.enc)?$/.test(name))
|
|
41
|
+
return undefined;
|
|
42
|
+
try {
|
|
43
|
+
const entry = lstatSync(path);
|
|
44
|
+
return !entry.isSymbolicLink() && entry.isFile() ? path : undefined;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function getManifestDirectory(directory, restoreKey) {
|
|
51
|
+
return join(directory, `${OWNED_RESTORE_SNAPSHOT_MANIFEST_PREFIX}-${restoreKey}`);
|
|
52
|
+
}
|
|
53
|
+
function ensureManifestDirectory(path, platform) {
|
|
54
|
+
if (platform !== "win32")
|
|
55
|
+
return ensureOwnerOnlyDirectory(path, platform) && !directoryContainsSymlink(path);
|
|
56
|
+
try {
|
|
57
|
+
mkdirSync(path, { recursive: true });
|
|
58
|
+
const entry = lstatSync(path);
|
|
59
|
+
return !entry.isSymbolicLink() && entry.isDirectory() && !directoryContainsSymlink(path);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function getCheckoutLineageHash(cwd, platform) {
|
|
66
|
+
const checkoutRoot = resolveManagedSessionRestoreCheckoutRoot(cwd, platform);
|
|
67
|
+
if (!checkoutRoot)
|
|
68
|
+
return undefined;
|
|
69
|
+
const normalizedRoot = platform === "win32" ? checkoutRoot.toLowerCase() : checkoutRoot;
|
|
70
|
+
return createHash("sha256").update(`managed-snapshot-lineage-v1:${platform}:${normalizedRoot}`).digest("hex");
|
|
71
|
+
}
|
|
72
|
+
function manifestHasLineage(directory, lineage, platform) {
|
|
73
|
+
const lineageDirectory = join(directory, OWNED_RESTORE_SNAPSHOT_LINEAGE_DIRECTORY);
|
|
74
|
+
try {
|
|
75
|
+
const directoryEntry = lstatSync(lineageDirectory);
|
|
76
|
+
if (directoryEntry.isSymbolicLink() || !directoryEntry.isDirectory() || directoryContainsSymlink(lineageDirectory))
|
|
77
|
+
return false;
|
|
78
|
+
if (platform !== "win32") {
|
|
79
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
80
|
+
if (uid === undefined || directoryEntry.uid !== uid || (directoryEntry.mode & 0o077) !== 0)
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const entry = lstatSync(join(lineageDirectory, lineage));
|
|
84
|
+
if (entry.isSymbolicLink() || !entry.isFile() || entry.size !== 0)
|
|
85
|
+
return false;
|
|
86
|
+
return platform === "win32" || (entry.mode & 0o077) === 0;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function ensureManifestLineage(directory, lineage, platform) {
|
|
93
|
+
const lineageDirectory = join(directory, OWNED_RESTORE_SNAPSHOT_LINEAGE_DIRECTORY);
|
|
94
|
+
if (!ensureManifestDirectory(lineageDirectory, platform))
|
|
95
|
+
return false;
|
|
96
|
+
const path = join(lineageDirectory, lineage);
|
|
97
|
+
try {
|
|
98
|
+
writeFileSync(path, "", { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
99
|
+
if (platform !== "win32")
|
|
100
|
+
chmodSync(path, 0o600);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error.code !== "EEXIST")
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return manifestHasLineage(directory, lineage, platform);
|
|
107
|
+
}
|
|
108
|
+
function removeManifestLineages(directory, platform) {
|
|
109
|
+
const lineageDirectory = join(directory, OWNED_RESTORE_SNAPSHOT_LINEAGE_DIRECTORY);
|
|
110
|
+
try {
|
|
111
|
+
if (!ensureManifestDirectory(lineageDirectory, platform))
|
|
112
|
+
return;
|
|
113
|
+
for (const entry of readdirSync(lineageDirectory, { withFileTypes: true })) {
|
|
114
|
+
if (entry.isFile() && /^[a-f\d]{64}$/.test(entry.name))
|
|
115
|
+
unlinkSync(join(lineageDirectory, entry.name));
|
|
116
|
+
}
|
|
117
|
+
rmdirSync(lineageDirectory);
|
|
118
|
+
}
|
|
119
|
+
catch { }
|
|
120
|
+
}
|
|
121
|
+
function getRecordPath(directory, snapshotPath) {
|
|
122
|
+
const digest = createHash("sha256").update(snapshotPath).digest("hex");
|
|
123
|
+
return join(directory, `${digest}.json`);
|
|
124
|
+
}
|
|
125
|
+
function writeRecord(directory, snapshotPath, platform) {
|
|
126
|
+
const content = JSON.stringify(snapshotPath);
|
|
127
|
+
if (Buffer.byteLength(content) > OWNED_RESTORE_SNAPSHOT_RECORD_MAX_BYTES)
|
|
128
|
+
return false;
|
|
129
|
+
const path = getRecordPath(directory, snapshotPath);
|
|
130
|
+
try {
|
|
131
|
+
const entry = lstatSync(path);
|
|
132
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
133
|
+
return false;
|
|
134
|
+
if (entry.size <= OWNED_RESTORE_SNAPSHOT_RECORD_MAX_BYTES && JSON.parse(readFileSync(path, "utf8")) === snapshotPath) {
|
|
135
|
+
if (platform !== "win32" && (entry.mode & 0o077) !== 0)
|
|
136
|
+
return false;
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
unlinkSync(path);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (error.code !== "ENOENT" && !(error instanceof SyntaxError))
|
|
143
|
+
return false;
|
|
144
|
+
try {
|
|
145
|
+
unlinkSync(path);
|
|
146
|
+
}
|
|
147
|
+
catch { }
|
|
148
|
+
}
|
|
149
|
+
const temporaryPath = join(directory, `.tmp-${process.pid}-${randomUUID()}`);
|
|
150
|
+
try {
|
|
151
|
+
writeFileSync(temporaryPath, content, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
152
|
+
renameSync(temporaryPath, path);
|
|
153
|
+
if (platform !== "win32")
|
|
154
|
+
chmodSync(path, 0o600);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
try {
|
|
159
|
+
const entry = lstatSync(path);
|
|
160
|
+
return !entry.isSymbolicLink()
|
|
161
|
+
&& entry.isFile()
|
|
162
|
+
&& entry.size <= OWNED_RESTORE_SNAPSHOT_RECORD_MAX_BYTES
|
|
163
|
+
&& (platform === "win32" || (entry.mode & 0o077) === 0)
|
|
164
|
+
&& JSON.parse(readFileSync(path, "utf8")) === snapshotPath;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
try {
|
|
172
|
+
unlinkSync(temporaryPath);
|
|
173
|
+
}
|
|
174
|
+
catch { }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function readRecord(options) {
|
|
178
|
+
try {
|
|
179
|
+
const entry = lstatSync(options.path);
|
|
180
|
+
if (entry.isSymbolicLink() || !entry.isFile() || entry.size > OWNED_RESTORE_SNAPSHOT_RECORD_MAX_BYTES)
|
|
181
|
+
return undefined;
|
|
182
|
+
if (options.platform !== "win32" && (entry.mode & 0o077) !== 0)
|
|
183
|
+
return undefined;
|
|
184
|
+
const parsed = JSON.parse(readFileSync(options.path, "utf8"));
|
|
185
|
+
if (typeof parsed !== "string" || !isAbsolute(parsed))
|
|
186
|
+
return undefined;
|
|
187
|
+
const snapshotPath = validateOwnedSnapshotPath({ home: options.home, namespace: options.namespace, path: parsed, restoreKey: options.restoreKey });
|
|
188
|
+
return snapshotPath && getRecordPath(dirname(options.path), snapshotPath) === options.path ? snapshotPath : undefined;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function pruneExpiredOtherRestoreKeys(options) {
|
|
195
|
+
let removed = 0;
|
|
196
|
+
let entries;
|
|
197
|
+
try {
|
|
198
|
+
entries = readdirSync(options.directory, { withFileTypes: true });
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
for (const entry of entries) {
|
|
204
|
+
if (!entry.isDirectory() || !entry.name.startsWith(`${OWNED_RESTORE_SNAPSHOT_MANIFEST_PREFIX}-`))
|
|
205
|
+
continue;
|
|
206
|
+
const restoreKey = entry.name.slice(`${OWNED_RESTORE_SNAPSHOT_MANIFEST_PREFIX}-`.length);
|
|
207
|
+
if (!isManagedSessionRestoreKey(restoreKey) || restoreKey === options.protectedRestoreKey)
|
|
208
|
+
continue;
|
|
209
|
+
const manifestDirectory = join(options.directory, entry.name);
|
|
210
|
+
if (!ensureManifestDirectory(manifestDirectory, options.platform))
|
|
211
|
+
continue;
|
|
212
|
+
if (!manifestHasLineage(manifestDirectory, options.lineage, options.platform))
|
|
213
|
+
continue;
|
|
214
|
+
let snapshots;
|
|
215
|
+
try {
|
|
216
|
+
snapshots = scanOwnedSnapshots({ home: options.home, manifestDirectory, namespace: options.namespace, platform: options.platform, restoreKey });
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
for (const snapshot of snapshots) {
|
|
222
|
+
if (snapshot.mtimeMs >= options.staleBefore)
|
|
223
|
+
continue;
|
|
224
|
+
try {
|
|
225
|
+
const current = lstatSync(snapshot.path);
|
|
226
|
+
if (current.isSymbolicLink() || !current.isFile() || current.mtimeMs !== snapshot.mtimeMs)
|
|
227
|
+
continue;
|
|
228
|
+
unlinkSync(snapshot.path);
|
|
229
|
+
try {
|
|
230
|
+
unlinkSync(snapshot.recordPath);
|
|
231
|
+
}
|
|
232
|
+
catch { }
|
|
233
|
+
removed += 1;
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
if (error.code === "ENOENT") {
|
|
237
|
+
try {
|
|
238
|
+
unlinkSync(snapshot.recordPath);
|
|
239
|
+
}
|
|
240
|
+
catch { }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
const remainingRecords = readdirSync(manifestDirectory).filter((name) => /^[a-f\d]{64}\.json$/.test(name));
|
|
246
|
+
if (remainingRecords.length === 0)
|
|
247
|
+
removeManifestLineages(manifestDirectory, options.platform);
|
|
248
|
+
if (readdirSync(manifestDirectory).length === 0)
|
|
249
|
+
rmdirSync(manifestDirectory);
|
|
250
|
+
}
|
|
251
|
+
catch { }
|
|
252
|
+
}
|
|
253
|
+
return removed;
|
|
254
|
+
}
|
|
255
|
+
function scanOwnedSnapshots(options) {
|
|
256
|
+
const snapshots = [];
|
|
257
|
+
for (const entry of readdirSync(options.manifestDirectory, { withFileTypes: true })) {
|
|
258
|
+
if (entry.isFile() && entry.name.startsWith(".tmp-")) {
|
|
259
|
+
const temporaryPath = join(options.manifestDirectory, entry.name);
|
|
260
|
+
try {
|
|
261
|
+
if (Date.now() - lstatSync(temporaryPath).mtimeMs > OWNED_RESTORE_SNAPSHOT_TEMP_MAX_AGE_MS)
|
|
262
|
+
unlinkSync(temporaryPath);
|
|
263
|
+
}
|
|
264
|
+
catch { }
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (!entry.isFile() || !/^[a-f\d]{64}\.json$/.test(entry.name))
|
|
268
|
+
continue;
|
|
269
|
+
const recordPath = join(options.manifestDirectory, entry.name);
|
|
270
|
+
const path = readRecord({ ...options, path: recordPath });
|
|
271
|
+
if (!path) {
|
|
272
|
+
try {
|
|
273
|
+
unlinkSync(recordPath);
|
|
274
|
+
}
|
|
275
|
+
catch { }
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
snapshots.push({ mtimeMs: lstatSync(path).mtimeMs, path, recordPath });
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
try {
|
|
283
|
+
unlinkSync(recordPath);
|
|
284
|
+
}
|
|
285
|
+
catch { }
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return snapshots.sort((left, right) => right.mtimeMs - left.mtimeMs || left.path.localeCompare(right.path));
|
|
289
|
+
}
|
|
290
|
+
/** After an owned close, expire only close-proven snapshots while retaining two fallbacks. */
|
|
291
|
+
export function pruneOwnedManagedSessionRestoreSnapshots(options) {
|
|
292
|
+
const parentEnv = options.parentEnv ?? process.env;
|
|
293
|
+
const platform = options.platform ?? process.platform;
|
|
294
|
+
const restoreKey = options.restoreKey === undefined
|
|
295
|
+
? hasManagedSessionRestoreProjectIdentity(options.cwd) ? createManagedSessionRestoreKey(options.cwd) : undefined
|
|
296
|
+
: isManagedSessionRestoreKey(options.restoreKey) ? options.restoreKey : undefined;
|
|
297
|
+
if (!restoreKey)
|
|
298
|
+
return 0;
|
|
299
|
+
const home = resolveManagedSessionRestoreHome(parentEnv, platform);
|
|
300
|
+
if (!home)
|
|
301
|
+
return 0;
|
|
302
|
+
const directory = getManagedRestoreSessionsDirectory(home, options.namespace);
|
|
303
|
+
const manifestDirectory = getManifestDirectory(directory, restoreKey);
|
|
304
|
+
const hasCurrentManifest = pathExistsOrIsUnreadable(manifestDirectory);
|
|
305
|
+
if (!ensureManagedSessionRestoreStorageIsSecure(parentEnv, platform, options.namespace))
|
|
306
|
+
return 0;
|
|
307
|
+
if ((options.statePath || hasCurrentManifest) && !ensureManifestDirectory(manifestDirectory, platform))
|
|
308
|
+
return 0;
|
|
309
|
+
const lineage = getCheckoutLineageHash(options.cwd, platform);
|
|
310
|
+
if ((options.statePath || hasCurrentManifest) && (!lineage || !ensureManifestLineage(manifestDirectory, lineage, platform)))
|
|
311
|
+
return 0;
|
|
312
|
+
if (options.statePath) {
|
|
313
|
+
const ownedPath = validateOwnedSnapshotPath({ home, namespace: options.namespace, path: options.statePath, restoreKey });
|
|
314
|
+
if (ownedPath && !writeRecord(manifestDirectory, ownedPath, platform))
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
const staleBefore = Date.now() - OWNED_RESTORE_SNAPSHOT_MAX_AGE_MS;
|
|
318
|
+
let removed = 0;
|
|
319
|
+
for (let pass = 0; (options.statePath || hasCurrentManifest) && pass <= OWNED_RESTORE_SNAPSHOT_MAX_RECORDS; pass += 1) {
|
|
320
|
+
const snapshots = scanOwnedSnapshots({ home, manifestDirectory, namespace: options.namespace, platform, restoreKey });
|
|
321
|
+
const candidates = snapshots.filter((snapshot, index) => index >= OWNED_RESTORE_SNAPSHOT_MAX_RECORDS
|
|
322
|
+
|| (index >= OWNED_RESTORE_SNAPSHOT_FAMILIES_TO_KEEP && snapshot.mtimeMs < staleBefore));
|
|
323
|
+
if (candidates.length === 0)
|
|
324
|
+
break;
|
|
325
|
+
let changed = false;
|
|
326
|
+
for (const snapshot of candidates) {
|
|
327
|
+
try {
|
|
328
|
+
const current = lstatSync(snapshot.path);
|
|
329
|
+
if (current.isSymbolicLink() || !current.isFile() || current.mtimeMs !== snapshot.mtimeMs)
|
|
330
|
+
continue;
|
|
331
|
+
unlinkSync(snapshot.path);
|
|
332
|
+
try {
|
|
333
|
+
unlinkSync(snapshot.recordPath);
|
|
334
|
+
}
|
|
335
|
+
catch { }
|
|
336
|
+
removed += 1;
|
|
337
|
+
changed = true;
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
if (error.code === "ENOENT") {
|
|
341
|
+
try {
|
|
342
|
+
unlinkSync(snapshot.recordPath);
|
|
343
|
+
}
|
|
344
|
+
catch { }
|
|
345
|
+
changed = true;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (!changed)
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
const protectedRestoreKey = hasManagedSessionRestoreProjectIdentity(options.cwd)
|
|
353
|
+
? createManagedSessionRestoreKey(options.cwd)
|
|
354
|
+
: restoreKey;
|
|
355
|
+
if (!lineage)
|
|
356
|
+
return removed;
|
|
357
|
+
removed += pruneExpiredOtherRestoreKeys({
|
|
358
|
+
directory,
|
|
359
|
+
home,
|
|
360
|
+
lineage,
|
|
361
|
+
namespace: options.namespace,
|
|
362
|
+
platform,
|
|
363
|
+
protectedRestoreKey,
|
|
364
|
+
staleBefore,
|
|
365
|
+
});
|
|
366
|
+
return removed;
|
|
367
|
+
}
|