@orion-agents/orion-code 0.1.0 → 0.1.2
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/README.md +107 -67
- package/README.zh-CN.md +192 -217
- package/dist/cli.js +23 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +397 -212
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/target-command.d.ts.map +1 -1
- package/dist/commands/target-command.js +43 -9
- package/dist/commands/target-command.js.map +1 -1
- package/dist/commands/types.d.ts +33 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +5 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +2 -2
- package/dist/core/agent.js.map +1 -1
- package/dist/framework/external-assertion.d.ts +65 -0
- package/dist/framework/external-assertion.d.ts.map +1 -0
- package/dist/framework/external-assertion.js +571 -0
- package/dist/framework/external-assertion.js.map +1 -0
- package/dist/framework/index.d.ts +6 -4
- package/dist/framework/index.d.ts.map +1 -1
- package/dist/framework/index.js +4 -1
- package/dist/framework/index.js.map +1 -1
- package/dist/framework/query.d.ts +18 -1
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +65 -28
- package/dist/framework/query.js.map +1 -1
- package/dist/framework/tool-scheduler.d.ts +2 -0
- package/dist/framework/tool-scheduler.d.ts.map +1 -1
- package/dist/framework/tool-scheduler.js +23 -9
- package/dist/framework/tool-scheduler.js.map +1 -1
- package/dist/framework/tool-serializer.d.ts.map +1 -1
- package/dist/framework/tool-serializer.js +20 -4
- package/dist/framework/tool-serializer.js.map +1 -1
- package/dist/framework/tool.d.ts +11 -5
- package/dist/framework/tool.d.ts.map +1 -1
- package/dist/framework/tool.js.map +1 -1
- package/dist/harness/context-harness.d.ts.map +1 -1
- package/dist/harness/context-harness.js +4 -3
- package/dist/harness/context-harness.js.map +1 -1
- package/dist/harness/drift-guard.d.ts.map +1 -1
- package/dist/harness/drift-guard.js +14 -5
- package/dist/harness/drift-guard.js.map +1 -1
- package/dist/harness/safety.d.ts.map +1 -1
- package/dist/harness/safety.js +19 -3
- package/dist/harness/safety.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +115 -34
- package/dist/init.js.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.js +4 -1
- package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
- package/dist/memory/storage.d.ts.map +1 -1
- package/dist/memory/storage.js +1 -0
- package/dist/memory/storage.js.map +1 -1
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +11 -5
- package/dist/memory/store.js.map +1 -1
- package/dist/migration/command.d.ts.map +1 -1
- package/dist/migration/command.js +6 -1
- package/dist/migration/command.js.map +1 -1
- package/dist/migration/migrate.d.ts.map +1 -1
- package/dist/migration/migrate.js +4 -1
- package/dist/migration/migrate.js.map +1 -1
- package/dist/print-ui/launch.d.ts +4 -0
- package/dist/print-ui/launch.d.ts.map +1 -1
- package/dist/print-ui/launch.js +102 -9
- package/dist/print-ui/launch.js.map +1 -1
- package/dist/product/identity.d.ts +2 -2
- package/dist/product/identity.d.ts.map +1 -1
- package/dist/product/identity.js +2 -2
- package/dist/product/identity.js.map +1 -1
- package/dist/product/index.d.ts +2 -1
- package/dist/product/index.d.ts.map +1 -1
- package/dist/product/index.js +5 -1
- package/dist/product/index.js.map +1 -1
- package/dist/product/version.d.ts +12 -0
- package/dist/product/version.d.ts.map +1 -0
- package/dist/product/version.js +31 -0
- package/dist/product/version.js.map +1 -0
- package/dist/runtime/agent-runtime-controller.d.ts +49 -0
- package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-controller.js +1048 -81
- package/dist/runtime/agent-runtime-controller.js.map +1 -1
- package/dist/runtime/agent-runtime-protocol.d.ts +14 -2
- package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-protocol.js +18 -0
- package/dist/runtime/agent-runtime-protocol.js.map +1 -1
- package/dist/runtime/chat-controller.d.ts +6 -0
- package/dist/runtime/chat-controller.d.ts.map +1 -1
- package/dist/runtime/chat-controller.js +226 -20
- package/dist/runtime/chat-controller.js.map +1 -1
- package/dist/runtime/goals/accounting.d.ts +28 -0
- package/dist/runtime/goals/accounting.d.ts.map +1 -1
- package/dist/runtime/goals/accounting.js +69 -0
- package/dist/runtime/goals/accounting.js.map +1 -1
- package/dist/runtime/goals/completion-audit.d.ts +13 -8
- package/dist/runtime/goals/completion-audit.d.ts.map +1 -1
- package/dist/runtime/goals/completion-audit.js +598 -16
- package/dist/runtime/goals/completion-audit.js.map +1 -1
- package/dist/runtime/goals/coordinator.d.ts +44 -5
- package/dist/runtime/goals/coordinator.d.ts.map +1 -1
- package/dist/runtime/goals/coordinator.js +1124 -58
- package/dist/runtime/goals/coordinator.js.map +1 -1
- package/dist/runtime/goals/evidence.d.ts +37 -0
- package/dist/runtime/goals/evidence.d.ts.map +1 -0
- package/dist/runtime/goals/evidence.js +300 -0
- package/dist/runtime/goals/evidence.js.map +1 -0
- package/dist/runtime/goals/presentation.d.ts +4 -0
- package/dist/runtime/goals/presentation.d.ts.map +1 -0
- package/dist/runtime/goals/presentation.js +50 -0
- package/dist/runtime/goals/presentation.js.map +1 -0
- package/dist/runtime/goals/prompt.d.ts.map +1 -1
- package/dist/runtime/goals/prompt.js +38 -8
- package/dist/runtime/goals/prompt.js.map +1 -1
- package/dist/runtime/goals/tools.d.ts +18 -1
- package/dist/runtime/goals/tools.d.ts.map +1 -1
- package/dist/runtime/goals/tools.js +308 -28
- package/dist/runtime/goals/tools.js.map +1 -1
- package/dist/runtime/goals/types.d.ts +241 -7
- package/dist/runtime/goals/types.d.ts.map +1 -1
- package/dist/runtime/goals/types.js +8 -2
- package/dist/runtime/goals/types.js.map +1 -1
- package/dist/runtime/subagents/child-executor-guard.d.ts +12 -4
- package/dist/runtime/subagents/child-executor-guard.d.ts.map +1 -1
- package/dist/runtime/subagents/child-executor-guard.js +397 -61
- package/dist/runtime/subagents/child-executor-guard.js.map +1 -1
- package/dist/runtime/subagents/presets.d.ts +4 -5
- package/dist/runtime/subagents/presets.d.ts.map +1 -1
- package/dist/runtime/subagents/presets.js +18 -33
- package/dist/runtime/subagents/presets.js.map +1 -1
- package/dist/runtime/subagents/production.d.ts +3 -1
- package/dist/runtime/subagents/production.d.ts.map +1 -1
- package/dist/runtime/subagents/production.js +37 -7
- package/dist/runtime/subagents/production.js.map +1 -1
- package/dist/runtime/subagents/runner.d.ts.map +1 -1
- package/dist/runtime/subagents/runner.js +24 -3
- package/dist/runtime/subagents/runner.js.map +1 -1
- package/dist/runtime/subagents/runtime-integration.d.ts +3 -0
- package/dist/runtime/subagents/runtime-integration.d.ts.map +1 -1
- package/dist/runtime/subagents/runtime-integration.js +18 -7
- package/dist/runtime/subagents/runtime-integration.js.map +1 -1
- package/dist/runtime/subagents/supervisor.d.ts +4 -4
- package/dist/runtime/subagents/supervisor.d.ts.map +1 -1
- package/dist/runtime/subagents/supervisor.js +39 -19
- package/dist/runtime/subagents/supervisor.js.map +1 -1
- package/dist/runtime/subagents/types.d.ts +7 -0
- package/dist/runtime/subagents/types.d.ts.map +1 -1
- package/dist/runtime/subagents/types.js +12 -1
- package/dist/runtime/subagents/types.js.map +1 -1
- package/dist/runtime/ui-events.d.ts +10 -0
- package/dist/runtime/ui-events.d.ts.map +1 -1
- package/dist/runtime/ui-events.js +5 -5
- package/dist/runtime/ui-events.js.map +1 -1
- package/dist/runtime/ui-view-model.d.ts +3 -3
- package/dist/runtime/ui-view-model.d.ts.map +1 -1
- package/dist/runtime/ui-view-model.js +11 -4
- package/dist/runtime/ui-view-model.js.map +1 -1
- package/dist/services/config.d.ts +18 -7
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +79 -57
- package/dist/services/config.js.map +1 -1
- package/dist/services/doctor.d.ts.map +1 -1
- package/dist/services/doctor.js +164 -18
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/global-config.d.ts +1 -1
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/goal-storage.d.ts +9 -4
- package/dist/services/goal-storage.d.ts.map +1 -1
- package/dist/services/goal-storage.js +1113 -75
- package/dist/services/goal-storage.js.map +1 -1
- package/dist/services/llm.d.ts +19 -0
- package/dist/services/llm.d.ts.map +1 -1
- package/dist/services/llm.js +153 -20
- package/dist/services/llm.js.map +1 -1
- package/dist/services/model-registry.d.ts +1 -1
- package/dist/services/model-registry.d.ts.map +1 -1
- package/dist/services/model-registry.js +69 -19
- package/dist/services/model-registry.js.map +1 -1
- package/dist/services/provider-resilience/coordinator.d.ts +2 -1
- package/dist/services/provider-resilience/coordinator.d.ts.map +1 -1
- package/dist/services/provider-resilience/coordinator.js +7 -6
- package/dist/services/provider-resilience/coordinator.js.map +1 -1
- package/dist/services/provider-resilience/error-classifier.d.ts.map +1 -1
- package/dist/services/provider-resilience/error-classifier.js +67 -17
- package/dist/services/provider-resilience/error-classifier.js.map +1 -1
- package/dist/services/provider-resilience/types.d.ts +1 -1
- package/dist/services/provider-resilience/types.d.ts.map +1 -1
- package/dist/services/provider-resilience/types.js.map +1 -1
- package/dist/services/redaction.d.ts.map +1 -1
- package/dist/services/redaction.js +9 -1
- package/dist/services/redaction.js.map +1 -1
- package/dist/services/session-storage.d.ts +12 -1
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +23 -7
- package/dist/services/session-storage.js.map +1 -1
- package/dist/services/storage-maintenance.d.ts +73 -3
- package/dist/services/storage-maintenance.d.ts.map +1 -1
- package/dist/services/storage-maintenance.js +771 -104
- package/dist/services/storage-maintenance.js.map +1 -1
- package/dist/services/task-manager.d.ts.map +1 -1
- package/dist/services/task-manager.js +2 -2
- package/dist/services/task-manager.js.map +1 -1
- package/dist/services/web-search-adapters.d.ts.map +1 -1
- package/dist/services/web-search-adapters.js +89 -49
- package/dist/services/web-search-adapters.js.map +1 -1
- package/dist/services/web-search-mcp.d.ts.map +1 -1
- package/dist/services/web-search-mcp.js +34 -37
- package/dist/services/web-search-mcp.js.map +1 -1
- package/dist/services/workspace-state.d.ts +4 -0
- package/dist/services/workspace-state.d.ts.map +1 -1
- package/dist/services/workspace-state.js +30 -9
- package/dist/services/workspace-state.js.map +1 -1
- package/dist/terminal-ui/completion.js +1 -1
- package/dist/terminal-ui/completion.js.map +1 -1
- package/dist/terminal-ui/launch.d.ts +3 -0
- package/dist/terminal-ui/launch.d.ts.map +1 -1
- package/dist/terminal-ui/launch.js +26 -98
- package/dist/terminal-ui/launch.js.map +1 -1
- package/dist/tools/git.d.ts.map +1 -1
- package/dist/tools/git.js +275 -27
- package/dist/tools/git.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +29 -10
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mcp.js +20 -12
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/web.d.ts.map +1 -1
- package/dist/tools/web.js +38 -14
- package/dist/tools/web.js.map +1 -1
- package/dist/tui-ui/layout.d.ts.map +1 -1
- package/dist/tui-ui/layout.js +36 -23
- package/dist/tui-ui/layout.js.map +1 -1
- package/dist/tui-ui/pickers.d.ts.map +1 -1
- package/dist/tui-ui/pickers.js +5 -4
- package/dist/tui-ui/pickers.js.map +1 -1
- package/dist/tui-ui/runner.d.ts +1 -0
- package/dist/tui-ui/runner.d.ts.map +1 -1
- package/dist/tui-ui/runner.js +15 -17
- package/dist/tui-ui/runner.js.map +1 -1
- package/dist/tui-ui/state.d.ts +6 -0
- package/dist/tui-ui/state.d.ts.map +1 -1
- package/dist/tui-ui/state.js +61 -30
- package/dist/tui-ui/state.js.map +1 -1
- package/package.json +14 -9
|
@@ -4,11 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.collectStorageReport = collectStorageReport;
|
|
7
|
+
exports.collectStorageCleanupPlan = collectStorageCleanupPlan;
|
|
7
8
|
exports.cleanupStorage = cleanupStorage;
|
|
9
|
+
exports.collectProjectMetadataRepairPlan = collectProjectMetadataRepairPlan;
|
|
8
10
|
exports.repairProjectMetadata = repairProjectMetadata;
|
|
11
|
+
exports.serializeStorageMaintenancePlan = serializeStorageMaintenancePlan;
|
|
12
|
+
exports.deserializeStorageMaintenancePlan = deserializeStorageMaintenancePlan;
|
|
13
|
+
exports.consumeStorageMaintenancePlan = consumeStorageMaintenancePlan;
|
|
14
|
+
exports.resetStorageMaintenancePlanRegistry = resetStorageMaintenancePlanRegistry;
|
|
9
15
|
exports.formatStorageReport = formatStorageReport;
|
|
10
16
|
exports.formatStorageCleanupResult = formatStorageCleanupResult;
|
|
11
17
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
18
|
+
const crypto_1 = require("crypto");
|
|
12
19
|
const fs_1 = require("fs");
|
|
13
20
|
const path_1 = require("path");
|
|
14
21
|
const session_storage_1 = require("./session-storage");
|
|
@@ -16,6 +23,8 @@ const config_dir_1 = require("./config-dir");
|
|
|
16
23
|
const format_1 = require("./format");
|
|
17
24
|
const HASH_PROJECT_RE = /^[a-f0-9]{16}$/;
|
|
18
25
|
const TEMP_PROJECT_RE = /^(private-tmp-|private-var-folders-.*orion-code-|tmp-orion-code-|test-project$)/;
|
|
26
|
+
const STORAGE_PLAN_TOKEN_TTL_MS = 10 * 60 * 1000;
|
|
27
|
+
const issuedStorageMaintenancePlans = new Map();
|
|
19
28
|
function collectStorageReport() {
|
|
20
29
|
const configHome = (0, config_dir_1.getConfigHome)();
|
|
21
30
|
const projectsDir = (0, config_dir_1.getProjectsDir)();
|
|
@@ -24,10 +33,11 @@ function collectStorageReport() {
|
|
|
24
33
|
.filter(project => !HASH_PROJECT_RE.test(project.projectKey) && !TEMP_PROJECT_RE.test(project.projectKey))
|
|
25
34
|
.map(project => project.projectKey));
|
|
26
35
|
const vectorProjects = inspectVectorProjects(configHome, validProjectKeys);
|
|
36
|
+
const metadataRepairPlan = collectProjectMetadataRepairPlan();
|
|
27
37
|
const issues = [];
|
|
28
38
|
const legacySessions = (0, path_1.join)(configHome, 'sessions');
|
|
29
39
|
const legacyMemory = (0, path_1.join)(configHome, 'memory');
|
|
30
|
-
if ((
|
|
40
|
+
if (safeIsDirectory(legacySessions)) {
|
|
31
41
|
issues.push({
|
|
32
42
|
kind: 'legacy-global-sessions',
|
|
33
43
|
path: legacySessions,
|
|
@@ -36,7 +46,7 @@ function collectStorageReport() {
|
|
|
36
46
|
canCleanup: true,
|
|
37
47
|
});
|
|
38
48
|
}
|
|
39
|
-
if ((
|
|
49
|
+
if (safeIsDirectory(legacyMemory)) {
|
|
40
50
|
issues.push({
|
|
41
51
|
kind: 'legacy-global-memory',
|
|
42
52
|
path: legacyMemory,
|
|
@@ -79,26 +89,19 @@ function collectStorageReport() {
|
|
|
79
89
|
});
|
|
80
90
|
continue;
|
|
81
91
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
path: (0, config_dir_1.getProjectMetadataPath)(project.projectPath),
|
|
96
|
-
projectKey: project.projectKey,
|
|
97
|
-
summary: 'Project metadata does not match the canonical project key',
|
|
98
|
-
bytes: project.bytes,
|
|
99
|
-
canCleanup: false,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
92
|
+
}
|
|
93
|
+
const projectsByKey = new Map(projects.map(project => [project.projectKey, project]));
|
|
94
|
+
for (const action of metadataRepairPlan.actions) {
|
|
95
|
+
issues.push({
|
|
96
|
+
kind: action.kind,
|
|
97
|
+
path: action.metadataPath,
|
|
98
|
+
projectKey: action.projectKey,
|
|
99
|
+
summary: action.kind === 'missing-project-metadata'
|
|
100
|
+
? 'Project metadata file is missing'
|
|
101
|
+
: 'Project metadata is invalid',
|
|
102
|
+
bytes: projectsByKey.get(action.projectKey)?.bytes,
|
|
103
|
+
canCleanup: false,
|
|
104
|
+
});
|
|
102
105
|
}
|
|
103
106
|
for (const vectorProject of vectorProjects) {
|
|
104
107
|
if (!vectorProject.orphan)
|
|
@@ -119,74 +122,187 @@ function collectStorageReport() {
|
|
|
119
122
|
totalBytes: dirSize(configHome),
|
|
120
123
|
};
|
|
121
124
|
}
|
|
122
|
-
|
|
125
|
+
/** Collect an immutable, serializable cleanup plan without modifying storage. */
|
|
126
|
+
function collectStorageCleanupPlan(options = {}) {
|
|
123
127
|
const opts = {
|
|
124
|
-
dryRun: options.dryRun ?? false,
|
|
125
128
|
includeLegacy: options.includeLegacy ?? true,
|
|
126
129
|
includeTemp: options.includeTemp ?? true,
|
|
127
130
|
includeEmpty: options.includeEmpty ?? true,
|
|
128
131
|
includeVectorOrphans: options.includeVectorOrphans ?? true,
|
|
129
132
|
};
|
|
130
133
|
const report = collectStorageReport();
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
let vectorDeletedRows = 0;
|
|
134
|
+
const actions = [];
|
|
135
|
+
const skipped = [];
|
|
134
136
|
for (const issue of report.issues) {
|
|
135
|
-
const shouldDeletePath = (opts.includeLegacy &&
|
|
137
|
+
const shouldDeletePath = (opts.includeLegacy &&
|
|
138
|
+
(issue.kind === 'legacy-global-sessions' ||
|
|
139
|
+
issue.kind === 'legacy-global-memory' ||
|
|
140
|
+
issue.kind === 'legacy-hash-project')) ||
|
|
136
141
|
(opts.includeTemp && issue.kind === 'temp-project') ||
|
|
137
142
|
(opts.includeEmpty && issue.kind === 'empty-project');
|
|
138
|
-
if (shouldDeletePath && issue.path) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
if (shouldDeletePath && issue.path && isPathCleanupKind(issue.kind)) {
|
|
144
|
+
const action = inspectCleanupPathAction(report, issue.kind, issue.path, issue.projectKey);
|
|
145
|
+
if (action)
|
|
146
|
+
actions.push(action);
|
|
147
|
+
else
|
|
148
|
+
skipped.push(issue.path);
|
|
143
149
|
continue;
|
|
144
150
|
}
|
|
145
151
|
if (opts.includeVectorOrphans && issue.kind === 'vector-orphan-project' && issue.projectKey) {
|
|
146
|
-
const
|
|
147
|
-
|
|
152
|
+
const action = inspectVectorCleanupAction(report, issue.projectKey);
|
|
153
|
+
if (action)
|
|
154
|
+
actions.push(action);
|
|
155
|
+
else
|
|
156
|
+
skipped.push(issue.projectKey);
|
|
148
157
|
continue;
|
|
149
158
|
}
|
|
150
|
-
if (issue.canCleanup)
|
|
151
|
-
|
|
152
|
-
}
|
|
159
|
+
if (issue.canCleanup)
|
|
160
|
+
skipped.push(issue.path || issue.projectKey || issue.kind);
|
|
153
161
|
}
|
|
154
|
-
|
|
155
|
-
|
|
162
|
+
actions.sort((a, b) => cleanupActionKey(a).localeCompare(cleanupActionKey(b)));
|
|
163
|
+
return {
|
|
164
|
+
version: 1,
|
|
165
|
+
configHome: report.configHome,
|
|
166
|
+
projectsDir: report.projectsDir,
|
|
167
|
+
actions,
|
|
168
|
+
skipped,
|
|
169
|
+
issues: report.issues,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/** Apply only actions captured in a cleanup plan, after exact state revalidation. */
|
|
173
|
+
function cleanupStorage(options = {}, plan = collectStorageCleanupPlan(options)) {
|
|
174
|
+
const dryRun = options.dryRun ?? false;
|
|
175
|
+
const deletedPaths = [];
|
|
176
|
+
const skippedPaths = [...plan.skipped];
|
|
177
|
+
const quarantinedPaths = [];
|
|
178
|
+
let vectorDeletedRows = 0;
|
|
179
|
+
if (!sameStorageRoots(plan.configHome, plan.projectsDir)) {
|
|
180
|
+
return {
|
|
181
|
+
dryRun,
|
|
182
|
+
deletedPaths,
|
|
183
|
+
skippedPaths: [...skippedPaths, ...plan.actions.map(cleanupActionKey)],
|
|
184
|
+
quarantinedPaths,
|
|
185
|
+
vectorDeletedRows,
|
|
186
|
+
issues: plan.issues,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
for (const action of plan.actions) {
|
|
190
|
+
if (action.type === 'path') {
|
|
191
|
+
const current = inspectCleanupPathAction({
|
|
192
|
+
configHome: plan.configHome,
|
|
193
|
+
projectsDir: plan.projectsDir,
|
|
194
|
+
}, action.kind, action.path, action.projectKey);
|
|
195
|
+
if (!current || !sameCleanupPathAction(current, action)) {
|
|
196
|
+
skippedPaths.push(action.path);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (dryRun) {
|
|
200
|
+
quarantinedPaths.push(action.path);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const quarantine = quarantinePlannedPath(action);
|
|
204
|
+
if (quarantine.status === 'restored' || quarantine.status === 'skipped') {
|
|
205
|
+
skippedPaths.push(action.path);
|
|
206
|
+
}
|
|
207
|
+
if (quarantine.quarantinePath)
|
|
208
|
+
quarantinedPaths.push(quarantine.quarantinePath);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const current = inspectVectorCleanupAction({ configHome: plan.configHome, projectsDir: plan.projectsDir }, action.projectKey);
|
|
212
|
+
if (!current || !sameVectorCleanupAction(current, action)) {
|
|
213
|
+
skippedPaths.push(action.projectKey);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (dryRun) {
|
|
217
|
+
vectorDeletedRows += action.rowCount;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const deletedRows = deletePlannedVectorRows(plan.configHome, action);
|
|
221
|
+
if (deletedRows !== action.rowCount) {
|
|
222
|
+
skippedPaths.push(action.projectKey);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
vectorDeletedRows += deletedRows;
|
|
156
226
|
}
|
|
157
227
|
return {
|
|
158
|
-
dryRun
|
|
228
|
+
dryRun,
|
|
159
229
|
deletedPaths,
|
|
160
230
|
skippedPaths,
|
|
231
|
+
quarantinedPaths,
|
|
161
232
|
vectorDeletedRows,
|
|
162
|
-
issues:
|
|
233
|
+
issues: plan.issues,
|
|
163
234
|
};
|
|
164
235
|
}
|
|
165
|
-
|
|
236
|
+
/** Collect a read-only, revalidatable plan for safe project metadata repairs. */
|
|
237
|
+
function collectProjectMetadataRepairPlan() {
|
|
166
238
|
const projectsDir = (0, config_dir_1.getProjectsDir)();
|
|
167
|
-
const
|
|
239
|
+
const actions = [];
|
|
168
240
|
const skipped = [];
|
|
169
|
-
if (!(
|
|
170
|
-
|
|
171
|
-
return { repaired, skipped };
|
|
172
|
-
}
|
|
241
|
+
if (!safeIsDirectory(projectsDir))
|
|
242
|
+
return { version: 1, projectsDir, actions, skipped };
|
|
173
243
|
for (const entry of (0, fs_1.readdirSync)(projectsDir)) {
|
|
174
244
|
if (HASH_PROJECT_RE.test(entry) || TEMP_PROJECT_RE.test(entry)) {
|
|
175
245
|
skipped.push(entry);
|
|
176
246
|
continue;
|
|
177
247
|
}
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (
|
|
248
|
+
const observation = inspectProjectMetadataRepair(projectsDir, entry);
|
|
249
|
+
if (observation.action) {
|
|
250
|
+
actions.push(observation.action);
|
|
251
|
+
}
|
|
252
|
+
else if (observation.skipped) {
|
|
183
253
|
skipped.push(entry);
|
|
184
|
-
continue;
|
|
185
254
|
}
|
|
186
|
-
(0, config_dir_1.updateProjectMetadata)(projectPath);
|
|
187
|
-
repaired.push(entry);
|
|
188
255
|
}
|
|
189
|
-
|
|
256
|
+
actions.sort((a, b) => a.projectKey.localeCompare(b.projectKey));
|
|
257
|
+
return { version: 1, projectsDir, actions, skipped };
|
|
258
|
+
}
|
|
259
|
+
/** Apply only the targets in a repair plan, after revalidating every target. */
|
|
260
|
+
function repairProjectMetadata(plan = collectProjectMetadataRepairPlan()) {
|
|
261
|
+
const projectsDir = (0, config_dir_1.getProjectsDir)();
|
|
262
|
+
const repaired = [];
|
|
263
|
+
const skipped = [...plan.skipped];
|
|
264
|
+
const blockedReason = 'Writable project metadata repair is disabled because Node.js cannot provide race-safe directory-relative writes on this platform.';
|
|
265
|
+
if (plan.version !== 1 || plan.projectsDir !== projectsDir) {
|
|
266
|
+
return {
|
|
267
|
+
repaired,
|
|
268
|
+
skipped: [...skipped, ...plan.actions.map(action => action.projectKey)],
|
|
269
|
+
writeDisabled: true,
|
|
270
|
+
blockedReason,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
skipped.push(...plan.actions.map(action => action.projectKey));
|
|
274
|
+
return { repaired, skipped, writeDisabled: true, blockedReason };
|
|
275
|
+
}
|
|
276
|
+
/** Issue an opaque, process-local, expiring token for a previewed maintenance plan. */
|
|
277
|
+
function serializeStorageMaintenancePlan(plan) {
|
|
278
|
+
if ((plan.kind === 'cleanup' && !isStorageCleanupPlan(plan.plan)) ||
|
|
279
|
+
(plan.kind === 'repair' && !isProjectMetadataRepairPlan(plan.plan))) {
|
|
280
|
+
throw new Error('Cannot issue an invalid storage maintenance plan');
|
|
281
|
+
}
|
|
282
|
+
pruneExpiredStorageMaintenancePlans();
|
|
283
|
+
let nonce;
|
|
284
|
+
do {
|
|
285
|
+
nonce = (0, crypto_1.randomBytes)(32).toString('base64url');
|
|
286
|
+
} while (issuedStorageMaintenancePlans.has(nonce));
|
|
287
|
+
issuedStorageMaintenancePlans.set(nonce, {
|
|
288
|
+
expiresAt: Date.now() + STORAGE_PLAN_TOKEN_TTL_MS,
|
|
289
|
+
plan: cloneStorageMaintenancePlan(plan),
|
|
290
|
+
});
|
|
291
|
+
return `v2.${nonce}`;
|
|
292
|
+
}
|
|
293
|
+
/** Peek at an active plan token without consuming it (used only for preview/dry-run). */
|
|
294
|
+
function deserializeStorageMaintenancePlan(token) {
|
|
295
|
+
const issued = readIssuedStorageMaintenancePlan(token, false);
|
|
296
|
+
return issued ? cloneStorageMaintenancePlan(issued) : undefined;
|
|
297
|
+
}
|
|
298
|
+
/** Consume a plan token exactly once before a confirmed mutation attempt. */
|
|
299
|
+
function consumeStorageMaintenancePlan(token) {
|
|
300
|
+
const issued = readIssuedStorageMaintenancePlan(token, true);
|
|
301
|
+
return issued ? cloneStorageMaintenancePlan(issued) : undefined;
|
|
302
|
+
}
|
|
303
|
+
/** Clear process-local plan capabilities, primarily for process-boundary tests. */
|
|
304
|
+
function resetStorageMaintenancePlanRegistry() {
|
|
305
|
+
issuedStorageMaintenancePlans.clear();
|
|
190
306
|
}
|
|
191
307
|
function formatStorageReport(report) {
|
|
192
308
|
const lines = [
|
|
@@ -223,7 +339,8 @@ function formatStorageCleanupResult(result) {
|
|
|
223
339
|
const lines = [
|
|
224
340
|
result.dryRun ? 'Orion Code Storage Cleanup (dry run)' : 'Orion Code Storage Cleanup',
|
|
225
341
|
'─'.repeat(40),
|
|
226
|
-
`
|
|
342
|
+
`Deleted ${result.deletedPaths.length}`,
|
|
343
|
+
`Quarantined ${result.quarantinedPaths.length}`,
|
|
227
344
|
`Vector rows ${result.vectorDeletedRows}`,
|
|
228
345
|
];
|
|
229
346
|
for (const path of result.deletedPaths.slice(0, 20)) {
|
|
@@ -235,65 +352,512 @@ function formatStorageCleanupResult(result) {
|
|
|
235
352
|
if (result.skippedPaths.length > 0) {
|
|
236
353
|
lines.push(`Skipped ${result.skippedPaths.length}`);
|
|
237
354
|
}
|
|
355
|
+
if (result.quarantinedPaths.length > 0) {
|
|
356
|
+
for (const path of result.quarantinedPaths.slice(0, 20)) {
|
|
357
|
+
lines.push(` ${result.dryRun ? 'would quarantine' : 'retained'} ${path}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
238
360
|
return lines.join('\n');
|
|
239
361
|
}
|
|
240
362
|
function listProjectSummaries(projectsDir) {
|
|
241
|
-
if (!(
|
|
363
|
+
if (!safeIsDirectory(projectsDir))
|
|
242
364
|
return [];
|
|
243
365
|
const summaries = [];
|
|
244
366
|
for (const projectKey of (0, fs_1.readdirSync)(projectsDir)) {
|
|
245
367
|
const projectDir = (0, path_1.join)(projectsDir, projectKey);
|
|
246
368
|
if (!safeIsDirectory(projectDir))
|
|
247
369
|
continue;
|
|
248
|
-
const
|
|
249
|
-
const
|
|
370
|
+
const metadata = inspectProjectMetadata(projectDir, projectKey);
|
|
371
|
+
const repair = inspectProjectMetadataRepair(projectsDir, projectKey).action;
|
|
372
|
+
const projectPath = metadata.valid ? metadata.projectPath : repair?.projectPath;
|
|
250
373
|
const sessionsDir = (0, path_1.join)(projectDir, 'sessions');
|
|
251
|
-
const sessions = (
|
|
252
|
-
? (0, fs_1.readdirSync)(sessionsDir).filter(file => file.endsWith('.json') &&
|
|
374
|
+
const sessions = safeIsDirectory(sessionsDir)
|
|
375
|
+
? (0, fs_1.readdirSync)(sessionsDir).filter(file => file.endsWith('.json') &&
|
|
376
|
+
!session_storage_1.SESSION_SIDECAR_SUFFIXES.some(s => file.endsWith(s)) &&
|
|
377
|
+
safeIsFile((0, path_1.join)(sessionsDir, file))).length
|
|
253
378
|
: 0;
|
|
254
379
|
summaries.push({
|
|
255
380
|
projectKey,
|
|
256
381
|
path: projectDir,
|
|
257
382
|
bytes: dirSize(projectDir),
|
|
258
|
-
hasMetadata:
|
|
383
|
+
hasMetadata: metadata.valid,
|
|
259
384
|
projectPath,
|
|
260
385
|
sessions,
|
|
261
386
|
});
|
|
262
387
|
}
|
|
263
388
|
return summaries.sort((a, b) => b.bytes - a.bytes || a.projectKey.localeCompare(b.projectKey));
|
|
264
389
|
}
|
|
265
|
-
function
|
|
390
|
+
function inspectProjectMetadata(projectDir, expectedProjectKey) {
|
|
266
391
|
const metadataPath = (0, path_1.join)(projectDir, 'project.json');
|
|
392
|
+
const inspected = readTextFileNoFollow(metadataPath);
|
|
393
|
+
if (inspected.status === 'missing') {
|
|
394
|
+
return { exists: false, valid: false, repairable: true, fingerprint: 'missing' };
|
|
395
|
+
}
|
|
396
|
+
if (inspected.status !== 'ok') {
|
|
397
|
+
return {
|
|
398
|
+
exists: true,
|
|
399
|
+
valid: false,
|
|
400
|
+
repairable: false,
|
|
401
|
+
fingerprint: inspected.fingerprint,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
const fingerprint = fingerprintText(inspected.raw);
|
|
267
405
|
try {
|
|
268
|
-
const parsed = JSON.parse(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
406
|
+
const parsed = JSON.parse(inspected.raw);
|
|
407
|
+
const projectPath = normalizeProjectPath(parsed.projectPath);
|
|
408
|
+
const valid = parsed.schemaVersion === config_dir_1.PROJECT_METADATA_SCHEMA_VERSION &&
|
|
409
|
+
parsed.projectKey === expectedProjectKey &&
|
|
410
|
+
!!projectPath &&
|
|
411
|
+
(0, config_dir_1.getCanonicalProjectKey)(projectPath) === expectedProjectKey;
|
|
412
|
+
return { exists: true, valid, repairable: true, fingerprint, projectPath };
|
|
272
413
|
}
|
|
273
414
|
catch {
|
|
274
|
-
|
|
415
|
+
return { exists: true, valid: false, repairable: true, fingerprint };
|
|
275
416
|
}
|
|
417
|
+
}
|
|
418
|
+
function inspectProjectSessions(projectDir) {
|
|
276
419
|
const sessionsDir = (0, path_1.join)(projectDir, 'sessions');
|
|
277
|
-
if (!(
|
|
278
|
-
return
|
|
279
|
-
|
|
420
|
+
if (!pathExistsNoFollow(sessionsDir)) {
|
|
421
|
+
return { safe: true, fingerprint: fingerprintText('missing'), projectPaths: [] };
|
|
422
|
+
}
|
|
423
|
+
if (!safeIsDirectory(sessionsDir)) {
|
|
424
|
+
return { safe: false, fingerprint: fingerprintText('unsafe-directory'), projectPaths: [] };
|
|
425
|
+
}
|
|
426
|
+
const evidence = [];
|
|
427
|
+
const projectPaths = [];
|
|
428
|
+
let files;
|
|
429
|
+
try {
|
|
430
|
+
files = (0, fs_1.readdirSync)(sessionsDir).sort();
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
return { safe: false, fingerprint: fingerprintText('unreadable-directory'), projectPaths: [] };
|
|
434
|
+
}
|
|
435
|
+
for (const file of files) {
|
|
280
436
|
if (!file.endsWith('.json') || session_storage_1.SESSION_SIDECAR_SUFFIXES.some(s => file.endsWith(s)))
|
|
281
437
|
continue;
|
|
438
|
+
const inspected = readTextFileNoFollow((0, path_1.join)(sessionsDir, file));
|
|
439
|
+
if (inspected.status !== 'ok') {
|
|
440
|
+
return {
|
|
441
|
+
safe: false,
|
|
442
|
+
fingerprint: fingerprintText(`${file}:${inspected.fingerprint}`),
|
|
443
|
+
projectPaths: [],
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
evidence.push(`${file}:${fingerprintText(inspected.raw)}`);
|
|
282
447
|
try {
|
|
283
|
-
const parsed = JSON.parse(
|
|
284
|
-
|
|
285
|
-
|
|
448
|
+
const parsed = JSON.parse(inspected.raw);
|
|
449
|
+
const projectPath = normalizeProjectPath(parsed.projectPath);
|
|
450
|
+
if (projectPath)
|
|
451
|
+
projectPaths.push(projectPath);
|
|
452
|
+
}
|
|
453
|
+
catch { }
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
safe: true,
|
|
457
|
+
fingerprint: fingerprintText(evidence.join('\0')),
|
|
458
|
+
projectPaths,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function inspectProjectMetadataRepair(projectsDir, projectKey) {
|
|
462
|
+
if (HASH_PROJECT_RE.test(projectKey) || TEMP_PROJECT_RE.test(projectKey)) {
|
|
463
|
+
return { skipped: true };
|
|
464
|
+
}
|
|
465
|
+
const projectDir = (0, path_1.join)(projectsDir, projectKey);
|
|
466
|
+
if (!safeIsDirectory(projectDir))
|
|
467
|
+
return { skipped: true };
|
|
468
|
+
const metadata = inspectProjectMetadata(projectDir, projectKey);
|
|
469
|
+
if (metadata.valid)
|
|
470
|
+
return { skipped: false };
|
|
471
|
+
if (!metadata.repairable)
|
|
472
|
+
return { skipped: true };
|
|
473
|
+
const sessions = inspectProjectSessions(projectDir);
|
|
474
|
+
if (!sessions.safe)
|
|
475
|
+
return { skipped: true };
|
|
476
|
+
const inferredPaths = new Set(sessions.projectPaths);
|
|
477
|
+
if (metadata.projectPath)
|
|
478
|
+
inferredPaths.add(metadata.projectPath);
|
|
479
|
+
if (inferredPaths.size !== 1)
|
|
480
|
+
return { skipped: true };
|
|
481
|
+
const projectPath = [...inferredPaths][0];
|
|
482
|
+
if ((0, config_dir_1.getCanonicalProjectKey)(projectPath) !== projectKey)
|
|
483
|
+
return { skipped: true };
|
|
484
|
+
return {
|
|
485
|
+
skipped: false,
|
|
486
|
+
action: {
|
|
487
|
+
kind: metadata.exists ? 'invalid-project-metadata' : 'missing-project-metadata',
|
|
488
|
+
projectKey,
|
|
489
|
+
projectDir,
|
|
490
|
+
projectPath,
|
|
491
|
+
metadataPath: (0, path_1.join)(projectDir, 'project.json'),
|
|
492
|
+
stateFingerprint: fingerprintText(`${pathIdentityFingerprint(projectDir)}\0${metadata.fingerprint}\0${sessions.fingerprint}`),
|
|
493
|
+
},
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function normalizeProjectPath(value) {
|
|
497
|
+
if (typeof value !== 'string' || !value.trim())
|
|
498
|
+
return undefined;
|
|
499
|
+
return (0, config_dir_1.resolveProjectStoragePath)(value);
|
|
500
|
+
}
|
|
501
|
+
function isPathCleanupKind(kind) {
|
|
502
|
+
return (kind === 'legacy-global-sessions' ||
|
|
503
|
+
kind === 'legacy-global-memory' ||
|
|
504
|
+
kind === 'legacy-hash-project' ||
|
|
505
|
+
kind === 'temp-project' ||
|
|
506
|
+
kind === 'empty-project');
|
|
507
|
+
}
|
|
508
|
+
function quarantinePlannedPath(action) {
|
|
509
|
+
const parent = (0, path_1.dirname)(action.path);
|
|
510
|
+
const quarantinePath = (0, path_1.join)(parent, `.${(0, path_1.basename)(action.path)}.orion-quarantine-${(0, crypto_1.randomBytes)(24).toString('hex')}`);
|
|
511
|
+
try {
|
|
512
|
+
if (pathExistsNoFollow(quarantinePath))
|
|
513
|
+
return { status: 'skipped' };
|
|
514
|
+
(0, fs_1.renameSync)(action.path, quarantinePath);
|
|
515
|
+
}
|
|
516
|
+
catch {
|
|
517
|
+
return { status: 'skipped' };
|
|
518
|
+
}
|
|
519
|
+
const quarantined = inspectPathTreeNoFollow(quarantinePath);
|
|
520
|
+
if (!quarantined || quarantined.fingerprint !== action.stateFingerprint) {
|
|
521
|
+
if (!pathExistsNoFollow(action.path)) {
|
|
522
|
+
try {
|
|
523
|
+
(0, fs_1.renameSync)(quarantinePath, action.path);
|
|
524
|
+
return { status: 'restored' };
|
|
286
525
|
}
|
|
526
|
+
catch { }
|
|
287
527
|
}
|
|
288
|
-
|
|
289
|
-
|
|
528
|
+
return {
|
|
529
|
+
status: 'skipped',
|
|
530
|
+
quarantinePath: pathExistsNoFollow(quarantinePath) ? quarantinePath : undefined,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
return { status: 'quarantined', quarantinePath };
|
|
534
|
+
}
|
|
535
|
+
function inspectCleanupPathAction(roots, kind, path, projectKey) {
|
|
536
|
+
if (!isExpectedCleanupPath(roots, kind, path, projectKey))
|
|
537
|
+
return undefined;
|
|
538
|
+
const state = inspectPathTreeNoFollow(path);
|
|
539
|
+
if (!state || state.type !== 'directory')
|
|
540
|
+
return undefined;
|
|
541
|
+
if (kind === 'empty-project' && state.bytes !== 0)
|
|
542
|
+
return undefined;
|
|
543
|
+
return { type: 'path', kind, path, projectKey, stateFingerprint: state.fingerprint };
|
|
544
|
+
}
|
|
545
|
+
function isExpectedCleanupPath(roots, kind, path, projectKey) {
|
|
546
|
+
if (kind === 'legacy-global-sessions')
|
|
547
|
+
return path === (0, path_1.join)(roots.configHome, 'sessions');
|
|
548
|
+
if (kind === 'legacy-global-memory')
|
|
549
|
+
return path === (0, path_1.join)(roots.configHome, 'memory');
|
|
550
|
+
if (!projectKey ||
|
|
551
|
+
!isSafeProjectKey(projectKey) ||
|
|
552
|
+
path !== (0, path_1.join)(roots.projectsDir, projectKey)) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
if (kind === 'legacy-hash-project')
|
|
556
|
+
return HASH_PROJECT_RE.test(projectKey);
|
|
557
|
+
if (kind === 'temp-project')
|
|
558
|
+
return TEMP_PROJECT_RE.test(projectKey);
|
|
559
|
+
return !HASH_PROJECT_RE.test(projectKey) && !TEMP_PROJECT_RE.test(projectKey);
|
|
560
|
+
}
|
|
561
|
+
function sameCleanupPathAction(current, planned) {
|
|
562
|
+
return (current.kind === planned.kind &&
|
|
563
|
+
current.path === planned.path &&
|
|
564
|
+
current.projectKey === planned.projectKey &&
|
|
565
|
+
current.stateFingerprint === planned.stateFingerprint);
|
|
566
|
+
}
|
|
567
|
+
function inspectVectorCleanupAction(roots, projectKey) {
|
|
568
|
+
if (!isSafeProjectKey(projectKey) || safeIsDirectory((0, path_1.join)(roots.projectsDir, projectKey))) {
|
|
569
|
+
return undefined;
|
|
570
|
+
}
|
|
571
|
+
const snapshot = readVectorProjectSnapshot(roots.configHome, projectKey);
|
|
572
|
+
if (!snapshot || snapshot.rows.length === 0)
|
|
573
|
+
return undefined;
|
|
574
|
+
return {
|
|
575
|
+
type: 'vector',
|
|
576
|
+
kind: 'vector-orphan-project',
|
|
577
|
+
projectKey,
|
|
578
|
+
rowCount: snapshot.rows.length,
|
|
579
|
+
databaseIdentity: snapshot.databaseIdentity,
|
|
580
|
+
rows: snapshot.rows,
|
|
581
|
+
stateFingerprint: fingerprintText(canonicalJson({ databaseIdentity: snapshot.databaseIdentity, rows: snapshot.rows })),
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
function sameVectorCleanupAction(current, planned) {
|
|
585
|
+
return (current.projectKey === planned.projectKey &&
|
|
586
|
+
current.databaseIdentity === planned.databaseIdentity &&
|
|
587
|
+
canonicalJson(current.rows) === canonicalJson(planned.rows) &&
|
|
588
|
+
current.stateFingerprint === planned.stateFingerprint &&
|
|
589
|
+
current.rowCount === planned.rowCount);
|
|
590
|
+
}
|
|
591
|
+
function cleanupActionKey(action) {
|
|
592
|
+
return action.type === 'path' ? action.path : `vector:${action.projectKey}`;
|
|
593
|
+
}
|
|
594
|
+
function sameStorageRoots(configHome, projectsDir) {
|
|
595
|
+
return configHome === (0, config_dir_1.getConfigHome)() && projectsDir === (0, config_dir_1.getProjectsDir)();
|
|
596
|
+
}
|
|
597
|
+
function isSafeProjectKey(projectKey) {
|
|
598
|
+
return (!!projectKey &&
|
|
599
|
+
projectKey !== '.' &&
|
|
600
|
+
projectKey !== '..' &&
|
|
601
|
+
!projectKey.includes('/') &&
|
|
602
|
+
!projectKey.includes('\\'));
|
|
603
|
+
}
|
|
604
|
+
function inspectPathTreeNoFollow(target) {
|
|
605
|
+
let before;
|
|
606
|
+
try {
|
|
607
|
+
before = (0, fs_1.lstatSync)(target);
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
return undefined;
|
|
611
|
+
}
|
|
612
|
+
if (before.isSymbolicLink())
|
|
613
|
+
return undefined;
|
|
614
|
+
if (before.isFile()) {
|
|
615
|
+
const inspected = readFileNoFollow(target);
|
|
616
|
+
if (inspected.status !== 'ok' || !inspected.raw)
|
|
617
|
+
return undefined;
|
|
618
|
+
return {
|
|
619
|
+
type: 'file',
|
|
620
|
+
bytes: inspected.raw.length,
|
|
621
|
+
fingerprint: fingerprintText(`file:${stableObjectIdentity(before)}:${inspected.fingerprint}`),
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
if (!before.isDirectory())
|
|
625
|
+
return undefined;
|
|
626
|
+
let entries;
|
|
627
|
+
try {
|
|
628
|
+
entries = (0, fs_1.readdirSync)(target).sort();
|
|
629
|
+
}
|
|
630
|
+
catch {
|
|
631
|
+
return undefined;
|
|
632
|
+
}
|
|
633
|
+
const children = [];
|
|
634
|
+
let bytes = 0;
|
|
635
|
+
for (const entry of entries) {
|
|
636
|
+
const state = inspectPathTreeNoFollow((0, path_1.join)(target, entry));
|
|
637
|
+
if (!state)
|
|
638
|
+
return undefined;
|
|
639
|
+
bytes += state.bytes;
|
|
640
|
+
children.push(`${entry}:${state.fingerprint}`);
|
|
641
|
+
}
|
|
642
|
+
let after;
|
|
643
|
+
try {
|
|
644
|
+
after = (0, fs_1.lstatSync)(target);
|
|
645
|
+
}
|
|
646
|
+
catch {
|
|
647
|
+
return undefined;
|
|
648
|
+
}
|
|
649
|
+
if (!after.isDirectory() || !sameStatIdentity(before, after))
|
|
650
|
+
return undefined;
|
|
651
|
+
return {
|
|
652
|
+
type: 'directory',
|
|
653
|
+
bytes,
|
|
654
|
+
fingerprint: fingerprintText(`directory:${stableObjectIdentity(after)}\0${children.join('\0')}`),
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function readTextFileNoFollow(path) {
|
|
658
|
+
const inspected = readFileNoFollow(path);
|
|
659
|
+
if (inspected.status !== 'ok')
|
|
660
|
+
return inspected;
|
|
661
|
+
return {
|
|
662
|
+
status: 'ok',
|
|
663
|
+
fingerprint: inspected.fingerprint,
|
|
664
|
+
raw: inspected.raw.toString('utf8'),
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function readFileNoFollow(path) {
|
|
668
|
+
let before;
|
|
669
|
+
try {
|
|
670
|
+
before = (0, fs_1.lstatSync)(path);
|
|
671
|
+
}
|
|
672
|
+
catch (error) {
|
|
673
|
+
const code = error.code;
|
|
674
|
+
return {
|
|
675
|
+
status: code === 'ENOENT' ? 'missing' : 'unreadable',
|
|
676
|
+
fingerprint: fingerprintText(code || 'lstat-error'),
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (before.isSymbolicLink() || !before.isFile()) {
|
|
680
|
+
return { status: 'unsafe', fingerprint: fingerprintText(`unsafe:${statIdentity(before)}`) };
|
|
681
|
+
}
|
|
682
|
+
let fd;
|
|
683
|
+
try {
|
|
684
|
+
fd = (0, fs_1.openSync)(path, fs_1.constants.O_RDONLY | fs_1.constants.O_NOFOLLOW);
|
|
685
|
+
const opened = (0, fs_1.fstatSync)(fd);
|
|
686
|
+
if (!opened.isFile() || !sameStatIdentity(before, opened)) {
|
|
687
|
+
return { status: 'unsafe', fingerprint: fingerprintText('identity-drift') };
|
|
290
688
|
}
|
|
689
|
+
const raw = (0, fs_1.readFileSync)(fd);
|
|
690
|
+
const after = (0, fs_1.fstatSync)(fd);
|
|
691
|
+
if (!sameStatIdentity(opened, after)) {
|
|
692
|
+
return { status: 'unreadable', fingerprint: fingerprintText('read-drift') };
|
|
693
|
+
}
|
|
694
|
+
return { status: 'ok', fingerprint: fingerprintText(raw.toString('base64')), raw };
|
|
695
|
+
}
|
|
696
|
+
catch (error) {
|
|
697
|
+
const code = error.code || 'read-error';
|
|
698
|
+
return {
|
|
699
|
+
status: code === 'ELOOP' ? 'unsafe' : 'unreadable',
|
|
700
|
+
fingerprint: fingerprintText(`${code}:${statIdentity(before)}`),
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
finally {
|
|
704
|
+
if (fd !== undefined)
|
|
705
|
+
(0, fs_1.closeSync)(fd);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function statIdentity(stat) {
|
|
709
|
+
return [stat.dev, stat.ino, stat.mode, stat.size, stat.mtimeMs, stat.ctimeMs].join(':');
|
|
710
|
+
}
|
|
711
|
+
function stableObjectIdentity(stat) {
|
|
712
|
+
return [stat.dev, stat.ino, stat.mode].join(':');
|
|
713
|
+
}
|
|
714
|
+
function sameStatIdentity(a, b) {
|
|
715
|
+
return statIdentity(a) === statIdentity(b);
|
|
716
|
+
}
|
|
717
|
+
function pathIdentityFingerprint(path) {
|
|
718
|
+
try {
|
|
719
|
+
const stat = (0, fs_1.lstatSync)(path);
|
|
720
|
+
if (stat.isSymbolicLink())
|
|
721
|
+
return fingerprintText('unsafe-symlink');
|
|
722
|
+
return fingerprintText(stableObjectIdentity(stat));
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
return fingerprintText(error.code || 'lstat-error');
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function pathExistsNoFollow(path) {
|
|
729
|
+
try {
|
|
730
|
+
(0, fs_1.lstatSync)(path);
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
return false;
|
|
291
735
|
}
|
|
292
|
-
|
|
736
|
+
}
|
|
737
|
+
function safeIsFile(path) {
|
|
738
|
+
try {
|
|
739
|
+
return (0, fs_1.lstatSync)(path).isFile();
|
|
740
|
+
}
|
|
741
|
+
catch {
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function isRecord(value) {
|
|
746
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
747
|
+
}
|
|
748
|
+
function cloneStorageMaintenancePlan(plan) {
|
|
749
|
+
return JSON.parse(JSON.stringify(plan));
|
|
750
|
+
}
|
|
751
|
+
function readIssuedStorageMaintenancePlan(token, consume) {
|
|
752
|
+
pruneExpiredStorageMaintenancePlans();
|
|
753
|
+
const match = token.match(/^v2\.([A-Za-z0-9_-]{43})$/);
|
|
754
|
+
if (!match)
|
|
755
|
+
return undefined;
|
|
756
|
+
const issued = issuedStorageMaintenancePlans.get(match[1]);
|
|
757
|
+
if (!issued || issued.expiresAt <= Date.now()) {
|
|
758
|
+
if (issued)
|
|
759
|
+
issuedStorageMaintenancePlans.delete(match[1]);
|
|
760
|
+
return undefined;
|
|
761
|
+
}
|
|
762
|
+
if (consume)
|
|
763
|
+
issuedStorageMaintenancePlans.delete(match[1]);
|
|
764
|
+
return issued.plan;
|
|
765
|
+
}
|
|
766
|
+
function pruneExpiredStorageMaintenancePlans() {
|
|
767
|
+
const now = Date.now();
|
|
768
|
+
for (const [nonce, issued] of issuedStorageMaintenancePlans) {
|
|
769
|
+
if (issued.expiresAt <= now)
|
|
770
|
+
issuedStorageMaintenancePlans.delete(nonce);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
function isSha256(value) {
|
|
774
|
+
return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
|
|
775
|
+
}
|
|
776
|
+
function isStringArray(value) {
|
|
777
|
+
return Array.isArray(value) && value.every(item => typeof item === 'string');
|
|
778
|
+
}
|
|
779
|
+
function isStorageIssue(value) {
|
|
780
|
+
if (!isRecord(value) || !isStorageIssueKind(value.kind))
|
|
781
|
+
return false;
|
|
782
|
+
return (typeof value.summary === 'string' &&
|
|
783
|
+
typeof value.canCleanup === 'boolean' &&
|
|
784
|
+
(value.path === undefined || typeof value.path === 'string') &&
|
|
785
|
+
(value.projectKey === undefined || typeof value.projectKey === 'string') &&
|
|
786
|
+
(value.bytes === undefined || typeof value.bytes === 'number'));
|
|
787
|
+
}
|
|
788
|
+
function isStorageIssueKind(value) {
|
|
789
|
+
return (value === 'legacy-global-sessions' ||
|
|
790
|
+
value === 'legacy-global-memory' ||
|
|
791
|
+
value === 'legacy-hash-project' ||
|
|
792
|
+
value === 'temp-project' ||
|
|
793
|
+
value === 'empty-project' ||
|
|
794
|
+
value === 'missing-project-metadata' ||
|
|
795
|
+
value === 'invalid-project-metadata' ||
|
|
796
|
+
value === 'vector-orphan-project');
|
|
797
|
+
}
|
|
798
|
+
function isStorageCleanupAction(value) {
|
|
799
|
+
if (!isRecord(value) || !isSha256(value.stateFingerprint))
|
|
800
|
+
return false;
|
|
801
|
+
if (value.type === 'path') {
|
|
802
|
+
return (isPathCleanupKind(value.kind) &&
|
|
803
|
+
typeof value.path === 'string' &&
|
|
804
|
+
(value.projectKey === undefined || typeof value.projectKey === 'string'));
|
|
805
|
+
}
|
|
806
|
+
return (value.type === 'vector' &&
|
|
807
|
+
value.kind === 'vector-orphan-project' &&
|
|
808
|
+
typeof value.projectKey === 'string' &&
|
|
809
|
+
isSafeProjectKey(value.projectKey) &&
|
|
810
|
+
Number.isSafeInteger(value.rowCount) &&
|
|
811
|
+
value.rowCount > 0 &&
|
|
812
|
+
isSha256(value.databaseIdentity) &&
|
|
813
|
+
Array.isArray(value.rows) &&
|
|
814
|
+
value.rows.length === value.rowCount &&
|
|
815
|
+
value.rows.every(isStorageVectorRowSnapshot) &&
|
|
816
|
+
new Set(value.rows.map(row => row.id)).size === value.rows.length &&
|
|
817
|
+
fingerprintText(canonicalJson({ databaseIdentity: value.databaseIdentity, rows: value.rows })) === value.stateFingerprint);
|
|
818
|
+
}
|
|
819
|
+
function isStorageVectorRowSnapshot(value) {
|
|
820
|
+
return (isRecord(value) &&
|
|
821
|
+
typeof value.id === 'string' &&
|
|
822
|
+
typeof value.canonicalRow === 'string' &&
|
|
823
|
+
isSha256(value.rowFingerprint) &&
|
|
824
|
+
fingerprintText(value.canonicalRow) === value.rowFingerprint);
|
|
825
|
+
}
|
|
826
|
+
function isStorageCleanupPlan(value) {
|
|
827
|
+
return (isRecord(value) &&
|
|
828
|
+
value.version === 1 &&
|
|
829
|
+
typeof value.configHome === 'string' &&
|
|
830
|
+
typeof value.projectsDir === 'string' &&
|
|
831
|
+
Array.isArray(value.actions) &&
|
|
832
|
+
value.actions.every(isStorageCleanupAction) &&
|
|
833
|
+
isStringArray(value.skipped) &&
|
|
834
|
+
Array.isArray(value.issues) &&
|
|
835
|
+
value.issues.every(isStorageIssue));
|
|
836
|
+
}
|
|
837
|
+
function isProjectMetadataRepairAction(value) {
|
|
838
|
+
return (isRecord(value) &&
|
|
839
|
+
(value.kind === 'missing-project-metadata' || value.kind === 'invalid-project-metadata') &&
|
|
840
|
+
typeof value.projectKey === 'string' &&
|
|
841
|
+
isSafeProjectKey(value.projectKey) &&
|
|
842
|
+
typeof value.projectDir === 'string' &&
|
|
843
|
+
typeof value.projectPath === 'string' &&
|
|
844
|
+
typeof value.metadataPath === 'string' &&
|
|
845
|
+
isSha256(value.stateFingerprint));
|
|
846
|
+
}
|
|
847
|
+
function isProjectMetadataRepairPlan(value) {
|
|
848
|
+
return (isRecord(value) &&
|
|
849
|
+
value.version === 1 &&
|
|
850
|
+
typeof value.projectsDir === 'string' &&
|
|
851
|
+
Array.isArray(value.actions) &&
|
|
852
|
+
value.actions.every(isProjectMetadataRepairAction) &&
|
|
853
|
+
isStringArray(value.skipped));
|
|
854
|
+
}
|
|
855
|
+
function fingerprintText(value) {
|
|
856
|
+
return (0, crypto_1.createHash)('sha256').update(value).digest('hex');
|
|
293
857
|
}
|
|
294
858
|
function inspectVectorProjects(configHome, validProjectKeys) {
|
|
295
859
|
const dbPath = (0, path_1.join)(configHome, 'vector.db');
|
|
296
|
-
if (!(
|
|
860
|
+
if (!safeIsFile(dbPath))
|
|
297
861
|
return [];
|
|
298
862
|
let db = null;
|
|
299
863
|
try {
|
|
@@ -301,12 +865,14 @@ function inspectVectorProjects(configHome, validProjectKeys) {
|
|
|
301
865
|
const hasMemories = db.prepare("SELECT name FROM sqlite_master WHERE name = 'memories'").get();
|
|
302
866
|
if (!hasMemories)
|
|
303
867
|
return [];
|
|
304
|
-
const rows = db
|
|
868
|
+
const rows = db
|
|
869
|
+
.prepare(`
|
|
305
870
|
SELECT COALESCE(project, 'global') as project, COUNT(*) as rows
|
|
306
871
|
FROM memories
|
|
307
872
|
GROUP BY COALESCE(project, 'global')
|
|
308
873
|
ORDER BY rows DESC, project ASC
|
|
309
|
-
`)
|
|
874
|
+
`)
|
|
875
|
+
.all();
|
|
310
876
|
return rows.map(row => ({
|
|
311
877
|
project: row.project,
|
|
312
878
|
rows: row.rows,
|
|
@@ -320,39 +886,140 @@ function inspectVectorProjects(configHome, validProjectKeys) {
|
|
|
320
886
|
db?.close();
|
|
321
887
|
}
|
|
322
888
|
}
|
|
323
|
-
function
|
|
889
|
+
function readVectorProjectSnapshot(configHome, project) {
|
|
324
890
|
const dbPath = (0, path_1.join)(configHome, 'vector.db');
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
891
|
+
const databaseIdentity = databaseFileIdentity(dbPath);
|
|
892
|
+
if (!databaseIdentity)
|
|
893
|
+
return undefined;
|
|
894
|
+
let db;
|
|
328
895
|
try {
|
|
896
|
+
db = new better_sqlite3_1.default(dbPath, { readonly: true });
|
|
897
|
+
db.defaultSafeIntegers(true);
|
|
329
898
|
const hasMemories = db.prepare("SELECT name FROM sqlite_master WHERE name = 'memories'").get();
|
|
330
899
|
if (!hasMemories)
|
|
331
|
-
return
|
|
332
|
-
const
|
|
333
|
-
if (
|
|
334
|
-
return
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
900
|
+
return undefined;
|
|
901
|
+
const rows = readVectorRowsFromDatabase(db, project);
|
|
902
|
+
if (!rows || databaseFileIdentity(dbPath) !== databaseIdentity)
|
|
903
|
+
return undefined;
|
|
904
|
+
return { databaseIdentity, rows };
|
|
905
|
+
}
|
|
906
|
+
catch {
|
|
907
|
+
return undefined;
|
|
908
|
+
}
|
|
909
|
+
finally {
|
|
910
|
+
db?.close();
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function deletePlannedVectorRows(configHome, action) {
|
|
914
|
+
const dbPath = (0, path_1.join)(configHome, 'vector.db');
|
|
915
|
+
if (databaseFileIdentity(dbPath) !== action.databaseIdentity)
|
|
916
|
+
return 0;
|
|
917
|
+
const db = new better_sqlite3_1.default(dbPath);
|
|
918
|
+
db.defaultSafeIntegers(true);
|
|
919
|
+
try {
|
|
920
|
+
const apply = db.transaction(() => {
|
|
921
|
+
const hasMemories = db
|
|
922
|
+
.prepare("SELECT name FROM sqlite_master WHERE name = 'memories'")
|
|
923
|
+
.get();
|
|
924
|
+
if (!hasMemories)
|
|
925
|
+
return 0;
|
|
926
|
+
if (databaseFileIdentity(dbPath) !== action.databaseIdentity)
|
|
927
|
+
return 0;
|
|
928
|
+
const currentRows = readVectorRowsFromDatabase(db, action.projectKey);
|
|
929
|
+
if (!currentRows)
|
|
930
|
+
return 0;
|
|
931
|
+
const currentStateFingerprint = fingerprintText(canonicalJson({ databaseIdentity: action.databaseIdentity, rows: currentRows }));
|
|
932
|
+
if (currentRows.length !== action.rowCount ||
|
|
933
|
+
canonicalJson(currentRows) !== canonicalJson(action.rows) ||
|
|
934
|
+
currentStateFingerprint !== action.stateFingerprint ||
|
|
935
|
+
databaseFileIdentity(dbPath) !== action.databaseIdentity) {
|
|
936
|
+
return 0;
|
|
937
|
+
}
|
|
938
|
+
const currentIds = action.rows.map(row => row.id);
|
|
939
|
+
const placeholders = currentIds.map(() => '?').join(', ');
|
|
940
|
+
if (db.prepare("SELECT name FROM sqlite_master WHERE name = 'vec_memories'").get() &&
|
|
941
|
+
db.prepare("SELECT name FROM sqlite_master WHERE name = 'memory_vectors'").get()) {
|
|
942
|
+
db.prepare(`DELETE FROM vec_memories WHERE rowid IN (SELECT vector_rowid FROM memory_vectors WHERE memory_id IN (${placeholders}))`).run(...currentIds);
|
|
943
|
+
db.prepare(`DELETE FROM memory_vectors WHERE memory_id IN (${placeholders})`).run(...currentIds);
|
|
944
|
+
}
|
|
945
|
+
db.prepare(`DELETE FROM memories WHERE id IN (${placeholders})`).run(...currentIds);
|
|
946
|
+
return currentIds.length;
|
|
947
|
+
});
|
|
948
|
+
return apply.immediate();
|
|
342
949
|
}
|
|
343
950
|
finally {
|
|
344
951
|
db.close();
|
|
345
952
|
}
|
|
346
953
|
}
|
|
954
|
+
function readVectorRowsFromDatabase(db, project) {
|
|
955
|
+
const rows = db
|
|
956
|
+
.prepare('SELECT * FROM memories WHERE project = ? ORDER BY id')
|
|
957
|
+
.all(project);
|
|
958
|
+
const snapshots = [];
|
|
959
|
+
for (const row of rows) {
|
|
960
|
+
if (typeof row.id !== 'string')
|
|
961
|
+
return undefined;
|
|
962
|
+
const canonicalRow = canonicalJson(row);
|
|
963
|
+
snapshots.push({
|
|
964
|
+
id: row.id,
|
|
965
|
+
canonicalRow,
|
|
966
|
+
rowFingerprint: fingerprintText(canonicalRow),
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
return snapshots;
|
|
970
|
+
}
|
|
971
|
+
function databaseFileIdentity(path) {
|
|
972
|
+
try {
|
|
973
|
+
const stat = (0, fs_1.lstatSync)(path);
|
|
974
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
975
|
+
return undefined;
|
|
976
|
+
return fingerprintText(stableObjectIdentity(stat));
|
|
977
|
+
}
|
|
978
|
+
catch {
|
|
979
|
+
return undefined;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function canonicalJson(value) {
|
|
983
|
+
return JSON.stringify(toCanonicalJsonValue(value));
|
|
984
|
+
}
|
|
985
|
+
function toCanonicalJsonValue(value) {
|
|
986
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
987
|
+
return value;
|
|
988
|
+
if (typeof value === 'number') {
|
|
989
|
+
if (Number.isFinite(value))
|
|
990
|
+
return Object.is(value, -0) ? 0 : value;
|
|
991
|
+
return { $number: String(value) };
|
|
992
|
+
}
|
|
993
|
+
if (typeof value === 'bigint')
|
|
994
|
+
return { $bigint: value.toString() };
|
|
995
|
+
if (Buffer.isBuffer(value))
|
|
996
|
+
return { $buffer: value.toString('base64') };
|
|
997
|
+
if (Array.isArray(value))
|
|
998
|
+
return value.map(toCanonicalJsonValue);
|
|
999
|
+
if (isRecord(value)) {
|
|
1000
|
+
const result = {};
|
|
1001
|
+
for (const key of Object.keys(value).sort()) {
|
|
1002
|
+
result[key] = toCanonicalJsonValue(value[key]);
|
|
1003
|
+
}
|
|
1004
|
+
return result;
|
|
1005
|
+
}
|
|
1006
|
+
return { $unsupported: String(value) };
|
|
1007
|
+
}
|
|
347
1008
|
function dirSize(target) {
|
|
348
|
-
|
|
1009
|
+
let stat;
|
|
1010
|
+
try {
|
|
1011
|
+
stat = (0, fs_1.lstatSync)(target);
|
|
1012
|
+
}
|
|
1013
|
+
catch {
|
|
1014
|
+
return 0;
|
|
1015
|
+
}
|
|
1016
|
+
if (stat.isSymbolicLink())
|
|
1017
|
+
return 0;
|
|
1018
|
+
if (stat.isFile())
|
|
1019
|
+
return stat.size;
|
|
1020
|
+
if (!stat.isDirectory())
|
|
349
1021
|
return 0;
|
|
350
1022
|
try {
|
|
351
|
-
const stat = (0, fs_1.statSync)(target);
|
|
352
|
-
if (stat.isFile())
|
|
353
|
-
return stat.size;
|
|
354
|
-
if (!stat.isDirectory())
|
|
355
|
-
return 0;
|
|
356
1023
|
return (0, fs_1.readdirSync)(target).reduce((sum, entry) => sum + dirSize((0, path_1.join)(target, entry)), 0);
|
|
357
1024
|
}
|
|
358
1025
|
catch {
|
|
@@ -361,7 +1028,7 @@ function dirSize(target) {
|
|
|
361
1028
|
}
|
|
362
1029
|
function safeIsDirectory(target) {
|
|
363
1030
|
try {
|
|
364
|
-
return (0, fs_1.
|
|
1031
|
+
return (0, fs_1.lstatSync)(target).isDirectory();
|
|
365
1032
|
}
|
|
366
1033
|
catch {
|
|
367
1034
|
return false;
|