@aiwg/cli 2026.10.4 → 2026.10.6
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/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
- package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
- package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
- package/agentic/code/providers/model-capabilities.v1.json +74 -50
- package/bin/aiwg.mjs +3 -1
- package/dist/src/artifacts/browser-export.js +1 -0
- package/dist/src/artifacts/citation-parser.js +8 -1
- package/dist/src/artifacts/cli.js +72 -9
- package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
- package/dist/src/artifacts/fortemi-core-sync.js +26 -8
- package/dist/src/artifacts/graph-query.js +18 -8
- package/dist/src/artifacts/index-adapters.js +339 -0
- package/dist/src/artifacts/index-builder.js +146 -16
- package/dist/src/artifacts/index-files.js +15 -4
- package/dist/src/artifacts/index-preparation.js +91 -26
- package/dist/src/artifacts/index-reader.js +44 -0
- package/dist/src/artifacts/index-refresh.js +130 -56
- package/dist/src/artifacts/index-status.js +20 -5
- package/dist/src/artifacts/index-worker.mjs +9 -7
- package/dist/src/artifacts/memory-maintenance.js +503 -0
- package/dist/src/artifacts/prebuilt-relations.js +3 -1
- package/dist/src/artifacts/query-engine.js +263 -108
- package/dist/src/artifacts/relations-builder.js +2 -2
- package/dist/src/artifacts/source-graph.js +2 -2
- package/dist/src/artifacts/types.js +9 -1
- package/dist/src/cli/handlers/decision.js +19 -1
- package/dist/src/cli/handlers/guided-setup.js +470 -0
- package/dist/src/cli/handlers/help.js +2 -2
- package/dist/src/cli/handlers/housekeeper.js +1110 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/init.js +41 -6
- package/dist/src/cli/handlers/local-executor.js +5 -3
- package/dist/src/cli/handlers/mc.js +509 -53
- package/dist/src/cli/handlers/models.js +32 -2
- package/dist/src/cli/handlers/ralph-launcher.js +76 -4
- package/dist/src/cli/handlers/rules.js +1 -1
- package/dist/src/cli/handlers/script-runner.js +73 -2
- package/dist/src/cli/handlers/serve.js +5 -3
- package/dist/src/cli/handlers/sessions.js +40 -6
- package/dist/src/cli/handlers/setup.js +428 -1
- package/dist/src/cli/handlers/skill-lint.js +1 -1
- package/dist/src/cli/handlers/subcommands.js +118 -8
- package/dist/src/cli/handlers/use.js +279 -93
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/prompt-utils.js +8 -0
- package/dist/src/cli/router.js +16 -1
- package/dist/src/config/aiwg-config.js +42 -0
- package/dist/src/config/cli.js +7 -3
- package/dist/src/config/decision-config.js +14 -1
- package/dist/src/config/native-sandbox-warnings.js +25 -0
- package/dist/src/contributors/validation.js +6 -5
- package/dist/src/decision/admission.js +7 -3
- package/dist/src/decision/benchmark.js +469 -0
- package/dist/src/decision/ledger.js +22 -3
- package/dist/src/decision/pricing.js +147 -0
- package/dist/src/decision/qualification/gate-evidence.js +18 -15
- package/dist/src/decision/qualification/load-staged.js +539 -0
- package/dist/src/decision/qualification/load.js +109 -33
- package/dist/src/decision/qualification/runner.js +5 -2
- package/dist/src/decision/review/service.js +6 -4
- package/dist/src/decision/review/validate.js +23 -2
- package/dist/src/decision/savings.js +8 -3
- package/dist/src/discovery/index.js +492 -0
- package/dist/src/discovery/interview.js +233 -0
- package/dist/src/discovery/plan-apply.js +571 -0
- package/dist/src/discovery/plan-store.js +171 -0
- package/dist/src/discovery/plan-types.js +2 -0
- package/dist/src/discovery/plan-validation.js +139 -0
- package/dist/src/discovery/types.js +2 -0
- package/dist/src/extensions/commands/definitions.js +49 -9
- package/dist/src/extensions/manifest.js +71 -44
- package/dist/src/extensions/project-local-discovery.js +1 -1
- package/dist/src/extensions/project-quickref.js +18 -17
- package/dist/src/extensions/validation.js +32 -29
- package/dist/src/housekeeper/daemon-config.js +148 -0
- package/dist/src/housekeeper/ipc.js +84 -0
- package/dist/src/housekeeper/lifecycle.js +148 -0
- package/dist/src/housekeeper/platform.js +35 -0
- package/dist/src/housekeeper/status.js +70 -0
- package/dist/src/housekeeper/units.js +114 -0
- package/dist/src/jobs/flow.js +22 -21
- package/dist/src/lint/rule-activation.js +20 -5
- package/dist/src/lint/runner.js +1 -1
- package/dist/src/mcp/cli.mjs +5 -6
- package/dist/src/mcp/config-file.mjs +6 -2
- package/dist/src/mcp/config-layers.mjs +23 -7
- package/dist/src/mcp/profiles.js +18 -31
- package/dist/src/mcp/profiles.mjs +18 -32
- package/dist/src/mcp/registry.js +7 -27
- package/dist/src/mcp/registry.mjs +7 -28
- package/dist/src/mcp/server.mjs +34 -1
- package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
- package/dist/src/mcp/tools/command-run.mjs +3 -2
- package/dist/src/mcp/tools/decision.mjs +49 -48
- package/dist/src/mcp/tools/discovery.mjs +46 -21
- package/dist/src/mcp/tools/orchestration.mjs +22 -22
- package/dist/src/mcp/tools/subsystems.mjs +9 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +17 -4
- package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
- package/dist/src/memory/audit.js +719 -0
- package/dist/src/memory/canonical-context.js +392 -13
- package/dist/src/memory/canonical-maintenance.js +95 -0
- package/dist/src/memory/cli-result.js +22 -0
- package/dist/src/memory/cli.js +176 -0
- package/dist/src/memory/context-pack.js +57 -6
- package/dist/src/memory/cost.js +512 -0
- package/dist/src/memory/firewall-classification.d.mts +7 -0
- package/dist/src/memory/firewall-classification.mjs +32 -0
- package/dist/src/memory/index.js +8 -0
- package/dist/src/memory/line-maintenance.js +68 -0
- package/dist/src/memory/maintenance-cli.js +286 -0
- package/dist/src/memory/maintenance-contract.js +136 -0
- package/dist/src/memory/maintenance-index.js +18 -0
- package/dist/src/memory/maintenance-owner.js +2 -0
- package/dist/src/memory/maintenance-record.js +138 -0
- package/dist/src/memory/maintenance.js +254 -0
- package/dist/src/memory/provider-memory-adapter.js +154 -0
- package/dist/src/memory/recommend-cli.js +83 -0
- package/dist/src/memory/recommend.js +269 -0
- package/dist/src/memory/scheduled.js +81 -0
- package/dist/src/memory/taxonomy.js +44 -0
- package/dist/src/mission-protocol/codecs.js +19 -9
- package/dist/src/models/config-loader.js +11 -1
- package/dist/src/models/model-capabilities.v1.json +74 -50
- package/dist/src/models/provider-policy.js +125 -62
- package/dist/src/models/resolver.js +45 -8
- package/dist/src/models/worker-installation.d.mts +7 -0
- package/dist/src/models/worker-installation.mjs +169 -37
- package/dist/src/models/wrapper-route.js +1 -1
- package/dist/src/output-modes/registry.js +8 -7
- package/dist/src/plugin/metadata-validator.js +1 -1
- package/dist/src/providers/deploy-version.d.mts +1 -0
- package/dist/src/providers/deploy-version.mjs +17 -0
- package/dist/src/providers/provider-definitions.js +19 -16
- package/dist/src/providers/skill-ownership.d.mts +11 -2
- package/dist/src/providers/skill-ownership.mjs +35 -17
- package/dist/src/rules/activation.d.mts +11 -2
- package/dist/src/rules/activation.mjs +82 -6
- package/dist/src/rules/inventory.js +1 -1
- package/dist/src/rules/rule-frontmatter.schema.json +2 -0
- package/dist/src/schema/zod-validation.d.mts +6 -0
- package/dist/src/schema/zod-validation.mjs +50 -0
- package/dist/src/serve/executor-registry.js +297 -20
- package/dist/src/serve/executor-token.d.mts +1 -0
- package/dist/src/serve/executor-token.mjs +8 -0
- package/dist/src/serve/shared-host-scheduler.js +1102 -31
- package/dist/src/serve/worker-invocation.js +114 -15
- package/dist/src/sessions/adapters/claude.js +19 -18
- package/dist/src/sessions/adapters/codex.js +33 -19
- package/dist/src/sessions/adapters/copilot.js +14 -13
- package/dist/src/sessions/adapters/cursor.js +19 -18
- package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
- package/dist/src/sessions/adapters/factory.js +11 -10
- package/dist/src/sessions/adapters/generic.js +32 -23
- package/dist/src/sessions/adapters/grokbot.js +35 -35
- package/dist/src/sessions/adapters/hermes.js +20 -19
- package/dist/src/sessions/adapters/muse.js +50 -49
- package/dist/src/sessions/adapters/omp.js +5 -4
- package/dist/src/sessions/adapters/openclaw.js +30 -29
- package/dist/src/sessions/adapters/opencode.js +26 -26
- package/dist/src/sessions/adapters/openhuman.js +24 -23
- package/dist/src/sessions/adapters/pi.js +5 -4
- package/dist/src/sessions/adapters/windsurf.js +8 -8
- package/dist/src/sessions/analytics.js +10 -9
- package/dist/src/sessions/batch-contracts.js +1 -1
- package/dist/src/sessions/batch-import.js +202 -2
- package/dist/src/sessions/candidates.js +7 -6
- package/dist/src/sessions/contracts.js +73 -54
- package/dist/src/sessions/coverage-preflight.js +26 -1
- package/dist/src/sessions/output-registration.js +11 -10
- package/dist/src/sessions/policy.js +18 -7
- package/dist/src/sessions/refresh.js +2 -3
- package/dist/src/sessions/repository.js +4 -2
- package/dist/src/sessions/workspace-discovery.js +92 -26
- package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +42 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +106 -10
- package/dist/src/update/checker.d.mts +4 -0
- package/dist/src/update/checker.mjs +14 -0
- package/dist/src/writing/exemplar-selection.js +6 -6
- package/dist/src/writing/voice-evaluation.js +24 -23
- package/dist/src/writing/writer-migration.js +14 -13
- package/dist/src/writing/writer-profile.js +19 -18
- package/dist/src/writing/writing-brief.js +18 -17
- package/dist/src/writing/writing-receipt.js +47 -46
- package/package.json +5 -2
- package/schemas/decision/DecisionReview.schema.json +78 -1
- package/schemas/mission-v1.schema.json +3 -0
- package/tools/agents/deploy-agents.mjs +67 -39
- package/tools/agents/providers/antigravity.mjs +6 -1
- package/tools/agents/providers/base.mjs +19 -13
- package/tools/agents/providers/claude.mjs +1 -1
- package/tools/agents/providers/codex.mjs +50 -56
- package/tools/agents/providers/copilot.mjs +8 -2
- package/tools/agents/providers/cursor.mjs +1 -1
- package/tools/agents/providers/deepseek-harness.mjs +1 -1
- package/tools/agents/providers/factory.mjs +7 -1
- package/tools/agents/providers/grok-build.mjs +18 -2
- package/tools/agents/providers/grokbot.mjs +1 -1
- package/tools/agents/providers/hermes.mjs +7 -13
- package/tools/agents/providers/muse-hooks.mjs +21 -14
- package/tools/agents/providers/muse.mjs +7 -2
- package/tools/agents/providers/omp.mjs +28 -4
- package/tools/agents/providers/opencode.mjs +14 -5
- package/tools/agents/providers/openhuman.mjs +1 -1
- package/tools/agents/providers/pi.mjs +1 -1
- package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
- package/tools/agents/providers/rule-delivery.mjs +263 -71
- package/tools/agents/providers/windsurf.mjs +7 -0
- package/tools/cli/workspace-status.mjs +5 -0
- package/tools/commands/deploy-prompts-codex.mjs +11 -8
- package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
- package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
- package/tools/ralph-external/process-monitor.mjs +7 -4
- package/tools/ralph-external/session-launcher.mjs +280 -32
- package/tools/skills/deploy-skills-codex.mjs +11 -7
- package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
|
@@ -87,9 +87,14 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
87
87
|
throw new Error(`Unknown graph: ${graph}`);
|
|
88
88
|
}
|
|
89
89
|
const artifactRoot = resolveProjectAiwgDir(cwd);
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
const artifactRoots = [path.join(cwd, DEFAULT_PROJECT_AIWG_DIR), artifactRoot];
|
|
91
|
+
// Aggregate owner state contains history, tombstones and transaction payloads.
|
|
92
|
+
// It is not an ordinary artifact: only each owner's public projection is a
|
|
93
|
+
// retrieval surface, never the backing store or maintenance receipts.
|
|
94
|
+
const privateDirs = ['context/compound-memory', 'memory/transactions', 'memory/maintenance'];
|
|
95
|
+
const privateFiles = ['memory/line-memory.meta.json', 'memory/line-memory.config.json', 'memory/line-memory.lock'];
|
|
96
|
+
const excludedDirs = artifactRoots.flatMap(root => [...INDEX_SCRATCH_DIRS, ...privateDirs].map(name => path.resolve(root, name)));
|
|
97
|
+
const excludedPaths = new Set([...excludedDirs, ...artifactRoots.flatMap(root => privateFiles.map(name => path.resolve(root, name)))]);
|
|
93
98
|
// A directory's ancestors are shared by thousands of sibling artifacts.
|
|
94
99
|
// Cache only within this scan so the next build still notices new symlinks.
|
|
95
100
|
const safePaths = new Map();
|
|
@@ -109,7 +114,7 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
109
114
|
}
|
|
110
115
|
const inputPath = file;
|
|
111
116
|
file = path.resolve(file);
|
|
112
|
-
if (excludedDirs.some(dir => process.platform === 'win32'
|
|
117
|
+
if (excludedPaths.has(file) || excludedDirs.some(dir => process.platform === 'win32'
|
|
113
118
|
? pathContains(dir, file) : file === dir || file.startsWith(dir + path.sep)))
|
|
114
119
|
return false;
|
|
115
120
|
if (knownStat && !knownStat.isSymbolicLink() && safePaths.get(path.dirname(file)) === true) {
|
|
@@ -190,6 +195,10 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
190
195
|
}
|
|
191
196
|
}
|
|
192
197
|
let sources = [...files];
|
|
198
|
+
const candidates = [...sources].sort();
|
|
199
|
+
if (options.selection?.files && JSON.stringify(candidates) === JSON.stringify(options.selection.candidates)) {
|
|
200
|
+
return [...options.selection.files];
|
|
201
|
+
}
|
|
193
202
|
if (!options.includeIgnored) {
|
|
194
203
|
// Many projects ignore the whole artifact root (`.aiwg/`). Honoring that
|
|
195
204
|
// would empty the index, so ignore rules apply only beneath a scan root
|
|
@@ -225,6 +234,8 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
225
234
|
if (!selected.has(file))
|
|
226
235
|
options.fileStats.delete(file);
|
|
227
236
|
}
|
|
237
|
+
if (options.selection)
|
|
238
|
+
Object.assign(options.selection, { candidates, files: [...sources] });
|
|
228
239
|
return sources;
|
|
229
240
|
}
|
|
230
241
|
//# sourceMappingURL=index-files.js.map
|
|
@@ -1,65 +1,130 @@
|
|
|
1
1
|
/** Bounded artifact-local preparation; ordered graph reduction stays in index-builder. */
|
|
2
2
|
import os from 'node:os';
|
|
3
|
-
import {
|
|
3
|
+
import { fork } from 'node:child_process';
|
|
4
|
+
import { signalChild } from '../process/signal-child.mjs';
|
|
5
|
+
const MIB = 1024 ** 2;
|
|
6
|
+
const WORKER_HEAP_MB = 256;
|
|
7
|
+
// Leave room for V8's young generation, executable code, IPC and native allocations.
|
|
8
|
+
const WORKER_BUDGET_BYTES = 512 * MIB;
|
|
9
|
+
// Parsing and metadata extraction amplify input size; do large files only after the pool exits.
|
|
10
|
+
const MAX_WORKER_ARTIFACT_BYTES = MIB;
|
|
11
|
+
export function resolveIndexWorkers(requested, env = process.env) {
|
|
12
|
+
const value = requested ?? env.AIWG_INDEX_WORKERS;
|
|
13
|
+
if (requested === undefined && (value === undefined || value === ''))
|
|
14
|
+
return undefined;
|
|
15
|
+
if ((typeof value === 'string' && !/^\d+$/.test(value))
|
|
16
|
+
|| !Number.isSafeInteger(Number(value)) || Number(value) < 0) {
|
|
17
|
+
throw new Error(`${requested === undefined ? 'AIWG_INDEX_WORKERS' : '--index-workers'} must be a nonnegative safe integer (0 = serial)`);
|
|
18
|
+
}
|
|
19
|
+
return Number(value);
|
|
20
|
+
}
|
|
4
21
|
export function indexPreparationWorkers(jobs, requested, cores = os.availableParallelism(), memory = Math.min(os.freemem(), process.availableMemory?.() ?? Infinity), totalBytes = 0) {
|
|
5
|
-
|
|
6
|
-
throw new Error('Index workers must be a nonnegative integer');
|
|
22
|
+
requested = resolveIndexWorkers(requested);
|
|
7
23
|
if (jobs < 64 || cores < 3 || requested === 0 || requested === 1)
|
|
8
24
|
return 0;
|
|
9
|
-
//
|
|
10
|
-
// Explicit
|
|
11
|
-
if (requested === undefined && (jobs < 1000 || !Number.isFinite(totalBytes) || totalBytes < 16 *
|
|
25
|
+
// Process startup costs dominate lightweight scans even at thousands of files.
|
|
26
|
+
// Explicit requests bypass only this conservative automatic workload gate.
|
|
27
|
+
if (requested === undefined && (jobs < 1000 || !Number.isFinite(totalBytes) || totalBytes < 16 * MIB))
|
|
12
28
|
return 0;
|
|
13
|
-
const byMemory = Math.floor((memory - 512 *
|
|
29
|
+
const byMemory = Math.floor((memory - 512 * MIB) / WORKER_BUDGET_BYTES);
|
|
14
30
|
const bounded = Math.max(0, Math.min(requested ?? 4, 4, cores - 1, byMemory, jobs));
|
|
15
31
|
return bounded >= 2 ? bounded : 0;
|
|
16
32
|
}
|
|
17
|
-
export async function runIndexPreparation(jobs, serial, moduleUrl, requested) {
|
|
18
|
-
const
|
|
33
|
+
export async function runIndexPreparation(jobs, serial, moduleUrl, requested, onDiagnostic) {
|
|
34
|
+
const diagnostics = [];
|
|
35
|
+
const diagnose = (message) => { diagnostics.push(message); onDiagnostic?.(message); };
|
|
36
|
+
const eligible = jobs.map((job, index) => ({ job, index })).filter(({ job }) => Number.isFinite(job.sizeBytes) && job.sizeBytes >= 0 && job.sizeBytes <= MAX_WORKER_ARTIFACT_BYTES);
|
|
37
|
+
const workerCount = indexPreparationWorkers(eligible.length, requested, undefined, undefined, eligible.reduce((total, { job }) => total + job.sizeBytes, 0));
|
|
19
38
|
if (!workerCount)
|
|
20
|
-
return { results: jobs.map(serial), workerCount: 0 };
|
|
39
|
+
return { results: jobs.map(serial), workerCount: 0, diagnostics };
|
|
21
40
|
const results = new Array(jobs.length), workers = [];
|
|
41
|
+
const closed = [];
|
|
22
42
|
let next = 0, completed = 0, stopped = false;
|
|
23
43
|
try {
|
|
24
44
|
await new Promise((resolve, reject) => {
|
|
25
|
-
|
|
45
|
+
// Stop dispatch on a process failure. Reap the entire pool before retrying
|
|
46
|
+
// unfinished work in the main isolate, avoiding overlapping memory peaks.
|
|
47
|
+
const recover = (message) => {
|
|
48
|
+
if (stopped)
|
|
49
|
+
return;
|
|
50
|
+
stopped = true;
|
|
51
|
+
diagnose(`${message}; retrying unfinished preparation serially`);
|
|
52
|
+
resolve();
|
|
53
|
+
};
|
|
26
54
|
const dispatch = (worker) => {
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
55
|
+
if (stopped || next >= eligible.length)
|
|
56
|
+
return;
|
|
57
|
+
const { index, job } = eligible[next++];
|
|
58
|
+
try {
|
|
59
|
+
worker.send({ index, job }, error => { if (error)
|
|
60
|
+
recover(`Index preparation IPC failed for ${job.relativePath}: ${error.message}`); });
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
recover(`Index preparation IPC failed for ${job.relativePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
30
64
|
}
|
|
31
65
|
};
|
|
32
|
-
for (let index = 0; index < workerCount; index++) {
|
|
33
|
-
|
|
34
|
-
|
|
66
|
+
for (let index = 0; index < workerCount && !stopped; index++) {
|
|
67
|
+
// A worker_threads resourceLimit is not a process boundary: V8 can abort
|
|
68
|
+
// Node during allocation before its isolate-limit error is delivered.
|
|
69
|
+
// A child heap fatal/SIGABRT is instead an ordinary, recoverable exit.
|
|
70
|
+
const worker = fork(new URL('./index-worker.mjs', import.meta.url), [moduleUrl], {
|
|
71
|
+
execArgv: [`--max-old-space-size=${WORKER_HEAP_MB}`, '--max-semi-space-size=16'],
|
|
72
|
+
stdio: ['ignore', 'ignore', 'pipe', 'ipc'], serialization: 'advanced',
|
|
35
73
|
});
|
|
36
74
|
workers.push(worker);
|
|
37
|
-
worker.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
worker.on('
|
|
75
|
+
closed.push(new Promise(done => worker.once('close', () => done())));
|
|
76
|
+
let stderr = '';
|
|
77
|
+
let activePath = '<startup>';
|
|
78
|
+
worker.stderr?.on('data', chunk => { stderr = (stderr + String(chunk)).slice(-8192); });
|
|
79
|
+
worker.on('error', error => recover(`Index preparation worker failed for ${activePath}: ${error.message}`));
|
|
80
|
+
worker.on('exit', (code, signal) => {
|
|
81
|
+
const oom = /heap out of memory|ERR_WORKER_OUT_OF_MEMORY/i.test(stderr) ? ' (heap out of memory)' : '';
|
|
82
|
+
recover(`Index preparation worker exited early for ${activePath} (${signal ?? code})${oom}`);
|
|
83
|
+
});
|
|
84
|
+
worker.on('message', (message) => {
|
|
41
85
|
if (stopped)
|
|
42
86
|
return;
|
|
43
87
|
if (message.error) {
|
|
44
|
-
|
|
88
|
+
stopped = true;
|
|
89
|
+
reject(new Error(message.error));
|
|
45
90
|
return;
|
|
46
91
|
}
|
|
47
92
|
results[message.index] = message.result;
|
|
48
93
|
completed++;
|
|
49
|
-
if (completed ===
|
|
94
|
+
if (completed === eligible.length) {
|
|
50
95
|
stopped = true;
|
|
51
96
|
resolve();
|
|
52
97
|
}
|
|
53
|
-
else
|
|
98
|
+
else {
|
|
99
|
+
activePath = eligible[next]?.job.relativePath ?? '<idle>';
|
|
54
100
|
dispatch(worker);
|
|
101
|
+
}
|
|
55
102
|
});
|
|
103
|
+
activePath = eligible[next]?.job.relativePath ?? '<idle>';
|
|
56
104
|
dispatch(worker);
|
|
57
105
|
}
|
|
58
106
|
});
|
|
59
|
-
return { results, workerCount };
|
|
60
107
|
}
|
|
61
108
|
finally {
|
|
62
|
-
|
|
109
|
+
stopped = true;
|
|
110
|
+
for (const worker of workers)
|
|
111
|
+
if (worker.exitCode === null && worker.signalCode === null)
|
|
112
|
+
signalChild(worker, 'SIGKILL');
|
|
113
|
+
await Promise.all(closed);
|
|
114
|
+
}
|
|
115
|
+
const largeCount = jobs.length - eligible.length;
|
|
116
|
+
if (largeCount)
|
|
117
|
+
diagnose(`${largeCount} artifact(s) above the worker size threshold prepared serially`);
|
|
118
|
+
for (let index = 0; index < jobs.length; index++) {
|
|
119
|
+
if (!results[index]) {
|
|
120
|
+
try {
|
|
121
|
+
results[index] = serial(jobs[index]);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw new Error(`${jobs[index].relativePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
63
127
|
}
|
|
128
|
+
return { results, workerCount, diagnostics };
|
|
64
129
|
}
|
|
65
130
|
//# sourceMappingURL=index-preparation.js.map
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { getGraphIndexDir, getProjectIndexRoot } from './types.js';
|
|
14
|
+
import { loadManifest, writeManifest } from './checksum-manifest.js';
|
|
14
15
|
/**
|
|
15
16
|
* Load a JSON file from the index directory
|
|
16
17
|
*
|
|
@@ -109,4 +110,47 @@ export function loadGraphIndexFile(cwd, filename, graph) {
|
|
|
109
110
|
return null;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
113
|
+
/** Publish a prepared snapshot with the existing atomic file writes under the owner lock. */
|
|
114
|
+
export function publishIndexFiles(staging, directory) {
|
|
115
|
+
const files = fs.readdirSync(staging).filter(file => fs.statSync(path.join(staging, file)).isFile())
|
|
116
|
+
.sort((a, b) => Number(a === 'metadata.json') - Number(b === 'metadata.json'));
|
|
117
|
+
const backups = path.join(staging, '.previous');
|
|
118
|
+
fs.mkdirSync(backups);
|
|
119
|
+
for (const file of files) {
|
|
120
|
+
const target = path.join(directory, file);
|
|
121
|
+
if (fs.existsSync(target))
|
|
122
|
+
fs.copyFileSync(target, path.join(backups, file));
|
|
123
|
+
}
|
|
124
|
+
const published = [];
|
|
125
|
+
try {
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
fs.renameSync(path.join(staging, file), path.join(directory, file));
|
|
128
|
+
published.push(file);
|
|
129
|
+
}
|
|
130
|
+
// Receipts bind to final inode signatures, after the atomic renames.
|
|
131
|
+
if (files.includes('checksum-manifest.json')) {
|
|
132
|
+
const manifest = loadManifest(directory);
|
|
133
|
+
const signature = (file) => {
|
|
134
|
+
const stat = fs.statSync(path.join(directory, file), { bigint: true });
|
|
135
|
+
return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':');
|
|
136
|
+
};
|
|
137
|
+
if (manifest.receipt) {
|
|
138
|
+
manifest.receipt.metadataSignature = signature('metadata.json');
|
|
139
|
+
if (manifest.receipt.relations)
|
|
140
|
+
manifest.receipt.relations.signature = signature('relations.json');
|
|
141
|
+
writeManifest(directory, manifest);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
for (const file of published.reverse()) {
|
|
147
|
+
const backup = path.join(backups, file);
|
|
148
|
+
if (fs.existsSync(backup))
|
|
149
|
+
fs.renameSync(backup, path.join(directory, file));
|
|
150
|
+
else
|
|
151
|
+
fs.rmSync(path.join(directory, file), { force: true });
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
112
156
|
//# sourceMappingURL=index-reader.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
@@ -6,13 +6,15 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
6
6
|
import { findPackageRoot } from '../cli/find-package-root.js';
|
|
7
7
|
import { projectAiwgPath, projectControlPath } from '../config/project-artifacts.js';
|
|
8
8
|
import { getUserStateDir } from '../config/user-state.js';
|
|
9
|
-
import {
|
|
9
|
+
import { resolveIndexWorkers } from './index-preparation.js';
|
|
10
|
+
import { resolveFrameworkIndexRoot } from './framework-index-paths.js';
|
|
10
11
|
import { collectIndexStatus } from './index-status.js';
|
|
12
|
+
import { graphIndexDigest, indexAdapterRegistry, selectIndexAdapters, indexFreshnessSnapshot, indexAdapterDigest, recordIndexVerification } from './index-adapters.js';
|
|
11
13
|
import { getFortemiCoreSyncStatus, syncFortemiCoreIndex } from './fortemi-core-sync.js';
|
|
12
14
|
import { acquireDirectoryLock, DirectoryLockContentionError } from './prebuilt-build-lock.js';
|
|
13
15
|
import { resolveImportExclusions } from '../sessions/import-exclusions.js';
|
|
14
16
|
import { resolveImportLimits } from '../sessions/import-limits.js';
|
|
15
|
-
import {
|
|
17
|
+
import { GRAPH_CONFIGS, getGraphIndexDir, graphBuildOrder, loadWorkspaceGraphs, resolveGraphScanDir, } from './types.js';
|
|
16
18
|
function readJson(file, tolerateMalformed = true) {
|
|
17
19
|
try {
|
|
18
20
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
@@ -28,14 +30,6 @@ function writeJson(file, value) {
|
|
|
28
30
|
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
29
31
|
fs.renameSync(temporary, file);
|
|
30
32
|
}
|
|
31
|
-
/** The registry is process-global; reload it per workspace to avoid cross-project leakage. */
|
|
32
|
-
function loadWorkspaceGraphs(workspace) {
|
|
33
|
-
for (const name of Object.keys(GRAPH_CONFIGS))
|
|
34
|
-
delete GRAPH_CONFIGS[name];
|
|
35
|
-
Object.assign(GRAPH_CONFIGS, structuredClone(BUILTIN_GRAPH_CONFIGS));
|
|
36
|
-
loadUserGraphConfigs(workspace);
|
|
37
|
-
loadGlobalGraphConfigs();
|
|
38
|
-
}
|
|
39
33
|
function workspacePaths(options) {
|
|
40
34
|
const cwd = options.cwd ?? process.cwd();
|
|
41
35
|
const found = new Map();
|
|
@@ -123,33 +117,38 @@ function planWorkspaceRefresh(options, workspaces) {
|
|
|
123
117
|
loadWorkspaceGraphs(workspace);
|
|
124
118
|
const graphs = collectIndexStatus(workspace).graphs
|
|
125
119
|
.sort((a, b) => graphBuildOrder(a.name, GRAPH_CONFIGS[a.name]) - graphBuildOrder(b.name, GRAPH_CONFIGS[b.name]) || a.name.localeCompare(b.name));
|
|
126
|
-
|
|
120
|
+
const adapters = indexAdapterRegistry(workspace);
|
|
121
|
+
const selected = selectIndexAdapters(adapters, options.only);
|
|
122
|
+
const includes = (name) => selected.some(adapter => adapter.name === name);
|
|
123
|
+
if (!options.only || options.only === 'all' || options.only === 'graphs' || selected.some(adapter => adapter.group === 'graphs')) {
|
|
127
124
|
for (const graph of graphs) {
|
|
128
|
-
if (
|
|
125
|
+
if (selected.some(adapter => adapter.group === 'graphs' && adapter.graph === graph.name)
|
|
126
|
+
&& (graph.built || graph.defaultBuild || graph.origin === 'registered'
|
|
127
|
+
|| (options.only && options.only !== 'all' && options.only !== 'graphs'))) {
|
|
129
128
|
jobs.push({ kind: 'graph', workspace, graph: graph.name, status: 'planned', wallMs: 0 });
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
|
-
if (!options.only || options.only === 'caches') {
|
|
132
|
+
if (!options.only || options.only === 'all' || options.only === 'caches' || options.only?.startsWith('caches:')) {
|
|
134
133
|
for (const graph of graphs) {
|
|
135
134
|
// Refresh only local, opted-in caches; never create a new cache from a packaged prebuilt.
|
|
136
|
-
if (fs.existsSync(getFortemiCoreSyncStatus(workspace, graph.name).location)) {
|
|
135
|
+
if (includes(`caches:${graph.name}`) && fs.existsSync(getFortemiCoreSyncStatus(workspace, graph.name).location)) {
|
|
137
136
|
jobs.push({ kind: 'cache', workspace, graph: graph.name, status: 'planned', wallMs: 0 });
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
|
-
if ((
|
|
140
|
+
if (includes('sessions') && fs.existsSync(path.join(workspace, '.aiwg/sessions/catalog.sqlite'))) {
|
|
142
141
|
jobs.push({ kind: 'sessions', workspace, status: 'planned', wallMs: 0, noManifest: 0 });
|
|
143
142
|
}
|
|
144
143
|
for (const kind of ['runtime', 'issues']) {
|
|
145
|
-
if (
|
|
144
|
+
if (!includes(kind))
|
|
146
145
|
continue;
|
|
147
146
|
if (kind === 'issues' && !fs.existsSync(projectAiwgPath(workspace, 'issues')))
|
|
148
147
|
continue;
|
|
149
148
|
jobs.push({ kind, workspace, status: 'planned', wallMs: 0 });
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
|
-
if (!options.only || options.only === 'models') {
|
|
151
|
+
if (!options.only || options.only === 'all' || options.only === 'models') {
|
|
153
152
|
jobs.push({ kind: 'models', scope: 'host', status: 'planned', wallMs: 0 });
|
|
154
153
|
}
|
|
155
154
|
return jobs;
|
|
@@ -176,23 +175,6 @@ export async function refreshResourceGate(workspace, live) {
|
|
|
176
175
|
const result = preflight.evaluateDispatchGate(live ?? preflight.readLiveHostResources({ paths: [workspace] }), thresholds);
|
|
177
176
|
return { ok: result.ok, reason: result.failures.map((failure) => failure.reason).join('; ') || undefined };
|
|
178
177
|
}
|
|
179
|
-
async function graphFingerprint(workspace, graph) {
|
|
180
|
-
const files = await collectGraphIndexFiles(workspace, graph);
|
|
181
|
-
// Source import resolution also depends on the compiler/package configuration.
|
|
182
|
-
if (graph === 'source') {
|
|
183
|
-
for (const file of ['tsconfig.json', 'package.json']) {
|
|
184
|
-
if (fs.existsSync(path.join(workspace, file)))
|
|
185
|
-
files.push(path.join(workspace, file));
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
const stats = [...new Set(files)].sort().map(file => {
|
|
189
|
-
const stat = fs.statSync(file);
|
|
190
|
-
return [file, stat.mtimeMs, stat.ctimeMs, stat.size];
|
|
191
|
-
});
|
|
192
|
-
return createHash('sha256').update(JSON.stringify([
|
|
193
|
-
INDEX_VERSION, INDEX_EXTRACTOR_VERSION, GRAPH_CONFIGS[graph], resolveGraphBackendType(graph), stats,
|
|
194
|
-
])).digest('hex');
|
|
195
|
-
}
|
|
196
178
|
async function refreshWorkspaceRuntime(workspace, full, runtime) {
|
|
197
179
|
const directory = projectAiwgPath(workspace, 'smiths', 'toolsmith');
|
|
198
180
|
const output = path.join(directory, 'runtime.json');
|
|
@@ -236,33 +218,54 @@ async function refreshWorkspaceRuntime(workspace, full, runtime) {
|
|
|
236
218
|
async function executeJob(job, options, aliases, runtime) {
|
|
237
219
|
const workspace = job.workspace ?? process.env.HOME ?? homedir();
|
|
238
220
|
if (job.kind === 'graph') {
|
|
221
|
+
const graphStart = Date.now();
|
|
239
222
|
const graph = job.graph;
|
|
240
223
|
const config = GRAPH_CONFIGS[graph];
|
|
241
|
-
|
|
224
|
+
const sourceWorkspace = graph === 'framework' ? resolveFrameworkIndexRoot(workspace) : workspace;
|
|
225
|
+
if (!config.scanDirs.some(dir => fs.existsSync(resolveGraphScanDir(sourceWorkspace, dir)))) {
|
|
242
226
|
job.status = 'skipped';
|
|
243
227
|
job.reason = 'scan directories missing';
|
|
244
228
|
return;
|
|
245
229
|
}
|
|
246
230
|
const directory = getGraphIndexDir(workspace, graph);
|
|
247
231
|
const markerPath = path.join(directory, 'refresh-state.json');
|
|
232
|
+
const fingerprint = await graphIndexDigest(sourceWorkspace, graph);
|
|
248
233
|
const marker = readJson(markerPath);
|
|
249
234
|
const metadata = readJson(path.join(directory, 'metadata.json'));
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
235
|
+
const adapter = indexAdapterRegistry(workspace).find(item => item.name === graph);
|
|
236
|
+
if (!options.full && marker?.fingerprint === fingerprint && marker.builtAt === metadata?.builtAt
|
|
237
|
+
&& indexFreshnessSnapshot(adapter, workspace).pendingWork === 0) {
|
|
238
|
+
const release = await acquireDirectoryLock(`${directory}.build.lock`, {
|
|
239
|
+
timeoutMs: options.lockWaitMs ?? 0, reclaimDeadOwner: true,
|
|
240
|
+
});
|
|
241
|
+
try {
|
|
242
|
+
const current = await graphIndexDigest(sourceWorkspace, graph);
|
|
243
|
+
if (current === fingerprint && indexFreshnessSnapshot(adapter, workspace).pendingWork === 0) {
|
|
244
|
+
recordIndexVerification(adapter, workspace, { before: fingerprint, after: current, wallMs: Date.now() - graphStart });
|
|
245
|
+
job.status = 'skipped';
|
|
246
|
+
job.reason = 'unchanged';
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
await release();
|
|
252
|
+
}
|
|
259
253
|
}
|
|
254
|
+
const beforeOutput = outputStamp(path.join(directory, 'metadata.json'));
|
|
260
255
|
const { buildIndex } = await import('./index-builder.js');
|
|
261
|
-
await buildIndex(
|
|
256
|
+
await buildIndex(sourceWorkspace, { graph, force: Boolean(options.full), explicit: false, quiet: true, workers: options.workers, lockWaitMs: options.lockWaitMs ?? 0,
|
|
257
|
+
onDiagnostic: message => (job.diagnostics ??= []).push(message) });
|
|
262
258
|
const built = readJson(path.join(directory, 'metadata.json'));
|
|
263
259
|
if (!built)
|
|
264
260
|
throw new Error(`graph did not produce metadata: ${graph}`);
|
|
265
|
-
|
|
261
|
+
const verification = readJson(path.join(directory, 'freshness.json'));
|
|
262
|
+
if (!options.full && beforeOutput && beforeOutput === outputStamp(path.join(directory, 'metadata.json'))) {
|
|
263
|
+
job.status = 'skipped';
|
|
264
|
+
job.reason = 'unchanged';
|
|
265
|
+
}
|
|
266
|
+
if (!verification?.pendingWork && verification?.lastVerifiedDigest === fingerprint) {
|
|
267
|
+
writeJson(markerPath, { fingerprint, builtAt: built.builtAt });
|
|
268
|
+
}
|
|
266
269
|
}
|
|
267
270
|
else if (job.kind === 'cache') {
|
|
268
271
|
const status = getFortemiCoreSyncStatus(workspace, job.graph);
|
|
@@ -331,7 +334,8 @@ async function executeJob(job, options, aliases, runtime) {
|
|
|
331
334
|
}
|
|
332
335
|
}
|
|
333
336
|
}
|
|
334
|
-
job.status
|
|
337
|
+
if (job.status !== 'skipped')
|
|
338
|
+
job.status = 'ok';
|
|
335
339
|
}
|
|
336
340
|
function outputStamp(file) {
|
|
337
341
|
try {
|
|
@@ -351,6 +355,7 @@ function jobKey(job, aliases) {
|
|
|
351
355
|
...(job.kind === 'sessions' && aliases.length > 1 ? [aliases] : [])]);
|
|
352
356
|
}
|
|
353
357
|
export async function refreshIndexes(options = {}, dependencies = {}) {
|
|
358
|
+
options = { ...options, workers: resolveIndexWorkers(options.workers) };
|
|
354
359
|
const start = Date.now();
|
|
355
360
|
const workspaces = workspacePaths(options);
|
|
356
361
|
const jobs = planWorkspaceRefresh(options, workspaces);
|
|
@@ -360,6 +365,13 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
|
|
|
360
365
|
totals: { jobs: jobs.length, ok: 0, skipped: 0, deferred: 0, errors: 0, noManifest: 0, excluded: 0, wallMs: 0 },
|
|
361
366
|
};
|
|
362
367
|
const finish = () => {
|
|
368
|
+
for (const job of jobs) {
|
|
369
|
+
for (const receipt of job.sessionImports ?? (job.sessionImport ? [job.sessionImport] : [])) {
|
|
370
|
+
for (const warning of receipt.run.preflight?.scopeWarnings ?? []) {
|
|
371
|
+
report.messages.push(`WARNING ${job.workspace}: ${warning.message}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
363
375
|
for (const job of jobs.filter(job => job.noManifest)) {
|
|
364
376
|
const command = `aiwg sessions discover --workspace '${job.workspace.replace(/'/g, "'\\''")}'`;
|
|
365
377
|
report.messages.push(`Session refresh skipped for ${job.workspace}: no authorized discovery manifest. Run \`${command}\` or configure sessions.discovery_manifests in .aiwg/aiwg.config.`);
|
|
@@ -375,6 +387,17 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
|
|
|
375
387
|
.reduce((total, receipt) => total + (receipt.totals.excluded ?? 0), 0), 0),
|
|
376
388
|
wallMs: Date.now() - start,
|
|
377
389
|
};
|
|
390
|
+
report.indices = [];
|
|
391
|
+
for (const workspace of workspaces.keys()) {
|
|
392
|
+
loadWorkspaceGraphs(workspace);
|
|
393
|
+
for (const adapter of selectIndexAdapters(indexAdapterRegistry(workspace), options.only)) {
|
|
394
|
+
report.indices.push({ ...indexFreshnessSnapshot(adapter, workspace), workspace });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (!jobs.length && options.only && report.indices.length && report.indices.every(index => index.status === 'unsupported')) {
|
|
398
|
+
report.status = 'deferred';
|
|
399
|
+
report.reason = 'unsupported';
|
|
400
|
+
}
|
|
378
401
|
return report;
|
|
379
402
|
};
|
|
380
403
|
if (options.dryRun)
|
|
@@ -425,6 +448,8 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
|
|
|
425
448
|
continue;
|
|
426
449
|
}
|
|
427
450
|
const jobStart = Date.now();
|
|
451
|
+
let adapter;
|
|
452
|
+
let beforeDigest;
|
|
428
453
|
try {
|
|
429
454
|
const gateWorkspace = job.workspace ?? workspaces.keys().next().value ?? options.cwd ?? process.cwd();
|
|
430
455
|
const gate = await (dependencies.gate ?? refreshResourceGate)(gateWorkspace);
|
|
@@ -437,6 +462,15 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
|
|
|
437
462
|
}
|
|
438
463
|
if (job.workspace)
|
|
439
464
|
loadWorkspaceGraphs(job.workspace);
|
|
465
|
+
const workspace = job.workspace ?? options.cwd ?? process.cwd();
|
|
466
|
+
adapter = indexAdapterRegistry(workspace).find(item => item.name === (job.kind === 'graph' ? job.graph
|
|
467
|
+
: job.kind === 'cache' ? `caches:${job.graph}` : job.kind));
|
|
468
|
+
if (adapter?.supported && job.kind !== 'graph') {
|
|
469
|
+
try {
|
|
470
|
+
beforeDigest = await indexAdapterDigest(adapter, workspace);
|
|
471
|
+
}
|
|
472
|
+
catch { /* Owner reports unavailable dependencies. */ }
|
|
473
|
+
}
|
|
440
474
|
if (job.kind === 'cache' && jobs.some(other => other.kind === 'graph'
|
|
441
475
|
&& other.workspace === job.workspace && other.graph === job.graph && other.status === 'error')) {
|
|
442
476
|
job.status = 'skipped';
|
|
@@ -461,13 +495,52 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
|
|
|
461
495
|
await executeJob(job, options, aliases, discoverRuntime);
|
|
462
496
|
}
|
|
463
497
|
catch (error) {
|
|
464
|
-
|
|
465
|
-
|
|
498
|
+
if (error instanceof DirectoryLockContentionError) {
|
|
499
|
+
job.status = 'deferred';
|
|
500
|
+
job.reason = 'lock-held';
|
|
501
|
+
report.status = 'deferred';
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
job.status = 'error';
|
|
505
|
+
job.error = error instanceof Error ? error.message : String(error);
|
|
506
|
+
}
|
|
466
507
|
}
|
|
467
508
|
finally {
|
|
468
509
|
job.wallMs = Date.now() - jobStart;
|
|
510
|
+
if (adapter) {
|
|
511
|
+
const workspace = job.workspace ?? options.cwd ?? process.cwd();
|
|
512
|
+
if (adapter.supported && job.kind !== 'graph' && job.status !== 'deferred') {
|
|
513
|
+
try {
|
|
514
|
+
let after;
|
|
515
|
+
let probeError;
|
|
516
|
+
try {
|
|
517
|
+
after = await indexAdapterDigest(adapter, workspace);
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
probeError = String(error);
|
|
521
|
+
}
|
|
522
|
+
if (job.kind === 'runtime' && job.status === 'ok')
|
|
523
|
+
beforeDigest = after;
|
|
524
|
+
const partial = job.reason && job.reason !== 'unchanged';
|
|
525
|
+
job.freshness = recordIndexVerification(adapter, workspace, { before: beforeDigest, after,
|
|
526
|
+
error: job.error ?? probeError, pendingWork: partial ? 1 : 0, wallMs: job.wallMs });
|
|
527
|
+
}
|
|
528
|
+
catch (error) {
|
|
529
|
+
job.freshness = { ...indexFreshnessSnapshot(adapter, workspace), status: 'degraded',
|
|
530
|
+
pendingWork: 1, lastError: String(error) };
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
else
|
|
534
|
+
job.freshness = indexFreshnessSnapshot(adapter, workspace);
|
|
535
|
+
if (job.freshness.pendingWork && !job.error && job.status !== 'deferred' && job.kind === 'graph'
|
|
536
|
+
&& job.reason !== 'scan directories missing') {
|
|
537
|
+
job.status = 'deferred';
|
|
538
|
+
job.reason = 'inputs changed during build';
|
|
539
|
+
report.status = 'deferred';
|
|
540
|
+
}
|
|
541
|
+
}
|
|
469
542
|
}
|
|
470
|
-
if (job.status !== 'error' && job.reason !== 'graph failed'
|
|
543
|
+
if (job.status !== 'error' && job.status !== 'deferred' && job.reason !== 'graph failed'
|
|
471
544
|
&& job.reason !== 'no authorized discovery manifest')
|
|
472
545
|
checkpoint.completed.push(key);
|
|
473
546
|
writeJson(checkpointPath, checkpoint);
|
|
@@ -507,7 +580,7 @@ export async function runIndexRefresh(args, cwd = process.cwd(), signal) {
|
|
|
507
580
|
options.dryRun = true;
|
|
508
581
|
else if (flag === '--json')
|
|
509
582
|
json = true;
|
|
510
|
-
else if (['--workspace', '--root', '--only', '--lock-wait-ms', '--max-record-bytes', '--max-source-bytes', '--exclude'].includes(flag)) {
|
|
583
|
+
else if (['--workspace', '--root', '--only', '--lock-wait-ms', '--max-record-bytes', '--max-source-bytes', '--exclude', '--index-workers'].includes(flag)) {
|
|
511
584
|
const value = args[++i];
|
|
512
585
|
if (!value || value.startsWith('--'))
|
|
513
586
|
throw new Error(`${flag} requires a value`);
|
|
@@ -519,13 +592,11 @@ export async function runIndexRefresh(args, cwd = process.cwd(), signal) {
|
|
|
519
592
|
options.maxRecordBytes = value;
|
|
520
593
|
else if (flag === '--max-source-bytes')
|
|
521
594
|
options.maxSourceBytes = value;
|
|
595
|
+
else if (flag === '--index-workers')
|
|
596
|
+
options.workers = resolveIndexWorkers(value);
|
|
522
597
|
else if (flag === '--exclude')
|
|
523
598
|
(options.exclude ??= []).push(value);
|
|
524
599
|
else if (flag === '--only') {
|
|
525
|
-
if (value !== 'graphs' && value !== 'sessions' && value !== 'caches'
|
|
526
|
-
&& value !== 'runtime' && value !== 'models' && value !== 'issues') {
|
|
527
|
-
throw new Error('--only must be graphs, sessions, caches, runtime, models, or issues');
|
|
528
|
-
}
|
|
529
600
|
options.only = value;
|
|
530
601
|
}
|
|
531
602
|
else {
|
|
@@ -537,6 +608,7 @@ export async function runIndexRefresh(args, cwd = process.cwd(), signal) {
|
|
|
537
608
|
else
|
|
538
609
|
throw new Error(`unknown refresh option: ${flag}`);
|
|
539
610
|
}
|
|
611
|
+
options.workers = resolveIndexWorkers(options.workers);
|
|
540
612
|
resolveImportLimits(undefined, { maxRecordBytes: options.maxRecordBytes, maxSourceBytes: options.maxSourceBytes });
|
|
541
613
|
resolveImportExclusions(undefined, options.exclude);
|
|
542
614
|
const report = await refreshIndexes(options);
|
|
@@ -547,6 +619,8 @@ export async function runIndexRefresh(args, cwd = process.cwd(), signal) {
|
|
|
547
619
|
for (const job of report.jobs)
|
|
548
620
|
console.log(` ${job.status}: ${job.kind} ${job.scope === 'host' ? '[host]' : job.workspace}${job.graph ? ` [${job.graph}]` : ''}${job.error || job.reason ? `: ${job.error ?? job.reason}` : ''}`);
|
|
549
621
|
for (const job of report.jobs) {
|
|
622
|
+
for (const diagnostic of job.diagnostics ?? [])
|
|
623
|
+
console.log(` preparation: ${diagnostic}`);
|
|
550
624
|
for (const receipt of job.sessionImports ?? (job.sessionImport ? [job.sessionImport] : [])) {
|
|
551
625
|
for (const source of receipt.run.sources) {
|
|
552
626
|
if (source.status === 'excluded')
|