@deksden-com/dd-flow-cli 0.9.0-beta.75 → 0.9.0-beta.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +6 -6
- package/dist/cli/command-inputs.js +300 -0
- package/dist/cli/help.js +1 -1
- package/dist/cli/hook-ingress.js +82 -0
- package/dist/cli/input-preparation.js +115 -0
- package/dist/cli/run-cli.js +1015 -423
- package/dist/cli.js +6 -2
- package/dist/harness-runtime/lib/dd-codex-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +7 -2
- package/dist/harness-runtime/lib/dd-codex.mjs +137 -13
- package/dist/harness-runtime/lib/dd-droid.mjs +2 -2
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +1 -1
- package/dist/harness-runtime/lib/delegation-instructions.mjs +13 -4
- package/dist/harness-runtime/lib/native-hook-command.d.mts +1 -0
- package/dist/services/canon.js +11 -8
- package/dist/services/cleanup.js +49 -8
- package/dist/services/cli-operation-classifier.js +12 -24
- package/dist/services/codex-hook-delivery.js +28 -0
- package/dist/services/config.js +11 -8
- package/dist/services/controller-fanout.js +2 -2
- package/dist/services/dashboard.js +94 -31
- package/dist/services/engines.js +75 -61
- package/dist/services/eval-snapshots.js +138 -38
- package/dist/services/execution-policy.js +5 -12
- package/dist/services/hooks.js +226 -203
- package/dist/services/lanes.js +60 -52
- package/dist/services/lifecycle-command.js +13 -1
- package/dist/services/lifecycle-invocations.js +285 -94
- package/dist/services/managed-processes.js +158 -18
- package/dist/services/merge-queue.js +173 -98
- package/dist/services/migrations.js +13 -8
- package/dist/services/plan-runtime.js +19 -8
- package/dist/services/plans.js +28 -25
- package/dist/services/projects.js +11 -2
- package/dist/services/prompts.js +23 -16
- package/dist/services/protocols.js +54 -19
- package/dist/services/recovery-observation-budget.js +1 -1
- package/dist/services/run-controller-adapter.js +31 -20
- package/dist/services/run-controller-state.js +5 -0
- package/dist/services/run-controller.js +102 -56
- package/dist/services/run-fork.js +36 -22
- package/dist/services/run-recovery.js +32 -17
- package/dist/services/runs.js +110 -38
- package/dist/services/runtime-budget.js +120 -48
- package/dist/services/runtime-scope-capture.js +2 -2
- package/dist/services/runtime-scope-control.js +2 -2
- package/dist/services/runtime-scope-resume.js +34 -12
- package/dist/services/runtime-scope-worker.js +49 -16
- package/dist/services/runtime-service.js +8 -7
- package/dist/services/schema-validation.js +9 -7
- package/dist/services/sessions.js +35 -69
- package/dist/services/stage-blocker.js +17 -7
- package/dist/services/stage-context.js +42 -16
- package/dist/services/stage-lifecycle.js +82 -100
- package/dist/services/stage-pause.js +54 -24
- package/dist/services/vnext-code-review.js +82 -32
- package/dist/services/vnext-code.js +78 -45
- package/dist/services/vnext-fanout.js +13 -12
- package/dist/services/vnext-merge.js +68 -35
- package/dist/services/vnext-plan-review.js +84 -41
- package/dist/services/vnext-plan.js +91 -70
- package/dist/services/vnext-protocolize.js +116 -50
- package/dist/services/vnext-specify.js +70 -57
- package/dist/services/work-registry.js +195 -92
- package/dist/services/worktrees.js +85 -35
- package/dist/storage/database.js +104 -10
- package/dist/storage/paths.js +17 -4
- package/package.json +1 -1
package/dist/cli/run-cli.js
CHANGED
|
@@ -3,71 +3,77 @@ import path from "node:path";
|
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { quote } from "shell-quote";
|
|
5
5
|
import { observeLifecycleCommand } from "../services/lifecycle-invocations.js";
|
|
6
|
-
import {
|
|
6
|
+
import { verifyCodexHookDelivery } from "../services/codex-hook-delivery.js";
|
|
7
|
+
import { assertLifecycleInvocationCurrent, awaitLifecycleInvocation, expandLifecycleInvocationArgs, issueLifecycleInvocation, prepareLifecycleIssuance, lifecycleInvocationScope, observedLifecycleInvocation, settleLifecycleInvocation, settleLifecycleRejection } from "../services/lifecycle-invocations.js";
|
|
7
8
|
import { createContext, createRouterContext } from "../runtime/context.js";
|
|
8
9
|
import { migrateStoreWriter } from "../storage/writer-migration.js";
|
|
9
10
|
import { helpForArgs } from "./help.js";
|
|
10
11
|
import { AppError, isAppError } from "../shared/errors.js";
|
|
11
12
|
import { writeJson } from "../shared/json.js";
|
|
12
|
-
import { archiveProject, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
|
|
13
|
-
import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, requireProtocol, syncProtocolFromRun, transitionProtocol } from "../services/protocols.js";
|
|
14
|
-
import {
|
|
15
|
-
import { cancelMergeQueueJob, claimNextMergeJob, claimMergeJob, claimMergeBundle, completeMergeBundle, completeMergeJob, failMergeBundle, failMergeJob, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, waitNextMergeJob } from "../services/merge-queue.js";
|
|
13
|
+
import { archiveProject, prepareProjectArchive, getProjectStatus, migrateProjectIds, registerProject, resolveProject } from "../services/projects.js";
|
|
14
|
+
import { cancelProtocol, getProtocolBlockers, getProtocolStatus, getReadyProtocols, implementProtocol, readyForMerge, registerProtocol, prepareProtocolRegistration, requireProtocol, syncProtocolFromRun, prepareProtocolTransition, transitionProtocol } from "../services/protocols.js";
|
|
15
|
+
import { getPlanStatus, preparePlanItemMutation, updatePlanItem } from "../services/plans.js";
|
|
16
|
+
import { cancelMergeQueueJob, prepareMergeQueueCancellation, prepareMergeQueueWait, prepareMergeClaim, prepareMergeBundleClaim, claimNextMergeJob, claimMergeJob, claimMergeBundle, completeMergeBundle, prepareMergeBundleCompletion, completeMergeJob, prepareMergeJobCompletion, failMergeBundle, prepareMergeBundleFailure, failMergeJob, prepareMergeJobFailure, getMergeBundleStatus, getMergeQueueStatus, noteMergeJob, prepareMergeJobNote, waitNextMergeJob } from "../services/merge-queue.js";
|
|
16
17
|
import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectMergeWorker } from "../services/merge-worker.js";
|
|
17
|
-
import { cleanupApply, cleanupScan } from "../services/cleanup.js";
|
|
18
|
-
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, handleAgyEvent, handleDroidEvent, handleGrokEvent, handleOpenCodeEvent, handleZcodeHook, handleZcodeEvent, isolatedDdFlowHomeFromHookStdin, claimBootstrapHookEvent, findRecentMatchingHookEvent, stageStartMatchKey, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
|
|
19
|
-
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
20
|
-
import { acquireLaneLock, cancelLaneWaiter, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
21
|
-
import { getProjectConfigStatus, setProjectConfigValue } from "../services/config.js";
|
|
22
|
-
import { getCanonStatus, registerCanonRoot, resolveCanonRoot } from "../services/canon.js";
|
|
18
|
+
import { cleanupApply, cleanupScan, prepareCleanupApply } from "../services/cleanup.js";
|
|
19
|
+
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, handleAgyEvent, handleDroidEvent, handleGrokEvent, handleOpenCodeEvent, handleZcodeHook, handleZcodeEvent, isolatedDdFlowHomeFromHookStdin, claimBootstrapHookEvent, findRecentMatchingHookEvent, stageStartMatchKey, initCodexHome, prepareCodexHome, prepareCodexHooks, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, prepareCodexHomeRemoval, removeCodexHooks } from "../services/hooks.js";
|
|
20
|
+
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, prepareWorktreeCreate, prepareWorktreeBootstrap, prepareWorktreeClose, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
21
|
+
import { acquireLaneLock, cancelLaneWaiter, prepareLaneWaiterCancellation, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, prepareLaneWorkspace, prepareLaneLock, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
22
|
+
import { getProjectConfigStatus, prepareProjectConfigValue, setProjectConfigValue } from "../services/config.js";
|
|
23
|
+
import { getCanonStatus, registerCanonRoot, resolveCanonRoot, prepareCanonRegistration } from "../services/canon.js";
|
|
23
24
|
import { getRuntimeStatus } from "../services/status.js";
|
|
24
25
|
import { getCliVersionReport } from "../services/build-info.js";
|
|
25
26
|
import { parseCommandArgs } from "../services/lifecycle-command.js";
|
|
26
|
-
import {
|
|
27
|
+
import { prepareError, prepareJsonFile, prepareJsonSchema, prepareJsonText, prepareTextFile } from "./input-preparation.js";
|
|
28
|
+
import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, prepareDashboardRefresh, prepareDashboardRender, prepareGlobalDashboardRender, prepareDashboardOptions, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
|
|
27
29
|
import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
|
|
28
|
-
import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
30
|
+
import { getFlowSessionStatus, prepareFlowSessionPayload, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
|
|
29
31
|
import { getProtocolBranchContext } from "../services/branch-context.js";
|
|
30
|
-
import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunSessions, getFlowRunTimeline, getStoredFlowRunUsage, getFlowRunFlagsStatus, getFlowRunStatus, getFlowRunConfig, getFlowRunVariables, listFlowRuns, reviseFlowRunFlags, setFlowRunConfig, setFlowRunVariable, startFlowRun } from "../services/runs.js";
|
|
32
|
+
import { attachFlowRunStage, prepareFlowRunStageAttachment, prepareFlowRunStageCompletion, completeFlowRun, prepareFlowRunCompletion, completeFlowRunStage, getFlowRunSessions, getFlowRunTimeline, getStoredFlowRunUsage, getFlowRunFlagsStatus, getFlowRunStatus, getFlowRunConfig, getFlowRunVariables, listFlowRuns, reviseFlowRunFlags, setFlowRunConfig, prepareFlowRunConfig, prepareFlowRunVariable, setFlowRunVariable, startFlowRun, prepareFlowRunStart } from "../services/runs.js";
|
|
31
33
|
import { previewNextEntityId } from "../services/ids.js";
|
|
32
34
|
import { validateSchema } from "../services/schema-validation.js";
|
|
33
35
|
import { preflightMemoryPermissions } from "../services/memory-permissions.js";
|
|
34
36
|
import { ingestDroidUsage, ingestAgyUsage, ingestGrokUsage, ingestOpenCodeUsage, ingestZcodeUsage } from "../services/usage.js";
|
|
35
|
-
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, verifyMigrationReport } from "../services/migrations.js";
|
|
37
|
+
import { applyMigration, assessMigrationImpact, assertUpgradeRunStorageReady, planMigration, prepareMigrationApply, verifyMigrationReport } from "../services/migrations.js";
|
|
36
38
|
import { publishProjectSummary } from "../services/project-summary.js";
|
|
37
39
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
38
40
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
39
|
-
import { doctorEngines, bindRunEngine, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
41
|
+
import { doctorEngines, bindRunEngine, prepareRunEngineBind, engineInfo, installCurrentEngine, listEngines, resolveEngine, resolveExplicitProjectRoot, resolveOperationProjectRoot, routeArgsThroughEngine } from "../services/engines.js";
|
|
40
42
|
import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
|
|
41
|
-
import { finishVnextSpecifyStage, getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, launchVnextSpecify } from "../services/vnext-specify.js";
|
|
42
|
-
import { finishVnextProtocolize, isVnextProtocolizeRun, startVnextProtocolize } from "../services/vnext-protocolize.js";
|
|
43
|
-
import { finishVnextPlan, isVnextPlanRun, startVnextPlan } from "../services/vnext-plan.js";
|
|
44
|
-
import { dispatchVnextPlanReview, finishVnextPlanReview, isVnextPlanReviewRun, recordVnextPlanReviewCapacity, startVnextPlanReview } from "../services/vnext-plan-review.js";
|
|
45
|
-
import { addVnextCodeRepair, finishVnextCode, startVnextCode } from "../services/vnext-code.js";
|
|
46
|
-
import {
|
|
47
|
-
import { applyVnextMerge, finishVnextMerge, getVnextMergeRequest, isVnextMergeRun, repairVnextMerge, routeVnextMergeRequest, startVnextMerge } from "../services/vnext-merge.js";
|
|
43
|
+
import { finishVnextSpecifyStage, getVnextWorkStatus, isVnextSpecifyFlow, isVnextSpecifyRun, launchVnextSpecify, prepareVnextSpecifyStart } from "../services/vnext-specify.js";
|
|
44
|
+
import { finishVnextProtocolize, isVnextProtocolizeRun, prepareProtocolizeResultPath, prepareVnextProtocolizeStart, startVnextProtocolize } from "../services/vnext-protocolize.js";
|
|
45
|
+
import { finishVnextPlan, isVnextPlanRun, startVnextPlan, prepareVnextPlanStart, prepareVnextPlanFinish } from "../services/vnext-plan.js";
|
|
46
|
+
import { dispatchVnextPlanReview, finishVnextPlanReview, isVnextPlanReviewRun, prepareVnextPlanReviewStart, recordVnextPlanReviewCapacity, startVnextPlanReview } from "../services/vnext-plan-review.js";
|
|
47
|
+
import { addVnextCodeRepair, finishVnextCode, prepareVnextCodeRepair, prepareVnextCodeStart, startVnextCode } from "../services/vnext-code.js";
|
|
48
|
+
import { finishVnextCodeReview, isVnextCodeReviewRun, prepareCodeReviewDecision, prepareVnextCodeReviewRepair, prepareVnextCodeReviewStart, startVnextCodeReview } from "../services/vnext-code-review.js";
|
|
49
|
+
import { applyVnextMerge, finishVnextMerge, getVnextMergeRequest, isVnextMergeRun, prepareVnextMergeFinish, prepareVnextMergeStart, repairVnextMerge, routeVnextMergeRequest, startVnextMerge } from "../services/vnext-merge.js";
|
|
48
50
|
import { mergeServerStatus, serveMergeRequests, stopMergeServer } from "../services/merge-server.js";
|
|
49
|
-
import { getVnextFanoutStatus, observeVnextNativeChildren, reconcileVnextFanout } from "../services/vnext-fanout.js";
|
|
50
|
-
import { pauseStageForUser, resumeStageAfterUser } from "../services/stage-pause.js";
|
|
51
|
-
import { blockStageForRuntime, unblockStageAfterRuntimeRepair } from "../services/stage-blocker.js";
|
|
51
|
+
import { getVnextFanoutStatus, observeVnextNativeChildren, prepareNativeObservations, reconcileVnextFanout } from "../services/vnext-fanout.js";
|
|
52
|
+
import { pauseStageForUser, resumeStageAfterUser, prepareStagePause, prepareStageResume } from "../services/stage-pause.js";
|
|
53
|
+
import { blockStageForRuntime, unblockStageAfterRuntimeRepair, prepareStageRuntimeBlock, prepareStageRuntimeUnblock } from "../services/stage-blocker.js";
|
|
52
54
|
import { classifyCliOperation, requiresExplicitUpgradeAuthorization } from "../services/cli-operation-classifier.js";
|
|
53
|
-
import { renderWorkerPrompt } from "../services/prompts.js";
|
|
54
|
-
import { finishStage, startStage } from "../services/stage-lifecycle.js";
|
|
55
|
-
import { addWorkBatch, assertStageLifecycleOwner, assertWorkLifecycleOwner, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
55
|
+
import { prepareWorkerPrompt, renderWorkerPrompt } from "../services/prompts.js";
|
|
56
|
+
import { finishStage, prepareStageFinish, startStage, prepareLegacyStageStart } from "../services/stage-lifecycle.js";
|
|
57
|
+
import { addWorkBatch, prepareWorkBatch, prepareWorkDeps, prepareWorkDelete, prepareWorkSettlement, prepareWorkRetry, assertStageLifecycleOwner, assertWorkLifecycleOwner, cancelWork, deleteWork, failWork, finishWork, listWorks, mutateWorkDeps, prepareWorkResult, retryWork, showWork, startWork } from "../services/work-registry.js";
|
|
56
58
|
import { launchExternalWork } from "../services/external-work-launch.js";
|
|
57
|
-
import { createEvalBootstrapSnapshot, createEvalRunSnapshot, prepareVnextSpecifyRun, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
|
|
58
|
-
import { forkRunFromSnapshot } from "../services/run-fork.js";
|
|
59
|
-
import { acceptRunRecovery, assertRunMutationAllowed, beginRunRecovery, inspectRunRecovery, recordRecoveryCapture, recoveryGuard, resumeRunRecovery, sealRunRecovery } from "../services/run-recovery.js";
|
|
59
|
+
import { createEvalBootstrapSnapshot, prepareEvalBootstrapSnapshot, createEvalRunSnapshot, prepareEvalRunSnapshotCreation, prepareVnextSpecifyRun, prepareEvalRunRestore, prepareEvalBootstrapRestore, restoreEvalBootstrapSnapshot, restoreEvalRunSnapshot } from "../services/eval-snapshots.js";
|
|
60
|
+
import { forkRunFromSnapshot, prepareRunFork } from "../services/run-fork.js";
|
|
61
|
+
import { acceptRunRecovery, assertRunMutationAllowed, beginRunRecovery, inspectRunRecovery, prepareRecoveryCapture, prepareRecoveryPayload, prepareRunRecoveryResume, recordRecoveryCapture, recoveryGuard, resumeRunRecovery, sealRunRecovery } from "../services/run-recovery.js";
|
|
60
62
|
import { recoveryRuntimeAdmission } from "../services/run-recovery-runtime.js";
|
|
61
|
-
import { loadExternalStageContext } from "../services/stage-context.js";
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
63
|
+
import { loadExternalStageContext, prepareExternalStageContext } from "../services/stage-context.js";
|
|
64
|
+
import { vnextStageDirectory } from "../domain/stage-catalog.js";
|
|
65
|
+
import { prepareCommandRoute } from "./command-inputs.js";
|
|
66
|
+
import { parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
67
|
+
import { prepareManagedProcessRegistration, prepareManagedProcessConfirmation, prepareManagedProcessHeartbeat, prepareManagedProcessFinish, prepareManagedProcessStopOperation, prepareManagedProcessReconcile, stopManagedProcess, confirmManagedProcess, finishManagedProcess, heartbeatManagedProcess, managedProcessRuntimeHome, managedProcessStatus, processIsAlive, reconcileExpiredManagedProcesses, registerManagedProcess } from "../services/managed-processes.js";
|
|
68
|
+
import { fenceRuntimeBudget, runtimeBudgetStatus, admitRuntimeCheck, parseRuntimeBudget, prepareRuntimeScopeControl, prepareRuntimeScopeFence, reserveProviderTurn, releaseProviderTurns, prepareProviderTurnRelease } from "../services/runtime-budget.js";
|
|
64
69
|
import { stopRuntimeScope } from "../services/runtime-scope-stop.js";
|
|
65
|
-
import { launchRuntimeScopeControl, launchRuntimeScopeResume, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
|
|
66
|
-
import {
|
|
70
|
+
import { launchRuntimeScopeControl, launchRuntimeScopeResume, prepareRuntimeScopeReconciliation, prepareRuntimeScopeServing, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
|
|
71
|
+
import { prepareRuntimeScopeResumeRequest } from "../services/runtime-scope-resume.js";
|
|
72
|
+
import { launchRunController, prepareRunControllerLaunch, resumeRunController, serveRunController, runControllerStatus, answerRunController, prepareRunControllerAnswer, supplyRunControllerContext, prepareRunControllerContext } from "../services/run-controller.js";
|
|
67
73
|
import { controllerHitlAdmission } from "../services/run-controller-adapter.js";
|
|
68
74
|
import { controlLifecycleReply, runControlStatus } from "../services/run-control.js";
|
|
69
75
|
import { reconcileRunControl, requestRunControl, serveRunControl } from "../services/run-control-worker.js";
|
|
70
|
-
import { startRuntimeService } from "../services/runtime-service.js";
|
|
76
|
+
import { startRuntimeService, prepareRuntimeService } from "../services/runtime-service.js";
|
|
71
77
|
const defaultIo = {
|
|
72
78
|
stdout: process.stdout,
|
|
73
79
|
stderr: process.stderr,
|
|
@@ -103,7 +109,7 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
103
109
|
io.stdout.write(`${help}\n`);
|
|
104
110
|
return 0;
|
|
105
111
|
}
|
|
106
|
-
|
|
112
|
+
const suppliedInvocationId = optionalOption(parseArgs(output.args.slice(2)), "invocation-id");
|
|
107
113
|
if (output.args[0] === "run" && ["drive", "control"].includes(output.args[1] ?? "")) {
|
|
108
114
|
const controlArgs = parseArgs(output.args.slice(2));
|
|
109
115
|
requiredOption(controlArgs, "run");
|
|
@@ -121,13 +127,32 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
121
127
|
});
|
|
122
128
|
}
|
|
123
129
|
const routerContext = createRouterContext(env);
|
|
130
|
+
const publicCommand = quote(["dd-flow", ...output.args]);
|
|
131
|
+
const lifecycleCommand = parseLifecycleCommand(publicCommand);
|
|
132
|
+
const observedInvocation = observedLifecycleInvocation(routerContext, publicCommand);
|
|
133
|
+
if (!observedInvocation && !suppliedInvocationId && env.DD_FLOW_DAEMON_ID && lifecycleCommand.kind === "standalone") {
|
|
134
|
+
throw new AppError("invocation_receipt_missing", "Managed lifecycle command has no committed native receipt", 1, { effect: "no_effect", recoverable: false });
|
|
135
|
+
}
|
|
136
|
+
const invocationId = observedInvocation?.id ?? suppliedInvocationId;
|
|
137
|
+
const boundInvocationArgs = invocationId && invocationId !== suppliedInvocationId
|
|
138
|
+
? [...output.args.filter((_, index, values) => values[index - 1] !== "--invocation-id" && values[index] !== "--invocation-id"), "--invocation-id", invocationId]
|
|
139
|
+
: output.args;
|
|
140
|
+
const observedOperation = lifecycleCommand.kind === "standalone" ? lifecycleCommand.invocation.operation : null;
|
|
141
|
+
if (observedInvocation && !observedOperation)
|
|
142
|
+
throw new AppError("invocation_command_invalid", "Observed lifecycle command could not be parsed", 1);
|
|
143
|
+
const invocationScope = invocationId ? lifecycleInvocationScope({ ...routerContext, env }, invocationId) : undefined;
|
|
144
|
+
const invocationArgs = invocationScope && observedOperation
|
|
145
|
+
? expandLifecycleInvocationArgs(routerContext, boundInvocationArgs, invocationScope, observedOperation)
|
|
146
|
+
: boundInvocationArgs;
|
|
124
147
|
emitHumanProgress(io, output, progress, "start");
|
|
125
148
|
if (output.progressJsonl) {
|
|
126
149
|
writeProgressJsonl(io, "start", `Starting ${output.args.slice(0, 3).join(" ")}`);
|
|
127
150
|
heartbeat = setInterval(() => writeProgressJsonl(io, "running", "Command is still running"), 15_000);
|
|
128
151
|
}
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
// Route by retained authority, so a mistyped project path reaches the CLI
|
|
153
|
+
// validator instead of selecting a different engine or failing preflight.
|
|
154
|
+
const scopeProjectRoot = invocationScope?.projectRoot ?? resolveProjectRootBeforeRouting(output.args, env);
|
|
155
|
+
const routedArgs = output.responseFile ? [...invocationArgs, "--response-file", output.responseFile] : invocationArgs;
|
|
131
156
|
const routed = await routeArgsThroughEngine(routerContext, output.json ? [...routedArgs, "--json", ...(output.progressJsonl ? ["--progress-jsonl"] : [])] : routedArgs, io, io.stdin, env, scopeProjectRoot);
|
|
132
157
|
if (routed?.routed && !routed.inProcess) {
|
|
133
158
|
if (heartbeat)
|
|
@@ -137,19 +162,31 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
137
162
|
writeProgressJsonl(io, "failed", "Routed engine failed", { exit_code: routed.exitCode });
|
|
138
163
|
return routed.exitCode;
|
|
139
164
|
}
|
|
165
|
+
let preparedInput;
|
|
166
|
+
let preparedRoute;
|
|
167
|
+
if (!invocationId) {
|
|
168
|
+
const preparationContext = createContext({ ...env }, "read_existing");
|
|
169
|
+
try {
|
|
170
|
+
preparedInput = await prepareCliInput(invocationArgs, preparationContext, io, scopeProjectRoot, output.responseFile);
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
preparationContext.db.close?.();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
140
176
|
const dispatchContext = routed?.inProcess
|
|
141
|
-
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(
|
|
142
|
-
: createContext({ ...env }, databaseModeForCommand(
|
|
177
|
+
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version }, databaseModeForCommand(invocationArgs, classification))
|
|
178
|
+
: createContext({ ...env }, databaseModeForCommand(invocationArgs, classification));
|
|
143
179
|
diagnosticHome = dispatchContext.ddFlowHome;
|
|
144
|
-
preflightCliCompatibility(dispatchContext, output.args, env, scopeProjectRoot);
|
|
145
180
|
dispatchContext.env.DD_FLOW_INVOCATION_READONLY = classification.mode === "read_only_diagnostics" ? "1" : undefined;
|
|
146
|
-
let dispatchArgs =
|
|
147
|
-
settleHook = observeLifecycleCommand(dispatchContext, quote(["dd-flow", ...dispatchArgs]));
|
|
181
|
+
let dispatchArgs = invocationArgs;
|
|
148
182
|
let retainedOutcome;
|
|
149
|
-
|
|
183
|
+
// Issued transports may deliver their receipt asynchronously. Observe only
|
|
184
|
+
// after admission; retained outcomes must not need a fresh hook receipt.
|
|
150
185
|
if (invocationId) {
|
|
151
|
-
const command = quote(["dd-flow", ...
|
|
186
|
+
const command = quote(["dd-flow", ...invocationArgs]);
|
|
152
187
|
const admission = await awaitLifecycleInvocation(dispatchContext, { id: invocationId, command,
|
|
188
|
+
validateArgs: () => { preparedRoute = prepareCommandRoute(invocationArgs); },
|
|
189
|
+
prepare: async () => { preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile, preparedRoute); },
|
|
153
190
|
assertCurrent: scope => assertLifecycleInvocationCurrent(dispatchContext, scope, command) });
|
|
154
191
|
if (admission.replay) {
|
|
155
192
|
retainedOutcome = admission.outcome;
|
|
@@ -158,7 +195,8 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
158
195
|
}
|
|
159
196
|
else {
|
|
160
197
|
invocationSettlement = { context: dispatchContext, id: invocationId };
|
|
161
|
-
dispatchArgs = [...
|
|
198
|
+
dispatchArgs = [...invocationArgs, "--hook-event-id", admission.eventKey];
|
|
199
|
+
preparedInput?.route?.parsed.options.set("hook-event-id", [admission.eventKey]);
|
|
162
200
|
dispatchContext.env.DD_FLOW_INVOCATION_SCOPE = JSON.stringify(lifecycleInvocationScope(dispatchContext, invocationId));
|
|
163
201
|
dispatchContext.env.DD_FLOW_CURRENT_INVOCATION = invocationId;
|
|
164
202
|
}
|
|
@@ -167,9 +205,13 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
167
205
|
const error = retainedOutcome.error;
|
|
168
206
|
throw new AppError(error.code, error.message, error.exitCode, error.details);
|
|
169
207
|
}
|
|
208
|
+
if (!retainedOutcome && !preparedInput)
|
|
209
|
+
preparedInput = await prepareCliInput(invocationArgs, dispatchContext, io, scopeProjectRoot, output.responseFile);
|
|
210
|
+
if (!retainedOutcome)
|
|
211
|
+
preflightCliCompatibility(dispatchContext, invocationArgs, env, scopeProjectRoot);
|
|
170
212
|
if (!retainedOutcome)
|
|
171
213
|
settleHook = observeLifecycleCommand(dispatchContext, quote(["dd-flow", ...dispatchArgs]));
|
|
172
|
-
const result = retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output);
|
|
214
|
+
const result = retainedOutcome ? retainedOutcome.result : await dispatch(dispatchArgs, dispatchContext, io, scopeProjectRoot, classification, output, preparedInput);
|
|
173
215
|
const completedHook = settleHook;
|
|
174
216
|
settleHook = undefined;
|
|
175
217
|
completedHook?.({ result });
|
|
@@ -233,13 +275,17 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
233
275
|
const payload = isAppError(error)
|
|
234
276
|
? { ok: false, error: { code: error.code, message: error.message, details: error.details } }
|
|
235
277
|
: { ok: false, error: { code: "unexpected", message: String(error) } };
|
|
278
|
+
progress.splice(0, progress.length, ...progress.filter(event => event.phase !== "done"));
|
|
279
|
+
if (progress.length)
|
|
280
|
+
progress.push({ phase: "failed", message: payload.error.message, at: new Date().toISOString() });
|
|
236
281
|
emitHumanProgress(io, output, progress, "failed");
|
|
237
282
|
if (output.progressJsonl)
|
|
238
283
|
writeProgressJsonl(io, "failed", payload.error.message, { code: payload.error.code });
|
|
239
284
|
const errorPayload = attachProgress(payload, progress, output);
|
|
240
285
|
let visible = errorPayload;
|
|
241
286
|
try {
|
|
242
|
-
|
|
287
|
+
if (error.details.parameter !== "response-file")
|
|
288
|
+
visible = writeResponseFile(errorPayload, output);
|
|
243
289
|
}
|
|
244
290
|
catch (publication) {
|
|
245
291
|
error.details.response_publication_error = { code: publication?.code ?? "response_publication_failed", message: publication instanceof Error ? publication.message : String(publication) };
|
|
@@ -267,6 +313,20 @@ function resolveProjectRootBeforeRouting(args, env) {
|
|
|
267
313
|
}
|
|
268
314
|
function databaseModeForCommand(args, classification) {
|
|
269
315
|
const [family, command] = args;
|
|
316
|
+
// Managed attempts are issued only from a prepared store. Opening that
|
|
317
|
+
// existing writer must not run migrations before their input is admitted.
|
|
318
|
+
if (parseArgs(args.slice(2)).options.has("invocation-id"))
|
|
319
|
+
return "hook";
|
|
320
|
+
// Native hook ingress must remain synchronous, but it must not run schema
|
|
321
|
+
// migration/VACUUM while the provider is waiting for its decision. Startup
|
|
322
|
+
// prepares the store; the hook mode only verifies the prepared contract and
|
|
323
|
+
// records the receipt.
|
|
324
|
+
if (family === "codex" && command === "hook" && args[2] === "handle") {
|
|
325
|
+
// Only the participating PreToolUse ingress needs the prepared hot-path
|
|
326
|
+
// contract. SessionStart/Stop/PostToolUse are observational no-ops here
|
|
327
|
+
// and retain the ordinary startup path for backwards compatibility.
|
|
328
|
+
return optionalOption(parseArgs(args.slice(3)), "event") === "PreToolUse" ? "hook" : "initialize";
|
|
329
|
+
}
|
|
270
330
|
if (family === "engine" ||
|
|
271
331
|
family === "storage" && command === "migrate-writer" ||
|
|
272
332
|
family === "version" ||
|
|
@@ -324,8 +384,10 @@ function parseOutputOptions(args) {
|
|
|
324
384
|
continue;
|
|
325
385
|
}
|
|
326
386
|
if (arg === "--response-file") {
|
|
387
|
+
if (responseFile !== null)
|
|
388
|
+
throw new AppError("usage", "--response-file may be supplied only once", 2);
|
|
327
389
|
const file = args[++index];
|
|
328
|
-
if (!file)
|
|
390
|
+
if (!file?.trim() || file.startsWith("--"))
|
|
329
391
|
throw new AppError("usage", "--response-file requires a path", 2);
|
|
330
392
|
responseFile = file;
|
|
331
393
|
continue;
|
|
@@ -337,6 +399,33 @@ function parseOutputOptions(args) {
|
|
|
337
399
|
const hookJsonOnly = stripped[0] === "codex" && stripped[1] === "hook" && stripped[2] === "handle";
|
|
338
400
|
return { json: json || hookJsonOnly, progressJsonl, args: stripped, hookJsonOnly, responseFile };
|
|
339
401
|
}
|
|
402
|
+
/** Validate the response destination without creating it. The response is an
|
|
403
|
+
* optional publication, but a known-bad destination should never be found
|
|
404
|
+
* only after a successful lifecycle mutation. */
|
|
405
|
+
function prepareResponseDestination(value) {
|
|
406
|
+
const file = path.resolve(value);
|
|
407
|
+
const parent = path.dirname(file);
|
|
408
|
+
try {
|
|
409
|
+
let ancestor = parent;
|
|
410
|
+
for (;;) {
|
|
411
|
+
try {
|
|
412
|
+
if (!fs.statSync(ancestor).isDirectory())
|
|
413
|
+
throw new AppError("response_file_parent_invalid", "--response-file parent must be a directory", 2, { file, parent });
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
if (error.code !== "ENOENT" || path.dirname(ancestor) === ancestor)
|
|
418
|
+
throw error;
|
|
419
|
+
ancestor = path.dirname(ancestor);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (fs.existsSync(file) && !fs.statSync(file).isFile())
|
|
423
|
+
throw new AppError("response_file_invalid", "--response-file must name a regular file", 2, { file });
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
return prepareError(error, { parameter: "response-file", file, parent });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
340
429
|
/** Persist the complete machine response for harnesses that cap tool output.
|
|
341
430
|
* The visible response keeps routing fields but omits the potentially large
|
|
342
431
|
* rendered prompt; an agent can safely read the named file without rerunning
|
|
@@ -617,13 +706,609 @@ function stringValue(value) {
|
|
|
617
706
|
function shortSha(value) {
|
|
618
707
|
return value ? value.slice(0, 12) : "unknown";
|
|
619
708
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
709
|
+
/** All caller supplied file/stdin data is consumed here, before a managed
|
|
710
|
+
* invocation is allowed to claim execution or a stage claims its hook event. */
|
|
711
|
+
async function prepareCliInput(...args) {
|
|
712
|
+
try {
|
|
713
|
+
return await prepareCliInputValue(...args);
|
|
714
|
+
}
|
|
715
|
+
catch (error) {
|
|
716
|
+
return prepareError(error);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
async function prepareCliInputValue(args, context, io, scopeProjectRoot, responseFile = null, route = prepareCommandRoute(args)) {
|
|
720
|
+
const { family, command, parsed } = route;
|
|
721
|
+
const prepared = { route };
|
|
722
|
+
if (route.key === "project register")
|
|
723
|
+
resolveProjectRoot(requiredOption(parsed, "root"));
|
|
724
|
+
if (route.key === "project archive")
|
|
725
|
+
prepareProjectArchive(context, { idOrAlias: parsed.positional[0], root: optionalOption(parsed, "root"), reason: requiredOption(parsed, "reason") });
|
|
726
|
+
if (route.key === "protocol register")
|
|
727
|
+
prepareProtocolRegistration(context, { handshakeId: requiredPosition(parsed, 0, "handshake-id"), projectRoot: requiredOption(parsed, "project-root"), workspacePath: optionalOption(parsed, "workspace-path") });
|
|
728
|
+
if (route.key === "canon register")
|
|
729
|
+
prepared.canonRegistration = prepareCanonRegistration(context, { root: requiredOption(parsed, "root") });
|
|
730
|
+
if (route.key === "run attach-stage")
|
|
731
|
+
prepareFlowRunStageAttachment(context, stageAttachmentInput(parsed));
|
|
732
|
+
if (route.key === "run complete-stage")
|
|
733
|
+
prepareFlowRunStageCompletion(context, stageCompletionInput(parsed));
|
|
734
|
+
if (route.key === "run override")
|
|
735
|
+
prepareFlowRunCompletion(context, runOverrideInput(parsed));
|
|
736
|
+
if (route.key === "engine bind-run")
|
|
737
|
+
prepared.engineBind = prepareRunEngineBind(context, engineBindInput(parsed));
|
|
738
|
+
if (route.key === "worktree create")
|
|
739
|
+
prepared.worktreeCreate = prepareWorktreeCreate(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree create"), branch: requiredOption(parsed, "branch"), base: requiredOption(parsed, "base"), path: requiredOption(parsed, "path") });
|
|
740
|
+
if (route.key === "worktree bootstrap")
|
|
741
|
+
prepared.worktreeBootstrap = prepareWorktreeBootstrap(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree bootstrap") });
|
|
742
|
+
if (route.key === "worktree close")
|
|
743
|
+
prepareWorktreeClose(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "worktree close"), mode: requiredOption(parsed, "mode") });
|
|
744
|
+
if (route.key.startsWith("plan item "))
|
|
745
|
+
preparePlanItemMutation(context, planItemInput(parsed, requiredScopeProjectRoot(scopeProjectRoot, route.key)));
|
|
746
|
+
if (route.key === "lane workspace set")
|
|
747
|
+
prepareLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
748
|
+
if (route.key === "lane waiter cancel")
|
|
749
|
+
prepareLaneWaiterCancellation(context, laneWaiterCancelInput(parsed));
|
|
750
|
+
if (route.key === "merge-queue complete")
|
|
751
|
+
prepareMergeJobCompletion(context, { ...mergeJobTarget(parsed, scopeProjectRoot), summary: requiredOption(parsed, "summary") });
|
|
752
|
+
if (route.key === "merge-queue cancel")
|
|
753
|
+
prepareMergeQueueCancellation(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
754
|
+
if (route.key === "merge-queue wait-next")
|
|
755
|
+
prepareMergeQueueWait(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
756
|
+
if (route.key === "merge-queue next" || route.key === "merge-queue claim")
|
|
757
|
+
prepareMergeClaim(context, { projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed), ...(route.key === "merge-queue claim" ? { protocolId: requiredPosition(parsed, 0, "protocol-id") } : {}) });
|
|
758
|
+
if (route.key === "merge-queue note")
|
|
759
|
+
prepareMergeJobNote(context, { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue note"), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
760
|
+
if (route.key === "merge bundle complete")
|
|
761
|
+
prepareMergeBundleCompletion(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), summary: requiredOption(parsed, "summary") });
|
|
762
|
+
if (route.key === "merge bundle claim")
|
|
763
|
+
prepareMergeBundleClaim(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed) });
|
|
764
|
+
if (route.key === "merge bundle fail")
|
|
765
|
+
prepareMergeBundleFailure(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: workspacePathOption(parsed), workerId: requiredWorkerId(parsed), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
766
|
+
if (route.key === "merge-queue fail")
|
|
767
|
+
prepareMergeJobFailure(context, { ...mergeJobTarget(parsed, scopeProjectRoot), reason: requiredOption(parsed, "reason"), requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue") });
|
|
768
|
+
if (route.key.startsWith("lane lock ") && route.key !== "lane lock status") {
|
|
769
|
+
const action = requiredPosition(parsed, 0, "lane lock action");
|
|
770
|
+
prepareLaneLock(context, {
|
|
771
|
+
projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"),
|
|
772
|
+
workspacePath: workspacePathOption(parsed), leaseToken: optionalOption(parsed, "lease-token"), reason: optionalOption(parsed, "reason"),
|
|
773
|
+
ttlSeconds: optionalNumber(parsed, "ttl"), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval")
|
|
774
|
+
}, action);
|
|
775
|
+
}
|
|
776
|
+
if (route.key === "run recovery begin")
|
|
777
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "interruption-json"), "interruption");
|
|
778
|
+
if (route.key === "run recovery seal")
|
|
779
|
+
prepared.recoveryPayload = prepareRecoveryPayload(requiredOption(parsed, "settlement-json"), "settlement");
|
|
780
|
+
if (route.key === "run recovery capture")
|
|
781
|
+
prepared.recoveryCapture = prepareRecoveryCapture(context, recoveryCaptureInput(parsed));
|
|
782
|
+
if (route.key === "run recovery resume")
|
|
783
|
+
prepared.recoveryResume = prepareRunRecoveryResume(context, recoveryResumeInput(parsed));
|
|
784
|
+
if (route.key === "run drive answer")
|
|
785
|
+
prepared.controllerAnswer = prepareRunControllerAnswer(context, controllerAnswerInput(parsed));
|
|
786
|
+
if (route.key === "run drive launch")
|
|
787
|
+
prepared.controllerLaunch = prepareRunControllerLaunch(context, controllerLaunchInput(parsed));
|
|
788
|
+
if (route.key === "run drive context")
|
|
789
|
+
prepared.controllerContext = prepareRunControllerContext(context, controllerContextInput(parsed));
|
|
790
|
+
if (route.key === "protocol transition" || route.key === "transition")
|
|
791
|
+
prepared.protocolTransition = prepareProtocolTransition(context, protocolTransitionInput(parsed, scopeProjectRoot ?? resolveOperationProjectRoot(context, args)));
|
|
792
|
+
if (route.key === "codex home init")
|
|
793
|
+
prepared.codexHome = prepareCodexHome(context, codexHomeInput(parsed));
|
|
794
|
+
if (route.key === "codex home remove")
|
|
795
|
+
prepareCodexHomeRemoval(context, { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), mode: requiredOption(parsed, "mode") });
|
|
796
|
+
if (["codex hooks install", "codex hooks remove"].includes(route.key))
|
|
797
|
+
prepared.codexHooks = prepareCodexHooks(context, { projectRoot: requiredOption(parsed, "project-root"), target: optionalOption(parsed, "target"), profile: optionalOption(parsed, "profile"), yes: hasOption(parsed, "yes") });
|
|
798
|
+
if (route.key === "migration apply")
|
|
799
|
+
prepared.migrationApply = prepareMigrationApply(context, migrationApplyInput(parsed, classifyCliOperation(args, context.env)));
|
|
800
|
+
if (route.key === "migration verify")
|
|
801
|
+
prepared.migrationVerification = verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
802
|
+
if (responseFile)
|
|
803
|
+
prepareResponseDestination(responseFile);
|
|
804
|
+
if (family === "dashboard") {
|
|
805
|
+
const action = dashboardAction(command);
|
|
806
|
+
prepareDashboardOptions({ format: optionalOption(parsed, "format"), viewer: optionalOption(parsed, "viewer"), open: optionalOption(parsed, "open") });
|
|
807
|
+
const target = resolveDashboardTarget(context, { action, project: optionalOption(parsed, "project"), projectRoot: optionalOption(parsed, "project-root"), protocol: optionalOption(parsed, "protocol"), all: hasOption(parsed, "all"), global: hasOption(parsed, "global") || command?.endsWith("-global") });
|
|
808
|
+
if (action === "refresh" && target.kind !== "global" && optionalOption(parsed, "output") !== undefined) {
|
|
809
|
+
throw new AppError("validation", "--output is supported only for global dashboard refresh; use dashboard render for a custom project/protocol output", 2, { parameter: "output" });
|
|
810
|
+
}
|
|
811
|
+
if (target.kind === "protocol")
|
|
812
|
+
requireProtocol(context, target.protocolId, target.project.id);
|
|
813
|
+
if (action !== "data") {
|
|
814
|
+
if (target.kind === "global" || target.kind === "all_projects") {
|
|
815
|
+
prepareGlobalDashboardRender(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") ?? (command?.endsWith("-global") ? undefined : "html") });
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
const format = optionalOption(parsed, "format") ?? (action === "open" || hasOption(parsed, "project") ? "html" : undefined);
|
|
819
|
+
const input = { projectRoot: target.project.root, format, ...(target.kind === "protocol" ? { protocol: target.protocolId } : {}) };
|
|
820
|
+
if (action === "refresh")
|
|
821
|
+
prepareDashboardRefresh(context, { ...input, open: optionalOption(parsed, "open") });
|
|
822
|
+
else if (action === "render")
|
|
823
|
+
prepareDashboardRender(context, { ...input, output: optionalOption(parsed, "output") });
|
|
824
|
+
else if (target.kind === "protocol") {
|
|
825
|
+
if (format !== "html")
|
|
826
|
+
throw new AppError("validation", "Protocol dashboard open supports --format html", 2, { protocol: target.protocolId });
|
|
827
|
+
prepareDashboardRender(context, input);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
if (route.key === "prompt render") {
|
|
833
|
+
const input = workerPromptInput(parsed);
|
|
834
|
+
prepared.workerPrompt = { input, value: prepareWorkerPrompt(context, input) };
|
|
835
|
+
}
|
|
836
|
+
if (route.key === "run snapshot restore" || route.key === "run snapshot bootstrap restore") {
|
|
837
|
+
try {
|
|
838
|
+
const input = snapshotRestoreInput(parsed);
|
|
839
|
+
if (route.key === "run snapshot restore")
|
|
840
|
+
prepared.snapshotRestore = prepareEvalRunRestore(context, input);
|
|
841
|
+
else
|
|
842
|
+
prepared.bootstrapRestore = prepareEvalBootstrapRestore(input);
|
|
843
|
+
}
|
|
844
|
+
catch (error) {
|
|
845
|
+
return prepareError(error, { parameter: "snapshot/project-root" });
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (route.key === "run snapshot bootstrap create") {
|
|
849
|
+
try {
|
|
850
|
+
prepareEvalBootstrapSnapshot(context, { projectRoot: requiredOption(parsed, "project-root"), output: requiredOption(parsed, "output") });
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
return prepareError(error, { parameter: "project-root/output" });
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (route.key === "run snapshot create") {
|
|
857
|
+
prepareEvalRunSnapshotCreation(context, snapshotCreateInput(parsed));
|
|
858
|
+
}
|
|
859
|
+
if (route.key === "run start") {
|
|
860
|
+
try {
|
|
861
|
+
const input = runStartInput(parsed);
|
|
862
|
+
prepared.runStart = { input, value: prepareFlowRunStart(context, input) };
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
return prepareError(error, { parameter: "run start" });
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (route.key === "run fork") {
|
|
869
|
+
try {
|
|
870
|
+
prepared.runFork = prepareRunFork(runForkInput(parsed));
|
|
871
|
+
}
|
|
872
|
+
catch (error) {
|
|
873
|
+
return prepareError(error, { parameter: "from/output" });
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
if (["runtime scope fence", "runtime scope stop"].includes(route.key))
|
|
877
|
+
prepareRuntimeScopeFence(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"));
|
|
878
|
+
if (route.key === "runtime scope resume")
|
|
879
|
+
prepareRuntimeScopeResumeRequest(context, runtimeScopeResumeInput(parsed));
|
|
880
|
+
if (route.key === "runtime scope reconcile")
|
|
881
|
+
prepareRuntimeScopeReconciliation(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")), requiredOption(parsed, "request-id"));
|
|
882
|
+
if (route.key === "runtime scope serve")
|
|
883
|
+
prepareRuntimeScopeServing(context, requiredOption(parsed, "scope-id"), Number(requiredOption(parsed, "generation")));
|
|
884
|
+
if (route.key === "runtime scope control") {
|
|
885
|
+
try {
|
|
886
|
+
const raw = optionalOption(parsed, "manifest-json");
|
|
887
|
+
prepared.runtimeManifest = prepareRuntimeScopeControl(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), requiredOption(parsed, "mode"), raw === undefined ? undefined : prepareJsonText(raw, { file: "<manifest-json>", label: "scope manifest" }).value, context);
|
|
888
|
+
}
|
|
889
|
+
catch (error) {
|
|
890
|
+
return prepareError(error, { parameter: "scope-id/request-id/manifest-json" });
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (route.key === "runtime process register") {
|
|
894
|
+
prepareManagedProcessRegistration(context, runtimeProcessRegistrationInput(context, parsed));
|
|
895
|
+
}
|
|
896
|
+
if (route.key === "runtime process heartbeat")
|
|
897
|
+
prepareManagedProcessHeartbeat(context, runtimeProcessHeartbeatInput(parsed));
|
|
898
|
+
if (route.key === "runtime process finish")
|
|
899
|
+
prepareManagedProcessFinish(context, runtimeProcessFinishInput(parsed));
|
|
900
|
+
if (route.key === "runtime process confirm")
|
|
901
|
+
prepareManagedProcessConfirmation(context, runtimeProcessConfirmationInput(parsed));
|
|
902
|
+
if (route.key === "runtime process stop")
|
|
903
|
+
prepareManagedProcessStopOperation(context, runtimeProcessStopInput(parsed));
|
|
904
|
+
if (route.key === "runtime process reconcile")
|
|
905
|
+
prepareManagedProcessReconcile(requiredOption(parsed, "owner"), optionalNumber(parsed, "grace-ms"));
|
|
906
|
+
if (route.key === "runtime process release-turn") {
|
|
907
|
+
prepared.providerTurnRelease = prepareProviderTurnRelease({ daemonId: requiredOption(parsed, "daemon-id"), operationId: optionalOption(parsed, "operation-id"), observationFile: optionalOption(parsed, "observation-file") });
|
|
908
|
+
}
|
|
909
|
+
if (route.key === "runtime process start") {
|
|
910
|
+
try {
|
|
911
|
+
prepareRuntimeService(context, runtimeServiceInput(parsed));
|
|
912
|
+
}
|
|
913
|
+
catch (error) {
|
|
914
|
+
return prepareError(error, { parameter: "runtime process start" });
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
if (family === "work" && command === "retry") {
|
|
918
|
+
try {
|
|
919
|
+
prepareWorkRetry(context, requiredPosition(parsed, 0, "work-id"), requiredOption(parsed, "reason"));
|
|
920
|
+
}
|
|
921
|
+
catch (error) {
|
|
922
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (family === "work" && (command === "fail" || command === "cancel")) {
|
|
926
|
+
try {
|
|
927
|
+
prepareWorkSettlement(context, requiredPosition(parsed, 0, "work-id"), command === "fail" ? "failed" : "cancelled", requiredOption(parsed, "reason"));
|
|
928
|
+
}
|
|
929
|
+
catch (error) {
|
|
930
|
+
return prepareError(error, { parameter: "work-id/reason" });
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (family === "work" && (command === "deps" || command === "delete")) {
|
|
934
|
+
try {
|
|
935
|
+
if (command === "delete")
|
|
936
|
+
prepareWorkDelete(context, requiredPosition(parsed, 0, "work-id"));
|
|
937
|
+
else
|
|
938
|
+
prepareWorkDeps(context, { workId: requiredPosition(parsed, 1, "work-id"), action: requiredPosition(parsed, 0, "action"), on: parsed.options.get("on") ?? [] });
|
|
939
|
+
}
|
|
940
|
+
catch (error) {
|
|
941
|
+
return prepareError(error, { parameter: "work-id/on" });
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (route.key === "cleanup apply") {
|
|
945
|
+
const planFile = requiredOption(parsed, "plan-file");
|
|
946
|
+
try {
|
|
947
|
+
prepared.cleanup = prepareCleanupApply(context, { projectRoot: requiredOption(parsed, "project-root"), planFile, reason: requiredOption(parsed, "reason"), force: hasOption(parsed, "force") });
|
|
948
|
+
}
|
|
949
|
+
catch (error) {
|
|
950
|
+
return prepareError(error, { parameter: "plan-file", file: planFile });
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (route.key === "stage block" || route.key === "stage unblock") {
|
|
954
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
955
|
+
try {
|
|
956
|
+
if (route.key === "stage block") {
|
|
957
|
+
if (!hasOption(parsed, "summary-stdin"))
|
|
958
|
+
throw new AppError("usage", "stage block requires --summary-stdin", 2);
|
|
959
|
+
prepared.interactionText = await readStdin(io.stdin);
|
|
960
|
+
prepareStageRuntimeBlock(context, { ...input, kind: requiredOption(parsed, "kind"), code: requiredOption(parsed, "code"), summary: prepared.interactionText, retryable: hasOption(parsed, "retryable") });
|
|
961
|
+
}
|
|
962
|
+
else
|
|
963
|
+
prepareStageRuntimeUnblock(context, input);
|
|
964
|
+
}
|
|
965
|
+
catch (error) {
|
|
966
|
+
return prepareError(error, { parameter: "stage/work/summary" });
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (route.key === "zcode invocation prepare") {
|
|
970
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label: "invocation issuance" }).value;
|
|
971
|
+
const payload = value && typeof value === "object" ? value : {};
|
|
972
|
+
try {
|
|
973
|
+
prepared.lifecycleIssuance = prepareLifecycleIssuance(payload.command, payload.scope);
|
|
974
|
+
}
|
|
975
|
+
catch (error) {
|
|
976
|
+
if (error instanceof AppError && ["invocation_command_invalid", "invocation_scope_invalid"].includes(error.code))
|
|
977
|
+
return prepareError(new AppError("usage", error.message, 2, { cause_code: error.code }), { parameter: "stdin" });
|
|
978
|
+
throw error;
|
|
979
|
+
}
|
|
980
|
+
assertLifecycleInvocationCurrent(context, prepared.lifecycleIssuance.scope, prepared.lifecycleIssuance.command);
|
|
981
|
+
}
|
|
982
|
+
if (route.key === "work add-batch") {
|
|
983
|
+
const file = requiredOption(parsed, "file");
|
|
984
|
+
try {
|
|
985
|
+
prepared.workBatch = prepareWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file });
|
|
986
|
+
}
|
|
987
|
+
catch (error) {
|
|
988
|
+
return prepareError(error, { parameter: "file/parent", file });
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (route.key === "work repair add") {
|
|
992
|
+
if (!hasOption(parsed, "task-stdin"))
|
|
993
|
+
throw new AppError("usage", "work repair add requires --task-stdin", 2);
|
|
994
|
+
if (io.stdin?.isTTY)
|
|
995
|
+
throw new AppError("usage", "--task-stdin requires piped or redirected input", 2);
|
|
996
|
+
prepared.workRepair = prepareCliWorkRepair(context, parsed, await readStdin(io.stdin), scopeProjectRoot);
|
|
997
|
+
}
|
|
998
|
+
if (route.key === "run config set" || route.key === "run vars set") {
|
|
999
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), key: requiredOption(parsed, "key"), value: requiredOption(parsed, "value") };
|
|
1000
|
+
try {
|
|
1001
|
+
if (route.key === "run config set")
|
|
1002
|
+
prepareFlowRunConfig(context, { ...input, reason: requiredOption(parsed, "reason") });
|
|
1003
|
+
else
|
|
1004
|
+
prepareFlowRunVariable(context, input);
|
|
1005
|
+
}
|
|
1006
|
+
catch (error) {
|
|
1007
|
+
return prepareError(error, { parameter: "run config/vars" });
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
if (route.key === "schema validate") {
|
|
1011
|
+
try {
|
|
1012
|
+
prepared.schemaValidation = dispatchSchema(context, command, parsed);
|
|
1013
|
+
}
|
|
1014
|
+
catch (error) {
|
|
1015
|
+
return prepareError(error, { parameter: "file", file: requiredOption(parsed, "file") });
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (route.key === "project config set") {
|
|
1019
|
+
try {
|
|
1020
|
+
prepareProjectConfigValue(requiredOption(parsed, "key"), requiredOption(parsed, "value"));
|
|
1021
|
+
}
|
|
1022
|
+
catch (error) {
|
|
1023
|
+
return prepareError(error, { parameter: "key/value" });
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
if (family === "session" && command === "register") {
|
|
1027
|
+
try {
|
|
1028
|
+
prepared.sessionPayload = prepareFlowSessionPayload({ payloadBase64: optionalOption(parsed, "payload-base64"), payloadFile: optionalOption(parsed, "payload-file"), payloadJson: optionalOption(parsed, "payload-json") });
|
|
1029
|
+
}
|
|
1030
|
+
catch (error) {
|
|
1031
|
+
return prepareError(error, { parameter: "session payload" });
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
const run = (projectRoot, runId) => {
|
|
1035
|
+
try {
|
|
1036
|
+
const project = requireProjectByRoot(context, resolveProjectRoot(projectRoot));
|
|
1037
|
+
const row = context.db.get("SELECT run_root, workspace_root, index_json FROM runs WHERE project_id = ? AND id = ?", [project.id, runId]);
|
|
1038
|
+
if (!row)
|
|
1039
|
+
throw new AppError("not_found", "RUN is not registered", 2, { run_id: runId });
|
|
1040
|
+
if (!row.run_root)
|
|
1041
|
+
throw new AppError("runtime_missing", "RUN artifact root is unavailable", 1, { run_id: runId });
|
|
1042
|
+
return { runRoot: row.run_root, workspaceRoot: row.workspace_root, indexJson: row.index_json };
|
|
1043
|
+
}
|
|
1044
|
+
catch (error) {
|
|
1045
|
+
return prepareError(error, { parameter: "run-id", run_id: runId });
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
const json = (file, stdin, label, schema) => {
|
|
1049
|
+
if (stdin) {
|
|
1050
|
+
// The caller awaits this helper; the synchronous facade below is only
|
|
1051
|
+
// used for file inputs. Keep stdin handling in the stage branch.
|
|
1052
|
+
throw new AppError("usage", `${label} stdin preparation must be awaited`, 2);
|
|
1053
|
+
}
|
|
1054
|
+
if (!file)
|
|
1055
|
+
throw new AppError("usage", `${label} input is required`, 2);
|
|
1056
|
+
return prepareJsonSchema(file, label, schema);
|
|
1057
|
+
};
|
|
1058
|
+
if (family === "stage" && command === "start") {
|
|
1059
|
+
const stage = requiredOption(parsed, "stage");
|
|
1060
|
+
if (stage !== "specify" && ["bootstrap", "intake-file", "intake-stdin"].some(option => hasOption(parsed, option)))
|
|
1061
|
+
throw new AppError("usage", "--bootstrap and intake inputs are only supported for SPECIFY", 2);
|
|
1062
|
+
requiredOption(parsed, "project-root");
|
|
1063
|
+
if (!hasOption(parsed, "bootstrap"))
|
|
1064
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1065
|
+
if (hasOption(parsed, "session-id"))
|
|
1066
|
+
throw new AppError("usage", "--session-id is managed by the harness lifecycle adapter and is not accepted", 2);
|
|
1067
|
+
const contextFile = optionalOption(parsed, "context-file"), contextSha256 = optionalOption(parsed, "context-sha256");
|
|
1068
|
+
if (Boolean(contextFile) !== Boolean(contextSha256))
|
|
1069
|
+
throw new AppError("usage", "stage start requires --context-file together with --context-sha256", 2);
|
|
1070
|
+
if ([hasOption(parsed, "intake-file"), hasOption(parsed, "intake-stdin"), Boolean(contextFile)].filter(Boolean).length > 1)
|
|
1071
|
+
throw new AppError("usage", "Use exactly one of --intake-file, --intake-stdin or --context-file", 2);
|
|
1072
|
+
if (contextFile && contextSha256) {
|
|
1073
|
+
try {
|
|
1074
|
+
prepared.stageStart = { externalContext: loadExternalStageContext({ file: contextFile, expectedSha256: contextSha256, stage }) };
|
|
1075
|
+
}
|
|
1076
|
+
catch (error) {
|
|
1077
|
+
return prepareError(error, { file: contextFile, parameter: "context-file" });
|
|
1078
|
+
}
|
|
1079
|
+
if (!hasOption(parsed, "bootstrap")) {
|
|
1080
|
+
const { runRoot } = run(requiredOption(parsed, "project-root"), requiredPosition(parsed, 0, "run-id"));
|
|
1081
|
+
prepareExternalStageContext({ stageRoot: path.join(runRoot, vnextStageDirectory(stage)), loaded: prepared.stageStart.externalContext });
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (stage === "specify") {
|
|
1085
|
+
const intakeFile = optionalOption(parsed, "intake-file");
|
|
1086
|
+
if (intakeFile)
|
|
1087
|
+
prepared.stageStart = { intakeMarkdown: prepareTextFile(intakeFile, "SPECIFY intake").text };
|
|
1088
|
+
else if (hasOption(parsed, "intake-stdin"))
|
|
1089
|
+
prepared.stageStart = { intakeMarkdown: await readStdin(io.stdin) };
|
|
1090
|
+
else if (prepared.stageStart?.externalContext)
|
|
1091
|
+
prepared.stageStart.intakeMarkdown = runnerIntake(prepared.stageStart.externalContext);
|
|
1092
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1093
|
+
const bootstrap = hasOption(parsed, "bootstrap");
|
|
1094
|
+
if (isVnextSpecifyFlow(projectRoot) && (bootstrap || isVnextSpecifyRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") }))) {
|
|
1095
|
+
prepared.specifyStart = prepareVnextSpecifyStart(context, { projectRoot, ...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : {}), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}), slug: optionalOption(parsed, "subject"), ...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {}) });
|
|
1096
|
+
}
|
|
1097
|
+
else if (bootstrap) {
|
|
1098
|
+
if (hasOption(parsed, "intake-stdin") || contextFile)
|
|
1099
|
+
throw new AppError("usage", "Legacy SPECIFY bootstrap supports --intake-file, not stdin or runner context", 2);
|
|
1100
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot, stage, bootstrap: { subject: requiredOption(parsed, "subject"), ...(intakeFile ? { intakeFile } : {}) }, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) }, prepared.stageStart?.intakeMarkdown);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
if (stage === "merge" && isVnextMergeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1104
|
+
prepared.mergeStart = prepareVnextMergeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1105
|
+
}
|
|
1106
|
+
if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1107
|
+
prepared.protocolizeStart = prepareVnextProtocolizeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1108
|
+
}
|
|
1109
|
+
if (stage === "plan" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1110
|
+
prepared.planStart = prepareVnextPlanStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1111
|
+
}
|
|
1112
|
+
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1113
|
+
prepared.planReviewStart = prepareVnextPlanReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1114
|
+
}
|
|
1115
|
+
if (stage === "code" && isVnextPlanRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1116
|
+
prepared.codeStart = prepareVnextCodeStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1117
|
+
}
|
|
1118
|
+
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1119
|
+
prepared.codeReviewStart = prepareVnextCodeReviewStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), ...(prepared.stageStart?.externalContext ? { externalContext: prepared.stageStart.externalContext } : {}) });
|
|
1120
|
+
}
|
|
1121
|
+
if (![prepared.specifyStart, prepared.protocolizeStart, prepared.planStart, prepared.planReviewStart, prepared.codeStart, prepared.codeReviewStart, prepared.mergeStart, prepared.legacyStageStart].some(Boolean)) {
|
|
1122
|
+
if (prepared.stageStart?.externalContext)
|
|
1123
|
+
throw new AppError("usage", "--context-file is currently supported only by vNext flow stages", 2);
|
|
1124
|
+
prepared.legacyStageStart = prepareLegacyStageStart(context, { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}) });
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
if (family === "stage" && (command === "pause" || command === "resume")) {
|
|
1128
|
+
requiredPosition(parsed, 0, "run-id");
|
|
1129
|
+
requiredOption(parsed, "project-root");
|
|
1130
|
+
requiredOption(parsed, "stage");
|
|
1131
|
+
requiredOption(parsed, "work");
|
|
1132
|
+
const parameter = command === "pause" ? "question-stdin" : "answer-stdin";
|
|
1133
|
+
if (!hasOption(parsed, parameter))
|
|
1134
|
+
throw new AppError("usage", `stage ${command} requires --${parameter}`, 2);
|
|
1135
|
+
if (io.stdin?.isTTY)
|
|
1136
|
+
throw new AppError("usage", `--${parameter} requires piped or redirected input`, 2);
|
|
1137
|
+
prepared.interactionText = await readStdin(io.stdin);
|
|
1138
|
+
if (!prepared.interactionText.trim())
|
|
1139
|
+
return prepareError(new AppError("validation", `stage ${command} requires non-empty --${parameter}`, 2), { parameter });
|
|
1140
|
+
const input = { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), workId: requiredOption(parsed, "work") };
|
|
1141
|
+
try {
|
|
1142
|
+
if (command === "pause")
|
|
1143
|
+
prepareStagePause(context, { ...input, question: prepared.interactionText });
|
|
1144
|
+
else
|
|
1145
|
+
prepared.resumeEvidence = prepareStageResume(context, { ...input, answer: prepared.interactionText }).evidence;
|
|
1146
|
+
}
|
|
1147
|
+
catch (error) {
|
|
1148
|
+
return prepareError(error, { parameter: "stage/work" });
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
if (family === "work" && command === "finish") {
|
|
1152
|
+
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = optionalOption(parsed, "reason");
|
|
1153
|
+
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
1154
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1155
|
+
const resultFile = optionalOption(parsed, "result-file");
|
|
1156
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1157
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1158
|
+
throw new AppError("usage", "work finish requires exactly one of --result-file or --result-stdin", 2);
|
|
1159
|
+
prepared.workFinish = { result: resultFile ? prepareTextFile(resultFile, "Work result").text : await readStdin(io.stdin) };
|
|
1160
|
+
prepareWorkResult(context, requiredPosition(parsed, 0, "work-id"), prepared.workFinish.result);
|
|
1161
|
+
}
|
|
1162
|
+
if (family === "stage" && command === "fanout" && requiredPosition(parsed, 0, "fanout action") === "reconcile") {
|
|
1163
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1164
|
+
}
|
|
1165
|
+
if (family === "stage" && command === "native" && requiredPosition(parsed, 0, "native action") === "observe") {
|
|
1166
|
+
prepared.observations = prepareNativeObservations(prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value);
|
|
1167
|
+
}
|
|
1168
|
+
if (family !== "stage" || command !== "finish")
|
|
1169
|
+
return prepared;
|
|
1170
|
+
const projectRoot = requiredOption(parsed, "project-root");
|
|
1171
|
+
const runId = requiredPosition(parsed, 0, "run-id");
|
|
1172
|
+
const stage = requiredOption(parsed, "stage");
|
|
1173
|
+
if (hasOption(parsed, "status"))
|
|
1174
|
+
throw new AppError("usage", "Stage status belongs in the semantic input file", 2);
|
|
1175
|
+
if (["result-file", "semantic-file", "data", "result-stdin"].filter(key => hasOption(parsed, key)).length > 1)
|
|
1176
|
+
throw new AppError("usage", "Use exactly one result input: --result-file, --semantic-file, --data or --result-stdin", 2);
|
|
1177
|
+
if (Boolean(optionalOption(parsed, "retry-check")) !== Boolean(optionalOption(parsed, "reason")))
|
|
1178
|
+
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
1179
|
+
const resultStdin = hasOption(parsed, "result-stdin");
|
|
1180
|
+
const semanticFile = optionalOption(parsed, "semantic-file") ?? optionalOption(parsed, "data");
|
|
1181
|
+
const resultFile = optionalOption(parsed, "result-file") ?? semanticFile;
|
|
1182
|
+
const runtime = run(projectRoot, runId);
|
|
1183
|
+
const terminalReplay = (name) => {
|
|
1184
|
+
let index;
|
|
1185
|
+
try {
|
|
1186
|
+
index = JSON.parse(runtime.indexJson);
|
|
1187
|
+
}
|
|
1188
|
+
catch (error) {
|
|
1189
|
+
throw new AppError("runtime_artifact_invalid", "RUN index is not valid retained JSON", 1, { run_id: runId, cause: error instanceof Error ? error.message : String(error) });
|
|
1190
|
+
}
|
|
1191
|
+
return index.stage_runs?.some(entry => entry.stage === name && entry.status === "done") === true
|
|
1192
|
+
&& fs.existsSync(path.join(runtime.runRoot, vnextStageDirectory(name), "stage-report.json"));
|
|
1193
|
+
};
|
|
1194
|
+
const acceptStageInputs = (allowed) => {
|
|
1195
|
+
const options = allowed.split(" ");
|
|
1196
|
+
for (const name of ["dir", "semantic-file", "data", "result-file", "result-stdin", "outcome", "decision-file", "verification-file", "retry-check", "reason", "request", "work"]) {
|
|
1197
|
+
if (hasOption(parsed, name) && !options.includes(name))
|
|
1198
|
+
throw new AppError("usage", `stage finish ${stage} does not accept --${name} for this flow`, 2, { parameter: name });
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
const fromStdin = async (label, schema) => {
|
|
1202
|
+
const value = prepareJsonText(await readStdin(io.stdin), { file: "<stdin>", label });
|
|
1203
|
+
try {
|
|
1204
|
+
validateSchema({ ...schema, file: value.file, data: value.value });
|
|
1205
|
+
}
|
|
1206
|
+
catch (error) {
|
|
1207
|
+
return prepareError(error, { file: value.file, label });
|
|
1208
|
+
}
|
|
1209
|
+
return value;
|
|
1210
|
+
};
|
|
1211
|
+
if (stage === "specify" && isVnextSpecifyRun(context, { projectRoot, runId })) {
|
|
1212
|
+
acceptStageInputs("result-file semantic-file data result-stdin outcome");
|
|
1213
|
+
const outcome = optionalOption(parsed, "outcome");
|
|
1214
|
+
if (!outcome || !["specified", "failed", "cancelled"].includes(outcome))
|
|
1215
|
+
throw new AppError("usage", "vNext SPECIFY requires --outcome specified|failed|cancelled; use stage pause for a user question", 2);
|
|
1216
|
+
if (Boolean(resultFile) === resultStdin)
|
|
1217
|
+
throw new AppError("usage", "vNext SPECIFY finish requires exactly one result input", 2);
|
|
1218
|
+
const schema = { schemaName: "vnext-specify", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot };
|
|
1219
|
+
const input = resultStdin ? await fromStdin("SPECIFY result", schema) : json(resultFile, false, "SPECIFY result", schema);
|
|
1220
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1221
|
+
}
|
|
1222
|
+
else if (stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId })) {
|
|
1223
|
+
acceptStageInputs("result-file semantic-file data");
|
|
1224
|
+
if (!resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1225
|
+
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
1226
|
+
let absolute;
|
|
1227
|
+
try {
|
|
1228
|
+
absolute = prepareProtocolizeResultPath(runtime.runRoot, resultFile);
|
|
1229
|
+
}
|
|
1230
|
+
catch (error) {
|
|
1231
|
+
return prepareError(error, { file: resultFile });
|
|
1232
|
+
}
|
|
1233
|
+
const input = json(absolute, false, "PROTOCOLIZE result", { schemaName: "vnext-protocolize-result", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1234
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1235
|
+
}
|
|
1236
|
+
else if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1237
|
+
acceptStageInputs("");
|
|
1238
|
+
if (hasOption(parsed, "code-work-batch-file"))
|
|
1239
|
+
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
1240
|
+
try {
|
|
1241
|
+
prepared.planFinish = prepareVnextPlanFinish(context, { projectRoot, runId });
|
|
1242
|
+
}
|
|
1243
|
+
catch (error) {
|
|
1244
|
+
return prepareError(error, { parameter: "PLAN artifacts" });
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
else if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
1248
|
+
acceptStageInputs("request work retry-check reason");
|
|
1249
|
+
if (resultStdin || resultFile || hasOption(parsed, "outcome"))
|
|
1250
|
+
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
1251
|
+
requiredOption(parsed, "request");
|
|
1252
|
+
requiredOption(parsed, "work");
|
|
1253
|
+
try {
|
|
1254
|
+
prepared.mergeFinish = prepareVnextMergeFinish(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(optionalOption(parsed, "retry-check") ? { retryCheckId: optionalOption(parsed, "retry-check"), retryReason: optionalOption(parsed, "reason") } : {}) });
|
|
1255
|
+
}
|
|
1256
|
+
catch (error) {
|
|
1257
|
+
return prepareError(error, { parameter: "MERGE finish" });
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
else if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
1261
|
+
acceptStageInputs("decision-file");
|
|
1262
|
+
if (resultStdin || hasOption(parsed, "outcome"))
|
|
1263
|
+
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
1264
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "04-plan-review", "decision.json");
|
|
1265
|
+
const input = json(decision, false, "PLAN-REVIEW decision", { schemaName: "plan-review-decision", projectRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1266
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1267
|
+
}
|
|
1268
|
+
else if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1269
|
+
acceptStageInputs("verification-file retry-check reason");
|
|
1270
|
+
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome"))
|
|
1271
|
+
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
1272
|
+
const verification = optionalOption(parsed, "verification-file");
|
|
1273
|
+
if (!verification)
|
|
1274
|
+
throw new AppError("usage", "vNext CODE finish requires --verification-file", 2);
|
|
1275
|
+
if (!terminalReplay(stage)) {
|
|
1276
|
+
const input = json(verification, false, "CODE verification", { schemaName: "code-verification", projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1277
|
+
prepared.stageFinish = { value: input.value, text: input.text };
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
else if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
1281
|
+
acceptStageInputs("decision-file retry-check reason");
|
|
1282
|
+
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
1283
|
+
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
1284
|
+
const decision = optionalOption(parsed, "decision-file") ?? path.join(runtime.runRoot, "06-code-review", "decision.json");
|
|
1285
|
+
if (!terminalReplay(stage)) {
|
|
1286
|
+
const input = prepareCodeReviewDecision(runtime.runRoot, optionalOption(parsed, "decision-file"));
|
|
1287
|
+
try {
|
|
1288
|
+
validateSchema({ schemaName: "code-review-decision", file: decision, data: input.value, projectRoot: runtime.workspaceRoot, ddFlowHome: context.ddFlowHome, runId, runRoot: runtime.runRoot });
|
|
1289
|
+
}
|
|
1290
|
+
catch (error) {
|
|
1291
|
+
return prepareError(error, { file: decision, label: "CODE-REVIEW decision" });
|
|
1292
|
+
}
|
|
1293
|
+
prepared.stageFinish = input;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
else {
|
|
1297
|
+
acceptStageInputs("dir semantic-file data");
|
|
1298
|
+
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin)
|
|
1299
|
+
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
1300
|
+
prepared.legacyStageFinish = prepareStageFinish(context, { projectRoot, runId, stage, ...(optionalOption(parsed, "dir") ? { dir: optionalOption(parsed, "dir") } : {}), ...(semanticFile ? { semanticFile } : {}) });
|
|
1301
|
+
}
|
|
1302
|
+
return prepared;
|
|
1303
|
+
}
|
|
1304
|
+
async function dispatch(args, context, io, scopeProjectRoot = null, classification = classifyCliOperation(args, context.env), output = parseOutputOptions(args), prepared = {}) {
|
|
1305
|
+
if (!prepared.route)
|
|
1306
|
+
throw new AppError("command_not_prepared", "Command dispatch requires prepared arguments", 1);
|
|
1307
|
+
const { family, command, parsed } = prepared.route;
|
|
623
1308
|
if (family === "zcode" && command === "invocation" && parsed.positional[0] === "prepare") {
|
|
624
|
-
const payload =
|
|
625
|
-
if (
|
|
626
|
-
throw new AppError("
|
|
1309
|
+
const payload = prepared.lifecycleIssuance;
|
|
1310
|
+
if (!payload)
|
|
1311
|
+
throw new AppError("command_not_prepared", "Invocation issuance requires prepared input", 1);
|
|
627
1312
|
assertLifecycleInvocationCurrent(context, payload.scope, payload.command);
|
|
628
1313
|
return { ok: true, ...issueLifecycleInvocation(context, payload.command, payload.scope) };
|
|
629
1314
|
}
|
|
@@ -635,23 +1320,22 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
635
1320
|
return { ok: true, ...migrateStoreWriter(requiredOption(parsed, "database"), requiredOption(parsed, "backup")) };
|
|
636
1321
|
}
|
|
637
1322
|
if (family === "engine") {
|
|
638
|
-
return dispatchEngine(context, command, parsed, classification);
|
|
1323
|
+
return dispatchEngine(context, command, parsed, classification, prepared);
|
|
639
1324
|
}
|
|
640
1325
|
if (family === "status") {
|
|
641
|
-
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
642
1326
|
return getRuntimeStatus(context, {
|
|
643
|
-
projectRoot: optionalOption(
|
|
644
|
-
checkRegistry: hasOption(
|
|
1327
|
+
projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root"),
|
|
1328
|
+
checkRegistry: hasOption(parsed, "check-registry")
|
|
645
1329
|
});
|
|
646
1330
|
}
|
|
647
1331
|
if (family === "version") {
|
|
648
1332
|
return getCliVersionReport();
|
|
649
1333
|
}
|
|
650
1334
|
if (family === "runtime") {
|
|
651
|
-
return await dispatchRuntime(context, command, parsed, commandProgress(io, output));
|
|
1335
|
+
return await dispatchRuntime(context, command, parsed, commandProgress(io, output), prepared);
|
|
652
1336
|
}
|
|
653
1337
|
if (family === "canon") {
|
|
654
|
-
return dispatchCanon(context, command, parsed);
|
|
1338
|
+
return dispatchCanon(context, command, parsed, prepared);
|
|
655
1339
|
}
|
|
656
1340
|
if (family === "project" && command === "register") {
|
|
657
1341
|
return registerProject(context, { root: requiredOption(parsed, "root") });
|
|
@@ -728,16 +1412,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
728
1412
|
return readyForMerge(context, { protocolId, projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol ready-for-merge") });
|
|
729
1413
|
}
|
|
730
1414
|
if (family === "protocol" && command === "transition") {
|
|
731
|
-
|
|
732
|
-
const reason = optionalOption(parsed, "reason");
|
|
733
|
-
return transitionProtocol(context, {
|
|
734
|
-
protocolId,
|
|
735
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol transition"),
|
|
736
|
-
to: requiredOption(parsed, "to"),
|
|
737
|
-
jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]),
|
|
738
|
-
force: hasOption(parsed, "force"),
|
|
739
|
-
...(reason ? { reason } : {})
|
|
740
|
-
});
|
|
1415
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
741
1416
|
}
|
|
742
1417
|
if (family === "protocol" && command === "sync-from-run") {
|
|
743
1418
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -762,17 +1437,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
762
1437
|
});
|
|
763
1438
|
}
|
|
764
1439
|
if (family === "transition") {
|
|
765
|
-
|
|
766
|
-
const protocolId = requiredPosition(transitionParsed, 0, "protocol-id");
|
|
767
|
-
const reason = optionalOption(transitionParsed, "reason");
|
|
768
|
-
return transitionProtocol(context, {
|
|
769
|
-
protocolId,
|
|
770
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "transition"),
|
|
771
|
-
to: requiredOption(transitionParsed, "to"),
|
|
772
|
-
jsonFile: requiredOptionAlias(transitionParsed, ["payload-file", "json-file"]),
|
|
773
|
-
force: hasOption(transitionParsed, "force"),
|
|
774
|
-
...(reason ? { reason } : {})
|
|
775
|
-
});
|
|
1440
|
+
return transitionProtocol(context, protocolTransitionInput(parsed, scopeProjectRoot), prepared.protocolTransition);
|
|
776
1441
|
}
|
|
777
1442
|
if (family === "plan" && command === "status") {
|
|
778
1443
|
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
@@ -792,7 +1457,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
792
1457
|
if (action === "status")
|
|
793
1458
|
return getVnextFanoutStatus(context, { projectRoot, runId, stage });
|
|
794
1459
|
if (action === "reconcile")
|
|
795
|
-
return reconcileVnextFanout(context, { projectRoot, runId, stage, observations:
|
|
1460
|
+
return reconcileVnextFanout(context, { projectRoot, runId, stage, observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
|
|
796
1461
|
if (action === "dispatch") {
|
|
797
1462
|
if (stage === "plan-review")
|
|
798
1463
|
return dispatchVnextPlanReview(context, { projectRoot, runId });
|
|
@@ -803,7 +1468,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
803
1468
|
if (family === "stage" && command === "native") {
|
|
804
1469
|
if (requiredPosition(parsed, 0, "native action") !== "observe")
|
|
805
1470
|
throw new AppError("usage", "Use: dd-flow stage native observe <RUN> --stage <stage> --observations-file <native-observations.json> --project-root <path>", 2);
|
|
806
|
-
return observeVnextNativeChildren(context, { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "stage native observe"), runId: requiredPosition(parsed, 1, "run-id"), stage: requiredOption(parsed, "stage"), observations:
|
|
1471
|
+
return observeVnextNativeChildren(context, { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "stage native observe"), runId: requiredPosition(parsed, 1, "run-id"), stage: requiredOption(parsed, "stage"), observations: prepared.observations ?? prepareJsonFile(requiredOption(parsed, "observations-file"), "native observations").value });
|
|
807
1472
|
}
|
|
808
1473
|
if (family === "run" && command === "capacity") {
|
|
809
1474
|
if (requiredPosition(parsed, 0, "capacity action") !== "record")
|
|
@@ -815,16 +1480,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
815
1480
|
return dispatchPlanItem(context, parsed, requiredScopeProjectRoot(scopeProjectRoot, "plan item"));
|
|
816
1481
|
}
|
|
817
1482
|
if (family === "prompt" && command === "render") {
|
|
818
|
-
|
|
819
|
-
const taskFile = optionalOption(parsed, "task-file");
|
|
820
|
-
return renderWorkerPrompt(context, {
|
|
821
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
822
|
-
runId: requiredOption(parsed, "run"),
|
|
823
|
-
stage: requiredOption(parsed, "stage"),
|
|
824
|
-
profile: requiredOption(parsed, "profile"),
|
|
825
|
-
...(taskFile ? { taskFile } : { planItemId: requiredOption(parsed, "plan-item") }),
|
|
826
|
-
...(workspaceRoot ? { workspaceRoot } : {})
|
|
827
|
-
});
|
|
1483
|
+
return renderWorkerPrompt(context, prepared.workerPrompt?.input ?? workerPromptInput(parsed), prepared.workerPrompt?.value);
|
|
828
1484
|
}
|
|
829
1485
|
if (family === "work" && command === "status") {
|
|
830
1486
|
return getVnextWorkStatus(context, {
|
|
@@ -833,7 +1489,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
833
1489
|
});
|
|
834
1490
|
}
|
|
835
1491
|
if (family === "work" && command === "add-batch") {
|
|
836
|
-
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file") });
|
|
1492
|
+
return addWorkBatch(context, { parentWorkId: requiredOption(parsed, "parent"), file: requiredOption(parsed, "file"), prepared: prepared.workBatch });
|
|
837
1493
|
}
|
|
838
1494
|
if (family === "work" && command === "ls") {
|
|
839
1495
|
return listWorks(context, { ...(optionalOption(parsed, "run") ? { runId: optionalOption(parsed, "run") } : {}), ...(optionalOption(parsed, "parent") ? { parentWorkId: optionalOption(parsed, "parent") } : {}), ...(optionalOption(parsed, "status") ? { status: optionalOption(parsed, "status") } : {}), ...(hasOption(parsed, "ready") ? { ready: true } : {}), ...(hasOption(parsed, "include-results") ? { includeResults: true } : {}), ...(optionalOption(parsed, "limit") ? { limit: Number(optionalOption(parsed, "limit")) } : {}) });
|
|
@@ -841,48 +1497,9 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
841
1497
|
if (family === "work" && command === "show")
|
|
842
1498
|
return showWork(context, requiredPosition(parsed, 0, "work-id"));
|
|
843
1499
|
if (family === "work" && command === "repair" && requiredPosition(parsed, 0, "action") === "add") {
|
|
844
|
-
if (!
|
|
845
|
-
throw new AppError("
|
|
846
|
-
|
|
847
|
-
const findings = parsed.options.get("from-finding") ?? [];
|
|
848
|
-
const unresolved = parsed.options.get("from-unresolved") ?? [];
|
|
849
|
-
if (!origins.length && !findings.length)
|
|
850
|
-
throw new AppError("usage", "work repair add requires --from-check with --origin-work, --from-finding, or --from-unresolved with --origin-work", 2);
|
|
851
|
-
if (findings.length)
|
|
852
|
-
return addVnextCodeReviewRepair(context, {
|
|
853
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
854
|
-
runId: requiredOption(parsed, "run"),
|
|
855
|
-
findingIds: findings,
|
|
856
|
-
checkRefsByFinding: Object.fromEntries(findings.map((finding) => [finding, parsed.options.get("check-ref") ?? []])),
|
|
857
|
-
objective: await readStdin(io.stdin)
|
|
858
|
-
});
|
|
859
|
-
if (unresolved.length) {
|
|
860
|
-
if (!origins.length)
|
|
861
|
-
throw new AppError("usage", "semantic repair requires at least one --origin-work", 2);
|
|
862
|
-
const verificationPath = requiredOption(parsed, "verification-file");
|
|
863
|
-
return addVnextCodeRepair(context, {
|
|
864
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
865
|
-
runId: requiredOption(parsed, "run"),
|
|
866
|
-
originWorkIds: origins,
|
|
867
|
-
semanticUnresolved: unresolved,
|
|
868
|
-
verificationPath,
|
|
869
|
-
objective: await readStdin(io.stdin)
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
if (!hasOption(parsed, "from-check"))
|
|
873
|
-
throw new AppError("usage", "check repair requires --from-check", 2);
|
|
874
|
-
if (!origins.length)
|
|
875
|
-
throw new AppError("usage", "check repair requires at least one --origin-work", 2);
|
|
876
|
-
const receipts = parsed.options.get("from-check") ?? [];
|
|
877
|
-
if (receipts.length !== 1)
|
|
878
|
-
throw new AppError("usage", "check repair accepts exactly one --from-check; create one causal repair Work per failed receipt", 2);
|
|
879
|
-
return addVnextCodeRepair(context, {
|
|
880
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
|
|
881
|
-
runId: requiredOption(parsed, "run"),
|
|
882
|
-
checkReceiptId: receipts[0],
|
|
883
|
-
originWorkIds: origins,
|
|
884
|
-
objective: await readStdin(io.stdin)
|
|
885
|
-
});
|
|
1500
|
+
if (!prepared.workRepair)
|
|
1501
|
+
throw new AppError("cli_input_unprepared", "Repair input was not prepared", 1);
|
|
1502
|
+
return addVnextCodeRepair(context, prepared.workRepair.input, prepared.workRepair.value);
|
|
886
1503
|
}
|
|
887
1504
|
if (family === "work" && command === "deps") {
|
|
888
1505
|
const on = parsed.options.get("on") ?? [];
|
|
@@ -909,7 +1526,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
909
1526
|
const retryReceiptId = optionalOption(parsed, "retry-check"), retryReason = optionalOption(parsed, "reason");
|
|
910
1527
|
if (Boolean(retryReceiptId) !== Boolean(retryReason))
|
|
911
1528
|
throw new AppError("usage", "--retry-check and --reason must be supplied together", 2);
|
|
912
|
-
const result = resultFile ?
|
|
1529
|
+
const result = prepared.workFinish?.result ?? (resultFile ? prepareTextFile(resultFile, "Work result").text : await readStdin(io.stdin));
|
|
913
1530
|
return await finishWork(context, workId, result, commandProgress(io, output), retryReceiptId ? { receiptId: retryReceiptId, reason: retryReason } : undefined);
|
|
914
1531
|
}
|
|
915
1532
|
if (family === "work" && command === "fail") {
|
|
@@ -939,7 +1556,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
939
1556
|
const requireSessionBinding = hasOption(parsed, "require-session-binding");
|
|
940
1557
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
941
1558
|
const stage = requiredOption(parsed, "stage");
|
|
942
|
-
const externalContext =
|
|
1559
|
+
const externalContext = prepared.stageStart?.externalContext;
|
|
943
1560
|
const claimVnextStageStart = (runId) => {
|
|
944
1561
|
registerProject(context, { root: projectRoot });
|
|
945
1562
|
return findRecentMatchingHookEvent(context, {
|
|
@@ -970,37 +1587,37 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
970
1587
|
}
|
|
971
1588
|
const result = launchVnextSpecify(context, {
|
|
972
1589
|
projectRoot,
|
|
973
|
-
...(externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
1590
|
+
...(prepared.stageStart?.intakeMarkdown !== undefined ? { intakeMarkdown: prepared.stageStart.intakeMarkdown } : externalContext ? { intakeMarkdown: runnerIntake(externalContext) } : intakeFile ? { intakeFile } : { intakeMarkdown: await readStdin(io.stdin) }),
|
|
974
1591
|
hookEventId,
|
|
975
1592
|
...(subject ? { slug: subject } : {}),
|
|
976
1593
|
...(externalContext ? { externalContext } : {}),
|
|
977
1594
|
...(!bootstrap ? { runId: requiredPosition(parsed, 0, "run-id") } : {})
|
|
978
|
-
});
|
|
1595
|
+
}, prepared.specifyStart);
|
|
979
1596
|
return attachExternalStageContext(result, externalContext);
|
|
980
1597
|
}
|
|
981
1598
|
if (!bootstrap && stage === "protocolize" && isVnextProtocolizeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
982
1599
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
983
|
-
return attachExternalStageContext(startVnextProtocolize(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
1600
|
+
return attachExternalStageContext(startVnextProtocolize(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.protocolizeStart), externalContext);
|
|
984
1601
|
}
|
|
985
1602
|
if (!bootstrap && stage === "plan" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
986
1603
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
987
|
-
return attachExternalStageContext(startVnextPlan(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
1604
|
+
return attachExternalStageContext(startVnextPlan(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planStart), externalContext);
|
|
988
1605
|
}
|
|
989
1606
|
if (!bootstrap && stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
990
1607
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
991
|
-
return attachExternalStageContext(startVnextPlanReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
1608
|
+
return attachExternalStageContext(startVnextPlanReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.planReviewStart), externalContext);
|
|
992
1609
|
}
|
|
993
1610
|
if (!bootstrap && stage === "code" && isVnextPlanRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
994
1611
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
995
|
-
return attachExternalStageContext(await startVnextCode(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
1612
|
+
return attachExternalStageContext(await startVnextCode(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeStart), externalContext);
|
|
996
1613
|
}
|
|
997
1614
|
if (!bootstrap && stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
998
1615
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
999
|
-
return attachExternalStageContext(startVnextCodeReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
1616
|
+
return attachExternalStageContext(startVnextCodeReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.codeReviewStart), externalContext);
|
|
1000
1617
|
}
|
|
1001
1618
|
if (!bootstrap && stage === "merge" && isVnextMergeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
|
|
1002
1619
|
hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
|
|
1003
|
-
return attachExternalStageContext(await startVnextMerge(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
|
|
1620
|
+
return attachExternalStageContext(await startVnextMerge(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }, prepared.mergeStart), externalContext);
|
|
1004
1621
|
}
|
|
1005
1622
|
if (intakeStdin) {
|
|
1006
1623
|
throw new AppError("usage", "--intake-stdin is only available for vNext SPECIFY", 2);
|
|
@@ -1015,7 +1632,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1015
1632
|
...(hookEventId ? { hookEventId } : {}),
|
|
1016
1633
|
...(requireSessionBinding ? { requireSessionBinding: true } : {}),
|
|
1017
1634
|
...(dir ? { dir } : {})
|
|
1018
|
-
});
|
|
1635
|
+
}, prepared.legacyStageStart);
|
|
1019
1636
|
}
|
|
1020
1637
|
if (family === "stage" && command === "finish") {
|
|
1021
1638
|
const dir = optionalOption(parsed, "dir");
|
|
@@ -1056,7 +1673,8 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1056
1673
|
return controlledReply(await finishVnextSpecifyStage(context, {
|
|
1057
1674
|
projectRoot,
|
|
1058
1675
|
runId,
|
|
1059
|
-
...(resultFile ? { resultFile } : { resultJson: await readStdin(io.stdin) }),
|
|
1676
|
+
...(resultFile ? { resultFile } : prepared.stageFinish?.value ? {} : { resultJson: await readStdin(io.stdin) }),
|
|
1677
|
+
...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}),
|
|
1060
1678
|
outcome: outcome
|
|
1061
1679
|
}));
|
|
1062
1680
|
}
|
|
@@ -1064,34 +1682,34 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1064
1682
|
if (!resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
1065
1683
|
throw new AppError("usage", "vNext PROTOCOLIZE requires exactly --result-file", 2);
|
|
1066
1684
|
}
|
|
1067
|
-
return controlledReply(await finishVnextProtocolize(context, { projectRoot, runId, resultFile }));
|
|
1685
|
+
return controlledReply(await finishVnextProtocolize(context, { projectRoot, runId, resultFile, ...(prepared.stageFinish?.value ? { result: prepared.stageFinish.value } : {}) }));
|
|
1068
1686
|
}
|
|
1069
1687
|
if (stage === "plan" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1070
1688
|
if (optionalOption(parsed, "code-work-batch-file"))
|
|
1071
1689
|
throw new AppError("usage", "vNext PLAN derives its CODE batch from accepted semantic plans; do not supply a batch file", 2);
|
|
1072
|
-
return controlledReply(await finishVnextPlan(context, { projectRoot, runId }));
|
|
1690
|
+
return controlledReply(await finishVnextPlan(context, { projectRoot, runId, ...(prepared.planFinish ? { prepared: prepared.planFinish } : {}) }));
|
|
1073
1691
|
}
|
|
1074
1692
|
if (stage === "plan-review" && isVnextPlanReviewRun(context, { projectRoot, runId })) {
|
|
1075
1693
|
if (resultStdin || hasOption(parsed, "outcome"))
|
|
1076
1694
|
throw new AppError("usage", "vNext PLAN-REVIEW uses --decision-file", 2);
|
|
1077
1695
|
const decisionFile = optionalOption(parsed, "decision-file");
|
|
1078
|
-
return controlledReply(await finishVnextPlanReview(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}) }));
|
|
1696
|
+
return controlledReply(await finishVnextPlanReview(context, { projectRoot, runId, ...(decisionFile ? { decisionFile } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}) }));
|
|
1079
1697
|
}
|
|
1080
1698
|
if (stage === "code" && isVnextPlanRun(context, { projectRoot, runId })) {
|
|
1081
1699
|
if (semanticFile || resultFile || resultStdin || hasOption(parsed, "outcome")) {
|
|
1082
1700
|
throw new AppError("usage", "vNext CODE finish accepts only --verification-file", 2);
|
|
1083
1701
|
}
|
|
1084
|
-
return controlledReply(await finishVnextCode(context, { projectRoot, runId, verificationFile: optionalOption(parsed, "verification-file") ?? path.join("<missing>", "code-verification.json"), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
|
|
1702
|
+
return controlledReply(await finishVnextCode(context, { projectRoot, runId, verificationFile: optionalOption(parsed, "verification-file") ?? path.join("<missing>", "code-verification.json"), ...(prepared.stageFinish?.value ? { verification: prepared.stageFinish.value } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
|
|
1085
1703
|
}
|
|
1086
1704
|
if (stage === "code-review" && isVnextCodeReviewRun(context, { projectRoot, runId })) {
|
|
1087
1705
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
1088
1706
|
throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
|
|
1089
|
-
return controlledReply(await finishVnextCodeReview(context, { projectRoot, runId, ...(optionalOption(parsed, "decision-file") ? { decisionFile: optionalOption(parsed, "decision-file") } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
|
|
1707
|
+
return controlledReply(await finishVnextCodeReview(context, { projectRoot, runId, ...(optionalOption(parsed, "decision-file") ? { decisionFile: optionalOption(parsed, "decision-file") } : {}), ...(prepared.stageFinish?.value ? { decision: prepared.stageFinish.value, decisionText: prepared.stageFinish.text } : {}), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
|
|
1090
1708
|
}
|
|
1091
1709
|
if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
|
|
1092
1710
|
if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
|
|
1093
1711
|
throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
|
|
1094
|
-
return controlledReply(await finishVnextMerge(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }));
|
|
1712
|
+
return controlledReply(await finishVnextMerge(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), ...(retryCheckId ? { retryCheckId, retryReason: retryReason } : {}), progress: commandProgress(io, output) }, prepared.mergeFinish));
|
|
1095
1713
|
}
|
|
1096
1714
|
if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin) {
|
|
1097
1715
|
throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
|
|
@@ -1102,7 +1720,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1102
1720
|
stage,
|
|
1103
1721
|
...(dir ? { dir } : {}),
|
|
1104
1722
|
...(semanticFile ? { semanticFile } : {})
|
|
1105
|
-
}));
|
|
1723
|
+
}, prepared.legacyStageFinish));
|
|
1106
1724
|
}
|
|
1107
1725
|
if (family === "stage" && command === "pause") {
|
|
1108
1726
|
if (!hasOption(parsed, "question-stdin"))
|
|
@@ -1124,7 +1742,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1124
1742
|
runId,
|
|
1125
1743
|
stage,
|
|
1126
1744
|
workId,
|
|
1127
|
-
question: await readStdin(io.stdin)
|
|
1745
|
+
question: prepared.interactionText ?? await readStdin(io.stdin)
|
|
1128
1746
|
});
|
|
1129
1747
|
}
|
|
1130
1748
|
if (family === "stage" && command === "resume") {
|
|
@@ -1136,7 +1754,8 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1136
1754
|
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1137
1755
|
stage: requiredOption(parsed, "stage"),
|
|
1138
1756
|
workId: requiredOption(parsed, "work"),
|
|
1139
|
-
answer: await readStdin(io.stdin),
|
|
1757
|
+
answer: prepared.interactionText ?? await readStdin(io.stdin),
|
|
1758
|
+
evidence: prepared.resumeEvidence,
|
|
1140
1759
|
...(hookEventId ? { hookEventId } : {})
|
|
1141
1760
|
});
|
|
1142
1761
|
}
|
|
@@ -1153,7 +1772,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1153
1772
|
workId: requiredOption(parsed, "work"),
|
|
1154
1773
|
kind,
|
|
1155
1774
|
code: requiredOption(parsed, "code"),
|
|
1156
|
-
summary: await readStdin(io.stdin),
|
|
1775
|
+
summary: prepared.interactionText ?? await readStdin(io.stdin),
|
|
1157
1776
|
retryable: hasOption(parsed, "retryable")
|
|
1158
1777
|
});
|
|
1159
1778
|
}
|
|
@@ -1178,10 +1797,10 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1178
1797
|
return dispatchMergeWorker(context, command, parsed);
|
|
1179
1798
|
}
|
|
1180
1799
|
if (family === "cleanup") {
|
|
1181
|
-
return dispatchCleanup(context, command, parsed);
|
|
1800
|
+
return dispatchCleanup(context, command, parsed, prepared.cleanup);
|
|
1182
1801
|
}
|
|
1183
1802
|
if (family === "session") {
|
|
1184
|
-
return dispatchSession(context, command, parsed);
|
|
1803
|
+
return dispatchSession(context, command, parsed, prepared.sessionPayload);
|
|
1185
1804
|
}
|
|
1186
1805
|
if (family === "run") {
|
|
1187
1806
|
if (command === "control") {
|
|
@@ -1217,16 +1836,16 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1217
1836
|
if (action === "status")
|
|
1218
1837
|
return runControllerStatus(context, { ...scope, ...(optionalOption(parsed, "after") ? { after: Number(optionalOption(parsed, "after")) } : {}) });
|
|
1219
1838
|
if (action === "launch")
|
|
1220
|
-
return await launchRunController(context,
|
|
1839
|
+
return await launchRunController(context, controllerLaunchInput(parsed), prepared.controllerLaunch);
|
|
1221
1840
|
if (action === "serve")
|
|
1222
1841
|
return await serveRunController(context, { ...scope, controllerId: requiredOption(parsed, "controller-id") });
|
|
1223
1842
|
if (action === "answer")
|
|
1224
|
-
return answerRunController(context,
|
|
1843
|
+
return answerRunController(context, controllerAnswerInput(parsed), prepared.controllerAnswer);
|
|
1225
1844
|
if (action === "context")
|
|
1226
|
-
return supplyRunControllerContext(context,
|
|
1845
|
+
return supplyRunControllerContext(context, controllerContextInput(parsed), prepared.controllerContext);
|
|
1227
1846
|
throw new AppError("usage", "Use: dd-flow run drive launch|status|answer|context", 2);
|
|
1228
1847
|
}
|
|
1229
|
-
return dispatchRun(context, command, parsed);
|
|
1848
|
+
return dispatchRun(context, command, parsed, prepared);
|
|
1230
1849
|
}
|
|
1231
1850
|
if (family === "stat") {
|
|
1232
1851
|
return dispatchStat(context, command, parsed);
|
|
@@ -1241,19 +1860,19 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1241
1860
|
return dispatchDashboard(context, command, parsed);
|
|
1242
1861
|
}
|
|
1243
1862
|
if (family === "schema") {
|
|
1244
|
-
return
|
|
1863
|
+
return prepared.schemaValidation;
|
|
1245
1864
|
}
|
|
1246
1865
|
if (family === "memory") {
|
|
1247
1866
|
return dispatchMemory(command, parsed);
|
|
1248
1867
|
}
|
|
1249
1868
|
if (family === "migration") {
|
|
1250
|
-
return dispatchMigration(context, command, parsed, classification);
|
|
1869
|
+
return dispatchMigration(context, command, parsed, classification, prepared);
|
|
1251
1870
|
}
|
|
1252
1871
|
if (family === "codex" && command === "hooks") {
|
|
1253
|
-
return dispatchCodexHooks(context, parsed);
|
|
1872
|
+
return dispatchCodexHooks(context, parsed, prepared);
|
|
1254
1873
|
}
|
|
1255
1874
|
if (family === "codex" && command === "home") {
|
|
1256
|
-
return dispatchCodexHome(context, parsed);
|
|
1875
|
+
return dispatchCodexHome(context, parsed, prepared);
|
|
1257
1876
|
}
|
|
1258
1877
|
if (family === "codex" && command === "hook") {
|
|
1259
1878
|
return dispatchCodexHook(context, parsed, await readStdin(io.stdin));
|
|
@@ -1303,11 +1922,14 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
|
|
|
1303
1922
|
return ingestAgyUsage(context, { projectRoot: requiredOption(parsed, "project-root"), stdin: await readStdin(io.stdin) });
|
|
1304
1923
|
}
|
|
1305
1924
|
if (family === "worktree") {
|
|
1306
|
-
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`));
|
|
1925
|
+
return dispatchWorktree(context, command, parsed, requiredScopeProjectRoot(scopeProjectRoot, `worktree ${command ?? ""}`), prepared);
|
|
1307
1926
|
}
|
|
1308
1927
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
1309
1928
|
}
|
|
1310
|
-
function
|
|
1929
|
+
function engineBindInput(parsed) {
|
|
1930
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), packageName: optionalOption(parsed, "package"), engineVersion: requiredOption(parsed, "engine-version"), integrityChecksum: optionalOption(parsed, "integrity-checksum"), reason: requiredOption(parsed, "reason") };
|
|
1931
|
+
}
|
|
1932
|
+
function dispatchEngine(context, command, parsed, classification, prepared) {
|
|
1311
1933
|
if (command === "install") {
|
|
1312
1934
|
return installCurrentEngine(context, { force: hasOption(parsed, "force") });
|
|
1313
1935
|
}
|
|
@@ -1332,18 +1954,11 @@ function dispatchEngine(context, command, parsed, classification) {
|
|
|
1332
1954
|
});
|
|
1333
1955
|
}
|
|
1334
1956
|
if (command === "bind-run") {
|
|
1335
|
-
return bindRunEngine(context,
|
|
1336
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1337
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1338
|
-
packageName: optionalOption(parsed, "package"),
|
|
1339
|
-
engineVersion: requiredOption(parsed, "engine-version"),
|
|
1340
|
-
integrityChecksum: optionalOption(parsed, "integrity-checksum"),
|
|
1341
|
-
reason: requiredOption(parsed, "reason")
|
|
1342
|
-
});
|
|
1957
|
+
return bindRunEngine(context, engineBindInput(parsed), prepared.engineBind);
|
|
1343
1958
|
}
|
|
1344
1959
|
throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
|
|
1345
1960
|
}
|
|
1346
|
-
function dispatchMigration(context, command, parsed, classification) {
|
|
1961
|
+
function dispatchMigration(context, command, parsed, classification, prepared) {
|
|
1347
1962
|
if (command === "impact")
|
|
1348
1963
|
return assessMigrationImpact(context, { projectRoot: requiredOption(parsed, "project-root"), ...(optionalOption(parsed, "target-version") ? { targetVersion: optionalOption(parsed, "target-version") } : {}), ...(optionalOption(parsed, "mode") ? { mode: optionalOption(parsed, "mode") } : {}) });
|
|
1349
1964
|
if (command === "plan" || command === "report") {
|
|
@@ -1359,26 +1974,42 @@ function dispatchMigration(context, command, parsed, classification) {
|
|
|
1359
1974
|
});
|
|
1360
1975
|
}
|
|
1361
1976
|
if (command === "verify") {
|
|
1362
|
-
return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
1977
|
+
return prepared.migrationVerification ?? verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
1363
1978
|
}
|
|
1364
1979
|
if (command === "apply") {
|
|
1365
|
-
return applyMigration(context,
|
|
1366
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1367
|
-
sourceVersion: optionalOption(parsed, "source-version"),
|
|
1368
|
-
targetVersion: optionalOption(parsed, "target-version"),
|
|
1369
|
-
runId: optionalOption(parsed, "run"),
|
|
1370
|
-
backupPath: optionalOption(parsed, "backup-path"),
|
|
1371
|
-
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
1372
|
-
reportFile: optionalOption(parsed, "file"),
|
|
1373
|
-
allowActive: false,
|
|
1374
|
-
classification
|
|
1375
|
-
});
|
|
1980
|
+
return applyMigration(context, migrationApplyInput(parsed, classification), prepared.migrationApply);
|
|
1376
1981
|
}
|
|
1377
1982
|
throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
|
|
1378
1983
|
}
|
|
1379
|
-
function
|
|
1984
|
+
function protocolTransitionInput(parsed, scopeProjectRoot) {
|
|
1985
|
+
const reason = optionalOption(parsed, "reason");
|
|
1986
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "protocol transition"), to: requiredOption(parsed, "to"), jsonFile: requiredOptionAlias(parsed, ["payload-file", "json-file"]), force: hasOption(parsed, "force"), ...(reason ? { reason } : {}) };
|
|
1987
|
+
}
|
|
1988
|
+
function controllerAnswerInput(parsed) {
|
|
1989
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), requestId: requiredOption(parsed, "request-id"), pauseId: requiredOption(parsed, "pause-id"), answerFile: requiredOption(parsed, "answer-file") };
|
|
1990
|
+
}
|
|
1991
|
+
function controllerLaunchInput(parsed) {
|
|
1992
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), requestId: requiredOption(parsed, "request-id"), contextFile: requiredOption(parsed, "context-file"), contextSha256: requiredOption(parsed, "context-sha256"), ...(optionalOption(parsed, "stop-after") ? { stopAfter: optionalOption(parsed, "stop-after") } : {}), ...(optionalOption(parsed, "capture-root") ? { captureRoot: optionalOption(parsed, "capture-root") } : {}) };
|
|
1993
|
+
}
|
|
1994
|
+
function controllerContextInput(parsed) {
|
|
1995
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), controllerId: requiredOption(parsed, "controller-id"), requestId: requiredOption(parsed, "request-id"), stage: requiredOption(parsed, "stage"), contextFile: requiredOption(parsed, "context-file"), contextSha256: requiredOption(parsed, "context-sha256") };
|
|
1996
|
+
}
|
|
1997
|
+
function migrationApplyInput(parsed, classification) {
|
|
1998
|
+
return {
|
|
1999
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
2000
|
+
sourceVersion: optionalOption(parsed, "source-version"),
|
|
2001
|
+
targetVersion: optionalOption(parsed, "target-version"),
|
|
2002
|
+
runId: optionalOption(parsed, "run"),
|
|
2003
|
+
backupPath: optionalOption(parsed, "backup-path"),
|
|
2004
|
+
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
2005
|
+
reportFile: optionalOption(parsed, "file"),
|
|
2006
|
+
allowActive: false,
|
|
2007
|
+
classification
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
function dispatchCanon(context, command, parsed, prepared) {
|
|
1380
2011
|
if (command === "register") {
|
|
1381
|
-
return registerCanonRoot(context, { root: requiredOption(parsed, "root") });
|
|
2012
|
+
return registerCanonRoot(context, { root: requiredOption(parsed, "root") }, prepared.canonRegistration);
|
|
1382
2013
|
}
|
|
1383
2014
|
if (command === "status") {
|
|
1384
2015
|
return getCanonStatus(context, { root: optionalOption(parsed, "root") });
|
|
@@ -1388,6 +2019,15 @@ function dispatchCanon(context, command, parsed) {
|
|
|
1388
2019
|
}
|
|
1389
2020
|
throw new AppError("usage", `Unknown canon command: ${command ?? "<empty>"}`, 2);
|
|
1390
2021
|
}
|
|
2022
|
+
function mergeJobTarget(parsed, scopeProjectRoot) {
|
|
2023
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue"), workerId: requiredWorkerId(parsed), workspacePath: workspacePathOption(parsed) };
|
|
2024
|
+
}
|
|
2025
|
+
function mergeCancellationInput(parsed, scopeProjectRoot) {
|
|
2026
|
+
return { protocolId: requiredPosition(parsed, 0, "protocol-id"), projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"), reason: requiredOption(parsed, "reason"), workerId: optionalOption(parsed, "worker-id"), workspacePath: optionalOption(parsed, "path"), force: hasOption(parsed, "force") };
|
|
2027
|
+
}
|
|
2028
|
+
function mergeWaitInput(parsed, scopeProjectRoot) {
|
|
2029
|
+
return { projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"), workerId: requiredWorkerId(parsed), timeoutSeconds: optionalNumber(parsed, "timeout"), pollIntervalSeconds: optionalNumber(parsed, "poll-interval"), workspacePath: workspacePathOption(parsed), acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false };
|
|
2030
|
+
}
|
|
1391
2031
|
function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
1392
2032
|
if (hasOption(parsed, "session-id")) {
|
|
1393
2033
|
throw new AppError("usage", "--session-id is no longer accepted on merge-queue commands; use --worker-id", 2);
|
|
@@ -1411,32 +2051,17 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1411
2051
|
});
|
|
1412
2052
|
}
|
|
1413
2053
|
if (command === "wait-next") {
|
|
1414
|
-
return waitNextMergeJob(context,
|
|
1415
|
-
projectRoot: requiredOptionOrScope(parsed, scopeProjectRoot, "project-root"),
|
|
1416
|
-
workerId: requiredWorkerId(parsed),
|
|
1417
|
-
timeoutSeconds: optionalNumber(parsed, "timeout"),
|
|
1418
|
-
pollIntervalSeconds: optionalNumber(parsed, "poll-interval"),
|
|
1419
|
-
workspacePath: workspacePathOption(parsed),
|
|
1420
|
-
acquireLock: parseOptionalBoolean(optionalOption(parsed, "acquire-lock"), "acquire-lock") ?? false
|
|
1421
|
-
});
|
|
2054
|
+
return waitNextMergeJob(context, mergeWaitInput(parsed, scopeProjectRoot));
|
|
1422
2055
|
}
|
|
1423
2056
|
if (command === "complete") {
|
|
1424
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1425
2057
|
return completeMergeJob(context, {
|
|
1426
|
-
|
|
1427
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue complete"),
|
|
1428
|
-
workerId: requiredWorkerId(parsed),
|
|
1429
|
-
workspacePath: workspacePathOption(parsed),
|
|
2058
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1430
2059
|
summary: requiredOption(parsed, "summary")
|
|
1431
2060
|
});
|
|
1432
2061
|
}
|
|
1433
2062
|
if (command === "fail") {
|
|
1434
|
-
const protocolId = requiredPosition(parsed, 0, "protocol-id");
|
|
1435
2063
|
return failMergeJob(context, {
|
|
1436
|
-
|
|
1437
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue fail"),
|
|
1438
|
-
workerId: requiredWorkerId(parsed),
|
|
1439
|
-
workspacePath: workspacePathOption(parsed),
|
|
2064
|
+
...mergeJobTarget(parsed, scopeProjectRoot),
|
|
1440
2065
|
reason: requiredOption(parsed, "reason"),
|
|
1441
2066
|
requeue: parseBoolean(requiredOption(parsed, "requeue"), "requeue")
|
|
1442
2067
|
});
|
|
@@ -1451,15 +2076,7 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
|
|
|
1451
2076
|
});
|
|
1452
2077
|
}
|
|
1453
2078
|
if (command === "cancel") {
|
|
1454
|
-
|
|
1455
|
-
return cancelMergeQueueJob(context, {
|
|
1456
|
-
protocolId,
|
|
1457
|
-
projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "merge-queue cancel"),
|
|
1458
|
-
reason: requiredOption(parsed, "reason"),
|
|
1459
|
-
workerId: optionalOption(parsed, "worker-id"),
|
|
1460
|
-
workspacePath: optionalOption(parsed, "path"),
|
|
1461
|
-
force: hasOption(parsed, "force")
|
|
1462
|
-
});
|
|
2079
|
+
return cancelMergeQueueJob(context, mergeCancellationInput(parsed, scopeProjectRoot));
|
|
1463
2080
|
}
|
|
1464
2081
|
throw new AppError("usage", `Unknown merge-queue command: ${command ?? "<empty>"}`, 2);
|
|
1465
2082
|
}
|
|
@@ -1577,7 +2194,7 @@ function completedCommandExitCode(payload) {
|
|
|
1577
2194
|
const exitCode = payload.exit_code;
|
|
1578
2195
|
return typeof exitCode === "number" && Number.isInteger(exitCode) ? exitCode : 0;
|
|
1579
2196
|
}
|
|
1580
|
-
function dispatchCleanup(context, command, parsed) {
|
|
2197
|
+
function dispatchCleanup(context, command, parsed, prepared) {
|
|
1581
2198
|
if (command === "scan") {
|
|
1582
2199
|
return cleanupScan(context, { projectRoot: requiredOption(parsed, "project-root") });
|
|
1583
2200
|
}
|
|
@@ -1585,6 +2202,7 @@ function dispatchCleanup(context, command, parsed) {
|
|
|
1585
2202
|
return cleanupApply(context, {
|
|
1586
2203
|
projectRoot: requiredOption(parsed, "project-root"),
|
|
1587
2204
|
planFile: requiredOption(parsed, "plan-file"),
|
|
2205
|
+
prepared,
|
|
1588
2206
|
reason: requiredOption(parsed, "reason"),
|
|
1589
2207
|
force: hasOption(parsed, "force")
|
|
1590
2208
|
});
|
|
@@ -1605,12 +2223,14 @@ function dispatchProjectConfig(context, parsed) {
|
|
|
1605
2223
|
}
|
|
1606
2224
|
throw new AppError("usage", `Unknown project config action: ${action}`, 2);
|
|
1607
2225
|
}
|
|
1608
|
-
function dispatchSession(context, command, parsed) {
|
|
2226
|
+
function dispatchSession(context, command, parsed, preparedPayload) {
|
|
1609
2227
|
if (command === "register") {
|
|
1610
2228
|
return registerFlowSession(context, {
|
|
2229
|
+
...(preparedPayload ? { preparedPayload } : {}),
|
|
1611
2230
|
payloadBase64: optionalOption(parsed, "payload-base64"),
|
|
1612
2231
|
payloadJson: optionalOption(parsed, "payload-json"),
|
|
1613
2232
|
payloadFile: optionalOption(parsed, "payload-file"),
|
|
2233
|
+
hookEventId: optionalOption(parsed, "hook-event-id"),
|
|
1614
2234
|
sessionId: optionalOption(parsed, "session-id")
|
|
1615
2235
|
});
|
|
1616
2236
|
}
|
|
@@ -1643,7 +2263,27 @@ function dispatchSession(context, command, parsed) {
|
|
|
1643
2263
|
}
|
|
1644
2264
|
throw new AppError("usage", `Unknown session command: ${command ?? "<empty>"}`, 2);
|
|
1645
2265
|
}
|
|
1646
|
-
function
|
|
2266
|
+
function snapshotCreateInput(parsed) {
|
|
2267
|
+
const recoveryId = optionalOption(parsed, "recovery-id"), stageEntry = optionalOption(parsed, "stage-entry");
|
|
2268
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), output: requiredOption(parsed, "output"), candidate: parsed.options.has("candidate"), incomplete: parsed.options.has("incomplete"), ...(recoveryId !== undefined ? { recoveryId } : {}), ...(stageEntry !== undefined ? { stageEntry } : {}) };
|
|
2269
|
+
}
|
|
2270
|
+
function snapshotRestoreInput(parsed) {
|
|
2271
|
+
return { snapshot: requiredOption(parsed, "snapshot"), projectRoot: requiredOption(parsed, "project-root"), executionRoutingFile: optionalOption(parsed, "execution-routing-file"), ...(optionalOption(parsed, "recovery-id") ? { recoveryId: requiredOption(parsed, "recovery-id") } : {}) };
|
|
2272
|
+
}
|
|
2273
|
+
function runStartInput(parsed) {
|
|
2274
|
+
const protocolOverrides = readOptionalJsonFile(optionalOption(parsed, "protocol-override-file"));
|
|
2275
|
+
return { projectRoot: requiredOption(parsed, "project-root"), workspaceRoot: optionalOption(parsed, "workspace-root"), flowKind: requiredOption(parsed, "flow-kind"), subjectType: requiredOption(parsed, "subject-type"), subjectId: requiredOption(parsed, "subject-id"), slug: requiredOption(parsed, "slug"), nextAction: optionalOption(parsed, "next-action"), preset: optionalOption(parsed, "preset"), taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")), executionRoutingFile: optionalOption(parsed, "execution-routing-file"), ...(protocolOverrides !== undefined ? { protocolOverrides } : {}), runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag")) };
|
|
2276
|
+
}
|
|
2277
|
+
function runForkInput(parsed) {
|
|
2278
|
+
return { snapshot: requiredOption(parsed, "from"), output: requiredOption(parsed, "output"), engineVersion: requiredOption(parsed, "engine-version"), requestId: requiredOption(parsed, "request-id"), ...(optionalOption(parsed, "integrity-checksum") ? { integrityChecksum: optionalOption(parsed, "integrity-checksum") } : {}) };
|
|
2279
|
+
}
|
|
2280
|
+
function recoveryCaptureInput(parsed) {
|
|
2281
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), capturePath: requiredOption(parsed, "capture-path") };
|
|
2282
|
+
}
|
|
2283
|
+
function recoveryResumeInput(parsed) {
|
|
2284
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 1, "run-id"), recoveryId: requiredOption(parsed, "recovery-id"), daemonId: requiredOption(parsed, "daemon-id"), harness: requiredOption(parsed, "harness"), nativeSessionId: requiredOption(parsed, "native-session-id") };
|
|
2285
|
+
}
|
|
2286
|
+
function dispatchRun(context, command, parsed, prepared = {}) {
|
|
1647
2287
|
if (command === "recovery") {
|
|
1648
2288
|
const action = requiredPosition(parsed, 0, "recovery action");
|
|
1649
2289
|
const projectRoot = requiredOption(parsed, "project-root");
|
|
@@ -1651,13 +2291,13 @@ function dispatchRun(context, command, parsed) {
|
|
|
1651
2291
|
if (action === "inspect")
|
|
1652
2292
|
return inspectRunRecovery(context, { projectRoot, runId });
|
|
1653
2293
|
if (action === "begin")
|
|
1654
|
-
return beginRunRecovery(context, { projectRoot, runId, interruption: requiredOption(parsed, "interruption-json") });
|
|
2294
|
+
return beginRunRecovery(context, { projectRoot, runId, interruption: requiredOption(parsed, "interruption-json") }, prepared.recoveryPayload);
|
|
1655
2295
|
if (action === "seal")
|
|
1656
|
-
return sealRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), settlement: requiredOption(parsed, "settlement-json") });
|
|
2296
|
+
return sealRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), settlement: requiredOption(parsed, "settlement-json") }, prepared.recoveryPayload);
|
|
1657
2297
|
if (action === "capture")
|
|
1658
|
-
return recordRecoveryCapture(context,
|
|
2298
|
+
return recordRecoveryCapture(context, recoveryCaptureInput(parsed), prepared.recoveryCapture);
|
|
1659
2299
|
if (action === "resume")
|
|
1660
|
-
return resumeRunRecovery(context,
|
|
2300
|
+
return resumeRunRecovery(context, recoveryResumeInput(parsed), prepared.recoveryResume);
|
|
1661
2301
|
if (action === "accept")
|
|
1662
2302
|
return acceptRunRecovery(context, { projectRoot, runId, recoveryId: requiredOption(parsed, "recovery-id"), ...(optionalOption(parsed, "hook-event-id") ? { hookEventId: optionalOption(parsed, "hook-event-id") } : {}) });
|
|
1663
2303
|
throw new AppError("usage", "Use: dd-flow run recovery inspect|begin|seal|capture|resume|accept", 2);
|
|
@@ -1672,49 +2312,18 @@ function dispatchRun(context, command, parsed) {
|
|
|
1672
2312
|
output: requiredOption(parsed, "output")
|
|
1673
2313
|
});
|
|
1674
2314
|
if (bootstrapAction === "restore")
|
|
1675
|
-
return restoreEvalBootstrapSnapshot(context,
|
|
1676
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1677
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1678
|
-
});
|
|
2315
|
+
return restoreEvalBootstrapSnapshot(context, snapshotRestoreInput(parsed), prepared.bootstrapRestore);
|
|
1679
2316
|
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore", 2);
|
|
1680
2317
|
}
|
|
1681
2318
|
if (action === "create") {
|
|
1682
|
-
|
|
1683
|
-
const incomplete = parsed.options.has("incomplete");
|
|
1684
|
-
const recoveryId = optionalOption(parsed, "recovery-id");
|
|
1685
|
-
const stageEntry = optionalOption(parsed, "stage-entry");
|
|
1686
|
-
if (Number(candidate) + Number(incomplete) + Number(stageEntry !== undefined) + Number(recoveryId !== undefined) !== 1)
|
|
1687
|
-
throw new AppError("usage", "Use exactly one of --candidate, --incomplete, --recovery-id or --stage-entry", 2);
|
|
1688
|
-
const input = {
|
|
1689
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1690
|
-
runId: requiredPosition(parsed, 1, "run-id"),
|
|
1691
|
-
output: requiredOption(parsed, "output")
|
|
1692
|
-
};
|
|
1693
|
-
if (incomplete)
|
|
1694
|
-
return createEvalRunSnapshot(context, { ...input, incomplete: true });
|
|
1695
|
-
if (recoveryId)
|
|
1696
|
-
return createEvalRunSnapshot(context, { ...input, recoveryId });
|
|
1697
|
-
return candidate
|
|
1698
|
-
? createEvalRunSnapshot(context, { ...input, candidate: true })
|
|
1699
|
-
: createEvalRunSnapshot(context, { ...input, stageEntry: stageEntry });
|
|
2319
|
+
return createEvalRunSnapshot(context, snapshotCreateInput(parsed));
|
|
1700
2320
|
}
|
|
1701
2321
|
if (action === "restore")
|
|
1702
|
-
return restoreEvalRunSnapshot(context,
|
|
1703
|
-
snapshot: requiredOption(parsed, "snapshot"),
|
|
1704
|
-
executionRoutingFile: optionalOption(parsed, "execution-routing-file"),
|
|
1705
|
-
...(optionalOption(parsed, "recovery-id") ? { recoveryId: requiredOption(parsed, "recovery-id") } : {}),
|
|
1706
|
-
projectRoot: requiredOption(parsed, "project-root")
|
|
1707
|
-
});
|
|
2322
|
+
return restoreEvalRunSnapshot(context, snapshotRestoreInput(parsed), prepared.snapshotRestore);
|
|
1708
2323
|
throw new AppError("usage", "Use: dd-flow run snapshot bootstrap create|restore | dd-flow run snapshot create <RUN> (--stage-entry <stage>|--candidate) --project-root <path> --output <directory> | dd-flow run snapshot restore --snapshot <directory> --project-root <path>", 2);
|
|
1709
2324
|
}
|
|
1710
2325
|
if (command === "fork") {
|
|
1711
|
-
return forkRunFromSnapshot(context,
|
|
1712
|
-
snapshot: requiredOption(parsed, "from"),
|
|
1713
|
-
output: requiredOption(parsed, "output"),
|
|
1714
|
-
engineVersion: requiredOption(parsed, "engine-version"),
|
|
1715
|
-
requestId: requiredOption(parsed, "request-id"),
|
|
1716
|
-
...(optionalOption(parsed, "integrity-checksum") ? { integrityChecksum: optionalOption(parsed, "integrity-checksum") } : {})
|
|
1717
|
-
});
|
|
2326
|
+
return forkRunFromSnapshot(context, runForkInput(parsed), prepared.runFork);
|
|
1718
2327
|
}
|
|
1719
2328
|
if (command === "prepare-vnext-specify") {
|
|
1720
2329
|
return prepareVnextSpecifyRun(context, { projectRoot: requiredOption(parsed, "project-root"), slug: requiredOption(parsed, "slug"), executionRoutingFile: optionalOption(parsed, "execution-routing-file") });
|
|
@@ -1723,21 +2332,7 @@ function dispatchRun(context, command, parsed) {
|
|
|
1723
2332
|
if (optionalOption(parsed, "flow-kind") === "mb-upgrade") {
|
|
1724
2333
|
assertUpgradeRunStorageReady(context, requiredOption(parsed, "project-root"));
|
|
1725
2334
|
}
|
|
1726
|
-
|
|
1727
|
-
return startFlowRun(context, {
|
|
1728
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1729
|
-
workspaceRoot: optionalOption(parsed, "workspace-root"),
|
|
1730
|
-
flowKind: requiredOption(parsed, "flow-kind"),
|
|
1731
|
-
subjectType: requiredOption(parsed, "subject-type"),
|
|
1732
|
-
subjectId: requiredOption(parsed, "subject-id"),
|
|
1733
|
-
slug: requiredOption(parsed, "slug"),
|
|
1734
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1735
|
-
preset: optionalOption(parsed, "preset"),
|
|
1736
|
-
taskProfile: readOptionalJsonFile(optionalOption(parsed, "task-profile-file")),
|
|
1737
|
-
executionRoutingFile: optionalOption(parsed, "execution-routing-file"),
|
|
1738
|
-
...(protocolOverrides ? { protocolOverrides } : {}),
|
|
1739
|
-
runOverrides: parseFlowFlagOverrides(allOptions(parsed, "flag"))
|
|
1740
|
-
});
|
|
2335
|
+
return startFlowRun(context, prepared.runStart?.input ?? runStartInput(parsed), prepared.runStart?.value);
|
|
1741
2336
|
}
|
|
1742
2337
|
if (command === "status") {
|
|
1743
2338
|
return getFlowRunStatus(context, {
|
|
@@ -1807,49 +2402,70 @@ function dispatchRun(context, command, parsed) {
|
|
|
1807
2402
|
throw new AppError("usage", `Unknown run flags command: ${action}`, 2);
|
|
1808
2403
|
}
|
|
1809
2404
|
if (command === "attach-stage") {
|
|
1810
|
-
return attachFlowRunStage(context,
|
|
1811
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1812
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1813
|
-
stage: requiredOption(parsed, "stage"),
|
|
1814
|
-
dir: requiredOption(parsed, "dir"),
|
|
1815
|
-
status: requiredOption(parsed, "status"),
|
|
1816
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id")
|
|
1817
|
-
});
|
|
2405
|
+
return attachFlowRunStage(context, stageAttachmentInput(parsed));
|
|
1818
2406
|
}
|
|
1819
2407
|
if (command === "complete-stage") {
|
|
1820
|
-
|
|
1821
|
-
return completeFlowRunStage(context, {
|
|
1822
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1823
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1824
|
-
stage: requiredOption(parsed, "stage"),
|
|
1825
|
-
status: requiredOption(parsed, "status"),
|
|
1826
|
-
stageReport,
|
|
1827
|
-
data: optionalOption(parsed, "data"),
|
|
1828
|
-
dataSchemaId: optionalOption(parsed, "data-schema-id"),
|
|
1829
|
-
report: optionalOption(parsed, "report"),
|
|
1830
|
-
aliases: allOptions(parsed, "alias")
|
|
1831
|
-
});
|
|
2408
|
+
return completeFlowRunStage(context, stageCompletionInput(parsed));
|
|
1832
2409
|
}
|
|
1833
2410
|
if (command === "complete") {
|
|
1834
2411
|
throw new AppError("usage", "run complete is retired; only an operator may use run override", 2);
|
|
1835
2412
|
}
|
|
1836
2413
|
if (command === "override") {
|
|
1837
|
-
|
|
1838
|
-
if (!['cancelled', 'failed'].includes(status)) {
|
|
1839
|
-
throw new AppError("usage", "run override status must be cancelled or failed", 2, { status });
|
|
1840
|
-
}
|
|
1841
|
-
return completeFlowRun(context, {
|
|
1842
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
1843
|
-
runId: requiredPosition(parsed, 0, "run-id"),
|
|
1844
|
-
status,
|
|
1845
|
-
verdict: optionalOption(parsed, "verdict"),
|
|
1846
|
-
nextAction: optionalOption(parsed, "next-action"),
|
|
1847
|
-
manualOverrideReason: requiredOption(parsed, "reason")
|
|
1848
|
-
});
|
|
2414
|
+
return completeFlowRun(context, runOverrideInput(parsed));
|
|
1849
2415
|
}
|
|
1850
2416
|
throw new AppError("usage", `Unknown run command: ${command ?? "<empty>"}`, 2);
|
|
1851
2417
|
}
|
|
1852
|
-
|
|
2418
|
+
function stageAttachmentInput(parsed) {
|
|
2419
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), dir: requiredOption(parsed, "dir"), status: requiredOption(parsed, "status"), dataSchemaId: optionalOption(parsed, "data-schema-id") };
|
|
2420
|
+
}
|
|
2421
|
+
function runOverrideInput(parsed) {
|
|
2422
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), status: requiredOption(parsed, "status"), verdict: optionalOption(parsed, "verdict"), nextAction: optionalOption(parsed, "next-action"), manualOverrideReason: requiredOption(parsed, "reason") };
|
|
2423
|
+
}
|
|
2424
|
+
function stageCompletionInput(parsed) {
|
|
2425
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredPosition(parsed, 0, "run-id"), stage: requiredOption(parsed, "stage"), status: requiredOption(parsed, "status"), stageReport: optionalOption(parsed, "stage-report") ?? optionalOption(parsed, "dashboard"), data: optionalOption(parsed, "data"), dataSchemaId: optionalOption(parsed, "data-schema-id"), report: optionalOption(parsed, "report"), aliases: allOptions(parsed, "alias") };
|
|
2426
|
+
}
|
|
2427
|
+
function runtimeServiceInput(parsed) {
|
|
2428
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), command: requiredOption(parsed, "command"), ports: requiredOption(parsed, "ports").split(","), readyPort: requiredOption(parsed, "ready-port"), readyPath: optionalOption(parsed, "ready-path") ?? "/", timeoutMs: optionalPositiveNumber(parsed, "ready-timeout-ms", true) ?? 30_000 };
|
|
2429
|
+
}
|
|
2430
|
+
function runtimeProcessStopInput(parsed) {
|
|
2431
|
+
const graceMs = optionalNumber(parsed, "grace-ms");
|
|
2432
|
+
return { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(graceMs !== undefined ? { graceMs } : {}) };
|
|
2433
|
+
}
|
|
2434
|
+
function runtimeScopeResumeInput(parsed) {
|
|
2435
|
+
return { scopeId: requiredOption(parsed, "scope-id"), requestId: requiredOption(parsed, "request-id"), generation: Number(requiredOption(parsed, "generation")), captureKey: requiredOption(parsed, "capture-key") };
|
|
2436
|
+
}
|
|
2437
|
+
function runtimeProcessRegistrationInput(context, parsed) {
|
|
2438
|
+
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
2439
|
+
const budget = parseRuntimeBudget(optionalOption(parsed, "budget-json"));
|
|
2440
|
+
const kind = requiredOption(parsed, "kind");
|
|
2441
|
+
const projectId = optionalOption(parsed, "project-id") ?? null, runId = optionalOption(parsed, "run") ?? null;
|
|
2442
|
+
const guard = projectId && runId ? recoveryGuard(context, projectId, runId) : null;
|
|
2443
|
+
const run = projectId && runId ? context.db.get("SELECT id FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]) : null;
|
|
2444
|
+
return {
|
|
2445
|
+
kind, ownerId: requiredOption(parsed, "owner"), uniqueActiveOperation: kind === "eval-observer",
|
|
2446
|
+
...(kind === "eval-observer" ? { pid: optionalPositiveNumber(parsed, "pid", true) ?? 0, ...(optionalPositiveNumber(parsed, "process-group-id", true) ? { processGroupId: optionalPositiveNumber(parsed, "process-group-id", true) } : {}) } : {}),
|
|
2447
|
+
ownerPid: optionalPositiveNumber(parsed, "owner-pid", true), projectId, runId,
|
|
2448
|
+
workId: optionalOption(parsed, "work") ?? null, checkId: optionalOption(parsed, "check") ?? null,
|
|
2449
|
+
operationId: optionalOption(parsed, "operation") ?? null, stdoutPath: optionalOption(parsed, "stdout") ?? null,
|
|
2450
|
+
stderrPath: optionalOption(parsed, "stderr") ?? null, ...(leaseMs !== undefined ? { leaseMs } : {}),
|
|
2451
|
+
metadata: { ...(budget ? { budget, harness: optionalOption(parsed, "harness") ?? null } : {}), ...(projectId && runId ? { run_generation: guard?.generation ?? 0, run_admitted: Boolean(run) && !["draining", "sealed", "resuming"].includes(guard?.status ?? "") } : {}), ...(optionalOption(parsed, "dispatch-barrier") ? { dispatch_barrier: optionalOption(parsed, "dispatch-barrier") } : {}), ...(optionalOption(parsed, "role") ? { role: optionalOption(parsed, "role") } : {}), ...(optionalOption(parsed, "state-dir") ? { state_dir: optionalOption(parsed, "state-dir") } : {}), ...(optionalOption(parsed, "adapter-executable") ? { adapter_executable: optionalOption(parsed, "adapter-executable") } : {}) }
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
function runtimeProcessConfirmationInput(parsed) {
|
|
2455
|
+
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true), processGroupId = optionalPositiveNumber(parsed, "process-group-id", true);
|
|
2456
|
+
return { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ownerPid: optionalPositiveNumber(parsed, "owner-pid", true), pid: optionalPositiveNumber(parsed, "pid", true) ?? 0, ...(processGroupId !== undefined ? { processGroupId } : {}), ...(leaseMs !== undefined ? { leaseMs } : {}) };
|
|
2457
|
+
}
|
|
2458
|
+
function runtimeProcessHeartbeatInput(parsed) {
|
|
2459
|
+
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
2460
|
+
return { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(leaseMs !== undefined ? { leaseMs } : {}) };
|
|
2461
|
+
}
|
|
2462
|
+
function runtimeProcessFinishInput(parsed) {
|
|
2463
|
+
const state = requiredOption(parsed, "state");
|
|
2464
|
+
if (state !== "stopped" && state !== "failed")
|
|
2465
|
+
throw new AppError("validation", "--state must be stopped or failed", 2);
|
|
2466
|
+
return { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), state, reason: optionalOption(parsed, "reason") ?? null };
|
|
2467
|
+
}
|
|
2468
|
+
async function dispatchRuntime(context, command, parsed, progress, prepared = {}) {
|
|
1853
2469
|
if (command === "scope") {
|
|
1854
2470
|
if (parsed.positional.length === 1 && parsed.positional[0] === "reconcile") {
|
|
1855
2471
|
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
@@ -1859,7 +2475,7 @@ async function dispatchRuntime(context, command, parsed, progress) {
|
|
|
1859
2475
|
if (parsed.positional.length === 1 && parsed.positional[0] === "resume") {
|
|
1860
2476
|
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "generation", "capture-key"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
1861
2477
|
throw new AppError("usage", "Use runtime scope resume --scope-id ID --request-id ID --generation N --capture-key SHA256", 2);
|
|
1862
|
-
return { ok: true, ...await launchRuntimeScopeResume(context,
|
|
2478
|
+
return { ok: true, ...await launchRuntimeScopeResume(context, runtimeScopeResumeInput(parsed)) };
|
|
1863
2479
|
}
|
|
1864
2480
|
if (parsed.positional.length === 1 && parsed.positional[0] === "control") {
|
|
1865
2481
|
if ([...parsed.options.keys()].some(key => !["scope-id", "request-id", "mode", "manifest-json"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
@@ -1867,7 +2483,7 @@ async function dispatchRuntime(context, command, parsed, progress) {
|
|
|
1867
2483
|
const mode = requiredOption(parsed, "mode");
|
|
1868
2484
|
if (mode !== "pause" && mode !== "stop")
|
|
1869
2485
|
throw new AppError("usage", "Scope control mode must be pause or stop", 2);
|
|
1870
|
-
return { ok: true, ...await launchRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), mode,
|
|
2486
|
+
return { ok: true, ...await launchRuntimeScopeControl(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"), mode, undefined, undefined, undefined, prepared.runtimeManifest) };
|
|
1871
2487
|
}
|
|
1872
2488
|
if (parsed.positional.length === 1 && parsed.positional[0] === "serve") {
|
|
1873
2489
|
if ([...parsed.options.keys()].some(key => !["scope-id", "generation"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
@@ -1894,37 +2510,17 @@ async function dispatchRuntime(context, command, parsed, progress) {
|
|
|
1894
2510
|
if (action === "check-admission")
|
|
1895
2511
|
return { ok: true, ...admitRuntimeCheck(context, requiredOption(parsed, "id"), requiredOption(parsed, "lease-token")) };
|
|
1896
2512
|
if (action === "start")
|
|
1897
|
-
return await startRuntimeService(context, {
|
|
2513
|
+
return await startRuntimeService(context, { ...runtimeServiceInput(parsed), progress });
|
|
1898
2514
|
if (action === "stop")
|
|
1899
|
-
return { ok: true, process: await stopManagedProcess(context,
|
|
2515
|
+
return { ok: true, process: await stopManagedProcess(context, runtimeProcessStopInput(parsed)) };
|
|
1900
2516
|
if (action === "register") {
|
|
1901
|
-
const
|
|
1902
|
-
const budget = parseRuntimeBudget(optionalOption(parsed, "budget-json"));
|
|
1903
|
-
const projectId = optionalOption(parsed, "project-id") ?? null, runId = optionalOption(parsed, "run") ?? null;
|
|
1904
|
-
const guard = projectId && runId ? recoveryGuard(context, projectId, runId) : null;
|
|
1905
|
-
const run = projectId && runId ? context.db.get("SELECT id FROM runs WHERE project_id = ? AND id = ?", [projectId, runId]) : null;
|
|
1906
|
-
const process = registerManagedProcess(context, {
|
|
1907
|
-
kind: requiredOption(parsed, "kind"), ownerId: requiredOption(parsed, "owner"),
|
|
1908
|
-
uniqueActiveOperation: requiredOption(parsed, "kind") === "eval-observer",
|
|
1909
|
-
...(requiredOption(parsed, "kind") === "eval-observer" ? { pid: optionalPositiveNumber(parsed, "pid", true) ?? 0, ...(optionalPositiveNumber(parsed, "process-group-id", true) ? { processGroupId: optionalPositiveNumber(parsed, "process-group-id", true) } : {}) } : {}),
|
|
1910
|
-
ownerPid: optionalPositiveNumber(parsed, "owner-pid", true),
|
|
1911
|
-
projectId, runId,
|
|
1912
|
-
workId: optionalOption(parsed, "work") ?? null, checkId: optionalOption(parsed, "check") ?? null,
|
|
1913
|
-
operationId: optionalOption(parsed, "operation") ?? null, stdoutPath: optionalOption(parsed, "stdout") ?? null,
|
|
1914
|
-
stderrPath: optionalOption(parsed, "stderr") ?? null, ...(leaseMs ? { leaseMs } : {}),
|
|
1915
|
-
metadata: { ...(budget ? { budget, harness: optionalOption(parsed, "harness") ?? null } : {}), ...(projectId && runId ? { run_generation: guard?.generation ?? 0, run_admitted: Boolean(run) && !["draining", "sealed", "resuming"].includes(guard?.status ?? "") } : {}), ...(optionalOption(parsed, "dispatch-barrier") ? { dispatch_barrier: optionalOption(parsed, "dispatch-barrier") } : {}), ...(optionalOption(parsed, "role") ? { role: optionalOption(parsed, "role") } : {}), ...(optionalOption(parsed, "state-dir") ? { state_dir: optionalOption(parsed, "state-dir") } : {}), ...(optionalOption(parsed, "adapter-executable") ? { adapter_executable: optionalOption(parsed, "adapter-executable") } : {}) }
|
|
1916
|
-
});
|
|
2517
|
+
const process = registerManagedProcess(context, runtimeProcessRegistrationInput(context, parsed));
|
|
1917
2518
|
return { ok: true, process };
|
|
1918
2519
|
}
|
|
1919
|
-
if (action === "confirm")
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
return { ok:
|
|
1923
|
-
}
|
|
1924
|
-
if (action === "heartbeat") {
|
|
1925
|
-
const leaseMs = optionalPositiveNumber(parsed, "lease-ms", true);
|
|
1926
|
-
return { ok: heartbeatManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), ...(leaseMs ? { leaseMs } : {}) }) };
|
|
1927
|
-
}
|
|
2520
|
+
if (action === "confirm")
|
|
2521
|
+
return { ok: true, process: confirmManagedProcess(context, runtimeProcessConfirmationInput(parsed)) };
|
|
2522
|
+
if (action === "heartbeat")
|
|
2523
|
+
return { ok: heartbeatManagedProcess(context, runtimeProcessHeartbeatInput(parsed)) };
|
|
1928
2524
|
if (action === "admission") {
|
|
1929
2525
|
const record = managedProcessStatus(context).find(process => process.id === requiredOption(parsed, "id"));
|
|
1930
2526
|
if (!record || record.lease_token !== requiredOption(parsed, "lease-token") || record.owner_id !== requiredOption(parsed, "owner") || record.state !== "running" || !record.pid || !record.pid_started_at || !processIsAlive(record))
|
|
@@ -1954,18 +2550,14 @@ async function dispatchRuntime(context, command, parsed, progress) {
|
|
|
1954
2550
|
const record = managedProcessStatus(context).find(process => process.id === requiredOption(parsed, "id"));
|
|
1955
2551
|
if (!record || record.lease_token !== requiredOption(parsed, "lease-token") || record.owner_id !== requiredOption(parsed, "owner") || managedProcessRuntimeHome(record, context) !== context.ddFlowHome)
|
|
1956
2552
|
throw new AppError("process_ownership_unknown", "Budget release requires its retained physical owner", 1);
|
|
1957
|
-
return releaseProviderTurns(context, record, { daemonId: requiredOption(parsed, "daemon-id"), operationId: optionalOption(parsed, "operation-id"), observationFile: optionalOption(parsed, "observation-file") });
|
|
1958
|
-
}
|
|
1959
|
-
if (action === "finish") {
|
|
1960
|
-
const state = requiredOption(parsed, "state");
|
|
1961
|
-
if (state !== "stopped" && state !== "failed")
|
|
1962
|
-
throw new AppError("validation", "--state must be stopped or failed", 2);
|
|
1963
|
-
return { ok: finishManagedProcess(context, { id: requiredOption(parsed, "id"), leaseToken: requiredOption(parsed, "lease-token"), state: state, reason: optionalOption(parsed, "reason") ?? null }) };
|
|
2553
|
+
return releaseProviderTurns(context, record, { daemonId: requiredOption(parsed, "daemon-id"), operationId: optionalOption(parsed, "operation-id"), observationFile: optionalOption(parsed, "observation-file") }, prepared.providerTurnRelease);
|
|
1964
2554
|
}
|
|
2555
|
+
if (action === "finish")
|
|
2556
|
+
return { ok: finishManagedProcess(context, runtimeProcessFinishInput(parsed)) };
|
|
1965
2557
|
if (action === "status")
|
|
1966
2558
|
return { ok: true, processes: managedProcessStatus(context) };
|
|
1967
2559
|
if (action === "reconcile")
|
|
1968
|
-
return { ok: true, processes: await reconcileExpiredManagedProcesses(context, requiredOption(parsed, "owner"),
|
|
2560
|
+
return { ok: true, processes: await reconcileExpiredManagedProcesses(context, requiredOption(parsed, "owner"), optionalNumber(parsed, "grace-ms")) };
|
|
1969
2561
|
throw new AppError("usage", "Usage: dd-flow runtime process <start|stop|register|confirm|heartbeat|admission|finish|status|reconcile>", 2);
|
|
1970
2562
|
}
|
|
1971
2563
|
function dispatchStat(context, command, parsed) {
|
|
@@ -2153,7 +2745,9 @@ function dispatchSchema(context, command, parsed) {
|
|
|
2153
2745
|
// searching project directories: run_id is a structural field in the
|
|
2154
2746
|
// submitted artifact and the database owns its materialized run_root.
|
|
2155
2747
|
const explicitRunId = optionalOption(parsed, "run");
|
|
2156
|
-
const
|
|
2748
|
+
const data = prepareJsonFile(file, "schema input").value;
|
|
2749
|
+
const declared = data && typeof data === "object" && "run_id" in data ? data.run_id : undefined;
|
|
2750
|
+
const declaredRunId = explicitRunId ?? (typeof declared === "string" && declared.trim() ? declared : undefined);
|
|
2157
2751
|
const fileRunRoot = path.basename(file) === "run.json" ? path.dirname(path.resolve(file)) : undefined;
|
|
2158
2752
|
const bound = projectRoot
|
|
2159
2753
|
? context.db.get("SELECT r.id, r.run_root FROM runs r JOIN projects p ON p.id = r.project_id WHERE p.root = ? AND (r.id = ? OR r.run_root = ?) LIMIT 1", [fs.realpathSync(projectRoot), declaredRunId ?? "", fileRunRoot ?? ""])
|
|
@@ -2161,6 +2755,7 @@ function dispatchSchema(context, command, parsed) {
|
|
|
2161
2755
|
return validateSchema({
|
|
2162
2756
|
schemaName: requiredOption(parsed, "schema"),
|
|
2163
2757
|
file,
|
|
2758
|
+
data,
|
|
2164
2759
|
...(schemaDir ? { schemaDir } : {}),
|
|
2165
2760
|
...(projectRoot ? { projectRoot } : {}),
|
|
2166
2761
|
...(declaredRunId || bound?.id ? { runId: declaredRunId ?? bound.id } : {}),
|
|
@@ -2170,14 +2765,11 @@ function dispatchSchema(context, command, parsed) {
|
|
|
2170
2765
|
}
|
|
2171
2766
|
throw new AppError("usage", `Unknown schema command: ${command ?? "<empty>"}`, 2);
|
|
2172
2767
|
}
|
|
2173
|
-
function
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
}
|
|
2178
|
-
catch {
|
|
2179
|
-
return undefined;
|
|
2180
|
-
}
|
|
2768
|
+
function laneWorkspaceInput(parsed) {
|
|
2769
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workspacePath: requiredOption(parsed, "path"), branch: optionalOption(parsed, "branch") };
|
|
2770
|
+
}
|
|
2771
|
+
function laneWaiterCancelInput(parsed) {
|
|
2772
|
+
return { projectRoot: requiredOption(parsed, "project-root"), lane: requiredOption(parsed, "lane"), workerId: requiredOption(parsed, "worker-id"), reason: requiredOption(parsed, "reason") };
|
|
2181
2773
|
}
|
|
2182
2774
|
async function dispatchLane(context, command, parsed) {
|
|
2183
2775
|
if (command === "status") {
|
|
@@ -2189,24 +2781,14 @@ async function dispatchLane(context, command, parsed) {
|
|
|
2189
2781
|
if (command === "waiter") {
|
|
2190
2782
|
const action = requiredPosition(parsed, 0, "lane waiter action");
|
|
2191
2783
|
if (action === "cancel") {
|
|
2192
|
-
return cancelLaneWaiter(context,
|
|
2193
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
2194
|
-
lane: requiredOption(parsed, "lane"),
|
|
2195
|
-
workerId: requiredOption(parsed, "worker-id"),
|
|
2196
|
-
reason: requiredOption(parsed, "reason")
|
|
2197
|
-
});
|
|
2784
|
+
return cancelLaneWaiter(context, laneWaiterCancelInput(parsed));
|
|
2198
2785
|
}
|
|
2199
2786
|
throw new AppError("usage", `Unknown lane waiter action: ${action}`, 2);
|
|
2200
2787
|
}
|
|
2201
2788
|
if (command === "workspace") {
|
|
2202
2789
|
const action = requiredPosition(parsed, 0, "lane workspace action");
|
|
2203
2790
|
if (action === "set") {
|
|
2204
|
-
return setLaneWorkspace(context,
|
|
2205
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
2206
|
-
lane: requiredOption(parsed, "lane"),
|
|
2207
|
-
workspacePath: requiredOption(parsed, "path"),
|
|
2208
|
-
branch: optionalOption(parsed, "branch")
|
|
2209
|
-
});
|
|
2791
|
+
return setLaneWorkspace(context, laneWorkspaceInput(parsed));
|
|
2210
2792
|
}
|
|
2211
2793
|
if (action === "check") {
|
|
2212
2794
|
return checkLaneWorkspace(context, {
|
|
@@ -2278,7 +2860,7 @@ async function dispatchLane(context, command, parsed) {
|
|
|
2278
2860
|
}
|
|
2279
2861
|
throw new AppError("usage", `Unknown lane command: ${command ?? "<empty>"}`, 2);
|
|
2280
2862
|
}
|
|
2281
|
-
function dispatchCodexHooks(context, parsed) {
|
|
2863
|
+
function dispatchCodexHooks(context, parsed, prepared) {
|
|
2282
2864
|
const action = requiredPosition(parsed, 0, "codex hooks action");
|
|
2283
2865
|
if (action === "print") {
|
|
2284
2866
|
return printCodexHooks(context, {
|
|
@@ -2300,7 +2882,7 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
2300
2882
|
target: optionalOption(parsed, "target"),
|
|
2301
2883
|
profile: optionalOption(parsed, "profile"),
|
|
2302
2884
|
yes: hasOption(parsed, "yes")
|
|
2303
|
-
});
|
|
2885
|
+
}, prepared.codexHooks);
|
|
2304
2886
|
}
|
|
2305
2887
|
if (action === "remove") {
|
|
2306
2888
|
return removeCodexHooks(context, {
|
|
@@ -2308,11 +2890,14 @@ function dispatchCodexHooks(context, parsed) {
|
|
|
2308
2890
|
target: optionalOption(parsed, "target"),
|
|
2309
2891
|
profile: optionalOption(parsed, "profile"),
|
|
2310
2892
|
yes: hasOption(parsed, "yes")
|
|
2311
|
-
});
|
|
2893
|
+
}, prepared.codexHooks);
|
|
2312
2894
|
}
|
|
2313
2895
|
throw new AppError("usage", `Unknown codex hooks action: ${action}`, 2);
|
|
2314
2896
|
}
|
|
2315
|
-
function
|
|
2897
|
+
function codexHomeInput(parsed) {
|
|
2898
|
+
return { projectRoot: requiredOption(parsed, "project-root"), profile: optionalOption(parsed, "profile"), sourceHome: optionalOption(parsed, "source-home"), targetHome: optionalOption(parsed, "target-home") };
|
|
2899
|
+
}
|
|
2900
|
+
function dispatchCodexHome(context, parsed, prepared) {
|
|
2316
2901
|
const action = requiredPosition(parsed, 0, "codex home action");
|
|
2317
2902
|
if (action === "plan") {
|
|
2318
2903
|
return planCodexHome(context, {
|
|
@@ -2323,12 +2908,7 @@ function dispatchCodexHome(context, parsed) {
|
|
|
2323
2908
|
});
|
|
2324
2909
|
}
|
|
2325
2910
|
if (action === "init") {
|
|
2326
|
-
return initCodexHome(context,
|
|
2327
|
-
projectRoot: requiredOption(parsed, "project-root"),
|
|
2328
|
-
profile: optionalOption(parsed, "profile"),
|
|
2329
|
-
sourceHome: optionalOption(parsed, "source-home"),
|
|
2330
|
-
targetHome: optionalOption(parsed, "target-home")
|
|
2331
|
-
});
|
|
2911
|
+
return initCodexHome(context, codexHomeInput(parsed), prepared.codexHome);
|
|
2332
2912
|
}
|
|
2333
2913
|
if (action === "status") {
|
|
2334
2914
|
return getCodexHomeStatus(context, {
|
|
@@ -2351,11 +2931,14 @@ function dispatchCodexHome(context, parsed) {
|
|
|
2351
2931
|
}
|
|
2352
2932
|
throw new AppError("usage", `Unknown codex home action: ${action}`, 2);
|
|
2353
2933
|
}
|
|
2354
|
-
function dispatchCodexHook(context, parsed, stdin) {
|
|
2934
|
+
async function dispatchCodexHook(context, parsed, stdin) {
|
|
2355
2935
|
const action = requiredPosition(parsed, 0, "codex hook action");
|
|
2356
2936
|
if (action === "handle") {
|
|
2357
2937
|
const isolatedHome = isolatedDdFlowHomeFromHookStdin(stdin);
|
|
2358
|
-
const hookContext = isolatedHome ? createContext({ ...context.env, DD_FLOW_HOME: isolatedHome }) : context;
|
|
2938
|
+
const hookContext = isolatedHome ? createContext({ ...context.env, DD_FLOW_HOME: isolatedHome }, "hook") : context;
|
|
2939
|
+
const proof = await verifyCodexHookDelivery(hookContext.env, JSON.parse(stdin || "{}"));
|
|
2940
|
+
if (proof)
|
|
2941
|
+
hookContext.codexHookProof = proof;
|
|
2359
2942
|
const result = handleCodexHook(hookContext, {
|
|
2360
2943
|
event: requiredOption(parsed, "event"),
|
|
2361
2944
|
projectRoot: optionalOption(parsed, "project-root"),
|
|
@@ -2410,7 +2993,7 @@ function dispatchAgyEvent(context, parsed, stdin) {
|
|
|
2410
2993
|
return handleAgyEvent(context, { projectRoot: requiredOption(parsed, "project-root"), stdin });
|
|
2411
2994
|
throw new AppError("usage", `Unknown agy event action: ${action}`, 2);
|
|
2412
2995
|
}
|
|
2413
|
-
function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
2996
|
+
function dispatchWorktree(context, command, parsed, scopeProjectRoot, prepared) {
|
|
2414
2997
|
if (command === "plan") {
|
|
2415
2998
|
return planWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2416
2999
|
}
|
|
@@ -2421,13 +3004,13 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2421
3004
|
branch: requiredOption(parsed, "branch"),
|
|
2422
3005
|
base: requiredOption(parsed, "base"),
|
|
2423
3006
|
path: requiredOption(parsed, "path")
|
|
2424
|
-
});
|
|
3007
|
+
}, prepared.worktreeCreate);
|
|
2425
3008
|
}
|
|
2426
3009
|
if (command === "status") {
|
|
2427
3010
|
return getWorktreeStatus(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
2428
3011
|
}
|
|
2429
3012
|
if (command === "bootstrap") {
|
|
2430
|
-
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot });
|
|
3013
|
+
return bootstrapWorktree(context, { protocolId: requiredOption(parsed, "protocol-id"), projectRoot: scopeProjectRoot }, prepared.worktreeBootstrap);
|
|
2431
3014
|
}
|
|
2432
3015
|
if (command === "close") {
|
|
2433
3016
|
return closeWorktree(context, {
|
|
@@ -2439,52 +3022,50 @@ function dispatchWorktree(context, command, parsed, scopeProjectRoot) {
|
|
|
2439
3022
|
throw new AppError("usage", `Unknown worktree command: ${command ?? "<empty>"}`, 2);
|
|
2440
3023
|
}
|
|
2441
3024
|
function dispatchPlanItem(context, parsed, projectRoot) {
|
|
3025
|
+
return updatePlanItem(context, planItemInput(parsed, projectRoot));
|
|
3026
|
+
}
|
|
3027
|
+
function planItemInput(parsed, projectRoot) {
|
|
2442
3028
|
const action = requiredPosition(parsed, 0, "plan item action");
|
|
2443
3029
|
const protocolId = requiredPosition(parsed, 1, "protocol-id");
|
|
2444
3030
|
const itemId = requiredPosition(parsed, 2, "item-id");
|
|
2445
3031
|
if (action === "start") {
|
|
2446
|
-
return
|
|
3032
|
+
return { protocolId, projectRoot, itemId, action };
|
|
2447
3033
|
}
|
|
2448
3034
|
if (action === "done") {
|
|
2449
|
-
return
|
|
3035
|
+
return {
|
|
3036
|
+
action,
|
|
2450
3037
|
protocolId,
|
|
2451
3038
|
projectRoot,
|
|
2452
3039
|
itemId,
|
|
2453
3040
|
summary: requiredOption(parsed, "summary"),
|
|
2454
3041
|
evidence: parsed.options.get("evidence") ?? []
|
|
2455
|
-
}
|
|
3042
|
+
};
|
|
2456
3043
|
}
|
|
2457
3044
|
if (action === "block") {
|
|
2458
|
-
return
|
|
3045
|
+
return {
|
|
3046
|
+
action,
|
|
2459
3047
|
protocolId,
|
|
2460
3048
|
projectRoot,
|
|
2461
3049
|
itemId,
|
|
2462
3050
|
reason: requiredOption(parsed, "reason"),
|
|
2463
3051
|
userRequired: parseBoolean(requiredOption(parsed, "user-required"), "user-required")
|
|
2464
|
-
}
|
|
3052
|
+
};
|
|
2465
3053
|
}
|
|
2466
3054
|
if (action === "skip") {
|
|
2467
|
-
return
|
|
3055
|
+
return {
|
|
3056
|
+
action,
|
|
2468
3057
|
protocolId,
|
|
2469
3058
|
projectRoot,
|
|
2470
3059
|
itemId,
|
|
2471
3060
|
reason: requiredOption(parsed, "reason")
|
|
2472
|
-
}
|
|
3061
|
+
};
|
|
2473
3062
|
}
|
|
2474
3063
|
throw new AppError("usage", `Unknown plan item action: ${action}`, 2);
|
|
2475
3064
|
}
|
|
2476
3065
|
function readOptionalJsonFile(file) {
|
|
2477
3066
|
if (!file)
|
|
2478
3067
|
return undefined;
|
|
2479
|
-
|
|
2480
|
-
throw new AppError("not_found", `JSON file does not exist: ${file}`, 1);
|
|
2481
|
-
}
|
|
2482
|
-
try {
|
|
2483
|
-
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
2484
|
-
}
|
|
2485
|
-
catch {
|
|
2486
|
-
throw new AppError("validation", `JSON file is malformed: ${file}`, 2);
|
|
2487
|
-
}
|
|
3068
|
+
return prepareJsonFile(file, "JSON input").value;
|
|
2488
3069
|
}
|
|
2489
3070
|
function parseFlowFlagOverrides(values) {
|
|
2490
3071
|
const result = {};
|
|
@@ -2509,21 +3090,6 @@ function parseFlowFlagOverrides(values) {
|
|
|
2509
3090
|
function parseArgs(args) {
|
|
2510
3091
|
return parseCommandArgs(args);
|
|
2511
3092
|
}
|
|
2512
|
-
/** Most CLI options name one authority, path or target. Silently taking the
|
|
2513
|
-
* last duplicate can widen a mutation after an operator typo. Keep only the
|
|
2514
|
-
* options consumed as intentional arrays repeatable. */
|
|
2515
|
-
function assertSingletonOptions(args) {
|
|
2516
|
-
const repeatable = new Set(["alias", "flag", "hide", "check-ref", "evidence", "from-check", "from-finding", "from-unresolved", "on", "origin-work"]);
|
|
2517
|
-
const parsed = parseCommandArgs(args);
|
|
2518
|
-
if (args[0] === "stage" && args[1] === "finish"
|
|
2519
|
-
&& ["result-file", "semantic-file", "data", "result-stdin"].filter(name => parsed.options.has(name)).length > 1) {
|
|
2520
|
-
throw new AppError("usage", "Stage finish requires exactly one result input; result-file, semantic-file, data and result-stdin are mutually exclusive", 2);
|
|
2521
|
-
}
|
|
2522
|
-
for (const [name, values] of parsed.options) {
|
|
2523
|
-
if (!repeatable.has(name) && values.length > 1)
|
|
2524
|
-
throw new AppError("usage", `--${name} may be supplied only once`, 2);
|
|
2525
|
-
}
|
|
2526
|
-
}
|
|
2527
3093
|
function requiredPosition(parsed, index, label) {
|
|
2528
3094
|
const value = parsed.positional[index];
|
|
2529
3095
|
if (!value) {
|
|
@@ -2534,7 +3100,11 @@ function requiredPosition(parsed, index, label) {
|
|
|
2534
3100
|
function optionalOption(parsed, key) {
|
|
2535
3101
|
const values = parsed.options.get(key);
|
|
2536
3102
|
const value = values?.[values.length - 1];
|
|
2537
|
-
|
|
3103
|
+
if (value === flagValue)
|
|
3104
|
+
throw new AppError("usage", `--${key} requires a value`, 2);
|
|
3105
|
+
if (value !== undefined && !value.trim())
|
|
3106
|
+
throw new AppError("usage", `--${key} requires a non-empty value`, 2);
|
|
3107
|
+
return value;
|
|
2538
3108
|
}
|
|
2539
3109
|
function allOptions(parsed, key) {
|
|
2540
3110
|
return (parsed.options.get(key) ?? []).filter((value) => value !== flagValue);
|
|
@@ -2769,14 +3339,36 @@ function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
|
2769
3339
|
function protocolProjectRoot(context, protocolId) {
|
|
2770
3340
|
return requireProtocol(context, protocolId).project_root;
|
|
2771
3341
|
}
|
|
3342
|
+
function prepareCliWorkRepair(context, parsed, objective, scopeProjectRoot) {
|
|
3343
|
+
const origins = parsed.options.get("origin-work") ?? [], findings = parsed.options.get("from-finding") ?? [], unresolved = parsed.options.get("from-unresolved") ?? [], receipts = parsed.options.get("from-check") ?? [];
|
|
3344
|
+
if ([findings.length > 0, unresolved.length > 0, receipts.length > 0].filter(Boolean).length !== 1)
|
|
3345
|
+
throw new AppError("usage", "Repair requires exactly one source: --from-check, --from-finding or --from-unresolved", 2);
|
|
3346
|
+
const scope = { projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"), runId: requiredOption(parsed, "run"), objective };
|
|
3347
|
+
if (findings.length) {
|
|
3348
|
+
if (origins.length || hasOption(parsed, "verification-file"))
|
|
3349
|
+
throw new AppError("usage", "Finding repair derives its origins and cannot accept --origin-work or --verification-file", 2);
|
|
3350
|
+
return prepareVnextCodeReviewRepair(context, { ...scope, findingIds: findings, checkRefsByFinding: Object.fromEntries(findings.map(finding => [finding, parsed.options.get("check-ref") ?? []])) });
|
|
3351
|
+
}
|
|
3352
|
+
if (!origins.length)
|
|
3353
|
+
throw new AppError("usage", "Repair requires at least one --origin-work", 2);
|
|
3354
|
+
if (hasOption(parsed, "check-ref"))
|
|
3355
|
+
throw new AppError("usage", "--check-ref is only supported with --from-finding", 2);
|
|
3356
|
+
if (receipts.length > 1)
|
|
3357
|
+
throw new AppError("usage", "Create one repair Work per --from-check receipt", 2);
|
|
3358
|
+
if (!unresolved.length && hasOption(parsed, "verification-file"))
|
|
3359
|
+
throw new AppError("usage", "--verification-file is only supported with --from-unresolved", 2);
|
|
3360
|
+
const input = { ...scope, originWorkIds: origins, ...(unresolved.length ? { semanticUnresolved: unresolved, verificationPath: requiredOption(parsed, "verification-file") } : { checkReceiptId: receipts[0] }) };
|
|
3361
|
+
return { input, value: prepareVnextCodeRepair(context, input) };
|
|
3362
|
+
}
|
|
3363
|
+
function workerPromptInput(parsed) {
|
|
3364
|
+
const workspaceRoot = optionalOption(parsed, "workspace-root"), taskFile = optionalOption(parsed, "task-file");
|
|
3365
|
+
return { projectRoot: requiredOption(parsed, "project-root"), runId: requiredOption(parsed, "run"), stage: requiredOption(parsed, "stage"), profile: requiredOption(parsed, "profile"), ...(taskFile ? { taskFile } : { planItemId: requiredOption(parsed, "plan-item") }), ...(workspaceRoot ? { workspaceRoot } : {}) };
|
|
3366
|
+
}
|
|
2772
3367
|
function runnerIntake(context) {
|
|
2773
3368
|
const input = context.value.task_input?.[0];
|
|
2774
3369
|
if (!input?.path)
|
|
2775
3370
|
throw new AppError("validation", "Runner SPECIFY context requires one ordered task_input file", 2);
|
|
2776
|
-
|
|
2777
|
-
if (!fs.existsSync(file) || !fs.statSync(file).isFile())
|
|
2778
|
-
throw new AppError("not_found", "Runner SPECIFY task input is missing", 2, { task_input: file });
|
|
2779
|
-
return fs.readFileSync(file, "utf8");
|
|
3371
|
+
return prepareTextFile(input.path, "Runner SPECIFY task input").text;
|
|
2780
3372
|
}
|
|
2781
3373
|
/** Stage services install context before their lifecycle transition. Keeping
|
|
2782
3374
|
* this check here makes an incomplete service migration fail closed instead of
|