@lesliechan721/aw 0.1.0-beta.10 → 0.1.0-beta.11
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/dist/authoring/assets/aw-config.d.mts +1 -50
- package/dist/authoring/assets/aw-config.mjs +58 -928
- package/dist/authoring/assets/aw-config.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +28 -33
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
- package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +165 -45
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/bundle-plugins.js +18 -4
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.d.ts +2 -0
- package/dist/scripts/test-plugin-scripts.js +21 -5
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +1 -1
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/commands/catalog-dev-source.d.ts +6 -3
- package/dist/src/commands/catalog-dev-source.js +96 -35
- package/dist/src/commands/catalog-dev-source.js.map +1 -1
- package/dist/src/commands/catalog-publication-inspection.d.ts +1 -1
- package/dist/src/commands/catalog-publication-inspection.js +54 -8
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-journal.js +1 -1
- package/dist/src/commands/catalog-publication-journal.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +19 -4
- package/dist/src/commands/catalog-source.js +121 -12
- package/dist/src/commands/catalog-source.js.map +1 -1
- package/dist/src/commands/catalog-store.d.ts +26 -7
- package/dist/src/commands/catalog-store.js +361 -42
- package/dist/src/commands/catalog-store.js.map +1 -1
- package/dist/src/commands/console.js +28 -17
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/operation-plan.d.ts +2 -1
- package/dist/src/commands/operation-plan.js +24 -0
- package/dist/src/commands/operation-plan.js.map +1 -1
- package/dist/src/commands/operation-planner.d.ts +2 -2
- package/dist/src/commands/operation-planner.js +11 -19
- package/dist/src/commands/operation-planner.js.map +1 -1
- package/dist/src/commands/options.d.ts +2 -2
- package/dist/src/commands/options.js +18 -4
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +2 -2
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +7 -0
- package/dist/src/commands/rebuild-command.js +769 -279
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +24 -47
- package/dist/src/commands/rebuild-integration.js +1074 -1011
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +50 -12
- package/dist/src/commands/rebuild-operations.js +207 -101
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/status-v6.d.ts +21 -2
- package/dist/src/commands/status-v6.js +163 -31
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/sync-command.d.ts +17 -0
- package/dist/src/commands/sync-command.js +45 -37
- package/dist/src/commands/sync-command.js.map +1 -1
- package/dist/src/commands/write-commands.d.ts +5 -0
- package/dist/src/commands/write-commands.js +50 -3
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/generators/local-plugin.js +14 -4
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.js +216 -85
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.js +1 -3
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/output/rebuild-progress.js +1 -3
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/materializer.js +6 -6
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/tui/catalog-generation.js +41 -1
- package/dist/src/tui/catalog-generation.js.map +1 -1
- package/dist/src/tui/catalog-sources/session.d.ts +1 -0
- package/dist/src/tui/catalog-sources/session.js +16 -3
- package/dist/src/tui/catalog-sources/session.js.map +1 -1
- package/dist/src/tui/catalog-sources/types.d.ts +3 -2
- package/dist/src/tui/components.d.ts +4 -0
- package/dist/src/tui/components.js +36 -24
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +13 -4
- package/dist/src/tui/console-app-v4.js +2067 -839
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.js +585 -33
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +152 -6
- package/dist/src/tui/controller.js +1168 -156
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/diagnostic-projection.d.ts +22 -0
- package/dist/src/tui/diagnostic-projection.js +437 -0
- package/dist/src/tui/diagnostic-projection.js.map +1 -0
- package/dist/src/tui/diagnostics.d.ts +1 -1
- package/dist/src/tui/diagnostics.js +6 -1
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/glossary.d.ts +23 -0
- package/dist/src/tui/glossary.js +42 -0
- package/dist/src/tui/glossary.js.map +1 -0
- package/dist/src/tui/index.d.ts +4 -1
- package/dist/src/tui/index.js +4 -1
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/keys.d.ts +44 -0
- package/dist/src/tui/keys.js +43 -15
- package/dist/src/tui/keys.js.map +1 -1
- package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
- package/dist/src/tui/lifecycle-action-view.js +34 -0
- package/dist/src/tui/lifecycle-action-view.js.map +1 -0
- package/dist/src/tui/model.d.ts +12 -2
- package/dist/src/tui/model.js +128 -52
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
- package/dist/src/tui/operation-diagnostic-screens.js +101 -0
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
- package/dist/src/tui/operation-history.d.ts +80 -0
- package/dist/src/tui/operation-history.js +130 -0
- package/dist/src/tui/operation-history.js.map +1 -0
- package/dist/src/tui/operation-review-planner.js +306 -86
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/router.d.ts +82 -17
- package/dist/src/tui/router.js +186 -38
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +546 -0
- package/dist/src/tui/screen-registry.js +179 -0
- package/dist/src/tui/screen-registry.js.map +1 -0
- package/dist/src/tui/session-console-app.d.ts +6 -0
- package/dist/src/tui/session-console-app.js +13 -0
- package/dist/src/tui/session-console-app.js.map +1 -0
- package/dist/src/tui/session-host.d.ts +44 -0
- package/dist/src/tui/session-host.js +197 -0
- package/dist/src/tui/session-host.js.map +1 -0
- package/dist/src/types.d.ts +64 -3
- package/dist/src/types.js +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.js +1 -1
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +2 -1
- package/dist/src/utils/artifact-renderer.js +52 -52
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/bundle-layout.js +4 -4
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
- package/dist/src/utils/catalog-capability-projection.js +14 -0
- package/dist/src/utils/catalog-capability-projection.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
- package/dist/src/utils/catalog-generation-resolver.js +49 -27
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
- package/dist/src/utils/catalog-generation-service.d.ts +8 -1
- package/dist/src/utils/catalog-generation-service.js +81 -47
- package/dist/src/utils/catalog-generation-service.js.map +1 -1
- package/dist/src/utils/catalog-generation-store.d.ts +1 -0
- package/dist/src/utils/catalog-generation-store.js +60 -5
- package/dist/src/utils/catalog-generation-store.js.map +1 -1
- package/dist/src/utils/catalog-operation-context.js +1 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -1
- package/dist/src/utils/catalog-public-error.js +4 -0
- package/dist/src/utils/catalog-public-error.js.map +1 -1
- package/dist/src/utils/catalog-source-impact.d.ts +3 -2
- package/dist/src/utils/catalog-source-impact.js +142 -20
- package/dist/src/utils/catalog-source-impact.js.map +1 -1
- package/dist/src/utils/catalog-source-references.js +95 -9
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +1 -1
- package/dist/src/utils/catalog-sources.js +19 -3
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/codex-manager.js +8 -0
- package/dist/src/utils/codex-manager.js.map +1 -1
- package/dist/src/utils/codex-native-observation.d.ts +34 -1
- package/dist/src/utils/codex-native-observation.js +132 -0
- package/dist/src/utils/codex-native-observation.js.map +1 -1
- package/dist/src/utils/codex-reconciler.d.ts +0 -2
- package/dist/src/utils/codex-reconciler.js.map +1 -1
- package/dist/src/utils/codex-shared-native.d.ts +2 -26
- package/dist/src/utils/codex-shared-native.js +8 -82
- package/dist/src/utils/codex-shared-native.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.d.ts +3 -0
- package/dist/src/utils/git-catalog-provider.js +40 -1
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +1 -0
- package/dist/src/utils/local-marketplace.js +69 -73
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/multi-source-catalog.js +1 -0
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +9 -4
- package/dist/src/utils/native-cli.js +280 -34
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/path-catalog-provider.js +2 -0
- package/dist/src/utils/path-catalog-provider.js.map +1 -1
- package/dist/src/utils/plugin-config-write.d.ts +15 -13
- package/dist/src/utils/plugin-config-write.js +418 -198
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +48 -4
- package/dist/src/utils/plugin-config.js +289 -38
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-scripts.d.ts +20 -12
- package/dist/src/utils/plugin-scripts.js +376 -109
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/process-tree.d.ts +2 -2
- package/dist/src/utils/process-tree.js +1 -1
- package/dist/src/utils/process-tree.js.map +1 -1
- package/dist/src/utils/project-boundary.d.ts +11 -1
- package/dist/src/utils/project-boundary.js +17 -8
- package/dist/src/utils/project-boundary.js.map +1 -1
- package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
- package/dist/src/utils/safe-operation-diagnostic.js +33 -0
- package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
- package/dist/src/utils/sensitive-content.d.ts +1 -0
- package/dist/src/utils/sensitive-content.js +15 -0
- package/dist/src/utils/sensitive-content.js.map +1 -0
- package/dist/src/utils/windows-claude-shim.d.ts +9 -0
- package/dist/src/utils/windows-claude-shim.js +141 -0
- package/dist/src/utils/windows-claude-shim.js.map +1 -0
- package/dist/vitest.config.js +91 -10
- package/dist/vitest.config.js.map +1 -1
- package/package.json +9 -9
- package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
- package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
- package/dist/authoring/templates/aw-config-runtime.mjs.map +0 -1
|
@@ -4,12 +4,29 @@ import fs from 'fs-extra';
|
|
|
4
4
|
import { CATALOG_STORE_DIRECTORY } from '../constants.js';
|
|
5
5
|
import { withAwHomeMutationLock } from '../utils/aw-home-mutation-lock.js';
|
|
6
6
|
import { createCatalogStoreKey, readCatalogStoreEntry } from '../utils/catalog-generation-store.js';
|
|
7
|
+
import { catalogGenerationErrorCode } from '../utils/catalog-generation-error.js';
|
|
7
8
|
import { readCatalogGenerationState } from '../utils/catalog-generation-state.js';
|
|
9
|
+
import { readCatalogSourcesConfig } from '../utils/catalog-sources.js';
|
|
8
10
|
import { resolveAwHome } from '../utils/state-paths.js';
|
|
9
11
|
const FORMAL_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
10
12
|
const TEMP_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
11
13
|
const HEX_SEGMENT = /^[0-9a-f]{64}$/u;
|
|
12
14
|
const TEMP_SEGMENT = /^\.(staging|replacement|quarantine)-[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.tmp$/u;
|
|
15
|
+
const DELETE_CLAIM_SEGMENT = /^\.delete-[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.tmp$/u;
|
|
16
|
+
const CATALOG_STORE_APPLICATION_ERROR_CODES = new Set([
|
|
17
|
+
'invalidArguments',
|
|
18
|
+
'catalogStoreInspectionFailed',
|
|
19
|
+
'catalogStoreDeleteFailed',
|
|
20
|
+
]);
|
|
21
|
+
export function catalogStoreErrorCode(error) {
|
|
22
|
+
const generationCode = catalogGenerationErrorCode(error);
|
|
23
|
+
if (generationCode !== 'catalogGenerationFailed')
|
|
24
|
+
return generationCode;
|
|
25
|
+
const explicitCode = error?.code;
|
|
26
|
+
return typeof explicitCode === 'string' && CATALOG_STORE_APPLICATION_ERROR_CODES.has(explicitCode)
|
|
27
|
+
? explicitCode
|
|
28
|
+
: 'catalogStoreInspectionFailed';
|
|
29
|
+
}
|
|
13
30
|
function opaqueForeignKey(parent, name) {
|
|
14
31
|
const digest = crypto.createHash('sha256').update(name).digest('hex').slice(0, 12);
|
|
15
32
|
return `${parent === '' ? '' : `${parent}/`}<foreign-${digest}>`;
|
|
@@ -17,21 +34,29 @@ function opaqueForeignKey(parent, name) {
|
|
|
17
34
|
async function inspectTree(root, nowMs) {
|
|
18
35
|
let newestMtimeMs = Number.NEGATIVE_INFINITY;
|
|
19
36
|
let bytes = 0;
|
|
37
|
+
let rootIdentity = null;
|
|
20
38
|
const pending = [root];
|
|
21
39
|
try {
|
|
22
40
|
while (pending.length > 0) {
|
|
23
41
|
const current = pending.pop();
|
|
24
42
|
const stat = await fs.lstat(current);
|
|
43
|
+
if (current === root)
|
|
44
|
+
rootIdentity = {
|
|
45
|
+
device: stat.dev,
|
|
46
|
+
inode: stat.ino,
|
|
47
|
+
mode: stat.mode,
|
|
48
|
+
birthtimeMs: stat.birthtimeMs,
|
|
49
|
+
};
|
|
25
50
|
newestMtimeMs = Math.max(newestMtimeMs, stat.mtimeMs);
|
|
26
51
|
if (stat.isSymbolicLink()) {
|
|
27
|
-
return { ageMs: null, bytes: null, safe: false, reason: 'unsafeSymlink' };
|
|
52
|
+
return { ageMs: null, bytes: null, safe: false, reason: 'unsafeSymlink', rootIdentity };
|
|
28
53
|
}
|
|
29
54
|
if (stat.isFile()) {
|
|
30
55
|
bytes += stat.size;
|
|
31
56
|
continue;
|
|
32
57
|
}
|
|
33
58
|
if (!stat.isDirectory()) {
|
|
34
|
-
return { ageMs: null, bytes: null, safe: false, reason: 'unsafeNode' };
|
|
59
|
+
return { ageMs: null, bytes: null, safe: false, reason: 'unsafeNode', rootIdentity };
|
|
35
60
|
}
|
|
36
61
|
const children = await fs.readdir(current);
|
|
37
62
|
for (const child of children)
|
|
@@ -41,10 +66,11 @@ async function inspectTree(root, nowMs) {
|
|
|
41
66
|
ageMs: Math.max(0, nowMs - newestMtimeMs),
|
|
42
67
|
bytes,
|
|
43
68
|
safe: true,
|
|
69
|
+
rootIdentity,
|
|
44
70
|
};
|
|
45
71
|
}
|
|
46
72
|
catch {
|
|
47
|
-
return { ageMs: null, bytes: null, safe: false, reason: 'unreadable' };
|
|
73
|
+
return { ageMs: null, bytes: null, safe: false, reason: 'unreadable', rootIdentity };
|
|
48
74
|
}
|
|
49
75
|
}
|
|
50
76
|
async function readEntryIdentity(entryDir) {
|
|
@@ -65,24 +91,29 @@ function retainedEntry(options) {
|
|
|
65
91
|
storeKey: options.storeKey,
|
|
66
92
|
category: options.category,
|
|
67
93
|
status: options.evidence.safe ? 'retained' : 'error',
|
|
94
|
+
generationReference: options.generationReference ?? 'none',
|
|
68
95
|
ageMs: options.evidence.ageMs,
|
|
69
96
|
reclaimableBytes: null,
|
|
70
97
|
reason: options.evidence.reason ?? options.reason,
|
|
71
98
|
absolutePath: options.absolutePath,
|
|
99
|
+
rootIdentity: options.evidence.rootIdentity,
|
|
72
100
|
};
|
|
73
101
|
}
|
|
74
102
|
async function inspectFormalEntry(options) {
|
|
75
103
|
const storeKey = `${options.identitySegment}/${options.digestSegment}`;
|
|
104
|
+
const generationReference = options.activeKeys.has(storeKey)
|
|
105
|
+
? 'active'
|
|
106
|
+
: options.orphanKeys.has(storeKey) ? 'orphan' : 'none';
|
|
76
107
|
if (!options.evidence.safe)
|
|
77
108
|
return retainedEntry({
|
|
78
109
|
storeKey, category: 'entry', absolutePath: options.entryDir,
|
|
79
|
-
evidence: options.evidence, reason: 'unsafeEntry',
|
|
110
|
+
evidence: options.evidence, reason: 'unsafeEntry', generationReference,
|
|
80
111
|
});
|
|
81
112
|
const identity = await readEntryIdentity(options.entryDir);
|
|
82
113
|
if (!identity)
|
|
83
114
|
return retainedEntry({
|
|
84
115
|
storeKey, category: 'entry', absolutePath: options.entryDir,
|
|
85
|
-
evidence: options.evidence, reason: 'validationFailed',
|
|
116
|
+
evidence: options.evidence, reason: 'validationFailed', generationReference,
|
|
86
117
|
});
|
|
87
118
|
try {
|
|
88
119
|
if (createCatalogStoreKey(identity.logicalIdentity, identity.snapshotDigest) !== storeKey) {
|
|
@@ -93,7 +124,7 @@ async function inspectFormalEntry(options) {
|
|
|
93
124
|
catch {
|
|
94
125
|
return retainedEntry({
|
|
95
126
|
storeKey, category: 'entry', absolutePath: options.entryDir,
|
|
96
|
-
evidence: options.evidence, reason: 'validationFailed',
|
|
127
|
+
evidence: options.evidence, reason: 'validationFailed', generationReference,
|
|
97
128
|
});
|
|
98
129
|
}
|
|
99
130
|
if (options.activeKeys.has(storeKey))
|
|
@@ -101,20 +132,24 @@ async function inspectFormalEntry(options) {
|
|
|
101
132
|
storeKey,
|
|
102
133
|
category: 'entry',
|
|
103
134
|
status: 'active',
|
|
135
|
+
generationReference,
|
|
104
136
|
ageMs: options.evidence.ageMs,
|
|
105
137
|
reclaimableBytes: null,
|
|
106
138
|
reason: 'active',
|
|
107
139
|
absolutePath: options.entryDir,
|
|
140
|
+
rootIdentity: options.evidence.rootIdentity,
|
|
108
141
|
};
|
|
109
142
|
const reclaimable = options.evidence.ageMs !== null && options.evidence.ageMs > FORMAL_RETENTION_MS;
|
|
110
143
|
return {
|
|
111
144
|
storeKey,
|
|
112
145
|
category: 'entry',
|
|
113
146
|
status: reclaimable ? 'reclaimable' : 'retained',
|
|
147
|
+
generationReference,
|
|
114
148
|
ageMs: options.evidence.ageMs,
|
|
115
149
|
reclaimableBytes: reclaimable ? options.evidence.bytes : null,
|
|
116
150
|
reason: reclaimable ? 'inactiveOlderThan30Days' : 'inactiveWithin30Days',
|
|
117
151
|
absolutePath: options.entryDir,
|
|
152
|
+
rootIdentity: options.evidence.rootIdentity,
|
|
118
153
|
};
|
|
119
154
|
}
|
|
120
155
|
async function inspectTempEntry(options) {
|
|
@@ -163,21 +198,104 @@ async function inspectTempEntry(options) {
|
|
|
163
198
|
storeKey,
|
|
164
199
|
category,
|
|
165
200
|
status: 'reclaimable',
|
|
201
|
+
generationReference: 'none',
|
|
166
202
|
ageMs: options.evidence.ageMs,
|
|
167
203
|
reclaimableBytes: options.evidence.bytes,
|
|
168
204
|
reason: 'temporaryOlderThan24Hours',
|
|
169
205
|
absolutePath: options.entryDir,
|
|
206
|
+
rootIdentity: options.evidence.rootIdentity,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async function inspectDeleteClaim(options) {
|
|
210
|
+
const storeKey = `${options.identitySegment}/${options.name}`;
|
|
211
|
+
const rootStat = await fs.lstat(options.entryDir).catch(() => null);
|
|
212
|
+
if (!rootStat?.isDirectory() || rootStat.isSymbolicLink())
|
|
213
|
+
return retainedEntry({
|
|
214
|
+
storeKey, category: 'deleteClaim', absolutePath: options.entryDir,
|
|
215
|
+
evidence: { ...options.evidence, safe: false, reason: 'unsafeDeleteClaimRoot' },
|
|
216
|
+
reason: 'unsafeDeleteClaimRoot',
|
|
217
|
+
});
|
|
218
|
+
if (!options.evidence.safe)
|
|
219
|
+
return retainedEntry({
|
|
220
|
+
storeKey, category: 'deleteClaim', absolutePath: options.entryDir,
|
|
221
|
+
evidence: options.evidence, reason: 'unsafeDeleteClaim',
|
|
222
|
+
});
|
|
223
|
+
const reclaimable = options.evidence.ageMs !== null && options.evidence.ageMs > TEMP_RETENTION_MS;
|
|
224
|
+
return {
|
|
225
|
+
storeKey,
|
|
226
|
+
category: 'deleteClaim',
|
|
227
|
+
status: reclaimable ? 'reclaimable' : 'retained',
|
|
228
|
+
generationReference: 'none',
|
|
229
|
+
ageMs: options.evidence.ageMs,
|
|
230
|
+
reclaimableBytes: reclaimable ? options.evidence.bytes : null,
|
|
231
|
+
reason: reclaimable ? 'deleteClaimOlderThan24Hours' : 'deleteClaimWithin24Hours',
|
|
232
|
+
absolutePath: options.entryDir,
|
|
233
|
+
rootIdentity: options.evidence.rootIdentity,
|
|
170
234
|
};
|
|
171
235
|
}
|
|
172
|
-
async function
|
|
173
|
-
const state = await
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
236
|
+
async function generationReferenceKeys(env) {
|
|
237
|
+
const [config, state] = await Promise.all([
|
|
238
|
+
readCatalogSourcesConfig(env),
|
|
239
|
+
readCatalogGenerationState(env),
|
|
240
|
+
]);
|
|
241
|
+
const configuredSourceIds = new Set(config.sources.map((source) => source.id));
|
|
242
|
+
const activeKeys = new Set();
|
|
243
|
+
const orphanKeys = new Set();
|
|
244
|
+
for (const [sourceId, source] of Object.entries(state.sources)) {
|
|
245
|
+
if (!source.active)
|
|
246
|
+
continue;
|
|
247
|
+
const storeKey = createCatalogStoreKey(source.active.logicalIdentity, source.active.snapshotDigest);
|
|
248
|
+
if (configuredSourceIds.has(sourceId))
|
|
249
|
+
activeKeys.add(storeKey);
|
|
250
|
+
else
|
|
251
|
+
orphanKeys.add(storeKey);
|
|
252
|
+
}
|
|
253
|
+
for (const storeKey of activeKeys)
|
|
254
|
+
orphanKeys.delete(storeKey);
|
|
255
|
+
return { activeKeys, orphanKeys };
|
|
256
|
+
}
|
|
257
|
+
async function reinspectCandidate(options) {
|
|
258
|
+
const identityDir = path.dirname(options.initial.absolutePath);
|
|
259
|
+
const storeRoot = path.dirname(identityDir);
|
|
260
|
+
const identitySegment = path.basename(identityDir);
|
|
261
|
+
const name = path.basename(options.initial.absolutePath);
|
|
262
|
+
const [storeRootStat, identityStat] = await Promise.all([
|
|
263
|
+
fs.lstat(storeRoot).catch(() => null),
|
|
264
|
+
fs.lstat(identityDir).catch(() => null),
|
|
265
|
+
]);
|
|
266
|
+
if (!storeRootStat?.isDirectory() || storeRootStat.isSymbolicLink()
|
|
267
|
+
|| !identityStat?.isDirectory() || identityStat.isSymbolicLink()
|
|
268
|
+
|| !HEX_SEGMENT.test(identitySegment)) {
|
|
269
|
+
return failedReinspectionEntry(options.initial);
|
|
270
|
+
}
|
|
271
|
+
const evidence = await inspectTree(options.initial.absolutePath, options.nowMs);
|
|
272
|
+
if (options.initial.category === 'entry')
|
|
273
|
+
return inspectFormalEntry({
|
|
274
|
+
identitySegment,
|
|
275
|
+
digestSegment: name,
|
|
276
|
+
entryDir: options.initial.absolutePath,
|
|
277
|
+
activeKeys: options.references.activeKeys,
|
|
278
|
+
orphanKeys: options.references.orphanKeys,
|
|
279
|
+
evidence,
|
|
280
|
+
env: options.env,
|
|
281
|
+
});
|
|
282
|
+
if (options.initial.category === 'deleteClaim')
|
|
283
|
+
return inspectDeleteClaim({
|
|
284
|
+
identitySegment,
|
|
285
|
+
name,
|
|
286
|
+
entryDir: options.initial.absolutePath,
|
|
287
|
+
evidence,
|
|
288
|
+
});
|
|
289
|
+
return inspectTempEntry({
|
|
290
|
+
identitySegment,
|
|
291
|
+
name,
|
|
292
|
+
entryDir: options.initial.absolutePath,
|
|
293
|
+
evidence,
|
|
294
|
+
env: options.env,
|
|
295
|
+
});
|
|
178
296
|
}
|
|
179
297
|
async function inspectCatalogStore(options) {
|
|
180
|
-
const activeKeys = await
|
|
298
|
+
const { activeKeys, orphanKeys } = await generationReferenceKeys(options.env);
|
|
181
299
|
const storeRoot = path.join(resolveAwHome(options.env), CATALOG_STORE_DIRECTORY);
|
|
182
300
|
const rootStat = await fs.lstat(storeRoot).catch((error) => {
|
|
183
301
|
if (error.code === 'ENOENT')
|
|
@@ -189,18 +307,14 @@ async function inspectCatalogStore(options) {
|
|
|
189
307
|
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
190
308
|
return [retainedEntry({
|
|
191
309
|
storeKey: '.', category: 'foreign', absolutePath: storeRoot,
|
|
192
|
-
evidence: {
|
|
310
|
+
evidence: {
|
|
311
|
+
ageMs: null, bytes: null, safe: false, reason: 'unsafeStoreRoot', rootIdentity: null,
|
|
312
|
+
},
|
|
193
313
|
reason: 'unsafeStoreRoot',
|
|
194
314
|
})];
|
|
195
315
|
}
|
|
196
316
|
const entries = [];
|
|
197
|
-
const identityNames = await fs.readdir(storeRoot).then((names) => names.sort())
|
|
198
|
-
if (!identityNames)
|
|
199
|
-
return [retainedEntry({
|
|
200
|
-
storeKey: '.', category: 'foreign', absolutePath: storeRoot,
|
|
201
|
-
evidence: { ageMs: null, bytes: null, safe: false, reason: 'unreadableStoreRoot' },
|
|
202
|
-
reason: 'unreadableStoreRoot',
|
|
203
|
-
})];
|
|
317
|
+
const identityNames = await fs.readdir(storeRoot).then((names) => names.sort());
|
|
204
318
|
for (const identityName of identityNames) {
|
|
205
319
|
const identityDir = path.join(storeRoot, identityName);
|
|
206
320
|
const identityStat = await fs.lstat(identityDir).catch(() => null);
|
|
@@ -216,7 +330,18 @@ async function inspectCatalogStore(options) {
|
|
|
216
330
|
if (!childNames) {
|
|
217
331
|
entries.push(retainedEntry({
|
|
218
332
|
storeKey: `${identityName}/<unreadable>`, category: 'foreign', absolutePath: identityDir,
|
|
219
|
-
evidence: {
|
|
333
|
+
evidence: {
|
|
334
|
+
ageMs: null,
|
|
335
|
+
bytes: null,
|
|
336
|
+
safe: false,
|
|
337
|
+
reason: 'unreadableIdentityDirectory',
|
|
338
|
+
rootIdentity: identityStat ? {
|
|
339
|
+
device: identityStat.dev,
|
|
340
|
+
inode: identityStat.ino,
|
|
341
|
+
mode: identityStat.mode,
|
|
342
|
+
birthtimeMs: identityStat.birthtimeMs,
|
|
343
|
+
} : null,
|
|
344
|
+
},
|
|
220
345
|
reason: 'unreadableIdentityDirectory',
|
|
221
346
|
}));
|
|
222
347
|
continue;
|
|
@@ -230,6 +355,7 @@ async function inspectCatalogStore(options) {
|
|
|
230
355
|
digestSegment: childName,
|
|
231
356
|
entryDir,
|
|
232
357
|
activeKeys,
|
|
358
|
+
orphanKeys,
|
|
233
359
|
evidence,
|
|
234
360
|
env: options.env,
|
|
235
361
|
}));
|
|
@@ -243,6 +369,14 @@ async function inspectCatalogStore(options) {
|
|
|
243
369
|
env: options.env,
|
|
244
370
|
}));
|
|
245
371
|
}
|
|
372
|
+
else if (DELETE_CLAIM_SEGMENT.test(childName)) {
|
|
373
|
+
entries.push(await inspectDeleteClaim({
|
|
374
|
+
identitySegment: identityName,
|
|
375
|
+
name: childName,
|
|
376
|
+
entryDir,
|
|
377
|
+
evidence,
|
|
378
|
+
}));
|
|
379
|
+
}
|
|
246
380
|
else {
|
|
247
381
|
entries.push(retainedEntry({
|
|
248
382
|
storeKey: opaqueForeignKey(identityName, childName), category: 'foreign', absolutePath: entryDir,
|
|
@@ -254,7 +388,7 @@ async function inspectCatalogStore(options) {
|
|
|
254
388
|
return entries;
|
|
255
389
|
}
|
|
256
390
|
function publicEntries(entries) {
|
|
257
|
-
return entries.map(({ absolutePath: _absolutePath, ...entry }) => entry);
|
|
391
|
+
return entries.map(({ absolutePath: _absolutePath, rootIdentity: _rootIdentity, ...entry }) => entry);
|
|
258
392
|
}
|
|
259
393
|
function reclaimableTotal(entries) {
|
|
260
394
|
if (entries.some((entry) => entry.status === 'error'))
|
|
@@ -264,61 +398,245 @@ function reclaimableTotal(entries) {
|
|
|
264
398
|
return null;
|
|
265
399
|
return reclaimable.reduce((sum, entry) => sum + entry.reclaimableBytes, 0);
|
|
266
400
|
}
|
|
401
|
+
function inspectionErrors(entries) {
|
|
402
|
+
return entries
|
|
403
|
+
.filter((entry) => entry.status === 'error')
|
|
404
|
+
.map((entry) => ({
|
|
405
|
+
storeKey: entry.storeKey,
|
|
406
|
+
code: 'catalogStoreInspectionFailed',
|
|
407
|
+
message: `Could not fully inspect Catalog store item ${entry.storeKey}: ${entry.reason}.`,
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
function sameNodeIdentity(left, right) {
|
|
411
|
+
return left !== null
|
|
412
|
+
&& right !== null
|
|
413
|
+
&& left.device === right.device
|
|
414
|
+
&& left.inode === right.inode
|
|
415
|
+
&& left.mode === right.mode
|
|
416
|
+
&& Object.is(left.birthtimeMs, right.birthtimeMs);
|
|
417
|
+
}
|
|
418
|
+
function sameDeletionEvidence(initial, current) {
|
|
419
|
+
return current.status === 'reclaimable'
|
|
420
|
+
&& current.storeKey === initial.storeKey
|
|
421
|
+
&& current.category === initial.category
|
|
422
|
+
&& current.generationReference === initial.generationReference
|
|
423
|
+
&& current.absolutePath === initial.absolutePath
|
|
424
|
+
&& current.ageMs === initial.ageMs
|
|
425
|
+
&& current.reclaimableBytes === initial.reclaimableBytes
|
|
426
|
+
&& current.reason === initial.reason
|
|
427
|
+
&& sameNodeIdentity(current.rootIdentity, initial.rootIdentity);
|
|
428
|
+
}
|
|
429
|
+
function staleInspectionEntry(initial, current) {
|
|
430
|
+
if (current?.status === 'active' || current?.status === 'error')
|
|
431
|
+
return current;
|
|
432
|
+
return {
|
|
433
|
+
...(current ?? initial),
|
|
434
|
+
status: 'retained',
|
|
435
|
+
ageMs: current?.ageMs ?? null,
|
|
436
|
+
reclaimableBytes: null,
|
|
437
|
+
reason: 'staleBeforeDelete',
|
|
438
|
+
rootIdentity: current?.rootIdentity ?? null,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function failedReinspectionEntry(initial) {
|
|
442
|
+
return {
|
|
443
|
+
...initial,
|
|
444
|
+
status: 'error',
|
|
445
|
+
ageMs: null,
|
|
446
|
+
reclaimableBytes: null,
|
|
447
|
+
reason: 'reinspectionFailed',
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
async function readNodeIdentity(target) {
|
|
451
|
+
const stat = await fs.lstat(target).catch(() => null);
|
|
452
|
+
if (!stat)
|
|
453
|
+
return null;
|
|
454
|
+
return {
|
|
455
|
+
device: stat.dev,
|
|
456
|
+
inode: stat.ino,
|
|
457
|
+
mode: stat.mode,
|
|
458
|
+
birthtimeMs: stat.birthtimeMs,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
async function readDirectoryIdentity(target) {
|
|
462
|
+
const stat = await fs.lstat(target).catch(() => null);
|
|
463
|
+
if (!stat?.isDirectory() || stat.isSymbolicLink())
|
|
464
|
+
return null;
|
|
465
|
+
return {
|
|
466
|
+
device: stat.dev,
|
|
467
|
+
inode: stat.ino,
|
|
468
|
+
mode: stat.mode,
|
|
469
|
+
birthtimeMs: stat.birthtimeMs,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
async function restoreStaleClaim(claimPath, originalPath) {
|
|
473
|
+
const originalExists = await fs.lstat(originalPath)
|
|
474
|
+
.then(() => true)
|
|
475
|
+
.catch(() => false);
|
|
476
|
+
if (originalExists)
|
|
477
|
+
return false;
|
|
478
|
+
return fs.rename(claimPath, originalPath)
|
|
479
|
+
.then(() => true)
|
|
480
|
+
.catch(() => false);
|
|
481
|
+
}
|
|
482
|
+
async function inspectDeleteClaimAfterFailure(options) {
|
|
483
|
+
const exists = await fs.lstat(options.claimPath)
|
|
484
|
+
.then(() => true)
|
|
485
|
+
.catch((error) => error.code === 'ENOENT' ? false : true);
|
|
486
|
+
if (!exists)
|
|
487
|
+
return null;
|
|
488
|
+
return inspectDeleteClaim({
|
|
489
|
+
identitySegment: path.basename(path.dirname(options.claimPath)),
|
|
490
|
+
name: path.basename(options.claimPath),
|
|
491
|
+
entryDir: options.claimPath,
|
|
492
|
+
evidence: await inspectTree(options.claimPath, options.nowMs),
|
|
493
|
+
});
|
|
494
|
+
}
|
|
267
495
|
export async function catalogStoreStatus(options = {}) {
|
|
268
496
|
const entries = publicEntries(await inspectCatalogStore({
|
|
269
497
|
env: options.env ?? process.env,
|
|
270
498
|
nowMs: (options.now ?? new Date()).getTime(),
|
|
271
499
|
}));
|
|
500
|
+
const errors = inspectionErrors(entries);
|
|
272
501
|
return {
|
|
273
|
-
catalogStoreSchemaVersion:
|
|
502
|
+
catalogStoreSchemaVersion: 2,
|
|
274
503
|
command: 'catalog:store:status',
|
|
275
|
-
ok:
|
|
504
|
+
ok: errors.length === 0,
|
|
276
505
|
entries,
|
|
277
506
|
totals: {
|
|
278
507
|
entries: entries.length,
|
|
279
508
|
reclaimableEntries: entries.filter((entry) => entry.status === 'reclaimable').length,
|
|
280
509
|
reclaimableBytes: reclaimableTotal(entries),
|
|
281
510
|
},
|
|
511
|
+
errors,
|
|
282
512
|
};
|
|
283
513
|
}
|
|
284
514
|
async function executePrune(options) {
|
|
285
515
|
const inspected = await inspectCatalogStore({ env: options.env, nowMs: options.nowMs });
|
|
286
516
|
const deleted = [];
|
|
287
|
-
const
|
|
517
|
+
const deleteErrors = [];
|
|
288
518
|
const retainedAfterError = new Set();
|
|
519
|
+
const retainedReasons = new Map();
|
|
289
520
|
if (!options.dryRun) {
|
|
290
|
-
|
|
521
|
+
const candidates = inspected
|
|
522
|
+
.map((entry, index) => ({ entry, index }))
|
|
523
|
+
.filter(({ entry }) => entry.status === 'reclaimable');
|
|
524
|
+
await options.runtime?.afterInitialInspection?.(publicEntries(inspected));
|
|
525
|
+
for (const { entry: initial, index } of candidates) {
|
|
526
|
+
let current;
|
|
527
|
+
try {
|
|
528
|
+
const references = await generationReferenceKeys(options.env);
|
|
529
|
+
current = await reinspectCandidate({
|
|
530
|
+
initial,
|
|
531
|
+
references,
|
|
532
|
+
env: options.env,
|
|
533
|
+
nowMs: options.nowMs,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
inspected[index] = failedReinspectionEntry(initial);
|
|
538
|
+
retainedReasons.set(initial.storeKey, 'reinspectionFailed');
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (!current || !sameDeletionEvidence(initial, current)) {
|
|
542
|
+
inspected[index] = staleInspectionEntry(initial, current);
|
|
543
|
+
retainedReasons.set(initial.storeKey, current?.status === 'active'
|
|
544
|
+
? 'active'
|
|
545
|
+
: current?.status === 'error' ? current.reason : 'staleBeforeDelete');
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
inspected[index] = current;
|
|
549
|
+
const parentPath = path.dirname(current.absolutePath);
|
|
550
|
+
const parentIdentity = await readDirectoryIdentity(parentPath);
|
|
551
|
+
if (!parentIdentity) {
|
|
552
|
+
inspected[index] = staleInspectionEntry(initial, current);
|
|
553
|
+
retainedReasons.set(initial.storeKey, 'staleBeforeDelete');
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
const claimPath = path.join(parentPath, `.delete-${crypto.randomUUID()}.tmp`);
|
|
291
557
|
try {
|
|
292
|
-
await
|
|
293
|
-
deleted.push({ storeKey: entry.storeKey, bytes: entry.reclaimableBytes });
|
|
558
|
+
await fs.rename(current.absolutePath, claimPath);
|
|
294
559
|
}
|
|
295
560
|
catch {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
561
|
+
inspected[index] = staleInspectionEntry(initial, current);
|
|
562
|
+
retainedReasons.set(initial.storeKey, 'staleBeforeDelete');
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
const claimTime = new Date(options.nowMs);
|
|
567
|
+
if (options.runtime?.updateDeleteClaimTime) {
|
|
568
|
+
await options.runtime.updateDeleteClaimTime(claimPath, claimTime);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
await fs.utimes(claimPath, claimTime, claimTime);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
catch {
|
|
575
|
+
const restored = await restoreStaleClaim(claimPath, current.absolutePath);
|
|
576
|
+
if (restored) {
|
|
577
|
+
inspected[index] = staleInspectionEntry(initial, current);
|
|
578
|
+
retainedReasons.set(initial.storeKey, 'staleBeforeDelete');
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
const failedClaim = await inspectDeleteClaimAfterFailure({
|
|
582
|
+
claimPath,
|
|
583
|
+
nowMs: options.nowMs,
|
|
584
|
+
});
|
|
585
|
+
inspected[index] = staleInspectionEntry(initial, failedClaim ?? undefined);
|
|
586
|
+
retainedReasons.set(inspected[index].storeKey, 'staleBeforeDelete');
|
|
587
|
+
}
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
await options.runtime?.afterDeleteClaim?.({ storeKey: initial.storeKey, claimPath });
|
|
591
|
+
const [claimedIdentity, currentParentIdentity] = await Promise.all([
|
|
592
|
+
readNodeIdentity(claimPath),
|
|
593
|
+
readDirectoryIdentity(parentPath),
|
|
594
|
+
]);
|
|
595
|
+
if (!sameNodeIdentity(current.rootIdentity, claimedIdentity)
|
|
596
|
+
|| !sameNodeIdentity(parentIdentity, currentParentIdentity)) {
|
|
597
|
+
await restoreStaleClaim(claimPath, current.absolutePath);
|
|
598
|
+
inspected[index] = staleInspectionEntry(initial, current);
|
|
599
|
+
retainedReasons.set(initial.storeKey, 'staleBeforeDelete');
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
try {
|
|
603
|
+
await options.remove(claimPath);
|
|
604
|
+
deleted.push({ storeKey: initial.storeKey, bytes: current.reclaimableBytes });
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
const failedClaim = await inspectDeleteClaimAfterFailure({
|
|
608
|
+
claimPath,
|
|
609
|
+
nowMs: options.nowMs,
|
|
610
|
+
});
|
|
611
|
+
if (!failedClaim) {
|
|
612
|
+
deleted.push({ storeKey: initial.storeKey, bytes: current.reclaimableBytes });
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
inspected[index] = failedClaim;
|
|
616
|
+
retainedAfterError.add(failedClaim.storeKey);
|
|
617
|
+
deleteErrors.push({
|
|
618
|
+
storeKey: failedClaim.storeKey,
|
|
303
619
|
code: 'catalogStoreDeleteFailed',
|
|
304
|
-
message: `Could not safely complete deletion of Catalog store item ${
|
|
620
|
+
message: `Could not safely complete deletion of Catalog store item ${failedClaim.storeKey}. Retry prune.`,
|
|
305
621
|
});
|
|
306
622
|
}
|
|
307
623
|
}
|
|
308
624
|
}
|
|
309
625
|
const entries = publicEntries(inspected);
|
|
626
|
+
const errors = [...inspectionErrors(entries), ...deleteErrors];
|
|
310
627
|
const deletedKeys = new Set(deleted.map((entry) => entry.storeKey));
|
|
311
628
|
const retained = entries
|
|
312
629
|
.filter((entry) => !deletedKeys.has(entry.storeKey)
|
|
313
|
-
&& (options.dryRun
|
|
630
|
+
&& (options.dryRun
|
|
631
|
+
|| entry.status !== 'reclaimable'
|
|
632
|
+
|| retainedAfterError.has(entry.storeKey)
|
|
633
|
+
|| retainedReasons.has(entry.storeKey)))
|
|
314
634
|
.map((entry) => ({
|
|
315
635
|
storeKey: entry.storeKey,
|
|
316
|
-
reason:
|
|
317
|
-
? 'deleteFailed'
|
|
318
|
-
: entry.reason,
|
|
636
|
+
reason: retainedReasons.get(entry.storeKey) ?? entry.reason,
|
|
319
637
|
}));
|
|
320
638
|
return {
|
|
321
|
-
catalogStoreSchemaVersion:
|
|
639
|
+
catalogStoreSchemaVersion: 2,
|
|
322
640
|
command: 'catalog:store:prune',
|
|
323
641
|
ok: errors.length === 0,
|
|
324
642
|
overallState: errors.length === 0 ? 'succeeded' : 'partial',
|
|
@@ -340,6 +658,7 @@ export async function catalogStorePrune(options = {}) {
|
|
|
340
658
|
nowMs,
|
|
341
659
|
dryRun,
|
|
342
660
|
remove: options.remove ?? ((target) => fs.remove(target)),
|
|
661
|
+
runtime: options.runtime,
|
|
343
662
|
});
|
|
344
663
|
if (dryRun)
|
|
345
664
|
return action();
|