@deksden-com/dd-flow-cli 0.9.0-beta.9 → 0.9.0-beta.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +460 -0
- package/README.md +65 -0
- package/dist/build-info.json +8 -8
- package/dist/cli/command-inputs.js +304 -0
- package/dist/cli/help.js +89 -13
- package/dist/cli/hook-ingress.js +82 -0
- package/dist/cli/input-preparation.js +115 -0
- package/dist/cli/run-cli.js +1409 -382
- package/dist/cli.js +6 -2
- package/dist/harness-runtime/bin/dd-agy.mjs +37 -0
- package/dist/harness-runtime/bin/dd-codex.mjs +25 -0
- package/dist/harness-runtime/bin/dd-droid.mjs +33 -0
- package/dist/harness-runtime/bin/dd-grok.mjs +30 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +21 -0
- package/dist/harness-runtime/bin/dd-zcode.mjs +83 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +215 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +423 -0
- package/dist/harness-runtime/lib/dd-agy.mjs +59 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +146 -0
- package/dist/harness-runtime/lib/dd-codex.mjs +562 -0
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +125 -0
- package/dist/harness-runtime/lib/dd-droid.mjs +471 -0
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +274 -0
- package/dist/harness-runtime/lib/dd-grok.mjs +174 -0
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +198 -0
- package/dist/harness-runtime/lib/dd-opencode.mjs +98 -0
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +644 -0
- package/dist/harness-runtime/lib/dd-zcode.mjs +910 -0
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +10 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +130 -0
- package/dist/harness-runtime/lib/dispatch-fence.mjs +14 -0
- package/dist/harness-runtime/lib/driver-recovery.mjs +129 -0
- package/dist/harness-runtime/lib/droid-observation.mjs +66 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +260 -0
- package/dist/harness-runtime/lib/model-observations.mjs +77 -0
- package/dist/harness-runtime/lib/native-hook-command.d.mts +1 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +34 -0
- package/dist/harness-runtime/lib/observation-clock.mjs +36 -0
- package/dist/harness-runtime/lib/operation-context.mjs +5 -0
- package/dist/harness-runtime/lib/operation-errors.mjs +19 -0
- package/dist/harness-runtime/lib/process-json.mjs +70 -0
- package/dist/harness-runtime/lib/process-snapshot.mjs +10 -0
- package/dist/harness-runtime/lib/runner-events.mjs +200 -0
- package/dist/harness-runtime/lib/runner-lock.mjs +42 -0
- package/dist/harness-runtime/lib/session-settlement.mjs +30 -0
- package/dist/harness-runtime/lib/tool-observations.d.mts +17 -0
- package/dist/harness-runtime/lib/tool-observations.mjs +153 -0
- package/dist/schemas/agent-profile.schema.json +1 -1
- package/dist/schemas/code-review-result.schema.json +2 -2
- package/dist/schemas/code-work-batch.schema.json +4 -3
- package/dist/schemas/code-work-result.schema.json +4 -4
- package/dist/schemas/plan-review-result.schema.json +2 -2
- package/dist/schemas/run-control-receipt.schema.json +99 -0
- package/dist/schemas/run-control-request.schema.json +36 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +2 -2
- package/dist/services/canon.js +11 -8
- package/dist/services/cleanup.js +57 -13
- package/dist/services/cli-operation-classifier.js +22 -24
- package/dist/services/code-checks.js +543 -96
- package/dist/services/codex-hook-delivery.js +28 -0
- package/dist/services/command-context.js +80 -0
- package/dist/services/config.js +11 -8
- package/dist/services/continuation-outcome.js +20 -0
- package/dist/services/controller-fanout.js +132 -0
- package/dist/services/dashboard.js +103 -31
- package/dist/services/engines.js +121 -76
- package/dist/services/eval-snapshots.js +750 -73
- package/dist/services/execution-policy.js +170 -0
- package/dist/services/external-work-launch.js +110 -0
- package/dist/services/harness-adapter.js +147 -24
- package/dist/services/harness-config.js +2 -0
- package/dist/services/hooks.js +587 -299
- package/dist/services/lanes.js +61 -53
- package/dist/services/lifecycle-command.js +118 -7
- package/dist/services/lifecycle-invocations.js +1055 -0
- package/dist/services/managed-daemon-binding.js +38 -0
- package/dist/services/managed-processes.js +283 -27
- package/dist/services/merge-queue.js +179 -104
- package/dist/services/merge-server.js +73 -28
- package/dist/services/migrations.js +14 -9
- package/dist/services/native-daemon-history.js +49 -0
- package/dist/services/native-session-control.js +39 -0
- package/dist/services/plan-runtime.js +19 -8
- package/dist/services/plans.js +28 -25
- package/dist/services/projects.js +18 -5
- package/dist/services/prompts.js +23 -16
- package/dist/services/protocols.js +64 -20
- package/dist/services/recovery-observation-budget.js +61 -0
- package/dist/services/recovery-snapshot-database.js +107 -0
- package/dist/services/repair-intents.js +40 -0
- package/dist/services/run-control-receipt.js +56 -0
- package/dist/services/run-control-worker.js +363 -0
- package/dist/services/run-control.js +876 -0
- package/dist/services/run-controller-adapter.js +222 -0
- package/dist/services/run-controller-capture.js +134 -0
- package/dist/services/run-controller-process.js +189 -0
- package/dist/services/run-controller-recovery.js +222 -0
- package/dist/services/run-controller-state.js +36 -0
- package/dist/services/run-controller.js +789 -0
- package/dist/services/run-engine-bindings.js +1 -1
- package/dist/services/run-fork.js +138 -0
- package/dist/services/run-observations.js +112 -0
- package/dist/services/run-recovery-runtime.js +69 -0
- package/dist/services/run-recovery.js +343 -0
- package/dist/services/runs.js +368 -74
- package/dist/services/runtime-budget.js +434 -0
- package/dist/services/runtime-command.js +43 -0
- package/dist/services/runtime-scope-capture.js +50 -0
- package/dist/services/runtime-scope-control.js +450 -0
- package/dist/services/runtime-scope-resume.js +565 -0
- package/dist/services/runtime-scope-stop.js +99 -0
- package/dist/services/runtime-scope-worker.js +243 -0
- package/dist/services/runtime-service.js +97 -0
- package/dist/services/schema-validation.js +10 -8
- package/dist/services/sessions.js +35 -69
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +44 -18
- package/dist/services/stage-lifecycle.js +99 -102
- package/dist/services/stage-pause.js +84 -55
- package/dist/services/stage-work-graph.js +35 -0
- package/dist/services/usage.js +37 -93
- package/dist/services/vnext-code-review.js +195 -88
- package/dist/services/vnext-code.js +285 -252
- package/dist/services/vnext-execution-profile.js +5 -3
- package/dist/services/vnext-fanout.js +160 -17
- package/dist/services/vnext-merge.js +291 -104
- package/dist/services/vnext-plan-review.js +139 -81
- package/dist/services/vnext-plan.js +138 -69
- package/dist/services/vnext-protocolize.js +129 -56
- package/dist/services/vnext-specify.js +88 -65
- package/dist/services/work-registry.js +716 -168
- package/dist/services/workspace-bootstrap.js +76 -0
- package/dist/services/worktrees.js +85 -35
- package/dist/shared/entity-references.js +8 -0
- package/dist/shared/errors.js +12 -0
- package/dist/storage/database.js +459 -32
- package/dist/storage/paths.js +17 -4
- package/dist/storage/work-references.js +23 -0
- package/dist/storage/writer-contract.js +77 -0
- package/dist/storage/writer-migration.js +102 -0
- package/package.json +24 -13
- package/tools/repair-paused-run-status.mjs +59 -0
package/dist/services/engines.js
CHANGED
|
@@ -12,6 +12,7 @@ import { requireProtocol } from "./protocols.js";
|
|
|
12
12
|
import { resolveCanonRootBeforeContext } from "./canon.js";
|
|
13
13
|
import { appendAudit } from "./audit.js";
|
|
14
14
|
import { findProjectByRoot } from "./projects.js";
|
|
15
|
+
import { parseCommandArgs } from "./lifecycle-command.js";
|
|
15
16
|
import { allRunEngineBindings, locateRunRoot, readRunEngineBinding, runEngineBindingSchemaId, writeRunEngineBinding } from "./run-engine-bindings.js";
|
|
16
17
|
export const engineManifestSchemaId = "dd-flow/engine-manifest@1";
|
|
17
18
|
const routerNativeFamilies = new Set(["engine", "version", "schema"]);
|
|
@@ -117,7 +118,7 @@ export function installCurrentEngine(context, input = {}) {
|
|
|
117
118
|
}
|
|
118
119
|
export function listEngines(context) {
|
|
119
120
|
const root = engineStoreRoot(context.ddFlowHome);
|
|
120
|
-
const engines = readInstalledManifests(root).map(manifestSummary);
|
|
121
|
+
const engines = readInstalledManifests(root).map(manifest => manifestSummary(manifest));
|
|
121
122
|
return { ok: true, schema_id: "dd-flow/engine-list@1", engine_store: root, engines };
|
|
122
123
|
}
|
|
123
124
|
export function engineInfo(context, input) {
|
|
@@ -138,11 +139,10 @@ export function resolveEngine(context, input = {}) {
|
|
|
138
139
|
export function doctorEngines(context, input = {}) {
|
|
139
140
|
const root = engineStoreRoot(context.ddFlowHome);
|
|
140
141
|
const installed = readInstalledManifests(root);
|
|
141
|
-
const checks = installed.map(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}));
|
|
142
|
+
const checks = installed.map(manifest => {
|
|
143
|
+
const diagnostics = manifestDiagnostics(manifest);
|
|
144
|
+
return { engine: manifestSummary(manifest, diagnostics), status: diagnostics.length ? "corrupt" : "ok", diagnostics };
|
|
145
|
+
});
|
|
146
146
|
const classification = input.classification ?? classifyCliOperation(["engine", "doctor"], input.env ?? context.env);
|
|
147
147
|
const selection = selectEngine(context, input, {}, classification);
|
|
148
148
|
return {
|
|
@@ -174,13 +174,16 @@ export function routeArgsThroughEngine(context, args, io, stdin, env, resolvedPr
|
|
|
174
174
|
if (!selection.selected)
|
|
175
175
|
return Promise.resolve(null);
|
|
176
176
|
const current = getCliBuildInfo();
|
|
177
|
-
|
|
177
|
+
const currentRoot = findPackageRoot(path.dirname(sourceFile));
|
|
178
|
+
if (selection.selected.package_name === current.package_name && selection.selected.package_version === current.version
|
|
179
|
+
&& currentRoot !== null && fs.realpathSync(selection.selected.snapshot_root) === fs.realpathSync(currentRoot)) {
|
|
178
180
|
return Promise.resolve({ routed: true, exitCode: 0, inProcess: true });
|
|
179
181
|
}
|
|
180
182
|
const entrypoint = resolveManifestEntrypoint(selection.selected);
|
|
181
183
|
return spawnEngine(selection.selected, entrypoint, args, io, stdin, env, current.version);
|
|
182
184
|
}
|
|
183
|
-
export function
|
|
185
|
+
export function prepareRunEngineBind(context, input) {
|
|
186
|
+
const reason = requiredText(input.reason, "reason");
|
|
184
187
|
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
185
188
|
const located = locateRunRoot(context.db, projectRoot, input.runId);
|
|
186
189
|
if (!located)
|
|
@@ -190,19 +193,31 @@ export function bindRunEngine(context, input) {
|
|
|
190
193
|
packageName: input.packageName ?? getCliBuildInfo().package_name,
|
|
191
194
|
packageVersion: input.engineVersion,
|
|
192
195
|
...(input.integrityChecksum ? { integrityChecksum: input.integrityChecksum } : {})
|
|
193
|
-
});
|
|
196
|
+
}, { checkEntrypoint: false });
|
|
194
197
|
const requested = engineIdentity(manifest);
|
|
195
198
|
if (existing) {
|
|
196
199
|
if (sameEngineIdentity(existing.engine, requested))
|
|
197
|
-
return {
|
|
200
|
+
return { kind: "replay", binding: existing };
|
|
198
201
|
throw new AppError("run_engine_binding_immutable", "RUN engine binding cannot be changed", 1, {
|
|
199
202
|
run_id: located.run_id,
|
|
200
203
|
current: existing.engine,
|
|
201
204
|
requested
|
|
202
205
|
});
|
|
203
206
|
}
|
|
207
|
+
return { kind: "create", projectRoot, located, manifest, requested, reason, entrypoint: resolveManifestEntrypoint(manifest) };
|
|
208
|
+
}
|
|
209
|
+
export function bindRunEngine(context, input, prepared = prepareRunEngineBind(context, input)) {
|
|
210
|
+
if (prepared.kind === "replay")
|
|
211
|
+
return { ok: true, action: "run_engine_bind", changed: false, binding: prepared.binding };
|
|
212
|
+
const { projectRoot, located, manifest, requested, reason, entrypoint } = prepared;
|
|
213
|
+
const existing = readRunEngineBinding(located.binding_path, { allowMissing: true });
|
|
214
|
+
if (existing) {
|
|
215
|
+
if (sameEngineIdentity(existing.engine, requested))
|
|
216
|
+
return { ok: true, action: "run_engine_bind", changed: false, binding: existing };
|
|
217
|
+
throw new AppError("run_engine_binding_immutable", "RUN engine binding cannot be changed", 1, { run_id: located.run_id, current: existing.engine, requested });
|
|
218
|
+
}
|
|
204
219
|
const probeArgs = ["run", "status", located.run_id, "--project-root", projectRoot, "--json"];
|
|
205
|
-
const probe = spawnSync(process.execPath, [
|
|
220
|
+
const probe = spawnSync(process.execPath, [entrypoint, ...probeArgs], {
|
|
206
221
|
cwd: manifest.snapshot_root,
|
|
207
222
|
env: { ...process.env, ...context.env, DD_FLOW_HOME: context.ddFlowHome, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: manifest.snapshot_root },
|
|
208
223
|
encoding: "utf8",
|
|
@@ -223,7 +238,7 @@ export function bindRunEngine(context, input) {
|
|
|
223
238
|
project_root: projectRoot,
|
|
224
239
|
bound_at: context.now(),
|
|
225
240
|
source: "legacy_recovery",
|
|
226
|
-
reason
|
|
241
|
+
reason,
|
|
227
242
|
engine: requested,
|
|
228
243
|
probe: { status: "passed", command: `dd-flow ${probeArgs.join(" ")}` }
|
|
229
244
|
};
|
|
@@ -252,6 +267,38 @@ export function bindCurrentEngineToRun(context, input) {
|
|
|
252
267
|
};
|
|
253
268
|
return writeRunEngineBinding(path.join(input.runRoot, "engine-binding.json"), binding).binding;
|
|
254
269
|
}
|
|
270
|
+
/** Forks are a new execution authority. The source binding stays immutable;
|
|
271
|
+
* this replaces only the copied binding after proving the selected engine can
|
|
272
|
+
* read the copied RUN. */
|
|
273
|
+
export function upgradeForkedRunEngine(context, input) {
|
|
274
|
+
const projectRoot = resolveProjectRoot(input.projectRoot);
|
|
275
|
+
const located = locateRunRoot(context.db, projectRoot, input.runId);
|
|
276
|
+
if (!located)
|
|
277
|
+
throw new AppError("not_found", `Forked RUN is not registered: ${input.runId}`, 1);
|
|
278
|
+
const manifest = requireBoundManifest(context.ddFlowHome, {
|
|
279
|
+
packageName: getCliBuildInfo().package_name,
|
|
280
|
+
packageVersion: input.engineVersion,
|
|
281
|
+
...(input.integrityChecksum ? { integrityChecksum: input.integrityChecksum } : {})
|
|
282
|
+
});
|
|
283
|
+
const requested = engineIdentity(manifest);
|
|
284
|
+
readRunEngineBinding(located.binding_path);
|
|
285
|
+
const lineage = path.join(located.run_root, "fork-lineage.json");
|
|
286
|
+
if (!fs.existsSync(lineage))
|
|
287
|
+
throw new AppError("fork_lineage_missing", "Engine upgrade is permitted only for a derived RUN", 1, { run_id: located.run_id });
|
|
288
|
+
const probeArgs = ["run", "status", located.run_id, "--project-root", projectRoot, "--json"];
|
|
289
|
+
const probe = spawnSync(process.execPath, [resolveManifestEntrypoint(manifest), ...probeArgs], {
|
|
290
|
+
cwd: manifest.snapshot_root,
|
|
291
|
+
env: { ...process.env, ...context.env, DD_FLOW_HOME: context.ddFlowHome, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: manifest.snapshot_root },
|
|
292
|
+
encoding: "utf8", timeout: 30_000
|
|
293
|
+
});
|
|
294
|
+
if (probe.status !== 0)
|
|
295
|
+
throw new AppError("fork_engine_probe_failed", "Selected engine cannot read the forked RUN", 1, { run_id: located.run_id, engine: manifestSummary(manifest), stderr: probe.stderr.slice(0, 2000) });
|
|
296
|
+
const binding = { schema_id: runEngineBindingSchemaId, run_id: located.run_id, project_root: projectRoot, bound_at: context.now(), source: "fork_upgrade", reason: requiredText(input.reason, "reason"), engine: requested, probe: { status: "passed", command: `dd-flow ${probeArgs.join(" ")}` } };
|
|
297
|
+
const temporary = `${located.binding_path}.fork-${process.pid}-${Date.now()}.tmp`;
|
|
298
|
+
fs.writeFileSync(temporary, `${JSON.stringify(binding, null, 2)}\n`);
|
|
299
|
+
fs.renameSync(temporary, located.binding_path);
|
|
300
|
+
return binding;
|
|
301
|
+
}
|
|
255
302
|
function boundRunEngineForArgs(context, args, projectRoot) {
|
|
256
303
|
const runId = runIdForArgs(args);
|
|
257
304
|
if (!runId || !projectRoot)
|
|
@@ -274,6 +321,8 @@ function runIdForArgs(args) {
|
|
|
274
321
|
const [family, command, ...rest] = args;
|
|
275
322
|
const parsed = parseLightArgs(rest);
|
|
276
323
|
if (family === "run" && command !== "start" && command !== "list") {
|
|
324
|
+
if (["drive", "control", "recovery", "capacity"].includes(command ?? ""))
|
|
325
|
+
return option(parsed, "run");
|
|
277
326
|
if (command === "flags")
|
|
278
327
|
return positional(parsed, 1);
|
|
279
328
|
return positional(parsed, 0);
|
|
@@ -290,7 +339,7 @@ function runIdForArgs(args) {
|
|
|
290
339
|
return option(parsed, "run");
|
|
291
340
|
return null;
|
|
292
341
|
}
|
|
293
|
-
function requireBoundManifest(ddFlowHome, input) {
|
|
342
|
+
function requireBoundManifest(ddFlowHome, input, options = {}) {
|
|
294
343
|
const manifest = input.snapshotRoot
|
|
295
344
|
? requireManifest(path.join(input.snapshotRoot, "engine.json"))
|
|
296
345
|
: findInstalledManifest(ddFlowHome, {
|
|
@@ -303,7 +352,7 @@ function requireBoundManifest(ddFlowHome, input) {
|
|
|
303
352
|
|| manifest.package_version !== input.packageVersion
|
|
304
353
|
|| input.integrityChecksum !== undefined && manifest.integrity.checksum !== input.integrityChecksum
|
|
305
354
|
|| input.snapshotRoot !== undefined && path.resolve(manifest.snapshot_root) !== path.resolve(input.snapshotRoot)
|
|
306
|
-
||
|
|
355
|
+
|| manifestDiagnostics(manifest, options).length !== 0) {
|
|
307
356
|
throw new AppError("run_engine_binding_invalid", "RUN engine binding points to a missing or unhealthy engine snapshot", 1, {
|
|
308
357
|
package_name: input.packageName,
|
|
309
358
|
package_version: input.packageVersion,
|
|
@@ -344,7 +393,7 @@ function executionManifest(context) {
|
|
|
344
393
|
return requireManifest(path.join(String(installed.path), "engine.json"));
|
|
345
394
|
}
|
|
346
395
|
function requiredText(value, label) {
|
|
347
|
-
const normalized = value.trim();
|
|
396
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
348
397
|
if (!normalized)
|
|
349
398
|
throw new AppError("usage", `Missing required value: ${label}`, 2);
|
|
350
399
|
return normalized;
|
|
@@ -383,11 +432,7 @@ export function selectEngine(context, input, options = {}, classification = inpu
|
|
|
383
432
|
const manifests = readInstalledManifests(engineStoreRoot(context.ddFlowHome))
|
|
384
433
|
.filter((manifest) => manifest.package_name === packageName)
|
|
385
434
|
.filter((manifest) => requiredRange !== null && satisfiesRange(manifest.package_version, requiredRange))
|
|
386
|
-
.filter((manifest) =>
|
|
387
|
-
&& manifest.package_name === build.package_name
|
|
388
|
-
&& manifest.package_version === build.version
|
|
389
|
-
? true
|
|
390
|
-
: manifestHealthy(manifest))
|
|
435
|
+
.filter((manifest) => manifestHealthy(manifest))
|
|
391
436
|
.sort((a, b) => {
|
|
392
437
|
const versionOrder = compareSemverStrings(b.package_version, a.package_version);
|
|
393
438
|
if (versionOrder !== 0)
|
|
@@ -560,35 +605,11 @@ function canonCompatibilitySha(memorybankRoot) {
|
|
|
560
605
|
return null;
|
|
561
606
|
}
|
|
562
607
|
}
|
|
563
|
-
|
|
564
|
-
const positional = [];
|
|
565
|
-
const options = new Map();
|
|
566
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
567
|
-
const value = args[index];
|
|
568
|
-
if (value?.startsWith("--")) {
|
|
569
|
-
const equal = value.indexOf("=");
|
|
570
|
-
if (equal > 2) {
|
|
571
|
-
const key = value.slice(2, equal);
|
|
572
|
-
options.set(key, [...(options.get(key) ?? []), value.slice(equal + 1)]);
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
const key = value.slice(2);
|
|
576
|
-
const next = args[index + 1];
|
|
577
|
-
if (!next || next.startsWith("--"))
|
|
578
|
-
options.set(key, [...(options.get(key) ?? []), ""]);
|
|
579
|
-
else {
|
|
580
|
-
options.set(key, [...(options.get(key) ?? []), next]);
|
|
581
|
-
index += 1;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
else if (value)
|
|
585
|
-
positional.push(value);
|
|
586
|
-
}
|
|
587
|
-
return { positional, options };
|
|
588
|
-
}
|
|
608
|
+
const parseLightArgs = parseCommandArgs;
|
|
589
609
|
function option(parsed, key) {
|
|
590
610
|
const values = parsed.options.get(key);
|
|
591
|
-
|
|
611
|
+
const value = values?.[values.length - 1];
|
|
612
|
+
return value && value !== "__dd_flow_flag__" ? value : null;
|
|
592
613
|
}
|
|
593
614
|
function positional(parsed, index) {
|
|
594
615
|
return parsed.positional[index] ?? null;
|
|
@@ -620,23 +641,37 @@ function buildManifest(context, packageName, version, packageRoot, snapshotRoot,
|
|
|
620
641
|
};
|
|
621
642
|
}
|
|
622
643
|
function copyPackageSnapshot(packageRoot, target) {
|
|
644
|
+
for (const [relative, source] of packageSnapshotFiles(packageRoot)) {
|
|
645
|
+
const destination = path.join(target, relative);
|
|
646
|
+
ensureDir(path.dirname(destination));
|
|
647
|
+
fs.copyFileSync(source, destination);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/** The same source inventory drives validation and copying; no staging writes. */
|
|
651
|
+
function packageSnapshotFiles(packageRoot) {
|
|
652
|
+
const files = new Map();
|
|
653
|
+
const collect = (source, relative, skipNodeModules = false) => {
|
|
654
|
+
if (!fs.statSync(source).isDirectory()) {
|
|
655
|
+
files.set(relative, source);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
659
|
+
if (skipNodeModules && entry.name === "node_modules")
|
|
660
|
+
continue;
|
|
661
|
+
const child = path.join(source, entry.name), name = path.join(relative, entry.name);
|
|
662
|
+
if (entry.isDirectory())
|
|
663
|
+
collect(child, name, skipNodeModules);
|
|
664
|
+
else if (entry.isFile())
|
|
665
|
+
files.set(name, child);
|
|
666
|
+
}
|
|
667
|
+
};
|
|
623
668
|
for (const item of ["dist", "package.json", "README.md", "CHANGELOG.md", "tools"]) {
|
|
624
669
|
const from = path.join(packageRoot, item);
|
|
625
670
|
if (!fs.existsSync(from))
|
|
626
671
|
continue;
|
|
627
|
-
|
|
628
|
-
const stat = fs.statSync(from);
|
|
629
|
-
if (stat.isDirectory())
|
|
630
|
-
copyDir(from, to);
|
|
631
|
-
else {
|
|
632
|
-
ensureDir(path.dirname(to));
|
|
633
|
-
fs.copyFileSync(from, to);
|
|
634
|
-
}
|
|
672
|
+
collect(from, item);
|
|
635
673
|
}
|
|
636
|
-
|
|
637
|
-
}
|
|
638
|
-
function copyProductionDependencies(packageRoot, target) {
|
|
639
|
-
const queue = [{ sourceRoot: packageRoot, targetRoot: target, ancestors: new Set() }];
|
|
674
|
+
const queue = [{ sourceRoot: packageRoot, targetRoot: "", ancestors: new Set() }];
|
|
640
675
|
const copied = new Set();
|
|
641
676
|
while (queue.length > 0) {
|
|
642
677
|
const current = queue.shift();
|
|
@@ -651,12 +686,35 @@ function copyProductionDependencies(packageRoot, target) {
|
|
|
651
686
|
if (copied.has(copyKey))
|
|
652
687
|
continue;
|
|
653
688
|
copied.add(copyKey);
|
|
654
|
-
|
|
689
|
+
collect(sourceRoot, targetRoot, true);
|
|
655
690
|
if (current.ancestors.has(sourceRoot))
|
|
656
691
|
continue;
|
|
657
692
|
queue.push({ sourceRoot, targetRoot, ancestors: new Set([...current.ancestors, sourceRoot]) });
|
|
658
693
|
}
|
|
659
694
|
}
|
|
695
|
+
return files;
|
|
696
|
+
}
|
|
697
|
+
export function prepareForkEngine(engineVersion, integrityChecksum) {
|
|
698
|
+
const build = getCliBuildInfo();
|
|
699
|
+
if (engineVersion !== build.version)
|
|
700
|
+
throw new AppError("fork_engine_unavailable", "Fork installs the invoking CLI engine; invoke the requested version to upgrade the fork", 2, { requested: engineVersion, available: build.version });
|
|
701
|
+
const packageRoot = findPackageRoot(path.dirname(sourceFile));
|
|
702
|
+
if (!packageRoot || !fs.existsSync(path.join(packageRoot, "dist", "cli.js")))
|
|
703
|
+
throw new AppError("harness_runtime_unbuilt", "Build the CLI before preparing a fork", 1);
|
|
704
|
+
const files = packageSnapshotFiles(packageRoot);
|
|
705
|
+
if (integrityChecksum !== undefined) {
|
|
706
|
+
const checksum = crypto.createHash("sha256");
|
|
707
|
+
for (const [relative, source] of [...files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
708
|
+
if (path.basename(relative) === "engine.json")
|
|
709
|
+
continue;
|
|
710
|
+
checksum.update(relative);
|
|
711
|
+
checksum.update("\0");
|
|
712
|
+
checksum.update(fs.readFileSync(source));
|
|
713
|
+
checksum.update("\0");
|
|
714
|
+
}
|
|
715
|
+
if (checksum.digest("hex") !== integrityChecksum)
|
|
716
|
+
throw new AppError("fork_engine_unavailable", "Requested engine checksum differs from the invoking CLI artifact", 2, { requested: integrityChecksum });
|
|
717
|
+
}
|
|
660
718
|
}
|
|
661
719
|
function readProductionDependencies(packageRoot) {
|
|
662
720
|
try {
|
|
@@ -713,19 +771,6 @@ function hasAncestorFile(start, name) {
|
|
|
713
771
|
current = parent;
|
|
714
772
|
}
|
|
715
773
|
}
|
|
716
|
-
function copyDir(from, to, skipNodeModules = false) {
|
|
717
|
-
ensureDir(to);
|
|
718
|
-
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
719
|
-
if (skipNodeModules && entry.name === "node_modules")
|
|
720
|
-
continue;
|
|
721
|
-
const source = path.join(from, entry.name);
|
|
722
|
-
const target = path.join(to, entry.name);
|
|
723
|
-
if (entry.isDirectory())
|
|
724
|
-
copyDir(source, target, skipNodeModules);
|
|
725
|
-
else if (entry.isFile())
|
|
726
|
-
fs.copyFileSync(source, target);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
774
|
function acquireInstallLock(lockDir) {
|
|
730
775
|
ensureDir(path.dirname(lockDir));
|
|
731
776
|
try {
|
|
@@ -847,7 +892,7 @@ function manifestDiagnostics(manifest, options = {}) {
|
|
|
847
892
|
}
|
|
848
893
|
return diagnostics;
|
|
849
894
|
}
|
|
850
|
-
function manifestSummary(manifest) {
|
|
895
|
+
function manifestSummary(manifest, diagnostics = manifestDiagnostics(manifest, { checkEntrypoint: false })) {
|
|
851
896
|
return {
|
|
852
897
|
schema_id: manifest.schema_id,
|
|
853
898
|
package_name: manifest.package_name,
|
|
@@ -859,8 +904,8 @@ function manifestSummary(manifest) {
|
|
|
859
904
|
snapshot_root: manifest.snapshot_root,
|
|
860
905
|
resolved_entrypoint: safeResolveManifestEntrypoint(manifest),
|
|
861
906
|
installed_at: manifest.installed_at,
|
|
862
|
-
healthy:
|
|
863
|
-
diagnostics
|
|
907
|
+
healthy: diagnostics.length === 0,
|
|
908
|
+
diagnostics
|
|
864
909
|
};
|
|
865
910
|
}
|
|
866
911
|
function resolveManifestEntrypoint(manifest) {
|