@deksden-com/dd-flow-cli 0.9.0-beta.141 → 0.9.0-beta.142
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 +7 -0
- package/dist/build-info.json +3 -3
- package/dist/cli/command-inputs.js +1 -1
- package/dist/cli/command-option-descriptions.js +1 -0
- package/dist/cli/help.js +1 -0
- package/dist/cli/run-cli.js +9 -3
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +3 -1
- package/dist/services/code-checks.js +42 -18
- package/dist/services/runtime-budget.js +16 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @deksden-com/dd-flow-cli
|
|
2
2
|
|
|
3
|
+
## 0.9.0-beta.142
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Wait for the owned OpenCode v2 listener through typed SDK transport startup failures and expose bounded read-only scope inventory batches for validated retirement-proof reuse.
|
|
8
|
+
- Retry no-effect physical confirmation on the same held check, bootstrap or service process before dispatch, without replaying commands or extending exhausted authority.
|
|
9
|
+
|
|
3
10
|
## 0.9.0-beta.141
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
package/dist/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cli_package": "@deksden-com/dd-flow-cli",
|
|
3
|
-
"cli_version": "0.9.0-beta.
|
|
4
|
-
"cli_commit": "
|
|
5
|
-
"built_at": "2026-10-
|
|
3
|
+
"cli_version": "0.9.0-beta.142",
|
|
4
|
+
"cli_commit": "a5b068e5cc52809ac0ea0687e40a49f9379bb198",
|
|
5
|
+
"built_at": "2026-10-18T10:20:25.955Z",
|
|
6
6
|
"built_with_canon": {
|
|
7
7
|
"version": "4.1.2",
|
|
8
8
|
"commit": "1ddedf9d3f086e046f8e7b194c739ec386e7c8b9",
|
|
@@ -156,7 +156,7 @@ export const commandInputs = {
|
|
|
156
156
|
"run control pause": route(0, `${control} request-id`, "grace-ms wait-ms escalate", "force"),
|
|
157
157
|
"run control stop": route(0, `${control} request-id`, "grace-ms wait-ms escalate", "force"),
|
|
158
158
|
"runtime scope fence": route(0, "scope-id request-id"),
|
|
159
|
-
"runtime scope status": route(0, "scope-id"),
|
|
159
|
+
"runtime scope status": route(0, "", "scope-id scope-ids-json"),
|
|
160
160
|
"runtime scope stop": route(0, "scope-id request-id"),
|
|
161
161
|
"runtime scope control": route(0, "scope-id request-id mode", "manifest-json", "", "", { mode: ["pause", "stop"] }),
|
|
162
162
|
"runtime scope serve": route(0, "scope-id generation"),
|
|
@@ -143,6 +143,7 @@ export const runtimeOptionDescriptions = {
|
|
|
143
143
|
schema: "Registered JSON schema name against which the supplied file is validated.",
|
|
144
144
|
"schema-dir": "Explicit schema directory for validation; not a workflow handler registry or permission to execute code.",
|
|
145
145
|
"scope-id": "Exact retained runtime/EVAL scope identity; operations must not affect neighboring scopes.",
|
|
146
|
+
"scope-ids-json": "Read-only JSON array of 1..16 unique retained scope IDs; returns ordered fresh inventories without fencing or stopping them.",
|
|
146
147
|
"semantic-file": "Stage semantic result artifact; applicable schema and finish checks are selected independently.",
|
|
147
148
|
session: "Physical Session selector for source-backed usage aggregation.",
|
|
148
149
|
"session-id": "Exact retained physical Session identity for registration, status, stop or usage synchronization.",
|
package/dist/cli/help.js
CHANGED
|
@@ -169,6 +169,7 @@ never registers a project, selects a model automatically or creates a runtime DB
|
|
|
169
169
|
["runtime scope", `Usage:
|
|
170
170
|
dd-flow runtime scope fence --scope-id EVAL_ID --request-id ID --json
|
|
171
171
|
dd-flow runtime scope status --scope-id EVAL_ID --json
|
|
172
|
+
dd-flow runtime scope status --scope-ids-json '["EVAL_ID","QUAL_ID"]' --json
|
|
172
173
|
dd-flow runtime scope stop --scope-id EVAL_ID --request-id ID --json
|
|
173
174
|
dd-flow runtime scope control --scope-id EVAL_ID --request-id ID --mode pause|stop [--manifest-json JSON] --json
|
|
174
175
|
dd-flow runtime scope resume --scope-id EVAL_ID --request-id ID --generation N --capture-key SHA256 --json
|
package/dist/cli/run-cli.js
CHANGED
|
@@ -92,7 +92,7 @@ import { prepareCommandRoute } from "./command-inputs.js";
|
|
|
92
92
|
import { parseLifecycleCommand } from "../services/lifecycle-command.js";
|
|
93
93
|
import { dispatchRuntimeProcess, prepareRuntimeProcess } from "./runtime-process.js";
|
|
94
94
|
import { reconcileFlowRunHealth } from "../services/run-health.js";
|
|
95
|
-
import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
|
|
95
|
+
import { fenceRuntimeBudget, runtimeBudgetStatus, prepareRuntimeScopeStatus, prepareRuntimeScopeControl, prepareRuntimeScopeFence } from "../services/runtime-budget.js";
|
|
96
96
|
import { stopRuntimeScope } from "../services/runtime-scope-stop.js";
|
|
97
97
|
import { launchRuntimeScopeControl, launchRuntimeScopeResume, prepareRuntimeScopeReconciliation, prepareRuntimeScopeServing, reconcileRuntimeScopeControl, serveRuntimeScopeControl } from "../services/runtime-scope-worker.js";
|
|
98
98
|
import { prepareRuntimeScopeResumeRequest } from "../services/runtime-scope-resume.js";
|
|
@@ -1017,6 +1017,8 @@ async function prepareCliInputValue(args, context, io, scopeProjectRoot, respons
|
|
|
1017
1017
|
return prepareError(error, { parameter: "from/output" });
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
1020
|
+
if (route.key === "runtime scope status")
|
|
1021
|
+
prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
|
|
1020
1022
|
if (["runtime scope fence", "runtime scope stop"].includes(route.key))
|
|
1021
1023
|
prepareRuntimeScopeFence(requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id"));
|
|
1022
1024
|
if (route.key === "runtime scope resume")
|
|
@@ -2778,8 +2780,12 @@ async function dispatchRuntime(context, command, parsed, progress, prepared = {}
|
|
|
2778
2780
|
return { ok: true, ...await stopRuntimeScope(context, requiredOption(parsed, "scope-id"), requiredOption(parsed, "request-id")) };
|
|
2779
2781
|
}
|
|
2780
2782
|
if (parsed.positional.length === 1 && parsed.positional[0] === "status") {
|
|
2781
|
-
if ([...parsed.options.keys()].some(key =>
|
|
2782
|
-
throw new AppError("usage", "
|
|
2783
|
+
if ([...parsed.options.keys()].some(key => !["scope-id", "scope-ids-json"].includes(key)) || parsed.options.size !== 1 || [...parsed.options.values()].some(values => values.length !== 1))
|
|
2784
|
+
throw new AppError("usage", "Use exactly one of --scope-id or --scope-ids-json", 2);
|
|
2785
|
+
const ids = prepareRuntimeScopeStatus(optionalOption(parsed, "scope-id"), optionalOption(parsed, "scope-ids-json"));
|
|
2786
|
+
if (parsed.options.has("scope-ids-json")) {
|
|
2787
|
+
return { ok: true, scopes: ids.map(id => runtimeBudgetStatus(context, id)) };
|
|
2788
|
+
}
|
|
2783
2789
|
return { ok: true, ...runtimeBudgetStatus(context, requiredOption(parsed, "scope-id")) };
|
|
2784
2790
|
}
|
|
2785
2791
|
if (parsed.positional.length !== 1 || parsed.positional[0] !== "fence" || [...parsed.options.keys()].some(key => !["scope-id", "request-id"].includes(key)) || [...parsed.options.values()].some(values => values.length !== 1))
|
|
@@ -499,7 +499,9 @@ export async function awaitOpenCodeStartup(client, state, child, { timeoutMs = S
|
|
|
499
499
|
if (["loading", "activating", "registered"].includes(plugin?.state?.status)) await mark(`required_plugin_${plugin.state.status}`);
|
|
500
500
|
} catch (error) {
|
|
501
501
|
const transient = error.code === "opencode_unavailable" && error.retryable === true
|
|
502
|
-
|
|
502
|
+
// Safe pre-identity reads use the normalized observation category,
|
|
503
|
+
// not a version-specific SDK error wrapper. No productive RPC here.
|
|
504
|
+
&& (phase === "server" || error.details?.native_type === "ServiceUnavailableError");
|
|
503
505
|
if (!transient) throw error;
|
|
504
506
|
}
|
|
505
507
|
const pause = Math.min([250, 500, 1000, 2000][Math.min(attempt++, 3)], remaining());
|
|
@@ -1245,6 +1245,7 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
|
|
|
1245
1245
|
let failure;
|
|
1246
1246
|
let stopping = false;
|
|
1247
1247
|
let closed = false;
|
|
1248
|
+
let admitted = false;
|
|
1248
1249
|
let progress;
|
|
1249
1250
|
let cleanup;
|
|
1250
1251
|
let cleanupUnconfirmed = false;
|
|
@@ -1280,7 +1281,7 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
|
|
|
1280
1281
|
});
|
|
1281
1282
|
};
|
|
1282
1283
|
const observationTimer = setInterval(() => {
|
|
1283
|
-
if (stopping || closed || observation.phase?.() === "serving")
|
|
1284
|
+
if (stopping || closed || !admitted || observation.phase?.() === "serving")
|
|
1284
1285
|
return;
|
|
1285
1286
|
if (runnerClock && runnerProgress) {
|
|
1286
1287
|
try {
|
|
@@ -1331,22 +1332,6 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
|
|
|
1331
1332
|
error = "check process did not provide a PID";
|
|
1332
1333
|
return;
|
|
1333
1334
|
}
|
|
1334
|
-
try {
|
|
1335
|
-
onSpawn(child.pid);
|
|
1336
|
-
}
|
|
1337
|
-
catch (cause) {
|
|
1338
|
-
stop(cause);
|
|
1339
|
-
return;
|
|
1340
|
-
}
|
|
1341
|
-
if (observation.leaseExpiresAt) {
|
|
1342
|
-
const expiry = observation.leaseExpiresAt();
|
|
1343
|
-
if (!expiry || !renewalConfirmed(renewal, performance.now(), expiry)) {
|
|
1344
|
-
stop("check confirmation has no current authority");
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
child.stdin.end("admitted\n");
|
|
1349
|
-
clock.sample(outputCursor);
|
|
1350
1335
|
const renew = () => {
|
|
1351
1336
|
if (stopping || closed)
|
|
1352
1337
|
return;
|
|
@@ -1394,7 +1379,46 @@ export function runCheck(command, cwd, environment, stdout, stderr, heartbeat, o
|
|
|
1394
1379
|
stop(cause);
|
|
1395
1380
|
}
|
|
1396
1381
|
};
|
|
1397
|
-
|
|
1382
|
+
// The shell is held on stdin: a no-effect identity read may be retried,
|
|
1383
|
+
// but never the command or an admission callback with unknown effects.
|
|
1384
|
+
const confirmation = createRenewalState();
|
|
1385
|
+
confirmation.uncertainSince = performance.now();
|
|
1386
|
+
let confirmationError;
|
|
1387
|
+
const admit = () => {
|
|
1388
|
+
renewalTimer = undefined;
|
|
1389
|
+
if (stopping || closed)
|
|
1390
|
+
return;
|
|
1391
|
+
if (confirmationError && !renewalRemaining(confirmation)) {
|
|
1392
|
+
stop(confirmationError);
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
try {
|
|
1396
|
+
onSpawn(child.pid);
|
|
1397
|
+
}
|
|
1398
|
+
catch (cause) {
|
|
1399
|
+
if (cause instanceof AppError && cause.code === "process_ownership_unknown"
|
|
1400
|
+
&& cause.details.reason === "physical_identity_unavailable" && maintenanceRetryable(cause)
|
|
1401
|
+
&& renewalFailure(confirmation)) {
|
|
1402
|
+
confirmationError = cause;
|
|
1403
|
+
renewalTimer = setTimeout(admit, Math.min(renewalDelay(confirmation), renewalRemaining(confirmation)));
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
stop(cause);
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
if (observation.leaseExpiresAt) {
|
|
1410
|
+
const expiry = observation.leaseExpiresAt();
|
|
1411
|
+
if (!expiry || !renewalConfirmed(renewal, performance.now(), expiry)) {
|
|
1412
|
+
stop("check confirmation has no current authority");
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
admitted = true;
|
|
1417
|
+
child.stdin.end("admitted\n");
|
|
1418
|
+
clock.sample(++outputCursor);
|
|
1419
|
+
progress = setInterval(renew, 15_000);
|
|
1420
|
+
};
|
|
1421
|
+
admit();
|
|
1398
1422
|
});
|
|
1399
1423
|
}
|
|
1400
1424
|
function checkShellScript() {
|
|
@@ -199,6 +199,22 @@ export function admitRuntimeCheck(context, id, leaseToken) {
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
/** Scope selection uses retained registration, never a current profile or PID search. */
|
|
202
|
+
export function prepareRuntimeScopeStatus(scopeId, scopeIdsJson) {
|
|
203
|
+
if (Boolean(scopeId) === Boolean(scopeIdsJson))
|
|
204
|
+
throw new AppError("usage", "Use exactly one of --scope-id or --scope-ids-json", 2);
|
|
205
|
+
let ids;
|
|
206
|
+
try {
|
|
207
|
+
ids = scopeId ? [scopeId] : JSON.parse(scopeIdsJson);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
throw new AppError("usage", "Scope inventory requires JSON IDs", 2);
|
|
211
|
+
}
|
|
212
|
+
if (!Array.isArray(ids) || !ids.length || ids.length > 16 || ids.some(id => typeof id !== "string") || new Set(ids).size !== ids.length)
|
|
213
|
+
throw new AppError("usage", "Scope inventory requires 1..16 unique scope IDs", 2);
|
|
214
|
+
for (const id of ids)
|
|
215
|
+
parseRuntimeBudget(JSON.stringify({ schema_id: "dd-flow/runtime-budget@1", scope_id: id, per_harness: {} }));
|
|
216
|
+
return ids;
|
|
217
|
+
}
|
|
202
218
|
export function runtimeBudgetStatus(context, scopeId) {
|
|
203
219
|
parseRuntimeBudget(JSON.stringify({ schema_id: "dd-flow/runtime-budget@1", scope_id: scopeId, per_harness: {} }));
|
|
204
220
|
return withResourceReadSnapshot(resourceHome(context), db => {
|