@deksden-com/dd-flow-cli 0.3.0 → 0.3.1
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 +43 -0
- package/README.md +74 -5
- package/dist/build-info.json +5 -5
- package/dist/cli/help.js +103 -15
- package/dist/cli/run-cli.js +307 -27
- package/dist/schemas/compatibility.schema.json +81 -2
- package/dist/schemas/engine-manifest.schema.json +61 -0
- package/dist/schemas/flow-guidance.schema.json +17 -0
- package/dist/schemas/global-dashboard-data.schema.json +60 -2
- package/dist/schemas/mb-upgrade-migration-report.schema.json +93 -0
- package/dist/schemas/project-dashboard-data.schema.json +26 -2
- package/dist/schemas/project-summary.schema.json +73 -0
- package/dist/schemas/protocol-dashboard-data.schema.json +25 -2
- package/dist/schemas/status-report.schema.json +4 -2
- package/dist/services/cleanup.js +31 -0
- package/dist/services/cli-operation-classifier.js +104 -0
- package/dist/services/compatibility-preflight.js +124 -0
- package/dist/services/config.js +6 -0
- package/dist/services/dashboard-targets.js +95 -0
- package/dist/services/dashboard.js +376 -59
- package/dist/services/engines.js +532 -0
- package/dist/services/flow-guidance.js +8 -1
- package/dist/services/hooks.js +1 -1
- package/dist/services/lanes.js +333 -1
- package/dist/services/merge-queue.js +97 -15
- package/dist/services/merge-worker.js +36 -2
- package/dist/services/migrations.js +231 -0
- package/dist/services/project-summary.js +122 -0
- package/dist/services/projects.js +41 -6
- package/dist/services/protocol-lifecycle.js +144 -0
- package/dist/services/protocols.js +29 -7
- package/dist/services/sessions.js +21 -4
- package/dist/services/status.js +10 -0
- package/dist/services/version-status.js +39 -15
- package/dist/storage/database.js +25 -0
- package/dist/storage/paths.js +12 -0
- package/package.json +3 -2
package/dist/cli/run-cli.js
CHANGED
|
@@ -10,19 +10,24 @@ import { getMergeWorkerStatus, oneShotMergeClaim, startMergeWorker, stopProjectM
|
|
|
10
10
|
import { cleanupApply, cleanupScan } from "../services/cleanup.js";
|
|
11
11
|
import { getCodexHomeStatus, getCodexHooksStatus, handleCodexHook, initCodexHome, installCodexHooks, planCodexHome, printCodexHomeEnv, printCodexHooks, removeCodexHome, removeCodexHooks } from "../services/hooks.js";
|
|
12
12
|
import { bootstrapWorktree, closeWorktree, createWorktreeRecord, getWorktreeStatus, planWorktree } from "../services/worktrees.js";
|
|
13
|
-
import { acquireLaneLock, checkLaneWorkspace, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, waitForLaneLock } from "../services/lanes.js";
|
|
13
|
+
import { acquireLaneLock, cancelLaneWaiter, checkLaneWorkspace, getLaneWaiters, getLaneStatus, heartbeatLaneLock, releaseLaneLock, setLaneWorkspace, waitAcquireLaneLock, waitForLaneLock } from "../services/lanes.js";
|
|
14
14
|
import { getProjectConfigStatus, setProjectConfigValue } from "../services/config.js";
|
|
15
15
|
import { getCanonStatus, registerCanonRoot, resolveCanonRoot } from "../services/canon.js";
|
|
16
16
|
import { getRuntimeStatus } from "../services/status.js";
|
|
17
17
|
import { getCliVersionReport } from "../services/build-info.js";
|
|
18
|
-
import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
|
|
18
|
+
import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
|
|
19
|
+
import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
|
|
19
20
|
import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker } from "../services/sessions.js";
|
|
20
21
|
import { attachFlowRunStage, completeFlowRun, completeFlowRunStage, getFlowRunStatus, listFlowRuns, startFlowRun } from "../services/runs.js";
|
|
21
22
|
import { previewNextEntityId } from "../services/ids.js";
|
|
22
23
|
import { validateSchema } from "../services/schema-validation.js";
|
|
23
24
|
import { preflightMemoryPermissions } from "../services/memory-permissions.js";
|
|
25
|
+
import { planMigration, verifyMigrationReport } from "../services/migrations.js";
|
|
26
|
+
import { publishProjectSummary } from "../services/project-summary.js";
|
|
24
27
|
import { requireProjectByRoot } from "../services/projects.js";
|
|
25
28
|
import { resolveProjectRoot } from "../storage/paths.js";
|
|
29
|
+
import { doctorEngines, engineInfo, installCurrentEngine, listEngines, resolveEngine, routeArgsThroughEngine } from "../services/engines.js";
|
|
30
|
+
import { preflightCliCompatibility } from "../services/compatibility-preflight.js";
|
|
26
31
|
const defaultIo = {
|
|
27
32
|
stdout: process.stdout,
|
|
28
33
|
stderr: process.stderr,
|
|
@@ -51,7 +56,16 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
51
56
|
}
|
|
52
57
|
const context = createContext(env);
|
|
53
58
|
emitHumanProgress(io, output, progress, "start");
|
|
54
|
-
const
|
|
59
|
+
const routed = await routeArgsThroughEngine(context, output.args, io, io.stdin, env);
|
|
60
|
+
if (routed?.routed && !routed.inProcess) {
|
|
61
|
+
emitHumanProgress(io, output, progress, "done");
|
|
62
|
+
return routed.exitCode;
|
|
63
|
+
}
|
|
64
|
+
const dispatchContext = routed?.inProcess
|
|
65
|
+
? createContext({ ...env, DD_FLOW_ENGINE_MODE: "1", DD_FLOW_ENGINE_HOME: "in-process", DD_FLOW_ROUTED_FROM: getCliVersionReport().cli.version })
|
|
66
|
+
: context;
|
|
67
|
+
preflightCliCompatibility(dispatchContext, output.args, env);
|
|
68
|
+
const result = await dispatch(output.args, dispatchContext, io);
|
|
55
69
|
refreshDashboardsAfterMutation(context, output.args, result);
|
|
56
70
|
emitHumanProgress(io, output, progress, "done");
|
|
57
71
|
const payload = attachProgress(result, progress, output);
|
|
@@ -70,10 +84,34 @@ export async function runCli(args, io = defaultIo, env = process.env) {
|
|
|
70
84
|
if (output.json)
|
|
71
85
|
writeJson(io.stderr, errorPayload);
|
|
72
86
|
else
|
|
73
|
-
io.stderr.write(
|
|
87
|
+
io.stderr.write(renderHumanError(payload));
|
|
74
88
|
return isAppError(error) ? error.exitCode : 1;
|
|
75
89
|
}
|
|
76
90
|
}
|
|
91
|
+
function renderHumanError(payload) {
|
|
92
|
+
const lines = [`dd-flow: ${payload.error.message}`];
|
|
93
|
+
const details = recordObject(payload.error.details);
|
|
94
|
+
const compatibility = recordObject(details?.compatibility);
|
|
95
|
+
const remediation = recordObject(details?.remediation);
|
|
96
|
+
if (compatibility) {
|
|
97
|
+
lines.push(`Compatibility: ${String(compatibility.verdict ?? "unknown")} (${String(compatibility.engine_resolution ?? "unknown")})`);
|
|
98
|
+
if (compatibility.blocked_operation)
|
|
99
|
+
lines.push(`Blocked operation: ${String(compatibility.blocked_operation)}`);
|
|
100
|
+
if (compatibility.install_hint)
|
|
101
|
+
lines.push(`Install compatible engine: ${String(compatibility.install_hint)}`);
|
|
102
|
+
}
|
|
103
|
+
if (remediation?.mb_upgrade_mode)
|
|
104
|
+
lines.push(`Upgrade mode: ${String(remediation.mb_upgrade_mode)}`);
|
|
105
|
+
if (details?.preferred_command)
|
|
106
|
+
lines.push(`Preferred command: ${String(details.preferred_command)}`);
|
|
107
|
+
const related = Array.isArray(details?.related_commands) ? details.related_commands.map(String) : [];
|
|
108
|
+
if (related.length > 0) {
|
|
109
|
+
lines.push("", "Related commands:");
|
|
110
|
+
for (const command of related)
|
|
111
|
+
lines.push(` ${command}`);
|
|
112
|
+
}
|
|
113
|
+
return `${lines.join("\n")}\n`;
|
|
114
|
+
}
|
|
77
115
|
function parseOutputOptions(args) {
|
|
78
116
|
const stripped = [];
|
|
79
117
|
let json = false;
|
|
@@ -96,7 +134,7 @@ function progressForCommand(args) {
|
|
|
96
134
|
{ phase: "done", message: "Merge queue wait finished", at: "" }
|
|
97
135
|
];
|
|
98
136
|
}
|
|
99
|
-
if (family === "lane" && command === "lock" && subcommand
|
|
137
|
+
if (family === "lane" && command === "lock" && ["wait", "wait-acquire"].includes(subcommand ?? "")) {
|
|
100
138
|
return [
|
|
101
139
|
{ phase: "start", message: "Starting lane lock wait", at: "" },
|
|
102
140
|
{ phase: "running", message: "Waiting for lane lock", at: "" },
|
|
@@ -164,7 +202,15 @@ function renderHumanResult(args, result) {
|
|
|
164
202
|
}
|
|
165
203
|
if (family === "version") {
|
|
166
204
|
const cli = recordObject(record.cli);
|
|
167
|
-
|
|
205
|
+
const engine = recordObject(record.engine);
|
|
206
|
+
const engineSuffix = engine ? ` (engine: ${String(engine.mode ?? "unknown")})` : "";
|
|
207
|
+
return `dd-flow ${stringValue(cli?.version) ?? "unknown"}${engineSuffix}\n`;
|
|
208
|
+
}
|
|
209
|
+
if (family === "engine") {
|
|
210
|
+
return renderEngineHuman(args, record);
|
|
211
|
+
}
|
|
212
|
+
if (family === "dashboard") {
|
|
213
|
+
return renderDashboardHuman(args, record);
|
|
168
214
|
}
|
|
169
215
|
const lines = [`dd-flow ${family ?? ""}${command ? ` ${command}` : ""}: ${ok}`.trim()];
|
|
170
216
|
const project = record.project && typeof record.project === "object" ? record.project : null;
|
|
@@ -230,6 +276,63 @@ function renderStatusHuman(record) {
|
|
|
230
276
|
appendFlowGuidanceHuman(lines, record);
|
|
231
277
|
return `${lines.join("\n")}\n`;
|
|
232
278
|
}
|
|
279
|
+
function renderDashboardHuman(args, record) {
|
|
280
|
+
const [, command] = args;
|
|
281
|
+
const ok = record.ok === false ? "failed" : "ok";
|
|
282
|
+
const target = recordObject(record.target);
|
|
283
|
+
const targetLabel = stringValue(target?.kind) ?? "unknown";
|
|
284
|
+
const lines = [`dd-flow dashboard ${command ?? ""}: ${ok}`.trim()];
|
|
285
|
+
lines.push(`target: ${targetLabel}`);
|
|
286
|
+
if (target?.project_id)
|
|
287
|
+
lines.push(`project: ${String(target.project_id)}`);
|
|
288
|
+
if (target?.protocol_id)
|
|
289
|
+
lines.push(`protocol: ${String(target.protocol_id)}`);
|
|
290
|
+
const dashboard = recordObject(record.dashboard);
|
|
291
|
+
if (dashboard?.path)
|
|
292
|
+
lines.push(`dashboard: ${String(dashboard.path)}`);
|
|
293
|
+
if (record.compatibility_alias)
|
|
294
|
+
lines.push(`compatibility_alias: true`);
|
|
295
|
+
if (record.preferred_command)
|
|
296
|
+
lines.push(`Preferred command: ${String(record.preferred_command)}`);
|
|
297
|
+
const related = Array.isArray(record.related_commands) ? record.related_commands.map(String) : [];
|
|
298
|
+
if (related.length > 0) {
|
|
299
|
+
lines.push("", "Related dashboard commands:");
|
|
300
|
+
for (const item of related)
|
|
301
|
+
lines.push(` ${item}`);
|
|
302
|
+
}
|
|
303
|
+
return `${lines.join("\n")}\n`;
|
|
304
|
+
}
|
|
305
|
+
function renderEngineHuman(args, record) {
|
|
306
|
+
const [, command] = args;
|
|
307
|
+
const lines = [`dd-flow engine ${command ?? ""}: ${record.ok === false ? "failed" : "ok"}`.trim()];
|
|
308
|
+
if (record.engine_store)
|
|
309
|
+
lines.push(`engine_store: ${String(record.engine_store)}`);
|
|
310
|
+
const engine = recordObject(record.engine);
|
|
311
|
+
if (engine) {
|
|
312
|
+
lines.push(`engine: ${String(engine.package_name ?? "unknown")}@${String(engine.package_version ?? "unknown")}`);
|
|
313
|
+
if (engine.entrypoint)
|
|
314
|
+
lines.push(`entrypoint: ${String(engine.entrypoint)}`);
|
|
315
|
+
if (engine.healthy !== undefined)
|
|
316
|
+
lines.push(`healthy: ${String(engine.healthy)}`);
|
|
317
|
+
}
|
|
318
|
+
const engines = Array.isArray(record.engines) ? record.engines : [];
|
|
319
|
+
if (engines.length > 0)
|
|
320
|
+
lines.push(`engines: ${engines.length}`);
|
|
321
|
+
const selection = recordObject(record.selection);
|
|
322
|
+
if (selection) {
|
|
323
|
+
lines.push(`selection: ${String(selection.status ?? "unknown")}`);
|
|
324
|
+
if (selection.selected && typeof selection.selected === "object") {
|
|
325
|
+
const selected = selection.selected;
|
|
326
|
+
lines.push(`selected: ${String(selected.package_name ?? "unknown")}@${String(selected.package_version ?? "unknown")}`);
|
|
327
|
+
}
|
|
328
|
+
if (selection.install_hint)
|
|
329
|
+
lines.push(`install_hint: ${String(selection.install_hint)}`);
|
|
330
|
+
}
|
|
331
|
+
const checks = Array.isArray(record.checks) ? record.checks : [];
|
|
332
|
+
if (checks.length > 0)
|
|
333
|
+
lines.push(`checks: ${checks.length}`);
|
|
334
|
+
return `${lines.join("\n")}\n`;
|
|
335
|
+
}
|
|
233
336
|
function appendFlowGuidanceHuman(lines, record) {
|
|
234
337
|
const guidance = recordObject(record.flow_guidance);
|
|
235
338
|
if (!guidance)
|
|
@@ -261,6 +364,9 @@ function shortSha(value) {
|
|
|
261
364
|
async function dispatch(args, context, io) {
|
|
262
365
|
const [family, command, ...rest] = args;
|
|
263
366
|
const parsed = parseArgs(rest);
|
|
367
|
+
if (family === "engine") {
|
|
368
|
+
return dispatchEngine(context, command, parsed);
|
|
369
|
+
}
|
|
264
370
|
if (family === "status") {
|
|
265
371
|
const rootParsed = parseArgs([command ?? "", ...rest]);
|
|
266
372
|
return getRuntimeStatus(context, {
|
|
@@ -280,6 +386,12 @@ async function dispatch(args, context, io) {
|
|
|
280
386
|
if (family === "project" && command === "status") {
|
|
281
387
|
return getProjectStatus(context, { root: requiredOption(parsed, "root") });
|
|
282
388
|
}
|
|
389
|
+
if (family === "project" && command === "summary") {
|
|
390
|
+
return publishProjectSummary(context, {
|
|
391
|
+
project: requireProjectByRoot(context, resolveProjectRoot(requiredOption(parsed, "project-root"))),
|
|
392
|
+
write: parseOptionalBoolean(optionalOption(parsed, "write"), "write") ?? true
|
|
393
|
+
});
|
|
394
|
+
}
|
|
283
395
|
if (family === "project" && command === "resolve") {
|
|
284
396
|
return resolveProject(context, { idOrAlias: requiredPosition(parsed, 0, "id-or-alias") });
|
|
285
397
|
}
|
|
@@ -418,6 +530,9 @@ async function dispatch(args, context, io) {
|
|
|
418
530
|
if (family === "memory") {
|
|
419
531
|
return dispatchMemory(command, parsed);
|
|
420
532
|
}
|
|
533
|
+
if (family === "migration") {
|
|
534
|
+
return dispatchMigration(context, command, parsed);
|
|
535
|
+
}
|
|
421
536
|
if (family === "codex" && command === "hooks") {
|
|
422
537
|
return dispatchCodexHooks(context, parsed);
|
|
423
538
|
}
|
|
@@ -432,6 +547,41 @@ async function dispatch(args, context, io) {
|
|
|
432
547
|
}
|
|
433
548
|
throw new AppError("usage", `Unknown command: ${args.join(" ") || "<empty>"}`, 2);
|
|
434
549
|
}
|
|
550
|
+
function dispatchEngine(context, command, parsed) {
|
|
551
|
+
if (command === "install") {
|
|
552
|
+
return installCurrentEngine(context, { force: hasOption(parsed, "force") });
|
|
553
|
+
}
|
|
554
|
+
if (command === "list") {
|
|
555
|
+
return listEngines(context);
|
|
556
|
+
}
|
|
557
|
+
if (command === "info") {
|
|
558
|
+
return engineInfo(context, { packageName: optionalOption(parsed, "package"), version: optionalOption(parsed, "version") });
|
|
559
|
+
}
|
|
560
|
+
if (command === "resolve") {
|
|
561
|
+
return resolveEngine(context, { projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root") });
|
|
562
|
+
}
|
|
563
|
+
if (command === "doctor") {
|
|
564
|
+
return doctorEngines(context, { projectRoot: optionalOption(parsed, "project-root") ?? optionalOption(parsed, "root") });
|
|
565
|
+
}
|
|
566
|
+
throw new AppError("usage", `Unknown engine command: ${command ?? "<empty>"}`, 2);
|
|
567
|
+
}
|
|
568
|
+
function dispatchMigration(context, command, parsed) {
|
|
569
|
+
if (command === "plan" || command === "report") {
|
|
570
|
+
return planMigration(context, {
|
|
571
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
572
|
+
sourceVersion: optionalOption(parsed, "source-version"),
|
|
573
|
+
targetVersion: optionalOption(parsed, "target-version"),
|
|
574
|
+
runId: optionalOption(parsed, "run"),
|
|
575
|
+
backupPath: optionalOption(parsed, "backup-path"),
|
|
576
|
+
backupCreatedAt: optionalOption(parsed, "backup-created-at"),
|
|
577
|
+
allowActive: parseOptionalBoolean(optionalOption(parsed, "allow-active"), "allow-active") ?? false
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
if (command === "verify") {
|
|
581
|
+
return verifyMigrationReport(context, { file: requiredOption(parsed, "file") });
|
|
582
|
+
}
|
|
583
|
+
throw new AppError("usage", `Unknown migration command: ${command ?? "<empty>"}`, 2);
|
|
584
|
+
}
|
|
435
585
|
function dispatchCanon(context, command, parsed) {
|
|
436
586
|
if (command === "register") {
|
|
437
587
|
return registerCanonRoot(context, { root: requiredOption(parsed, "root") });
|
|
@@ -690,49 +840,149 @@ function dispatchCmux(context, parsed) {
|
|
|
690
840
|
throw new AppError("usage", `Unknown integration cmux action: ${action}`, 2);
|
|
691
841
|
}
|
|
692
842
|
function dispatchDashboard(context, command, parsed) {
|
|
843
|
+
const action = dashboardAction(command);
|
|
693
844
|
if (command === "data") {
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
projectRoot:
|
|
698
|
-
protocol: optionalOption(parsed, "protocol")
|
|
845
|
+
const target = resolveDashboardTarget(context, {
|
|
846
|
+
action,
|
|
847
|
+
project: optionalOption(parsed, "project"),
|
|
848
|
+
projectRoot: optionalOption(parsed, "project-root"),
|
|
849
|
+
protocol: optionalOption(parsed, "protocol"),
|
|
850
|
+
global: hasOption(parsed, "global")
|
|
699
851
|
});
|
|
852
|
+
return withDashboardGuidance(action, target, dashboardDataForResolvedTarget(context, target));
|
|
700
853
|
}
|
|
701
854
|
if (command === "render") {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
855
|
+
const target = resolveDashboardTarget(context, {
|
|
856
|
+
action,
|
|
857
|
+
project: optionalOption(parsed, "project"),
|
|
858
|
+
projectRoot: optionalOption(parsed, "project-root"),
|
|
706
859
|
protocol: optionalOption(parsed, "protocol")
|
|
707
860
|
});
|
|
861
|
+
return withDashboardGuidance(action, target, renderDashboardForResolvedTarget(context, target, parsed));
|
|
708
862
|
}
|
|
709
863
|
if (command === "render-global") {
|
|
710
|
-
|
|
864
|
+
const target = resolveDashboardTarget(context, { action: "render", global: true });
|
|
865
|
+
return withDashboardGuidance("render", target, renderGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") }), {
|
|
866
|
+
compatibilityAlias: true,
|
|
867
|
+
preferredCommand: "dd-flow dashboard render"
|
|
868
|
+
});
|
|
711
869
|
}
|
|
712
870
|
if (command === "open") {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
871
|
+
const target = resolveDashboardTarget(context, {
|
|
872
|
+
action,
|
|
873
|
+
project: optionalOption(parsed, "project"),
|
|
874
|
+
projectRoot: optionalOption(parsed, "project-root"),
|
|
875
|
+
protocol: optionalOption(parsed, "protocol")
|
|
717
876
|
});
|
|
877
|
+
return withDashboardGuidance(action, target, openDashboardForResolvedTarget(context, target, parsed));
|
|
718
878
|
}
|
|
719
879
|
if (command === "refresh") {
|
|
720
880
|
const open = optionalOption(parsed, "open");
|
|
721
881
|
if (typeof open !== "undefined" && !["auto", "true", "false"].includes(open)) {
|
|
722
882
|
throw new AppError("validation", "--open must be auto, true, or false", 2);
|
|
723
883
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
protocol: optionalOption(parsed, "protocol")
|
|
884
|
+
const target = resolveDashboardTarget(context, {
|
|
885
|
+
action,
|
|
886
|
+
project: optionalOption(parsed, "project"),
|
|
887
|
+
projectRoot: optionalOption(parsed, "project-root"),
|
|
888
|
+
protocol: optionalOption(parsed, "protocol"),
|
|
889
|
+
all: hasOption(parsed, "all")
|
|
729
890
|
});
|
|
891
|
+
return withDashboardGuidance(action, target, refreshDashboardForResolvedTarget(context, target, parsed, open));
|
|
730
892
|
}
|
|
731
893
|
if (command === "refresh-global") {
|
|
732
|
-
|
|
894
|
+
const target = resolveDashboardTarget(context, { action: "refresh", global: true });
|
|
895
|
+
return withDashboardGuidance("refresh", target, refreshGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") }), {
|
|
896
|
+
compatibilityAlias: true,
|
|
897
|
+
preferredCommand: "dd-flow dashboard refresh"
|
|
898
|
+
});
|
|
733
899
|
}
|
|
734
900
|
throw new AppError("usage", `Unknown dashboard command: ${command ?? "<empty>"}`, 2);
|
|
735
901
|
}
|
|
902
|
+
function dashboardAction(command) {
|
|
903
|
+
if (command === "data" || command === "render" || command === "open" || command === "refresh") {
|
|
904
|
+
return command;
|
|
905
|
+
}
|
|
906
|
+
if (command === "render-global")
|
|
907
|
+
return "render";
|
|
908
|
+
if (command === "refresh-global")
|
|
909
|
+
return "refresh";
|
|
910
|
+
throw new AppError("usage", `Unknown dashboard command: ${command ?? "<empty>"}`, 2);
|
|
911
|
+
}
|
|
912
|
+
function dashboardDataForResolvedTarget(context, target) {
|
|
913
|
+
if (target.kind === "all_projects") {
|
|
914
|
+
throw new AppError("validation", "dashboard data does not support --all", 2, { related_commands: relatedDashboardCommands("data") });
|
|
915
|
+
}
|
|
916
|
+
if (target.kind === "global") {
|
|
917
|
+
return dashboardData(context, { global: true });
|
|
918
|
+
}
|
|
919
|
+
if (target.kind === "protocol") {
|
|
920
|
+
return dashboardData(context, { projectRoot: target.project.root, protocol: target.protocolId });
|
|
921
|
+
}
|
|
922
|
+
return dashboardData(context, { projectRoot: target.project.root });
|
|
923
|
+
}
|
|
924
|
+
function renderDashboardForResolvedTarget(context, target, parsed) {
|
|
925
|
+
if (target.kind === "all_projects") {
|
|
926
|
+
throw new AppError("validation", "dashboard render does not support --all", 2, { related_commands: relatedDashboardCommands("render") });
|
|
927
|
+
}
|
|
928
|
+
if (target.kind === "global") {
|
|
929
|
+
return renderGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") ?? "html" });
|
|
930
|
+
}
|
|
931
|
+
return renderDashboard(context, {
|
|
932
|
+
projectRoot: target.project.root,
|
|
933
|
+
output: optionalOption(parsed, "output"),
|
|
934
|
+
format: optionalOption(parsed, "format") ?? (hasOption(parsed, "project") ? "html" : undefined),
|
|
935
|
+
...(target.kind === "protocol" ? { protocol: target.protocolId } : {})
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
function openDashboardForResolvedTarget(context, target, parsed) {
|
|
939
|
+
if (target.kind === "all_projects") {
|
|
940
|
+
throw new AppError("validation", "dashboard open --all is not supported", 2, {
|
|
941
|
+
preferred_command: "dd-flow dashboard refresh --all",
|
|
942
|
+
related_commands: relatedDashboardCommands("open", "all_projects")
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
if (target.kind === "global") {
|
|
946
|
+
return openGlobalDashboard(context, { viewer: optionalOption(parsed, "viewer"), format: optionalOption(parsed, "format") ?? "html" });
|
|
947
|
+
}
|
|
948
|
+
if (target.kind === "protocol") {
|
|
949
|
+
return openProtocolDashboard(context, {
|
|
950
|
+
projectRoot: target.project.root,
|
|
951
|
+
protocol: target.protocolId,
|
|
952
|
+
viewer: optionalOption(parsed, "viewer"),
|
|
953
|
+
format: optionalOption(parsed, "format") ?? "html"
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
return openDashboard(context, {
|
|
957
|
+
projectRoot: target.project.root,
|
|
958
|
+
viewer: optionalOption(parsed, "viewer"),
|
|
959
|
+
format: optionalOption(parsed, "format") ?? (hasOption(parsed, "project") ? "html" : undefined)
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
function refreshDashboardForResolvedTarget(context, target, parsed, open) {
|
|
963
|
+
if (target.kind === "all_projects") {
|
|
964
|
+
return refreshAllDashboards(context, { format: optionalOption(parsed, "format") ?? "html" });
|
|
965
|
+
}
|
|
966
|
+
if (target.kind === "global") {
|
|
967
|
+
return refreshGlobalDashboard(context, { output: optionalOption(parsed, "output"), format: optionalOption(parsed, "format") ?? "html" });
|
|
968
|
+
}
|
|
969
|
+
return refreshDashboard(context, {
|
|
970
|
+
projectRoot: target.project.root,
|
|
971
|
+
open,
|
|
972
|
+
format: optionalOption(parsed, "format") ?? (hasOption(parsed, "project") ? "html" : undefined),
|
|
973
|
+
...(target.kind === "protocol" ? { protocol: target.protocolId } : {})
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
function withDashboardGuidance(action, target, result, options = {}) {
|
|
977
|
+
const record = result && typeof result === "object" && !Array.isArray(result) ? result : { ok: true, result };
|
|
978
|
+
return {
|
|
979
|
+
...record,
|
|
980
|
+
target: dashboardTargetSummary(target),
|
|
981
|
+
...(options.compatibilityAlias ? { compatibility_alias: true } : {}),
|
|
982
|
+
preferred_command: options.preferredCommand ?? preferredDashboardCommand(action, target),
|
|
983
|
+
related_commands: relatedDashboardCommands(action, target.kind)
|
|
984
|
+
};
|
|
985
|
+
}
|
|
736
986
|
function dispatchSchema(command, parsed) {
|
|
737
987
|
if (command === "validate") {
|
|
738
988
|
const schemaDir = optionalOption(parsed, "schema-dir");
|
|
@@ -750,6 +1000,21 @@ async function dispatchLane(context, command, parsed) {
|
|
|
750
1000
|
if (command === "status") {
|
|
751
1001
|
return getLaneStatus(context, { projectRoot: requiredOption(parsed, "project-root"), lane: optionalOption(parsed, "lane") });
|
|
752
1002
|
}
|
|
1003
|
+
if (command === "waiters") {
|
|
1004
|
+
return getLaneWaiters(context, { projectRoot: requiredOption(parsed, "project-root"), lane: optionalOption(parsed, "lane") });
|
|
1005
|
+
}
|
|
1006
|
+
if (command === "waiter") {
|
|
1007
|
+
const action = requiredPosition(parsed, 0, "lane waiter action");
|
|
1008
|
+
if (action === "cancel") {
|
|
1009
|
+
return cancelLaneWaiter(context, {
|
|
1010
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1011
|
+
lane: requiredOption(parsed, "lane"),
|
|
1012
|
+
workerId: requiredOption(parsed, "worker-id"),
|
|
1013
|
+
reason: requiredOption(parsed, "reason")
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
throw new AppError("usage", `Unknown lane waiter action: ${action}`, 2);
|
|
1017
|
+
}
|
|
753
1018
|
if (command === "workspace") {
|
|
754
1019
|
const action = requiredPosition(parsed, 0, "lane workspace action");
|
|
755
1020
|
if (action === "set") {
|
|
@@ -814,6 +1079,18 @@ async function dispatchLane(context, command, parsed) {
|
|
|
814
1079
|
pollIntervalSeconds: optionalNumber(parsed, "poll-interval")
|
|
815
1080
|
});
|
|
816
1081
|
}
|
|
1082
|
+
if (action === "wait-acquire") {
|
|
1083
|
+
return waitAcquireLaneLock(context, {
|
|
1084
|
+
projectRoot: requiredOption(parsed, "project-root"),
|
|
1085
|
+
lane: requiredOption(parsed, "lane"),
|
|
1086
|
+
workerId: requiredOption(parsed, "worker-id"),
|
|
1087
|
+
workspacePath: workspacePathOption(parsed),
|
|
1088
|
+
timeoutSeconds: optionalNumber(parsed, "timeout"),
|
|
1089
|
+
pollIntervalSeconds: optionalNumber(parsed, "poll-interval"),
|
|
1090
|
+
ttlSeconds: optionalNumber(parsed, "ttl"),
|
|
1091
|
+
reason: requiredOption(parsed, "reason")
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
817
1094
|
throw new AppError("usage", `Unknown lane lock action: ${action}`, 2);
|
|
818
1095
|
}
|
|
819
1096
|
throw new AppError("usage", `Unknown lane command: ${command ?? "<empty>"}`, 2);
|
|
@@ -1148,11 +1425,14 @@ function projectRootForLaneMutation(command, parsed) {
|
|
|
1148
1425
|
if (command === "workspace" && requiredPosition(parsed, 0, "lane workspace action") === "set") {
|
|
1149
1426
|
return requiredOption(parsed, "project-root");
|
|
1150
1427
|
}
|
|
1428
|
+
if (command === "waiter" && requiredPosition(parsed, 0, "lane waiter action") === "cancel") {
|
|
1429
|
+
return requiredOption(parsed, "project-root");
|
|
1430
|
+
}
|
|
1151
1431
|
if (command !== "lock") {
|
|
1152
1432
|
return undefined;
|
|
1153
1433
|
}
|
|
1154
1434
|
const action = requiredPosition(parsed, 0, "lane lock action");
|
|
1155
|
-
return ["acquire", "heartbeat", "release"].includes(action) ? requiredOption(parsed, "project-root") : undefined;
|
|
1435
|
+
return ["acquire", "heartbeat", "release", "wait-acquire"].includes(action) ? requiredOption(parsed, "project-root") : undefined;
|
|
1156
1436
|
}
|
|
1157
1437
|
function projectRootForMergeQueueMutation(context, command, parsed) {
|
|
1158
1438
|
if (["next", "wait-next"].includes(command ?? "")) {
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "dd-flow/compatibility@1",
|
|
4
4
|
"title": "dd-flow compatibility manifest",
|
|
5
|
-
"description": "Canonical compatibility map between a Memory Bank release
|
|
5
|
+
"description": "Canonical compatibility map between a Memory Bank release, tool packages, routed CLI engine expectations and machine data contracts required or recommended by that release.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
|
-
"required": ["schema_id", "memory_bank_version", "dd_flow_cli"],
|
|
8
|
+
"required": ["schema_id", "memory_bank_version", "dd_flow_cli", "router", "engine", "contracts", "migrations", "mb_lint"],
|
|
9
9
|
"properties": {
|
|
10
10
|
"schema_id": { "const": "dd-flow/compatibility@1" },
|
|
11
11
|
"memory_bank_version": { "type": "string", "minLength": 1 },
|
|
@@ -21,6 +21,85 @@
|
|
|
21
21
|
"version_contract": { "type": "string", "minLength": 1 },
|
|
22
22
|
"flow_contract": { "type": "string", "minLength": 1 }
|
|
23
23
|
}
|
|
24
|
+
},
|
|
25
|
+
"router": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"required": ["package_name", "min_version", "recommended_version", "routing_model", "engine_store"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"package_name": { "type": "string", "minLength": 1 },
|
|
31
|
+
"min_version": { "type": "string", "minLength": 1 },
|
|
32
|
+
"recommended_version": { "type": "string", "minLength": 1 },
|
|
33
|
+
"routing_model": { "type": "string", "minLength": 1 },
|
|
34
|
+
"engine_store": { "type": "string", "minLength": 1 },
|
|
35
|
+
"notes": { "type": "string" }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"engine": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"required": ["package_name", "version_range", "recommended_version", "install_hint", "old_engine_install_hint", "first_engine_installable_version", "legacy_direct_cli_before", "manifest_contract", "status"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"package_name": { "type": "string", "minLength": 1 },
|
|
44
|
+
"version_range": { "type": "string", "minLength": 1 },
|
|
45
|
+
"recommended_version": { "type": "string", "minLength": 1 },
|
|
46
|
+
"install_hint": { "type": "string", "minLength": 1 },
|
|
47
|
+
"old_engine_install_hint": { "type": "string", "minLength": 1 },
|
|
48
|
+
"first_engine_installable_version": { "type": "string", "minLength": 1 },
|
|
49
|
+
"legacy_direct_cli_before": { "type": "string", "minLength": 1 },
|
|
50
|
+
"manifest_contract": { "type": "string", "minLength": 1 },
|
|
51
|
+
"status": { "type": "string", "minLength": 1 }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"contracts": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"required": ["storage_contract_set", "project_summary_contract", "project_dashboard_contract", "global_dashboard_contract", "status_contract", "version_contract"],
|
|
58
|
+
"properties": {
|
|
59
|
+
"storage_contract_set": { "type": "string", "minLength": 1 },
|
|
60
|
+
"project_summary_contract": { "type": "string", "minLength": 1 },
|
|
61
|
+
"project_dashboard_contract": { "type": "string", "minLength": 1 },
|
|
62
|
+
"global_dashboard_contract": { "type": "string", "minLength": 1 },
|
|
63
|
+
"status_contract": { "type": "string", "minLength": 1 },
|
|
64
|
+
"version_contract": { "type": "string", "minLength": 1 }
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"global_dashboard": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"required": ["supported_project_summary_contracts", "unsupported_project_visibility", "summary_version_groups"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"supported_project_summary_contracts": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"minItems": 1,
|
|
75
|
+
"items": { "type": "string", "minLength": 1 }
|
|
76
|
+
},
|
|
77
|
+
"unsupported_project_visibility": { "type": "boolean" },
|
|
78
|
+
"summary_version_groups": { "type": "boolean" }
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"migrations": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"required": ["mode", "from_previous", "to_this", "adjacent_only", "normal_commands_may_migrate"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"mode": { "const": "mb-upgrade-only" },
|
|
87
|
+
"from_previous": { "type": ["string", "null"] },
|
|
88
|
+
"to_this": { "type": "string", "minLength": 1 },
|
|
89
|
+
"adjacent_only": { "type": "boolean" },
|
|
90
|
+
"normal_commands_may_migrate": { "const": false }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"mb_lint": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"additionalProperties": false,
|
|
96
|
+
"required": ["package_name", "min_version", "recommended_version", "command_contract"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"package_name": { "type": "string", "minLength": 1 },
|
|
99
|
+
"min_version": { "type": "string", "minLength": 1 },
|
|
100
|
+
"recommended_version": { "type": "string", "minLength": 1 },
|
|
101
|
+
"command_contract": { "type": "string", "minLength": 1 }
|
|
102
|
+
}
|
|
24
103
|
}
|
|
25
104
|
}
|
|
26
105
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "dd-flow/engine-manifest@1",
|
|
4
|
+
"title": "dd-flow engine manifest",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_id",
|
|
9
|
+
"package_name",
|
|
10
|
+
"package_version",
|
|
11
|
+
"engine_id",
|
|
12
|
+
"engine_version",
|
|
13
|
+
"installed_at",
|
|
14
|
+
"install_source",
|
|
15
|
+
"package_root",
|
|
16
|
+
"snapshot_root",
|
|
17
|
+
"entrypoint",
|
|
18
|
+
"supports_memorybank",
|
|
19
|
+
"supports_contracts",
|
|
20
|
+
"integrity"
|
|
21
|
+
],
|
|
22
|
+
"properties": {
|
|
23
|
+
"schema_id": { "const": "dd-flow/engine-manifest@1" },
|
|
24
|
+
"package_name": { "type": "string", "minLength": 1 },
|
|
25
|
+
"package_version": { "type": "string", "minLength": 1 },
|
|
26
|
+
"engine_id": { "const": "dd-flow-engine" },
|
|
27
|
+
"engine_version": { "type": "string", "minLength": 1 },
|
|
28
|
+
"installed_at": { "type": "string", "format": "date-time" },
|
|
29
|
+
"install_source": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": ["bundled_package", "npx_package", "local_development"]
|
|
32
|
+
},
|
|
33
|
+
"package_root": { "type": "string", "minLength": 1 },
|
|
34
|
+
"snapshot_root": { "type": "string", "minLength": 1 },
|
|
35
|
+
"entrypoint": { "type": "string", "minLength": 1 },
|
|
36
|
+
"supports_memorybank": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": { "type": "string", "minLength": 1 }
|
|
39
|
+
},
|
|
40
|
+
"supports_contracts": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"additionalProperties": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": { "type": "string", "minLength": 1 }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"integrity": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"required": ["source", "checksum", "mode"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"source": { "const": "package_snapshot" },
|
|
53
|
+
"checksum": { "type": ["string", "null"] },
|
|
54
|
+
"mode": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"enum": ["file_list", "full_content", "package_manifest", "degraded_local_development"]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -18,6 +18,23 @@
|
|
|
18
18
|
"type": "string",
|
|
19
19
|
"minLength": 1
|
|
20
20
|
},
|
|
21
|
+
"lifecycle": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"additionalProperties": true,
|
|
24
|
+
"required": ["flow", "stage", "status", "terminal", "raw_stage", "raw_status"],
|
|
25
|
+
"properties": {
|
|
26
|
+
"flow": { "type": "string", "minLength": 1 },
|
|
27
|
+
"stage": { "type": "string", "minLength": 1 },
|
|
28
|
+
"substage": { "type": ["string", "null"] },
|
|
29
|
+
"status": { "type": "string", "minLength": 1 },
|
|
30
|
+
"terminal": { "type": "boolean" },
|
|
31
|
+
"legacy_stage": { "type": ["string", "null"] },
|
|
32
|
+
"legacy_status": { "type": ["string", "null"] },
|
|
33
|
+
"raw_stage": { "type": "string", "minLength": 1 },
|
|
34
|
+
"raw_status": { "type": "string", "minLength": 1 },
|
|
35
|
+
"queue_status": { "type": ["string", "null"] }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
21
38
|
"allowed_next_stages": {
|
|
22
39
|
"type": "array",
|
|
23
40
|
"items": {
|