cool-workflow 0.1.97 → 0.2.0
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +11 -2
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/cli/dispatch.js +236 -0
- package/dist/cli/entry.js +120 -0
- package/dist/cli/io.js +21 -4
- package/dist/cli/parseargv.js +157 -0
- package/dist/cli.js +6 -33
- package/dist/core/capability-table.js +3534 -0
- package/dist/core/format/help.js +314 -0
- package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
- package/dist/core/hash.js +137 -0
- package/dist/core/multi-agent/candidate-scoring.js +219 -0
- package/dist/core/multi-agent/collaboration.js +481 -0
- package/dist/core/multi-agent/coordinator.js +515 -0
- package/dist/core/multi-agent/eval-replay.js +306 -0
- package/dist/core/multi-agent/runtime.js +929 -0
- package/dist/core/multi-agent/topology.js +298 -0
- package/dist/core/multi-agent/trust-policy.js +197 -0
- package/dist/core/pipeline/commit-gate.js +320 -0
- package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
- package/dist/core/pipeline/dispatch.js +103 -0
- package/dist/core/pipeline/drive-decide.js +227 -0
- package/dist/core/pipeline/error-feedback.js +161 -0
- package/dist/core/pipeline/loop-expansion.js +124 -0
- package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
- package/dist/core/pipeline/runner.js +230 -0
- package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
- package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
- package/dist/core/state/node-projection.js +95 -0
- package/dist/core/state/node-snapshot.js +230 -0
- package/dist/core/state/run-paths.js +93 -0
- package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
- package/dist/core/state/schema.js +50 -0
- package/dist/core/state/state-explosion/digest.js +243 -0
- package/dist/core/state/state-explosion/graph.js +527 -0
- package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
- package/dist/core/state/state-explosion/report.js +187 -0
- package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
- package/dist/{state-node.js → core/state/state-node.js} +90 -113
- package/dist/core/state/types.js +19 -0
- package/dist/{validation.js → core/state/validation.js} +64 -131
- package/dist/core/trust/evidence-grounding.js +134 -0
- package/dist/core/trust/ledger.js +199 -0
- package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
- package/dist/core/trust/telemetry-ledger.js +127 -0
- package/dist/core/types.js +20 -0
- package/dist/core/version.js +16 -0
- package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
- package/dist/mcp/dispatch.js +104 -0
- package/dist/mcp/server.js +144 -0
- package/dist/mcp-server.js +6 -84
- package/dist/{agent-config.js → shell/agent-config.js} +118 -71
- package/dist/shell/app-run-cli.js +88 -0
- package/dist/shell/audit-cli.js +259 -0
- package/dist/shell/audit-provenance.js +83 -0
- package/dist/shell/candidate-scoring-io.js +459 -0
- package/dist/shell/collaboration-io.js +264 -0
- package/dist/shell/commit-summary.js +104 -0
- package/dist/shell/commit.js +290 -0
- package/dist/shell/coordinator-io.js +476 -0
- package/dist/shell/demo-cli.js +19 -0
- package/dist/shell/dispatch.js +162 -0
- package/dist/{doctor.js → shell/doctor.js} +123 -56
- package/dist/shell/drive.js +873 -0
- package/dist/shell/error-feedback-io.js +305 -0
- package/dist/shell/eval-io.js +473 -0
- package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
- package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
- package/dist/shell/exec-backend-cli.js +88 -0
- package/dist/shell/execution-backend/agent.js +475 -0
- package/dist/shell/execution-backend/ci.js +15 -0
- package/dist/shell/execution-backend/container.js +69 -0
- package/dist/shell/execution-backend/envelopes.js +55 -0
- package/dist/shell/execution-backend/local.js +113 -0
- package/dist/shell/execution-backend/probes.js +175 -0
- package/dist/shell/execution-backend/registry.js +402 -0
- package/dist/shell/execution-backend/remote.js +128 -0
- package/dist/shell/execution-backend/types.js +11 -0
- package/dist/shell/feedback-cli.js +81 -0
- package/dist/shell/feedback-operations.js +48 -0
- package/dist/shell/fs-atomic.js +276 -0
- package/dist/shell/harness.js +98 -0
- package/dist/shell/ledger-cli.js +212 -0
- package/dist/shell/ledger-io.js +169 -0
- package/dist/shell/man-cli.js +89 -0
- package/dist/shell/metrics-cli.js +98 -0
- package/dist/shell/multi-agent-cli.js +1002 -0
- package/dist/shell/multi-agent-host.js +563 -0
- package/dist/shell/multi-agent-io.js +387 -0
- package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
- package/dist/shell/node-store.js +124 -0
- package/dist/{observability/format.js → shell/observability-format.js} +7 -1
- package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
- package/dist/{observability.js → shell/observability.js} +159 -332
- package/dist/{onramp.js → shell/onramp.js} +11 -0
- package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
- package/dist/shell/operator-ux.js +431 -0
- package/dist/shell/orchestrator.js +231 -0
- package/dist/shell/pipeline-cli.js +667 -0
- package/dist/shell/pipeline.js +217 -0
- package/dist/shell/reclamation-io.js +1366 -0
- package/dist/shell/registry-cli.js +329 -0
- package/dist/{remote-source.js → shell/remote-source.js} +12 -5
- package/dist/shell/report-cli.js +101 -0
- package/dist/shell/report-view-cli.js +117 -0
- package/dist/{orchestrator → shell}/report.js +289 -282
- package/dist/shell/reporter.js +62 -0
- package/dist/shell/run-export-cli.js +106 -0
- package/dist/shell/run-export.js +680 -0
- package/dist/shell/run-registry-io.js +1014 -0
- package/dist/shell/run-store.js +164 -0
- package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
- package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
- package/dist/shell/scheduling-io.js +311 -0
- package/dist/shell/state-cli.js +181 -0
- package/dist/shell/state-explosion-cli.js +197 -0
- package/dist/shell/telemetry-cli.js +85 -0
- package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
- package/dist/shell/telemetry-ledger-io.js +132 -0
- package/dist/{term.js → shell/term.js} +36 -46
- package/dist/shell/topology-io.js +361 -0
- package/dist/shell/trust-audit.js +471 -0
- package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
- package/dist/shell/verifier.js +48 -0
- package/dist/shell/workbench-host.js +250 -0
- package/dist/shell/workbench-text.js +18 -0
- package/dist/shell/workbench.js +175 -0
- package/dist/shell/worker-cli.js +124 -0
- package/dist/shell/worker-isolation.js +852 -0
- package/dist/shell/workflow-app-loader.js +650 -0
- package/docs/agent-delegation-drive.7.md +4 -0
- package/docs/cli-mcp-parity.7.md +284 -211
- package/docs/contract-migration-tooling.7.md +4 -0
- package/docs/control-plane-scheduling.7.md +4 -0
- package/docs/cross-agent-ledger.7.md +217 -0
- package/docs/designs/handoff-ledger.md +145 -0
- package/docs/durable-state-and-locking.7.md +4 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
- package/docs/execution-backends.7.md +4 -0
- package/docs/handoff-setup.md +120 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
- package/docs/multi-agent-eval-replay-harness.7.md +4 -0
- package/docs/multi-agent-operator-ux.7.md +4 -0
- package/docs/node-snapshot-diff-replay.7.md +4 -0
- package/docs/observability-cost-accounting.7.md +4 -0
- package/docs/project-index.md +143 -71
- package/docs/real-execution-backends.7.md +4 -0
- package/docs/release-and-migration.7.md +4 -0
- package/docs/release-tooling.7.md +4 -0
- package/docs/run-registry-control-plane.7.md +4 -0
- package/docs/run-retention-reclamation.7.md +25 -0
- package/docs/state-explosion-management.7.md +4 -0
- package/docs/team-collaboration.7.md +4 -0
- package/docs/web-desktop-workbench.7.md +4 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +9 -13
- package/package.json +1 -1
- package/scripts/agents/codex-agent.js +34 -4
- package/scripts/agents/cw-attest-wrap.js +2 -2
- package/scripts/bump-version.js +4 -3
- package/scripts/canonical-apps.js +4 -4
- package/scripts/children/batch-delegate-child.js +30 -10
- package/scripts/dogfood-architecture-review.js +2 -3
- package/scripts/dogfood-release.js +3 -3
- package/scripts/gen-parity-doc.js +15 -2
- package/scripts/golden-path.js +4 -4
- package/scripts/onramp-check.js +1 -1
- package/scripts/parity-check.js +38 -21
- package/scripts/release-flow.js +9 -3
- package/scripts/sync-project-index.js +51 -27
- package/scripts/validate-run-state-schema.js +2 -2
- package/scripts/version-sync-check.js +30 -30
- package/dist/candidate-scoring.js +0 -729
- package/dist/capability-core.js +0 -1186
- package/dist/capability-registry.js +0 -879
- package/dist/cli/command-surface.js +0 -490
- package/dist/cli/format.js +0 -56
- package/dist/cli/handlers/audit.js +0 -82
- package/dist/cli/handlers/blackboard.js +0 -81
- package/dist/cli/handlers/candidate.js +0 -40
- package/dist/cli/handlers/clones.js +0 -34
- package/dist/cli/handlers/collaboration.js +0 -61
- package/dist/cli/handlers/eval.js +0 -40
- package/dist/cli/handlers/maintenance.js +0 -107
- package/dist/cli/handlers/multi-agent.js +0 -165
- package/dist/cli/handlers/node.js +0 -41
- package/dist/cli/handlers/operational.js +0 -155
- package/dist/cli/handlers/operator.js +0 -146
- package/dist/cli/handlers/registry.js +0 -68
- package/dist/cli/handlers/run.js +0 -153
- package/dist/cli/handlers/scheduling.js +0 -132
- package/dist/cli/handlers/workbench.js +0 -41
- package/dist/cli/handlers/worker.js +0 -45
- package/dist/cli/run-summary.js +0 -45
- package/dist/clones.js +0 -162
- package/dist/collaboration.js +0 -726
- package/dist/commit.js +0 -592
- package/dist/compare.js +0 -18
- package/dist/coordinator/classify.js +0 -45
- package/dist/coordinator/paths.js +0 -42
- package/dist/coordinator/util.js +0 -126
- package/dist/coordinator.js +0 -990
- package/dist/daemon.js +0 -44
- package/dist/dispatch.js +0 -250
- package/dist/drive.js +0 -827
- package/dist/error-feedback.js +0 -419
- package/dist/evidence-grounding.js +0 -184
- package/dist/execution-backend/agent.js +0 -294
- package/dist/execution-backend/probes.js +0 -112
- package/dist/execution-backend/util.js +0 -47
- package/dist/execution-backend.js +0 -961
- package/dist/gates.js +0 -48
- package/dist/harness.js +0 -61
- package/dist/loop-expansion.js +0 -60
- package/dist/mcp/tool-call.js +0 -434
- package/dist/mcp/tool-definitions.js +0 -1040
- package/dist/mcp-surface.js +0 -30
- package/dist/multi-agent/graph.js +0 -84
- package/dist/multi-agent/helpers.js +0 -141
- package/dist/multi-agent/ids.js +0 -20
- package/dist/multi-agent/paths.js +0 -22
- package/dist/multi-agent-eval/normalize.js +0 -51
- package/dist/multi-agent-eval.js +0 -678
- package/dist/multi-agent-host.js +0 -777
- package/dist/multi-agent.js +0 -984
- package/dist/node-projection.js +0 -59
- package/dist/node-snapshot.js +0 -260
- package/dist/operator-ux.js +0 -631
- package/dist/orchestrator/app-operations.js +0 -211
- package/dist/orchestrator/audit-operations.js +0 -182
- package/dist/orchestrator/candidate-operations.js +0 -117
- package/dist/orchestrator/cli-options.js +0 -294
- package/dist/orchestrator/collaboration-operations.js +0 -86
- package/dist/orchestrator/feedback-operations.js +0 -81
- package/dist/orchestrator/host-operations.js +0 -78
- package/dist/orchestrator/lifecycle-operations.js +0 -626
- package/dist/orchestrator/migration-operations.js +0 -44
- package/dist/orchestrator/multi-agent-operations.js +0 -362
- package/dist/orchestrator/topology-operations.js +0 -84
- package/dist/orchestrator.js +0 -917
- package/dist/pipeline-runner.js +0 -285
- package/dist/reclamation/hash.js +0 -72
- package/dist/reclamation.js +0 -812
- package/dist/reporter.js +0 -67
- package/dist/run-export.js +0 -784
- package/dist/run-registry/derive.js +0 -175
- package/dist/run-registry/format.js +0 -124
- package/dist/run-registry/gc.js +0 -251
- package/dist/run-registry/policy.js +0 -16
- package/dist/run-registry/queue.js +0 -115
- package/dist/run-registry.js +0 -850
- package/dist/run-state-schema.js +0 -68
- package/dist/scheduling.js +0 -184
- package/dist/state-explosion.js +0 -1014
- package/dist/state.js +0 -367
- package/dist/telemetry-ledger.js +0 -196
- package/dist/topology.js +0 -565
- package/dist/triggers.js +0 -184
- package/dist/trust-audit.js +0 -644
- package/dist/types/blackboard.js +0 -2
- package/dist/types/candidate.js +0 -2
- package/dist/types/collaboration.js +0 -2
- package/dist/types/core.js +0 -2
- package/dist/types/drive.js +0 -10
- package/dist/types/error-feedback.js +0 -2
- package/dist/types/evidence-reasoning.js +0 -2
- package/dist/types/execution-backend.js +0 -2
- package/dist/types/multi-agent.js +0 -2
- package/dist/types/observability.js +0 -2
- package/dist/types/pipeline.js +0 -2
- package/dist/types/reclamation.js +0 -8
- package/dist/types/report-bundle.js +0 -6
- package/dist/types/result.js +0 -2
- package/dist/types/run-registry.js +0 -2
- package/dist/types/run.js +0 -2
- package/dist/types/sandbox.js +0 -2
- package/dist/types/schedule.js +0 -2
- package/dist/types/state-node.js +0 -2
- package/dist/types/topology.js +0 -2
- package/dist/types/trust.js +0 -2
- package/dist/types/workbench.js +0 -2
- package/dist/types/worker.js +0 -2
- package/dist/types/workflow-app.js +0 -2
- package/dist/types.js +0 -44
- package/dist/util/fingerprint.js +0 -19
- package/dist/util/fingerprint.test.js +0 -27
- package/dist/verifier.js +0 -78
- package/dist/version.js +0 -8
- package/dist/workbench-host.js +0 -182
- package/dist/workbench.js +0 -192
- package/dist/worker-accept/acceptance.js +0 -114
- package/dist/worker-accept/blackboard-fanout.js +0 -80
- package/dist/worker-accept/blackboard-linkage.js +0 -19
- package/dist/worker-accept/context.js +0 -2
- package/dist/worker-accept/telemetry-ledger.js +0 -126
- package/dist/worker-accept/validation.js +0 -77
- package/dist/worker-accept/verifier-completion.js +0 -73
- package/dist/worker-isolation/helpers.js +0 -51
- package/dist/worker-isolation/paths.js +0 -46
- package/dist/worker-isolation.js +0 -656
- package/dist/workflow-api.js +0 -131
- /package/dist/{types → core/types}/boundary.js +0 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// shell/pipeline-cli.ts — CLI-facing entry points for the pipeline/drive
|
|
3
|
+
// spine: planRun, runDrivePreview, runDriveStep, quickstartRun,
|
|
4
|
+
// dispatchRun, recordResultRun, commitRun.
|
|
5
|
+
//
|
|
6
|
+
// MILESTONE 6+7 (combined). Wires the pieces built in shell/pipeline.ts,
|
|
7
|
+
// shell/drive.ts, shell/dispatch.ts, shell/commit.ts, shell/worker-
|
|
8
|
+
// isolation.ts, shell/workflow-app-loader.ts into the shapes
|
|
9
|
+
// core/capability-table.ts's CLI bindings call.
|
|
10
|
+
//
|
|
11
|
+
// Evidence: SPEC/pipeline-run.md "CLI / MCP surface (capability layer)".
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.planRun = planRun;
|
|
47
|
+
exports.runDrivePreview = runDrivePreview;
|
|
48
|
+
exports.runDriveStep = runDriveStep;
|
|
49
|
+
exports.quickstartRun = quickstartRun;
|
|
50
|
+
exports.dispatchRun = dispatchRun;
|
|
51
|
+
exports.recordResultRun = recordResultRun;
|
|
52
|
+
exports.commitRun = commitRun;
|
|
53
|
+
const fs = __importStar(require("node:fs"));
|
|
54
|
+
const path = __importStar(require("node:path"));
|
|
55
|
+
const pipeline_1 = require("./pipeline");
|
|
56
|
+
const workflow_app_loader_1 = require("./workflow-app-loader");
|
|
57
|
+
const drive_1 = require("./drive");
|
|
58
|
+
const dispatch_1 = require("./dispatch");
|
|
59
|
+
const commit_1 = require("./commit");
|
|
60
|
+
const worker_isolation_1 = require("./worker-isolation");
|
|
61
|
+
const observability_1 = require("./observability");
|
|
62
|
+
const run_store_1 = require("./run-store");
|
|
63
|
+
const report_1 = require("./report");
|
|
64
|
+
const agent_config_1 = require("./agent-config");
|
|
65
|
+
const remote_source_1 = require("./remote-source");
|
|
66
|
+
const trust_audit_1 = require("./trust-audit");
|
|
67
|
+
const report_cli_1 = require("./report-cli");
|
|
68
|
+
const QUICKSTART_DEFAULT_APP = "architecture-review";
|
|
69
|
+
/** True when `value` is a truthy CLI flag (present, or "true"/"1"/"yes"/"on"). */
|
|
70
|
+
function truthyFlag(value) {
|
|
71
|
+
if (value === true)
|
|
72
|
+
return true;
|
|
73
|
+
if (typeof value === "string")
|
|
74
|
+
return /^(1|true|yes|on)$/i.test(value.trim());
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/** First non-empty string among the given arg values (mirrors the old
|
|
78
|
+
* build's optionalString read over several alias keys). */
|
|
79
|
+
function firstString(...values) {
|
|
80
|
+
for (const value of values) {
|
|
81
|
+
if (typeof value === "string" && value.trim())
|
|
82
|
+
return value.trim();
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
/** Shell-quote a token for a copy-pasteable next command (byte-exact to the
|
|
87
|
+
* old build's shellWord). */
|
|
88
|
+
function shellWord(value) {
|
|
89
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
|
|
90
|
+
return value;
|
|
91
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
92
|
+
}
|
|
93
|
+
/** The copy-pasteable `cw quickstart …` line the `--check` payload echoes,
|
|
94
|
+
* weaving in --bundle / --with-trust-key / --strict-signatures so the
|
|
95
|
+
* suggested resume preserves bundle intent. Byte-exact port of the old
|
|
96
|
+
* build's quickstartNextCommand. */
|
|
97
|
+
function quickstartNextCommand(appId, repo, args) {
|
|
98
|
+
const parts = ["cw", "quickstart", shellWord(appId), "--repo", shellWord(repo)];
|
|
99
|
+
const question = firstString(args.question);
|
|
100
|
+
if (question)
|
|
101
|
+
parts.push("--question", shellWord(question));
|
|
102
|
+
const command = firstString(args.agentCommand, args["agent-command"]);
|
|
103
|
+
if (command)
|
|
104
|
+
parts.push("--agent-command", shellWord(command));
|
|
105
|
+
if (truthyFlag(args.bundle))
|
|
106
|
+
parts.push("--bundle");
|
|
107
|
+
const trustKey = firstString(args["with-trust-key"], args.withTrustKey, args.trustKey);
|
|
108
|
+
if (trustKey)
|
|
109
|
+
parts.push("--with-trust-key", shellWord(trustKey));
|
|
110
|
+
if (truthyFlag(args["strict-signatures"]) || truthyFlag(args.strictSignatures) || truthyFlag(args.strictSigs))
|
|
111
|
+
parts.push("--strict-signatures");
|
|
112
|
+
return parts.join(" ");
|
|
113
|
+
}
|
|
114
|
+
/** Runtime keys that must NEVER leak into run.inputs (they are drive/CLI
|
|
115
|
+
* plumbing, not workflow-declared inputs). Byte-exact to the old
|
|
116
|
+
* build's DRIVE_RUNTIME_KEYS list. */
|
|
117
|
+
const RUNTIME_KEYS = new Set([
|
|
118
|
+
"once", "now", "preview", "step", "drive", "json", "format", "run", "runId", "cwd",
|
|
119
|
+
"agentCommand", "agent-command", "agentArgs", "agent-args", "agentEndpoint", "agent-endpoint",
|
|
120
|
+
"agentModel", "agent-model", "agentTimeoutMs", "agent-timeout-ms", "resume", "incremental",
|
|
121
|
+
"concurrency", "link", "ref", "branch", "refresh", "check", "app", "appId", "workflowId", "question", "repo",
|
|
122
|
+
]);
|
|
123
|
+
/** Byte-exact port of the old build's `normalizeInputs`
|
|
124
|
+
* (src/orchestrator/lifecycle-operations.ts:465-480): repeated `--arg
|
|
125
|
+
* key=value` pairs unpack into inputs (key = text before the first "=",
|
|
126
|
+
* value = the rest re-joined with "="); `repo` copies to `cwd` when `cwd`
|
|
127
|
+
* is not already set. Per SPEC/orchestrator.md's "Plan input rules" and
|
|
128
|
+
* SPEC/workflow-apps.md. */
|
|
129
|
+
function planInputsFor(args) {
|
|
130
|
+
const out = {};
|
|
131
|
+
for (const [key, value] of Object.entries(args)) {
|
|
132
|
+
if (key === "arg") {
|
|
133
|
+
const pairs = Array.isArray(value) ? value : [value];
|
|
134
|
+
for (const pair of pairs) {
|
|
135
|
+
const [argKey, ...rest] = String(pair).split("=");
|
|
136
|
+
out[argKey] = rest.join("=");
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (RUNTIME_KEYS.has(key))
|
|
141
|
+
continue;
|
|
142
|
+
out[key] = value;
|
|
143
|
+
}
|
|
144
|
+
if (typeof args.repo === "string")
|
|
145
|
+
out.repo = args.repo;
|
|
146
|
+
if (typeof args.question === "string")
|
|
147
|
+
out.question = args.question;
|
|
148
|
+
// An explicit --cwd is stripped by RUNTIME_KEYS above, but the old build
|
|
149
|
+
// honored it for the run anchor. Re-add it (like repo) so a caller-supplied
|
|
150
|
+
// cwd is not silently dropped to process.cwd() — a cross-request bleed.
|
|
151
|
+
if (typeof args.cwd === "string" && args.cwd.trim())
|
|
152
|
+
out.cwd = args.cwd;
|
|
153
|
+
if (out.repo && !out.cwd)
|
|
154
|
+
out.cwd = out.repo;
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
function invocationCwd(args) {
|
|
158
|
+
return typeof args.cwd === "string" && args.cwd.trim() ? path.resolve(args.cwd) : process.cwd();
|
|
159
|
+
}
|
|
160
|
+
/** True for a value that counts as "not supplied" — byte-exact to the old
|
|
161
|
+
* build's cli-options.isMissing (undefined / null / empty string). */
|
|
162
|
+
function isMissingInput(value) {
|
|
163
|
+
return value === undefined || value === null || value === "";
|
|
164
|
+
}
|
|
165
|
+
/** Resolve a workflow app for `plan`/`run --drive` over the SAME surface `cw
|
|
166
|
+
* list` shows — bundled apps AND legacy `<name>.workflow.js` files. The old
|
|
167
|
+
* build's plan() resolved via loadWorkflowAppById (full discovery over both the
|
|
168
|
+
* workflows/ and apps/ roots — app-operations.ts:64), so any id `cw list`
|
|
169
|
+
* surfaces is plannable. v2's fast-path loadWorkflowApp only reads
|
|
170
|
+
* apps/<id>/app.json, so a legacy workflow-file wrapper (e.g.
|
|
171
|
+
* legacy-research-synthesis) that `list` CAN resolve died with "Workflow app
|
|
172
|
+
* not found". Fall back to the full-discovery record (loadWorkflowAppRecordById)
|
|
173
|
+
* and adapt it to the minimal LoadedWorkflowApp plan() consumes. */
|
|
174
|
+
function resolveWorkflowAppForPlan(appId) {
|
|
175
|
+
try {
|
|
176
|
+
return (0, workflow_app_loader_1.loadWorkflowApp)(appId);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (!(error instanceof workflow_app_loader_1.WorkflowAppNotFoundError))
|
|
180
|
+
throw error;
|
|
181
|
+
const record = (0, workflow_app_loader_1.loadWorkflowAppRecordById)(appId);
|
|
182
|
+
const author = typeof record.app.author === "string" ? record.app.author : record.app.author?.name;
|
|
183
|
+
return {
|
|
184
|
+
id: record.app.id,
|
|
185
|
+
title: record.app.title,
|
|
186
|
+
summary: record.app.summary || record.app.workflow.summary || "",
|
|
187
|
+
version: record.app.version,
|
|
188
|
+
...(author !== undefined ? { author } : {}),
|
|
189
|
+
workflow: record.app.workflow,
|
|
190
|
+
sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles || [],
|
|
191
|
+
sourcePath: record.source.manifestPath || record.source.path,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** `cw plan <workflowId>` — real: loads the app, plans a fresh run,
|
|
196
|
+
* returns the canonical plan summary. */
|
|
197
|
+
function planRun(args) {
|
|
198
|
+
const appId = String(args.workflowId || args.app || QUICKSTART_DEFAULT_APP);
|
|
199
|
+
// POLA: `cw plan <app>` does NOT default repo to the caller's cwd (unlike the
|
|
200
|
+
// one-command quickstart). The old build validated required inputs FIRST
|
|
201
|
+
// (lifecycle-operations.ts validateInputs, message `Missing required input
|
|
202
|
+
// --<name>`), so a missing --repo surfaces the copy-pasteable `-dir` recovery
|
|
203
|
+
// line (cli/entry.ts recoveryHint's "missing"+"repo" branch) instead of a
|
|
204
|
+
// silent cwd-anchored run. Auto-filling repo here would hide that recovery.
|
|
205
|
+
const app = resolveWorkflowAppForPlan(appId);
|
|
206
|
+
const planInputs = planInputsFor(args);
|
|
207
|
+
for (const declared of app.workflow.inputs || []) {
|
|
208
|
+
if (declared.required && isMissingInput(planInputs[declared.name])) {
|
|
209
|
+
throw new Error(`Missing required input --${declared.name}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const run = (0, pipeline_1.plan)(app, planInputs);
|
|
213
|
+
// `pendingTasks` is the canonical plan-payload key both `cw plan` and
|
|
214
|
+
// `cw_plan` carry (old build src/capability-core.ts:79 planSummary:
|
|
215
|
+
// `pendingTasks: run.tasks.filter(status === "pending").length`, and
|
|
216
|
+
// SPEC/workflow-apps.md). `taskCount` stays as a harmless extra.
|
|
217
|
+
const pendingTasks = run.tasks.filter((task) => task.status === "pending").length;
|
|
218
|
+
return { schemaVersion: 1, runId: run.id, workflowId: run.workflow.id, statePath: run.paths.state, reportPath: run.paths.report, pendingTasks, taskCount: run.tasks.length };
|
|
219
|
+
}
|
|
220
|
+
function runDrivePreview(args) {
|
|
221
|
+
const runId = String(args.runId || args.run || "");
|
|
222
|
+
const cwd = invocationCwd(args);
|
|
223
|
+
return (0, drive_1.drivePreview)(runId, cwd, args);
|
|
224
|
+
}
|
|
225
|
+
/** `cw run <app|--run id> --drive [--once]` — plans a fresh run (unless
|
|
226
|
+
* `--run` continues an existing one) and drives it. */
|
|
227
|
+
function runDriveStep(args) {
|
|
228
|
+
const existingRunId = String(args.runId || args.run || "");
|
|
229
|
+
const options = {
|
|
230
|
+
once: Boolean(args.once),
|
|
231
|
+
now: typeof args.now === "string" ? args.now : undefined,
|
|
232
|
+
args,
|
|
233
|
+
concurrency: args.concurrency !== undefined ? Number(args.concurrency) : undefined,
|
|
234
|
+
incremental: Boolean(args.incremental),
|
|
235
|
+
};
|
|
236
|
+
if (existingRunId) {
|
|
237
|
+
const cwd = invocationCwd(args);
|
|
238
|
+
const run = (0, run_store_1.loadRunFromCwd)(existingRunId, cwd);
|
|
239
|
+
return (0, drive_1.drive)(existingRunId, run.cwd, options);
|
|
240
|
+
}
|
|
241
|
+
const appId = String(args.appId || args.app || args.positionalApp || "");
|
|
242
|
+
if (!appId)
|
|
243
|
+
throw new Error("run --drive requires an app id (or --run <run-id> to continue)");
|
|
244
|
+
if (!args.repo && !args.cwd)
|
|
245
|
+
args.repo = invocationCwd(args);
|
|
246
|
+
const app = (0, workflow_app_loader_1.loadWorkflowApp)(appId);
|
|
247
|
+
const run = (0, pipeline_1.plan)(app, planInputsFor(args));
|
|
248
|
+
return (0, drive_1.drive)(run.id, run.cwd, options);
|
|
249
|
+
}
|
|
250
|
+
/** `cw quickstart [app] --check` — read-only preflight: does the app
|
|
251
|
+
* resolve, is the repo readable/writable, is a question set, is an
|
|
252
|
+
* agent backend configured. Never plans or writes a run. Byte-exact
|
|
253
|
+
* port of the old build's `quickstartCheck` (src/capability-core.ts),
|
|
254
|
+
* local-repo path only (the --link/remote preflight variant is not
|
|
255
|
+
* ported — no conformance case exercises it). */
|
|
256
|
+
function quickstartCheck(appId, args, remoteCandidate) {
|
|
257
|
+
// `--link`/URL preflight: validate the URL SHAPE + tooling WITHOUT fetching
|
|
258
|
+
// (a clone is heavy + side-effecting; --check stays read-only). Swaps the
|
|
259
|
+
// local-repo readability checks for link + tooling. `repo` carries the
|
|
260
|
+
// sanitized URL so the result reports what would be reviewed.
|
|
261
|
+
if (remoteCandidate)
|
|
262
|
+
return remoteQuickstartCheck(appId, args, remoteCandidate);
|
|
263
|
+
const base = invocationCwd(args);
|
|
264
|
+
const repoArg = typeof args.repo === "string" && args.repo.trim() ? args.repo : base;
|
|
265
|
+
const repo = path.resolve(base, repoArg);
|
|
266
|
+
const checks = [];
|
|
267
|
+
try {
|
|
268
|
+
(0, workflow_app_loader_1.showWorkflowApp)(appId);
|
|
269
|
+
checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
checks.push({
|
|
273
|
+
name: "app",
|
|
274
|
+
status: "blocked",
|
|
275
|
+
detail: `Workflow app ${appId} is not available.`,
|
|
276
|
+
fix: "Run `cw app list` and choose one of the listed app ids.",
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
let repoReadable = false;
|
|
280
|
+
let repoStateWritable = false;
|
|
281
|
+
try {
|
|
282
|
+
const stat = fs.statSync(repo);
|
|
283
|
+
repoReadable = stat.isDirectory();
|
|
284
|
+
if (!repoReadable)
|
|
285
|
+
throw new Error("not a directory");
|
|
286
|
+
fs.accessSync(repo, fs.constants.R_OK);
|
|
287
|
+
checks.push({ name: "repo", status: "ok", detail: `Repository path is readable (${repo}).` });
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
checks.push({
|
|
291
|
+
name: "repo",
|
|
292
|
+
status: "blocked",
|
|
293
|
+
detail: `Repository path is not readable (${repo}).`,
|
|
294
|
+
fix: "Pass --repo PATH for a readable repository directory.",
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const cwDir = path.join(repo, ".cw");
|
|
299
|
+
fs.accessSync(fs.existsSync(cwDir) ? cwDir : repo, fs.constants.W_OK);
|
|
300
|
+
repoStateWritable = repoReadable;
|
|
301
|
+
checks.push({ name: "repo-state", status: "ok", detail: "Run state location is writable." });
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
checks.push({
|
|
305
|
+
name: "repo-state",
|
|
306
|
+
status: "blocked",
|
|
307
|
+
detail: "Run state location is not writable.",
|
|
308
|
+
fix: "Use a writable repo, fix directory permissions, or pass --repo to a writable checkout.",
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
if (typeof args.question === "string" && args.question.trim()) {
|
|
312
|
+
checks.push({ name: "question", status: "ok", detail: "Question is set." });
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
checks.push({ name: "question", status: "blocked", detail: "Question is missing.", fix: "Pass --question TEXT." });
|
|
316
|
+
}
|
|
317
|
+
if ((0, agent_config_1.agentConfigured)(args)) {
|
|
318
|
+
checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
checks.push({
|
|
322
|
+
name: "agent",
|
|
323
|
+
status: "blocked",
|
|
324
|
+
detail: "No agent backend is configured.",
|
|
325
|
+
fix: 'Pass --agent-command "claude -p", set $CW_AGENT_COMMAND, or use --agent-command builtin:claude.',
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// --bundle preflight: a completed run sealed into a bundle re-verifies offline
|
|
329
|
+
// only with a public trust key. Warn (not block) by default; block only under
|
|
330
|
+
// --strict-signatures where an unkeyed bundle would fail verification.
|
|
331
|
+
if (truthyFlag(args.bundle)) {
|
|
332
|
+
const trustKey = firstString(args["with-trust-key"], args.withTrustKey, args.trustKey, args.pubkey) || process.env.CW_AGENT_ATTEST_PUBKEY;
|
|
333
|
+
if (trustKey) {
|
|
334
|
+
checks.push({ name: "bundle-trust-key", status: "ok", detail: "Bundle trust public key is configured." });
|
|
335
|
+
}
|
|
336
|
+
else if (truthyFlag(args["strict-signatures"]) || truthyFlag(args.strictSignatures) || truthyFlag(args.strictSigs)) {
|
|
337
|
+
checks.push({ name: "bundle-trust-key", status: "blocked", detail: "Strict signature verification needs a public trust key.", fix: "Pass --with-trust-key PATH or set $CW_AGENT_ATTEST_PUBKEY." });
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
checks.push({ name: "bundle-trust-key", status: "warn", detail: "No public trust key is configured; unsigned or unkeyed bundles may verify with reduced signature proof.", fix: "Pass --with-trust-key PATH to embed the public key." });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const ok = checks.every((check) => check.status !== "blocked") && repoStateWritable;
|
|
344
|
+
return { schemaVersion: 1, mode: "check", ok, appId, repo, checks, nextCommand: quickstartNextCommand(appId, repo, args) };
|
|
345
|
+
}
|
|
346
|
+
/** `--check` for a `--link`/URL review: validates the URL shape + git tooling
|
|
347
|
+
* WITHOUT fetching (byte-behavior port of the old build's remoteQuickstartCheck).
|
|
348
|
+
* `repo` carries the sanitized URL. */
|
|
349
|
+
function remoteQuickstartCheck(appId, args, candidate) {
|
|
350
|
+
const validation = (0, remote_source_1.validateRemoteUrl)(candidate);
|
|
351
|
+
const checks = [];
|
|
352
|
+
try {
|
|
353
|
+
(0, workflow_app_loader_1.showWorkflowApp)(appId);
|
|
354
|
+
checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
checks.push({ name: "app", status: "blocked", detail: `Workflow app ${appId} is not available.`, fix: "Run `cw app list` and choose one of the listed app ids." });
|
|
358
|
+
}
|
|
359
|
+
if (validation.ok) {
|
|
360
|
+
checks.push({ name: "link", status: "ok", detail: `Remote source is a valid ${validation.kind} URL (${validation.url}).` });
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
checks.push({ name: "link", status: "blocked", detail: `Remote source is not usable: ${validation.reason}.`, fix: "Pass a git URL (https/ssh/git/file or git@host:repo)." });
|
|
364
|
+
}
|
|
365
|
+
if ((0, remote_source_1.gitAvailable)()) {
|
|
366
|
+
checks.push({ name: "tooling", status: "ok", detail: "git is available to clone the remote." });
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
checks.push({ name: "tooling", status: "blocked", detail: "git was not found on PATH.", fix: "Install git, then re-run." });
|
|
370
|
+
}
|
|
371
|
+
if (typeof args.question === "string" && args.question.trim()) {
|
|
372
|
+
checks.push({ name: "question", status: "ok", detail: "Question is set." });
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
checks.push({ name: "question", status: "blocked", detail: "Question is missing.", fix: "Pass --question TEXT." });
|
|
376
|
+
}
|
|
377
|
+
if ((0, agent_config_1.agentConfigured)(args)) {
|
|
378
|
+
checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
checks.push({ name: "agent", status: "blocked", detail: "No agent backend is configured.", fix: 'Pass --agent-command "claude -p", set $CW_AGENT_COMMAND, or use --agent-command builtin:claude.' });
|
|
382
|
+
}
|
|
383
|
+
const ok = checks.every((check) => check.status !== "blocked");
|
|
384
|
+
const question = firstString(args.question);
|
|
385
|
+
const nextCommand = `cw quickstart ${shellWord(appId)} --link ${shellWord(validation.url)}${question ? ` --question ${shellWord(question)}` : ""}`;
|
|
386
|
+
return { schemaVersion: 1, mode: "check", ok, appId, repo: validation.url, checks, nextCommand };
|
|
387
|
+
}
|
|
388
|
+
/** `cw quickstart [app] --question ...` — composes plan -> runDrive ->
|
|
389
|
+
* report in one call. Default app is architecture-review. `--check` is a
|
|
390
|
+
* read-only preflight that never plans/drives/writes (see
|
|
391
|
+
* `quickstartCheck` above). `--preview` is a read-only next-step
|
|
392
|
+
* projection (never drives), `--resume` advances one step (no --run) or
|
|
393
|
+
* continues a named run to completion (--run <id>) — both ported byte-for-
|
|
394
|
+
* byte from the old build's src/capability-core.ts quickstart(). */
|
|
395
|
+
function quickstartRun(args) {
|
|
396
|
+
const appId = String(args.appId || args.app || args.workflowId || QUICKSTART_DEFAULT_APP);
|
|
397
|
+
// Remote source: a `--link <url>` — or a URL passed to `--repo`/`-dir` — is
|
|
398
|
+
// materialized to a LOCAL checkout HERE (capability/shell layer). Cloning is
|
|
399
|
+
// non-deterministic network I/O and must never enter the replay-deterministic
|
|
400
|
+
// core, so we rewrite `args.repo`/`args.cwd` to the local path; everything
|
|
401
|
+
// downstream is a normal local run.
|
|
402
|
+
const linkArg = typeof args.link === "string" && args.link.trim() ? args.link.trim() : undefined;
|
|
403
|
+
const repoArgRaw = typeof args.repo === "string" && args.repo.trim() ? args.repo.trim() : undefined;
|
|
404
|
+
const remoteCandidate = linkArg || (repoArgRaw && (0, remote_source_1.isRemoteUrl)(repoArgRaw) ? repoArgRaw : undefined);
|
|
405
|
+
if (!remoteCandidate && !args.repo && !args.cwd)
|
|
406
|
+
args.repo = invocationCwd(args);
|
|
407
|
+
if (Boolean(args.check))
|
|
408
|
+
return quickstartCheck(appId, args, remoteCandidate);
|
|
409
|
+
// Materialize the remote NOW — after `--check` (never fetches) and before any
|
|
410
|
+
// plan/drive — so the core only ever sees the local checkout. Fails closed: a
|
|
411
|
+
// bad URL / blocked scheme / missing git / fetch failure throws here.
|
|
412
|
+
let remoteSource;
|
|
413
|
+
if (remoteCandidate) {
|
|
414
|
+
remoteSource = (0, remote_source_1.materializeRemote)(remoteCandidate, {
|
|
415
|
+
ref: typeof args.ref === "string" ? args.ref : typeof args.branch === "string" ? args.branch : undefined,
|
|
416
|
+
refresh: Boolean(args.refresh),
|
|
417
|
+
});
|
|
418
|
+
args.repo = remoteSource.localPath;
|
|
419
|
+
args.cwd = remoteSource.localPath;
|
|
420
|
+
// Record the origin as plan INPUTS so it rides into run.inputs → the report
|
|
421
|
+
// header (report.ts renders `- Source: url@sha` from run.inputs.sourceUrl).
|
|
422
|
+
args.sourceUrl = remoteSource.url;
|
|
423
|
+
args.sourceCommit = remoteSource.commit;
|
|
424
|
+
if (remoteSource.ref)
|
|
425
|
+
args.sourceRef = remoteSource.ref;
|
|
426
|
+
}
|
|
427
|
+
// `--resume`: a discoverability flag over the existing continuation. With no
|
|
428
|
+
// `--run`, advance exactly ONE step (reuse the `--once` path) and print a
|
|
429
|
+
// copy-pasteable continue line; with `--run <id>`, continue that run to
|
|
430
|
+
// completion (the default drive). It adds no new execution path. Byte-exact to
|
|
431
|
+
// the old build's src/capability-core.ts quickstart().
|
|
432
|
+
const resume = Boolean(args.resume);
|
|
433
|
+
const existingRunId = String(args.runId || args.run || "");
|
|
434
|
+
const resumeRunId = resume && existingRunId ? existingRunId : undefined;
|
|
435
|
+
// `--preview`: read-only, deterministic next-step projection (no spawn, no
|
|
436
|
+
// commit). Plan a fresh run (the read-only first verb) then project the next
|
|
437
|
+
// drive step. Never drives.
|
|
438
|
+
if (Boolean(args.preview)) {
|
|
439
|
+
let previewRunId = existingRunId;
|
|
440
|
+
let repoCwd = typeof args.cwd === "string" && args.cwd.trim() ? args.cwd : typeof args.repo === "string" ? args.repo : undefined;
|
|
441
|
+
if (!previewRunId) {
|
|
442
|
+
const run = resolveWorkflowAppForPlan(appId);
|
|
443
|
+
const planned = (0, pipeline_1.plan)(run, planInputsFor(args));
|
|
444
|
+
previewRunId = planned.id;
|
|
445
|
+
repoCwd = planned.cwd;
|
|
446
|
+
}
|
|
447
|
+
const target = repoCwd && fs.existsSync(repoCwd) ? repoCwd : invocationCwd(args);
|
|
448
|
+
return (0, drive_1.drivePreview)(previewRunId, target, args);
|
|
449
|
+
}
|
|
450
|
+
const options = {
|
|
451
|
+
// `--resume` with no run id advances a SINGLE step (reuse `--once`), so a
|
|
452
|
+
// newcomer WITNESSES the stop-then-resume; `--resume --run <id>` continues to
|
|
453
|
+
// completion (the default drive). Non-resume paths keep the caller's --once.
|
|
454
|
+
once: Boolean(args.once) || (resume && !resumeRunId),
|
|
455
|
+
now: typeof args.now === "string" ? args.now : undefined,
|
|
456
|
+
args,
|
|
457
|
+
concurrency: args.concurrency !== undefined ? Number(args.concurrency) : undefined,
|
|
458
|
+
incremental: Boolean(args.incremental),
|
|
459
|
+
};
|
|
460
|
+
let run;
|
|
461
|
+
if (existingRunId) {
|
|
462
|
+
run = (0, run_store_1.loadRunFromCwd)(existingRunId, invocationCwd(args));
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
run = (0, pipeline_1.plan)(resolveWorkflowAppForPlan(appId), planInputsFor(args));
|
|
466
|
+
}
|
|
467
|
+
const result = (0, drive_1.drive)(run.id, run.cwd, options);
|
|
468
|
+
const finalRun = (0, run_store_1.loadRunFromCwd)(run.id, run.cwd);
|
|
469
|
+
(0, report_1.writeReport)(finalRun);
|
|
470
|
+
// Tamper-evident provenance: bind the remote origin (url@sha) into the run's
|
|
471
|
+
// hash-chained trust-audit log so `cw audit verify` re-proves where the code
|
|
472
|
+
// came from. Best-effort — the origin is already in run.inputs/report/result.
|
|
473
|
+
if (remoteSource) {
|
|
474
|
+
try {
|
|
475
|
+
(0, trust_audit_1.recordTrustAuditEvent)(finalRun, {
|
|
476
|
+
kind: remoteSource.kind === "archive" ? "source.download" : "source.clone",
|
|
477
|
+
decision: "recorded",
|
|
478
|
+
source: "operator-recorded",
|
|
479
|
+
metadata: { url: remoteSource.url, commit: remoteSource.commit, ref: remoteSource.ref || null, kind: remoteSource.kind, depth: 1 },
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
/* provenance is additive; never fail a completed review over an audit hiccup */
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// --bundle: after a COMPLETE drive, seal the run into a portable, self-verified
|
|
487
|
+
// bundle so the one command yields a client-verifiable artifact. Pure composition
|
|
488
|
+
// of reportBundleCli (export sealed + offline self-verify); spawns nothing. Gated
|
|
489
|
+
// on completion: a partial/blocked run is NEVER sealed. Run-state resolution
|
|
490
|
+
// anchors to the run's OWN repo (run.cwd) — quickstart runs cross-directory — but
|
|
491
|
+
// OUTPUT paths resolve against the CALLER's cwd so artifacts land where the
|
|
492
|
+
// operator ran the command. Byte-behavior port of the old build's quickstart().
|
|
493
|
+
const wantsBundle = truthyFlag(args.bundle);
|
|
494
|
+
let bundle;
|
|
495
|
+
if (wantsBundle && result.status === "complete") {
|
|
496
|
+
const callerBase = invocationCwd(args);
|
|
497
|
+
const outArg = firstString(args.output, args.path, args.archive);
|
|
498
|
+
const extractArg = firstString(args["extract-report"], args.extractReport, args.extractReportTo);
|
|
499
|
+
bundle = (0, report_cli_1.reportBundleCli)(result.runId, {
|
|
500
|
+
...args,
|
|
501
|
+
cwd: run.cwd,
|
|
502
|
+
output: path.resolve(callerBase, outArg || `${result.runId}.cwrun.json`),
|
|
503
|
+
...(extractArg ? { "extract-report": path.resolve(callerBase, extractArg) } : {}),
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
// Human-facing triage `hint` (stderr-side; absent on a clean completion so the
|
|
507
|
+
// default payload is byte-identical). Byte-exact wording to the old build's
|
|
508
|
+
// src/capability-core.ts quickstart(): the fail-closed "not configured …
|
|
509
|
+
// DELEGATES" line reaffirms the red line; the resume/once lines are copy-paste
|
|
510
|
+
// continue commands.
|
|
511
|
+
let hint;
|
|
512
|
+
if (!result.agentConfigured) {
|
|
513
|
+
hint =
|
|
514
|
+
"agent backend not configured — set CW_AGENT_COMMAND (e.g. \"claude -p\") or pass --agent-command, then re-run. The one command DELEGATES worker execution to YOUR agent; it never executes a model itself.";
|
|
515
|
+
}
|
|
516
|
+
else if (result.status === "parked") {
|
|
517
|
+
hint = `a worker parked past its retry budget — inspect: cw run show ${result.runId}`;
|
|
518
|
+
}
|
|
519
|
+
else if (result.status === "blocked") {
|
|
520
|
+
hint = `the drive is blocked — inspect: cw run drive ${result.runId}`;
|
|
521
|
+
}
|
|
522
|
+
else if (result.status === "in-progress") {
|
|
523
|
+
hint = resume
|
|
524
|
+
? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume${wantsBundle ? " --bundle" : ""}`
|
|
525
|
+
: `one step advanced (--once) — continue: cw quickstart ${appId} --run ${result.runId} --once`;
|
|
526
|
+
}
|
|
527
|
+
// --bundle on a run that did not complete is a NO-OP, not silence: tell the
|
|
528
|
+
// operator why nothing was sealed (the Rule of Silence permits a human hint).
|
|
529
|
+
if (wantsBundle && result.status !== "complete") {
|
|
530
|
+
hint = `${hint ? `${hint} ` : ""}--bundle skipped: the run did not complete (status=${result.status}); no bundle was sealed.`;
|
|
531
|
+
}
|
|
532
|
+
// Byte-exact to the old build's quickstart() return shape
|
|
533
|
+
// (src/capability-core.ts): `appId` is the resolved app id (the
|
|
534
|
+
// argument, or its architecture-review default), distinct from
|
|
535
|
+
// `workflowId` which is the driven run's own workflow id (equal for a
|
|
536
|
+
// top-level run, different for a sub-workflow hop). `remote` is present only
|
|
537
|
+
// for a --link/URL source, so a local-repo run stays byte-identical.
|
|
538
|
+
// `resumedFrom` is stamped ONLY when an explicit --run was continued
|
|
539
|
+
// (conditional spread keeps the key absent on the fresh/default path).
|
|
540
|
+
return {
|
|
541
|
+
appId,
|
|
542
|
+
...result,
|
|
543
|
+
hint,
|
|
544
|
+
...(resumeRunId ? { resumedFrom: resumeRunId } : {}),
|
|
545
|
+
// `bundle` is present only when --bundle sealed a completed run (conditional
|
|
546
|
+
// spread keeps the key absent on the default path → byte-identical output).
|
|
547
|
+
...(bundle ? { bundle } : {}),
|
|
548
|
+
...(remoteSource
|
|
549
|
+
? { remote: { url: remoteSource.url, commit: remoteSource.commit, kind: remoteSource.kind, cached: remoteSource.cached, ...(remoteSource.ref ? { ref: remoteSource.ref } : {}) } }
|
|
550
|
+
: {}),
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function dispatchRun(args) {
|
|
554
|
+
const runId = String(args.runId);
|
|
555
|
+
const run = (0, run_store_1.loadRunFromCwd)(runId, invocationCwd(args));
|
|
556
|
+
// parseArgv keys long flags in kebab-case; accept camelCase as a fallback.
|
|
557
|
+
const flag = (kebab, camel) => {
|
|
558
|
+
const v = args[kebab] ?? args[camel];
|
|
559
|
+
return typeof v === "string" && v.trim() ? v : undefined;
|
|
560
|
+
};
|
|
561
|
+
const manifest = (0, dispatch_1.createDispatchManifest)(run, args.limit !== undefined ? Number(args.limit) : undefined, {
|
|
562
|
+
sandboxProfileId: typeof args.sandbox === "string" ? args.sandbox : undefined,
|
|
563
|
+
sandbox: typeof args.sandbox === "string" ? args.sandbox : undefined,
|
|
564
|
+
backendId: typeof args.backend === "string" ? args.backend : undefined,
|
|
565
|
+
multiAgentRunId: flag("multi-agent-run", "multiAgentRun"),
|
|
566
|
+
multiAgentGroupId: flag("multi-agent-group", "multiAgentGroup"),
|
|
567
|
+
multiAgentRoleId: flag("multi-agent-role", "multiAgentRole"),
|
|
568
|
+
multiAgentFanoutId: flag("multi-agent-fanout", "multiAgentFanout"),
|
|
569
|
+
});
|
|
570
|
+
if (manifest.dispatchId) {
|
|
571
|
+
(0, commit_1.commitState)(run, `dispatch:${manifest.dispatchId}`);
|
|
572
|
+
(0, run_store_1.saveCheckpoint)(run);
|
|
573
|
+
(0, report_1.writeReport)(run);
|
|
574
|
+
}
|
|
575
|
+
return manifest;
|
|
576
|
+
}
|
|
577
|
+
function recordResultRun(args) {
|
|
578
|
+
const runId = String(args.runId);
|
|
579
|
+
const taskId = String(args.taskId);
|
|
580
|
+
const resultPath = String(args.resultPath);
|
|
581
|
+
const run = (0, run_store_1.loadRunFromCwd)(runId, invocationCwd(args));
|
|
582
|
+
const task = run.tasks.find((t) => t.id === taskId);
|
|
583
|
+
if (!task || !task.workerId)
|
|
584
|
+
throw new Error(`Unknown task id for run ${runId}: ${taskId}`);
|
|
585
|
+
const absolute = path.resolve(resultPath);
|
|
586
|
+
// A result path inside a system directory is never accepted (POLA): the
|
|
587
|
+
// operator file gets copied into the worker's result.md, so a /etc/passwd
|
|
588
|
+
// source would smuggle system content into a run. Byte-behavior port of the
|
|
589
|
+
// old build's recordResult system-directory blacklist.
|
|
590
|
+
if (/^\/(etc|bin|sbin|usr|Library|System|Applications|boot|dev|proc|sys|root|var\/log|var\/run)\//.test(absolute)) {
|
|
591
|
+
throw new Error(`Result path must not be a system directory: ${resultPath}`);
|
|
592
|
+
}
|
|
593
|
+
if (!fs.existsSync(absolute))
|
|
594
|
+
throw new Error(`Result file does not exist: ${resultPath}`);
|
|
595
|
+
const workerId = String(task.workerId);
|
|
596
|
+
// Host-attested `cw result <run> <task> <file>` intake: the operator hands CW
|
|
597
|
+
// an EXTERNAL result file that lives OUTSIDE the worker's read-only write
|
|
598
|
+
// boundary. The old task-level recordResult (lifecycle-operations.ts:279-280)
|
|
599
|
+
// COPIED that external file into the run's results area and recorded the
|
|
600
|
+
// internal path — it never ran the external path through validateSandboxWrite.
|
|
601
|
+
// v2 collapsed the two intakes into recordWorkerOutput, which sandbox-validates
|
|
602
|
+
// its input against the worker boundary, so a bare external path is rejected
|
|
603
|
+
// ("write path is outside sandbox profile <id>"). Restore the copy-in: stage
|
|
604
|
+
// the operator file at the worker's OWN result.md (which IS inside the write
|
|
605
|
+
// boundary), then record that internal path exactly like a driven worker.
|
|
606
|
+
const manifest = (0, worker_isolation_1.showWorkerManifest)(run, workerId);
|
|
607
|
+
fs.mkdirSync(path.dirname(manifest.resultPath), { recursive: true });
|
|
608
|
+
fs.copyFileSync(absolute, manifest.resultPath);
|
|
609
|
+
const output = (0, worker_isolation_1.recordWorkerOutput)(run, workerId, manifest.resultPath);
|
|
610
|
+
// Host-attested token usage (v0.1.31): record it verbatim as provenance when
|
|
611
|
+
// the operator supplied `--usage-*` flags; CW never synthesizes usage. The old
|
|
612
|
+
// task-level recordResult set `task.usage = usage` (lifecycle-operations.ts:286)
|
|
613
|
+
// and its unit was the TASK. v2 records through recordWorkerOutput, which gives
|
|
614
|
+
// the worker an `output` record — so the observability usage UNIT becomes the
|
|
615
|
+
// WORKER (deriveUsageTotals reads worker.usage for workers with output, and
|
|
616
|
+
// EXCLUDES that task). Attach the usage to the worker scope so the report counts
|
|
617
|
+
// it as an attested unit; also stamp task.usage for byte-parity with the old
|
|
618
|
+
// task-level record.
|
|
619
|
+
const usage = (0, observability_1.parseUsageFromArgs)(args, new Date().toISOString());
|
|
620
|
+
if (usage) {
|
|
621
|
+
task.usage = usage;
|
|
622
|
+
const scope = (0, worker_isolation_1.getWorkerScope)(run, workerId);
|
|
623
|
+
if (scope)
|
|
624
|
+
scope.usage = usage;
|
|
625
|
+
}
|
|
626
|
+
// Byte-exact to the old build's orchestrator recordWorkerOutput()
|
|
627
|
+
// wrapper: an accepted result is its own checkpoint commit, not just a
|
|
628
|
+
// bare saveCheckpoint (SPEC/pipeline-run.md's persist-ordering rule).
|
|
629
|
+
(0, commit_1.commitState)(run, `worker:${workerId}:result`);
|
|
630
|
+
(0, run_store_1.saveCheckpoint)(run);
|
|
631
|
+
(0, report_1.writeReport)(run);
|
|
632
|
+
return output;
|
|
633
|
+
}
|
|
634
|
+
/** `cw commit <run-id>` — byte-exact port of the old build's
|
|
635
|
+
* `orchestrator/lifecycle-operations.ts`'s `commit()`: the CLI/MCP
|
|
636
|
+
* payload wraps the commit record as `{runId, commit}` (NOT the commit
|
|
637
|
+
* record at top level). Both the success AND the throw path write the
|
|
638
|
+
* report + checkpoint before returning/re-throwing — a gate failure
|
|
639
|
+
* still leaves the run's report/state current on disk. */
|
|
640
|
+
function commitRun(args) {
|
|
641
|
+
const runId = String(args.runId);
|
|
642
|
+
const run = (0, run_store_1.loadRunFromCwd)(runId, invocationCwd(args));
|
|
643
|
+
const allowCheckpoint = Boolean(args.allowUnverifiedCheckpoint || args["allow-unverified-checkpoint"]);
|
|
644
|
+
const hasGateOption = Boolean(args.verifier || args.verifierNode || args["verifier-node"] || args.candidate || args.selection);
|
|
645
|
+
try {
|
|
646
|
+
const commit = (0, commit_1.commitState)(run, {
|
|
647
|
+
reason: typeof args.reason === "string" && args.reason ? args.reason : "manual",
|
|
648
|
+
verifierNodeId: (typeof args.verifier === "string" && args.verifier) ||
|
|
649
|
+
(typeof args.verifierNode === "string" && args.verifierNode) ||
|
|
650
|
+
(typeof args["verifier-node"] === "string" && args["verifier-node"]) ||
|
|
651
|
+
undefined,
|
|
652
|
+
candidateId: typeof args.candidate === "string" ? args.candidate : undefined,
|
|
653
|
+
selectionId: typeof args.selection === "string" ? args.selection : undefined,
|
|
654
|
+
verifierGated: hasGateOption || !allowCheckpoint,
|
|
655
|
+
allowUnverifiedCheckpoint: allowCheckpoint,
|
|
656
|
+
source: "cli",
|
|
657
|
+
});
|
|
658
|
+
(0, report_1.writeReport)(run);
|
|
659
|
+
(0, run_store_1.saveCheckpoint)(run);
|
|
660
|
+
return { runId: run.id, commit };
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
(0, report_1.writeReport)(run);
|
|
664
|
+
(0, run_store_1.saveCheckpoint)(run);
|
|
665
|
+
throw error;
|
|
666
|
+
}
|
|
667
|
+
}
|