@deksden-com/dd-flow-cli 0.9.0-beta.104 → 0.9.0-beta.105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/run-cli.js +23 -8
- package/dist/harness-runtime/bin/dd-agy.mjs +1 -1
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +2 -2
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +4 -3
- package/dist/harness-runtime/lib/delegation-instructions.d.mts +2 -0
- package/dist/harness-runtime/lib/delegation-instructions.mjs +8 -1
- package/dist/protocol/local-files.js +18 -9
- package/dist/runtime/context.js +2 -2
- package/dist/services/cleanup.js +81 -32
- package/dist/services/code-checks.js +18 -19
- package/dist/services/continuation-outcome.js +1 -1
- package/dist/services/controller-fanout.js +3 -3
- package/dist/services/eval-snapshots.js +69 -41
- package/dist/services/execution-policy.js +12 -1
- package/dist/services/hooks.js +150 -42
- package/dist/services/lanes.js +23 -18
- package/dist/services/lifecycle-command.js +11 -2
- package/dist/services/lifecycle-invocations.js +291 -54
- package/dist/services/managed-processes.js +35 -23
- package/dist/services/merge-queue.js +71 -67
- package/dist/services/merge-server.js +3 -11
- package/dist/services/migrations.js +28 -12
- package/dist/services/plan-runtime.js +8 -4
- package/dist/services/projects.js +3 -11
- package/dist/services/protocols.js +98 -68
- package/dist/services/recovery-snapshot-database.js +43 -8
- package/dist/services/run-control-worker.js +15 -32
- package/dist/services/run-control.js +3 -17
- package/dist/services/run-controller-capture.js +58 -27
- package/dist/services/run-controller-process.js +21 -30
- package/dist/services/run-controller-recovery.js +36 -37
- package/dist/services/run-controller.js +81 -86
- package/dist/services/run-fork.js +10 -6
- package/dist/services/run-projection.js +19 -64
- package/dist/services/run-recovery-runtime.js +24 -14
- package/dist/services/run-recovery.js +56 -54
- package/dist/services/runs.js +218 -160
- package/dist/services/runtime-budget.js +19 -45
- package/dist/services/runtime-scope-capture.js +8 -6
- package/dist/services/runtime-scope-control.js +49 -39
- package/dist/services/runtime-scope-resume.js +134 -122
- package/dist/services/runtime-scope-worker.js +48 -49
- package/dist/services/scope-writer-cleanup.js +26 -0
- package/dist/services/stage-context.js +6 -1
- package/dist/services/stage-lifecycle.js +1 -1
- package/dist/services/stage-pause.js +13 -29
- package/dist/services/stage-report-renderer.js +116 -6
- package/dist/services/usage.js +2 -9
- package/dist/services/vnext-code-review.js +10 -7
- package/dist/services/vnext-code.js +9 -17
- package/dist/services/vnext-merge.js +64 -21
- package/dist/services/vnext-plan-review.js +43 -30
- package/dist/services/vnext-plan.js +28 -24
- package/dist/services/vnext-protocolize.js +183 -20
- package/dist/services/vnext-specify.js +27 -19
- package/dist/services/work-registry.js +229 -150
- package/dist/storage/database.js +286 -56
- package/dist/storage/foreign-key-transaction.js +39 -0
- package/dist/storage/immutable-input.js +45 -0
- package/dist/storage/import-guard.js +102 -0
- package/dist/storage/native-cleanup.js +52 -0
- package/dist/storage/projection-lock.js +73 -0
- package/dist/storage/writer-contract.js +3 -3
- package/dist/storage/writer-migration.js +24 -41
- package/package.json +1 -1
- package/tools/repair-paused-run-status.mjs +48 -23
package/dist/services/lanes.js
CHANGED
|
@@ -23,14 +23,13 @@ export function prepareLaneLock(context, input, action) {
|
|
|
23
23
|
const { timeoutSeconds, pollIntervalSeconds } = prepareLaneWaitTiming(input);
|
|
24
24
|
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
25
25
|
const lane = normalizeLane(input.lane);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
else if (["acquire", "wait", "wait-acquire"].includes(action))
|
|
26
|
+
const workspace = input.workspacePath !== undefined ? requireMatchingLaneWorkspace(context, project.id, lane, input.workspacePath) : undefined;
|
|
27
|
+
if (input.workspacePath === undefined && ["acquire", "wait", "wait-acquire"].includes(action))
|
|
29
28
|
throw new AppError("validation", "Lane lock requires a workspace path", 2);
|
|
30
29
|
if (action === "acquire" || action === "wait-acquire")
|
|
31
30
|
assertWorkerMayAcquireLaneLock(context, project.id, lane, input.workerId);
|
|
32
31
|
const lock = action === "heartbeat" || action === "release" ? requireOwnedActiveLock(context, project.id, lane, input.workerId, input.leaseToken) : undefined;
|
|
33
|
-
return { project, lane, ttlSeconds, timeoutSeconds, pollIntervalSeconds, lock };
|
|
32
|
+
return { project, lane, ttlSeconds, timeoutSeconds, pollIntervalSeconds, lock, workspace };
|
|
34
33
|
}
|
|
35
34
|
export function getLaneStatus(context, input) {
|
|
36
35
|
const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
|
|
@@ -99,7 +98,12 @@ export function checkLaneWorkspace(context, input) {
|
|
|
99
98
|
};
|
|
100
99
|
}
|
|
101
100
|
export function acquireLaneLock(context, input) {
|
|
102
|
-
const
|
|
101
|
+
const prepared = prepareLaneLock(context, input, "acquire");
|
|
102
|
+
return context.db.writeTransaction(() => acquirePreparedLaneLock(context, input, prepared));
|
|
103
|
+
}
|
|
104
|
+
function acquirePreparedLaneLock(context, input, prepared) {
|
|
105
|
+
const { project, lane, ttlSeconds } = prepared;
|
|
106
|
+
assertPreparedLaneLock(context, prepared, input.workerId);
|
|
103
107
|
expireStaleLocks(context, project.id);
|
|
104
108
|
const active = activeLaneLock(context, project.id, lane);
|
|
105
109
|
if (active) {
|
|
@@ -141,6 +145,12 @@ export function acquireLaneLock(context, input) {
|
|
|
141
145
|
...(expired ? { expired_lock_id: expired.id } : {})
|
|
142
146
|
};
|
|
143
147
|
}
|
|
148
|
+
function assertPreparedLaneLock(context, prepared, workerId) {
|
|
149
|
+
if (JSON.stringify(laneRecord(context, prepared.project.id, prepared.lane)) !== JSON.stringify(prepared.workspace)
|
|
150
|
+
|| context.db.get("SELECT root FROM projects WHERE id = ?", [prepared.project.id])?.root !== prepared.project.root)
|
|
151
|
+
throw new AppError("lane_workspace_changed", "Lane registration changed after workspace preparation", 1);
|
|
152
|
+
assertWorkerMayAcquireLaneLock(context, prepared.project.id, prepared.lane, workerId);
|
|
153
|
+
}
|
|
144
154
|
export function heartbeatLaneLock(context, input) {
|
|
145
155
|
const { project, lock: preparedLock, ttlSeconds } = prepareLaneLock(context, input, "heartbeat");
|
|
146
156
|
const lock = preparedLock;
|
|
@@ -524,13 +534,15 @@ function ensureQueuedLaneWaiter(context, input) {
|
|
|
524
534
|
return waiter;
|
|
525
535
|
}
|
|
526
536
|
function tryAcquireQueuedLaneLock(context, input) {
|
|
527
|
-
context
|
|
528
|
-
|
|
537
|
+
const prepared = prepareLaneLock(context, input, "wait-acquire");
|
|
538
|
+
if (prepared.project.id !== input.projectId || prepared.lane !== input.lane)
|
|
539
|
+
throw new AppError("lane_workspace_changed", "Lane identity changed while waiting", 1);
|
|
540
|
+
return context.db.writeTransaction(() => {
|
|
541
|
+
assertPreparedLaneLock(context, prepared, input.workerId);
|
|
529
542
|
expireStaleLocks(context, input.projectId);
|
|
530
543
|
expireStaleWaiters(context, input.projectId);
|
|
531
544
|
const existingLock = activeLaneLock(context, input.projectId, input.lane);
|
|
532
545
|
if (existingLock?.worker_id === input.workerId) {
|
|
533
|
-
context.db.exec("COMMIT");
|
|
534
546
|
return {
|
|
535
547
|
acquired: true,
|
|
536
548
|
payload: {
|
|
@@ -548,7 +560,6 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
548
560
|
}
|
|
549
561
|
const latest = latestLaneWaiterForWorker(context, input.projectId, input.lane, input.workerId);
|
|
550
562
|
if (latest && ["cancelled", "expired"].includes(latest.status) && latest.updated_at >= input.invocationStartedAt) {
|
|
551
|
-
context.db.exec("COMMIT");
|
|
552
563
|
return {
|
|
553
564
|
acquired: false,
|
|
554
565
|
payload: {
|
|
@@ -572,14 +583,14 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
572
583
|
});
|
|
573
584
|
const head = firstQueuedLaneWaiter(context, input.projectId, input.lane);
|
|
574
585
|
if (!existingLock && head?.id === waiter.id) {
|
|
575
|
-
const lockResult =
|
|
586
|
+
const lockResult = acquirePreparedLaneLock(context, {
|
|
576
587
|
projectRoot: input.projectRoot,
|
|
577
588
|
lane: input.lane,
|
|
578
589
|
workerId: input.workerId,
|
|
579
590
|
workspacePath: input.workspacePath,
|
|
580
591
|
ttlSeconds: input.ttlSeconds,
|
|
581
592
|
reason: input.reason
|
|
582
|
-
});
|
|
593
|
+
}, prepared);
|
|
583
594
|
const now = context.now();
|
|
584
595
|
context.db.run(`UPDATE lane_waiters
|
|
585
596
|
SET status = 'acquired', acquired_at = ?, updated_at = ?
|
|
@@ -591,7 +602,6 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
591
602
|
reason: input.reason,
|
|
592
603
|
payload: { project_id: input.projectId, lane: input.lane, worker_id: input.workerId, waiter_id: waiter.id }
|
|
593
604
|
});
|
|
594
|
-
context.db.exec("COMMIT");
|
|
595
605
|
return {
|
|
596
606
|
acquired: true,
|
|
597
607
|
payload: {
|
|
@@ -608,7 +618,6 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
608
618
|
};
|
|
609
619
|
}
|
|
610
620
|
const position = queuedWaiterPosition(context, input.projectId, input.lane, waiter.id);
|
|
611
|
-
context.db.exec("COMMIT");
|
|
612
621
|
return {
|
|
613
622
|
acquired: false,
|
|
614
623
|
payload: {
|
|
@@ -622,11 +631,7 @@ function tryAcquireQueuedLaneLock(context, input) {
|
|
|
622
631
|
position
|
|
623
632
|
}
|
|
624
633
|
};
|
|
625
|
-
}
|
|
626
|
-
catch (error) {
|
|
627
|
-
context.db.exec("ROLLBACK");
|
|
628
|
-
throw error;
|
|
629
|
-
}
|
|
634
|
+
});
|
|
630
635
|
}
|
|
631
636
|
function resolveExistingPath(value) {
|
|
632
637
|
return resolveProjectRoot(value);
|
|
@@ -160,8 +160,16 @@ function invocationFromSegment(segment, heredoc, wrapped, inheritedEnv) {
|
|
|
160
160
|
index += 1;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
let executable = segment.words[index];
|
|
164
|
+
let nodeLauncher = false;
|
|
165
|
+
let launcher;
|
|
166
|
+
if (executable && path.basename(executable) === "node"
|
|
167
|
+
&& (path.basename(segment.words[index + 1] ?? "") === "cli.js" || segment.words[index + 1] === "$DD_FLOW_BIN")) {
|
|
168
|
+
launcher = executable;
|
|
169
|
+
executable = segment.words[++index];
|
|
170
|
+
nodeLauncher = true;
|
|
171
|
+
}
|
|
172
|
+
if (!executable || path.basename(executable) !== "dd-flow" && executable !== "$DD_FLOW_BIN" && !(nodeLauncher && path.basename(executable) === "cli.js"))
|
|
165
173
|
return null;
|
|
166
174
|
const argv = segment.words.slice(index + 1);
|
|
167
175
|
const operation = lifecycleOperation(argv);
|
|
@@ -170,6 +178,7 @@ function invocationFromSegment(segment, heredoc, wrapped, inheritedEnv) {
|
|
|
170
178
|
return {
|
|
171
179
|
operation,
|
|
172
180
|
executable,
|
|
181
|
+
...(launcher ? { launcher } : {}),
|
|
173
182
|
argv,
|
|
174
183
|
env,
|
|
175
184
|
args: parseCommandArgs(argv.slice(operation === "recovery_accept" ? 3 : 2)),
|