cool-workflow 0.1.98 → 0.2.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +18 -7
- package/apps/architecture-review/app.json +2 -2
- package/apps/architecture-review/workflow.js +12 -12
- 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 +3518 -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/shell/doctor.js +292 -0
- package/dist/shell/drive.js +885 -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 +507 -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 +100 -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 +344 -0
- package/dist/{remote-source.js → shell/remote-source.js} +2 -2
- 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} +257 -48
- package/dist/shell/scheduling-io.js +321 -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 +472 -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 +258 -0
- package/dist/shell/workbench-text.js +18 -0
- package/dist/shell/workbench.js +170 -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 +282 -219
- package/docs/contract-migration-tooling.7.md +4 -0
- package/docs/control-plane-scheduling.7.md +4 -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/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 +6 -1
- package/docs/project-index.md +137 -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/remote-source-review.7.md +4 -4
- package/docs/report-verifiable-bundle.7.md +1 -1
- package/docs/run-registry-control-plane.7.md +4 -0
- package/docs/run-retention-reclamation.7.md +25 -0
- package/docs/security-trust-hardening.7.md +3 -1
- package/docs/state-explosion-management.7.md +4 -0
- package/docs/team-collaboration.7.md +4 -0
- package/docs/web-desktop-workbench.7.md +15 -1
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +9 -13
- package/package.json +1 -1
- package/scripts/agents/agent-adapter-core.js +22 -2
- package/scripts/agents/claude-p-agent.js +8 -3
- package/scripts/agents/cw-attest-wrap.js +2 -2
- package/scripts/agents/gemini-agent.js +5 -1
- package/scripts/agents/opencode-agent.js +5 -1
- package/scripts/bump-version.js +4 -3
- package/scripts/canonical-apps.js +4 -4
- 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 +2 -2
- 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 -1189
- package/dist/capability-registry.js +0 -885
- package/dist/cli/command-surface.js +0 -494
- 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/ledger.js +0 -169
- 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/doctor.js +0 -212
- package/dist/drive.js +0 -864
- package/dist/error-feedback.js +0 -419
- package/dist/evidence-grounding.js +0 -184
- package/dist/execution-backend/agent.js +0 -354
- 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/ledger.js +0 -313
- package/dist/loop-expansion.js +0 -60
- package/dist/mcp/tool-call.js +0 -470
- package/dist/mcp/tool-definitions.js +0 -1066
- 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 -650
- 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 -925
- 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 -815
- 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 -192
- 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
package/dist/capability-core.js
DELETED
|
@@ -1,1189 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Shared capability core: the SINGLE source of truth for composite capabilities
|
|
3
|
-
// whose payload is assembled from more than one orchestrator call.
|
|
4
|
-
//
|
|
5
|
-
// BSD discipline (mechanism vs policy): these functions are MECHANISM. They live
|
|
6
|
-
// here — never in cli.ts or mcp-server.ts — so the CLI and the MCP surface are
|
|
7
|
-
// two renderings of ONE data source. Any capability that composes multiple
|
|
8
|
-
// runner calls (plan summary, app run, sandbox choice, commit envelope) MUST be
|
|
9
|
-
// expressed here and called identically by both surfaces. A composite that lives
|
|
10
|
-
// in only one surface is exactly the cross-surface drift v0.1.27 forbids.
|
|
11
|
-
//
|
|
12
|
-
// Capability metadata (which entry is on which surface, tool names, jsonMode) is
|
|
13
|
-
// declared in ONE place — the BUILTIN_CAPABILITIES table in capability-registry.ts,
|
|
14
|
-
// the single source of truth both surfaces and the parity gate read. New
|
|
15
|
-
// capabilities add a row there. (A v0.1.46 "self-register at load time" mechanism
|
|
16
|
-
// was removed: the registry snapshot was taken before those registrations ran, so
|
|
17
|
-
// they were silently dead duplicates of the table — see capability-registry.ts.)
|
|
18
|
-
//
|
|
19
|
-
// See docs/cli-mcp-parity.7.md and src/capability-registry.ts.
|
|
20
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
-
};
|
|
23
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.gcClones = exports.listClones = exports.QUICKSTART_DEFAULT_APP = void 0;
|
|
25
|
-
exports.planSummary = planSummary;
|
|
26
|
-
exports.appRun = appRun;
|
|
27
|
-
exports.sandboxChoose = sandboxChoose;
|
|
28
|
-
exports.commitEnvelope = commitEnvelope;
|
|
29
|
-
exports.compactOperatorStatus = compactOperatorStatus;
|
|
30
|
-
exports.runRegistryFor = runRegistryFor;
|
|
31
|
-
exports.runRegistryRefresh = runRegistryRefresh;
|
|
32
|
-
exports.runRegistryShow = runRegistryShow;
|
|
33
|
-
exports.runSearch = runSearch;
|
|
34
|
-
exports.runList = runList;
|
|
35
|
-
exports.runShow = runShow;
|
|
36
|
-
exports.runResume = runResume;
|
|
37
|
-
exports.runArchive = runArchive;
|
|
38
|
-
exports.runRerun = runRerun;
|
|
39
|
-
exports.runExportArchive = runExportArchive;
|
|
40
|
-
exports.runImportArchive = runImportArchive;
|
|
41
|
-
exports.runInspectArchive = runInspectArchive;
|
|
42
|
-
exports.runVerifyImport = runVerifyImport;
|
|
43
|
-
exports.runRestoreArchive = runRestoreArchive;
|
|
44
|
-
exports.reportBundle = reportBundle;
|
|
45
|
-
exports.runVerifyReportBundle = runVerifyReportBundle;
|
|
46
|
-
exports.queueAdd = queueAdd;
|
|
47
|
-
exports.queueList = queueList;
|
|
48
|
-
exports.queueDrain = queueDrain;
|
|
49
|
-
exports.queueShow = queueShow;
|
|
50
|
-
exports.schedPlan = schedPlan;
|
|
51
|
-
exports.schedLease = schedLease;
|
|
52
|
-
exports.schedRelease = schedRelease;
|
|
53
|
-
exports.schedComplete = schedComplete;
|
|
54
|
-
exports.schedReclaim = schedReclaim;
|
|
55
|
-
exports.schedReset = schedReset;
|
|
56
|
-
exports.schedPolicyShow = schedPolicyShow;
|
|
57
|
-
exports.schedPolicySet = schedPolicySet;
|
|
58
|
-
exports.runDrivePreview = runDrivePreview;
|
|
59
|
-
exports.runDrive = runDrive;
|
|
60
|
-
exports.collectRunFindings = collectRunFindings;
|
|
61
|
-
exports.quickstart = quickstart;
|
|
62
|
-
exports.backendAgentConfigShow = backendAgentConfigShow;
|
|
63
|
-
exports.backendAgentConfigSet = backendAgentConfigSet;
|
|
64
|
-
exports.gcPlan = gcPlan;
|
|
65
|
-
exports.gcRun = gcRun;
|
|
66
|
-
exports.gcVerify = gcVerify;
|
|
67
|
-
exports.runHistory = runHistory;
|
|
68
|
-
exports.metricsSummary = metricsSummary;
|
|
69
|
-
exports.sandboxProfileIdFrom = sandboxProfileIdFrom;
|
|
70
|
-
exports.withoutRuntimeKeys = withoutRuntimeKeys;
|
|
71
|
-
exports.optionalString = optionalString;
|
|
72
|
-
exports.isRecord = isRecord;
|
|
73
|
-
exports.telemetryVerify = telemetryVerify;
|
|
74
|
-
exports.auditVerify = auditVerify;
|
|
75
|
-
exports.demoTamper = demoTamper;
|
|
76
|
-
exports.demoBundle = demoBundle;
|
|
77
|
-
const drive_1 = require("./drive");
|
|
78
|
-
const agent_config_1 = require("./agent-config");
|
|
79
|
-
const run_registry_1 = require("./run-registry");
|
|
80
|
-
const observability_1 = require("./observability");
|
|
81
|
-
const telemetry_ledger_1 = require("./telemetry-ledger");
|
|
82
|
-
const telemetry_attestation_1 = require("./telemetry-attestation");
|
|
83
|
-
const trust_audit_1 = require("./trust-audit");
|
|
84
|
-
const remote_source_1 = require("./remote-source");
|
|
85
|
-
const telemetry_demo_1 = require("./telemetry-demo");
|
|
86
|
-
const state_1 = require("./state");
|
|
87
|
-
const run_export_1 = require("./run-export");
|
|
88
|
-
const result_normalize_1 = require("./result-normalize");
|
|
89
|
-
const cli_options_1 = require("./orchestrator/cli-options");
|
|
90
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
91
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
92
|
-
const scheduling_1 = require("./scheduling");
|
|
93
|
-
// ---- canonical plan payload -----------------------------------------------
|
|
94
|
-
// Both `cw plan` (default + --json) and `cw_plan` resolve to this exact object.
|
|
95
|
-
function planSummary(runner, workflowId, options) {
|
|
96
|
-
const run = runner.plan(workflowId, options);
|
|
97
|
-
return {
|
|
98
|
-
runId: run.id,
|
|
99
|
-
workflowId: run.workflow.id,
|
|
100
|
-
statePath: run.paths.state,
|
|
101
|
-
reportPath: run.paths.report,
|
|
102
|
-
pendingTasks: run.tasks.filter((task) => task.status === "pending").length
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
// ---- canonical app-run payload --------------------------------------------
|
|
106
|
-
// Both `cw app run` and `cw_app_run` resolve to this exact object. Structured
|
|
107
|
-
// app inputs + optional sandbox resolution, then a compact operator status.
|
|
108
|
-
function appRun(runner, args) {
|
|
109
|
-
const appId = String(args.appId || args.workflowId || "");
|
|
110
|
-
const inputs = isRecord(args.inputs) ? args.inputs : {};
|
|
111
|
-
const planOptions = { ...inputs, ...withoutRuntimeKeys(args) };
|
|
112
|
-
const sandboxProfileId = sandboxProfileIdFrom(args);
|
|
113
|
-
const resolvedSandbox = sandboxProfileId ? runner.showSandboxProfile(sandboxProfileId, args) : undefined;
|
|
114
|
-
const run = runner.plan(appId, planOptions);
|
|
115
|
-
const status = runner.operatorStatus(run.id);
|
|
116
|
-
return {
|
|
117
|
-
runId: run.id,
|
|
118
|
-
workflowId: run.workflow.id,
|
|
119
|
-
appId: run.workflow.app?.id || appId,
|
|
120
|
-
appVersion: run.workflow.app?.version,
|
|
121
|
-
statePath: run.paths.state,
|
|
122
|
-
reportPath: run.paths.report,
|
|
123
|
-
pendingTasks: run.tasks.filter((task) => task.status === "pending").length,
|
|
124
|
-
operatorStatus: compactOperatorStatus(status),
|
|
125
|
-
nextActions: status.nextActions,
|
|
126
|
-
sandboxProfileId,
|
|
127
|
-
sandboxProfile: resolvedSandbox
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
// ---- canonical sandbox choice payload -------------------------------------
|
|
131
|
-
// Both `cw sandbox choose|resolve` and `cw_sandbox_choose|cw_sandbox_resolve`
|
|
132
|
-
// resolve to this exact object.
|
|
133
|
-
function sandboxChoose(runner, args) {
|
|
134
|
-
const profileId = sandboxProfileIdFrom(args) || "readonly";
|
|
135
|
-
const profile = runner.showSandboxProfile(profileId, args);
|
|
136
|
-
return {
|
|
137
|
-
profileId,
|
|
138
|
-
sandboxProfileId: profile.id,
|
|
139
|
-
valid: true,
|
|
140
|
-
profile
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
// ---- canonical commit envelope --------------------------------------------
|
|
144
|
-
// `cw_commit` resolves to this operator-facing envelope. The CLI `commit`
|
|
145
|
-
// command intentionally emits the raw StateCommitResult for scripting; both
|
|
146
|
-
// derive from the single core entry runner.commit (declared, not drift — see
|
|
147
|
-
// the capability registry's `commit` descriptor).
|
|
148
|
-
function commitEnvelope(runner, runId, args) {
|
|
149
|
-
const result = runner.commit(runId, args);
|
|
150
|
-
const commit = result.commit;
|
|
151
|
-
const status = runner.operatorStatus(runId);
|
|
152
|
-
return {
|
|
153
|
-
runId,
|
|
154
|
-
commitId: commit.id,
|
|
155
|
-
verifierGated: commit.verifierGated,
|
|
156
|
-
checkpoint: commit.checkpoint,
|
|
157
|
-
verifierNodeId: commit.verifierNodeId,
|
|
158
|
-
candidateId: commit.candidateId,
|
|
159
|
-
selectionId: commit.selectionId,
|
|
160
|
-
evidenceCount: (commit.evidence || []).length,
|
|
161
|
-
snapshotPath: commit.snapshotPath,
|
|
162
|
-
nextActions: status.nextActions,
|
|
163
|
-
commit
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function compactOperatorStatus(status) {
|
|
167
|
-
return {
|
|
168
|
-
runId: status.runId,
|
|
169
|
-
workflowId: status.workflowId,
|
|
170
|
-
appId: status.appId,
|
|
171
|
-
appVersion: status.appVersion,
|
|
172
|
-
loopStage: status.loopStage,
|
|
173
|
-
activePhase: status.activePhase,
|
|
174
|
-
blocked: status.blocked,
|
|
175
|
-
blockedReasons: status.blockedReasons,
|
|
176
|
-
pendingTasks: status.tasks.pending.length,
|
|
177
|
-
runningTasks: status.tasks.running.length,
|
|
178
|
-
completedTasks: status.tasks.completed.length,
|
|
179
|
-
nextActions: status.nextActions
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
// ---- run registry / control plane (v0.1.28) -------------------------------
|
|
183
|
-
// MECHANISM, ONE SOURCE: the CLI and MCP surfaces both route through these
|
|
184
|
-
// functions so `cw <cmd> --json` is byte-identical to `cw_<tool>`. Each accepts
|
|
185
|
-
// the raw CLI options OR the raw MCP arguments and normalizes them identically,
|
|
186
|
-
// then calls the single RunRegistry method. The registry is constructed from the
|
|
187
|
-
// same resolved cwd on both surfaces (CLI: --cwd|process.cwd(); MCP passes a
|
|
188
|
-
// resolved cwd and scopes the runner), so repo/home roots line up.
|
|
189
|
-
function runRegistryFor(args, planner) {
|
|
190
|
-
return new run_registry_1.RunRegistry(String(args.cwd || process.cwd()), planner);
|
|
191
|
-
}
|
|
192
|
-
function scopeOf(args, fallback) {
|
|
193
|
-
if (args.scope === "repo")
|
|
194
|
-
return "repo";
|
|
195
|
-
if (args.scope === "home")
|
|
196
|
-
return "home";
|
|
197
|
-
return fallback;
|
|
198
|
-
}
|
|
199
|
-
function lifecycleOf(value) {
|
|
200
|
-
return (0, run_registry_1.isRunLifecycleState)(value) ? value : undefined;
|
|
201
|
-
}
|
|
202
|
-
function flag(value) {
|
|
203
|
-
if (value === undefined)
|
|
204
|
-
return undefined;
|
|
205
|
-
if (value === false || value === "false" || value === "no" || value === "0")
|
|
206
|
-
return false;
|
|
207
|
-
return Boolean(value);
|
|
208
|
-
}
|
|
209
|
-
// F7: explicit invocation cwd — no more process.chdir bracket.
|
|
210
|
-
// The runner resolves a run from process.cwd() by default; to operate WITH a run's
|
|
211
|
-
// repo as base (run --drive --repo X from anywhere; cross-directory quickstart; run
|
|
212
|
-
// import/export against a target dir) we now pass that base EXPLICITLY —
|
|
213
|
-
// runner.withBaseDir(dir).loadRun(...) for run resolution (see
|
|
214
|
-
// CoolWorkflowRunner.withBaseDir), and invocationCwd(args) to anchor relative path
|
|
215
|
-
// args. Nothing mutates the global process.cwd(), so concurrent in-process callers
|
|
216
|
-
// can no longer corrupt each other's working directory (the former reentrancy hazard).
|
|
217
|
-
function invocationCwd(args) {
|
|
218
|
-
return node_path_1.default.resolve(optionalString(args.cwd) || process.cwd());
|
|
219
|
-
}
|
|
220
|
-
function runRegistryRefresh(reg, args) {
|
|
221
|
-
return reg.refresh({ scope: scopeOf(args, "repo") });
|
|
222
|
-
}
|
|
223
|
-
function runRegistryShow(reg, args) {
|
|
224
|
-
return reg.show({ scope: scopeOf(args, "repo") });
|
|
225
|
-
}
|
|
226
|
-
function runSearch(reg, args) {
|
|
227
|
-
return reg.search({
|
|
228
|
-
scope: scopeOf(args, "home"),
|
|
229
|
-
text: optionalString(args.text || args.q || args.query),
|
|
230
|
-
app: optionalString(args.app || args.appId),
|
|
231
|
-
status: lifecycleOf(args.status),
|
|
232
|
-
repo: optionalString(args.repo),
|
|
233
|
-
since: optionalString(args.since),
|
|
234
|
-
until: optionalString(args.until),
|
|
235
|
-
includeArchived: flag(args.includeArchived ?? args["include-archived"]),
|
|
236
|
-
limit: args.limit === undefined ? undefined : Number(args.limit),
|
|
237
|
-
offset: args.offset === undefined ? undefined : Number(args.offset)
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
function runList(reg, args) {
|
|
241
|
-
return reg.list({
|
|
242
|
-
scope: scopeOf(args, "home"),
|
|
243
|
-
includeArchived: flag(args.includeArchived ?? args["include-archived"]),
|
|
244
|
-
limit: args.limit === undefined ? undefined : Number(args.limit),
|
|
245
|
-
offset: args.offset === undefined ? undefined : Number(args.offset)
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
function runShow(reg, runId, args) {
|
|
249
|
-
return reg.showRun(runId, { scope: scopeOf(args, "home") });
|
|
250
|
-
}
|
|
251
|
-
function runResume(reg, runner, runId, args) {
|
|
252
|
-
const base = reg.resume(runId, {
|
|
253
|
-
scope: scopeOf(args, "home"),
|
|
254
|
-
limit: args.limit === undefined ? undefined : Number(args.limit)
|
|
255
|
-
});
|
|
256
|
-
// Default (no --drive/--once): read-only, byte-identical to before.
|
|
257
|
-
if (!isTrue(args.drive) && !isTrue(args.once))
|
|
258
|
-
return base;
|
|
259
|
-
// Opt-in continuation: hand the resolved run to the EXISTING agent-delegation
|
|
260
|
-
// drive loop (re-plans nothing; picks up pending/running tasks from durable
|
|
261
|
-
// state). An unconfigured agent surfaces drive.status="blocked" (fail-closed).
|
|
262
|
-
const drive = runDrive(runner, { ...args, runId: base.runId, repo: base.repo, once: isTrue(args.once) });
|
|
263
|
-
return { ...base, drive };
|
|
264
|
-
}
|
|
265
|
-
function runArchive(reg, runId, args) {
|
|
266
|
-
if (runId) {
|
|
267
|
-
return reg.archive(runId, {
|
|
268
|
-
scope: scopeOf(args, "home"),
|
|
269
|
-
reason: optionalString(args.reason),
|
|
270
|
-
unarchive: flag(args.unarchive)
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
const days = Number(args.olderThanDays ?? args["older-than-days"]);
|
|
274
|
-
const states = parseLifecycleList(args.state ?? args.status);
|
|
275
|
-
return reg.archiveByPolicy({
|
|
276
|
-
schemaVersion: 1,
|
|
277
|
-
archiveOlderThanDays: Number.isFinite(days) ? days : 0,
|
|
278
|
-
archiveStates: states.length ? states : ["completed", "failed"],
|
|
279
|
-
defaultQueuePriority: 100
|
|
280
|
-
}, { scope: scopeOf(args, "home") });
|
|
281
|
-
}
|
|
282
|
-
function runRerun(reg, runId, args) {
|
|
283
|
-
return reg.rerun(runId, { scope: scopeOf(args, "home"), reason: optionalString(args.reason) });
|
|
284
|
-
}
|
|
285
|
-
function runExportArchive(runner, runId, args) {
|
|
286
|
-
const base = invocationCwd(args);
|
|
287
|
-
const output = optionalString(args.output || args.path || args.archive) || `${runId}.cwrun.json`;
|
|
288
|
-
// Optionally seal in the operator's PUBLIC trust key so the bundle re-verifies
|
|
289
|
-
// offline. Default falls back to the same env the verify gate reads, so a single
|
|
290
|
-
// configured key both attests at record-time and travels with the export.
|
|
291
|
-
const trustPublicKey = optionalString(args["with-trust-key"] || args.withTrustKey || args.trustKey || args.pubkey) || process.env.CW_AGENT_ATTEST_PUBKEY;
|
|
292
|
-
const resolvedOutput = node_path_1.default.resolve(base, output);
|
|
293
|
-
const sysDirs = /^\/(etc|bin|sbin|usr|Library|System|Applications|boot|dev|proc|sys|root|var\/log|var\/run)\//;
|
|
294
|
-
if (sysDirs.test(resolvedOutput)) {
|
|
295
|
-
throw new Error(`Refusing to write archive to a system directory: ${output}`);
|
|
296
|
-
}
|
|
297
|
-
return (0, run_export_1.exportRun)(runner.withBaseDir(optionalString(args.cwd)).loadRun(runId), resolvedOutput, { trustPublicKey });
|
|
298
|
-
}
|
|
299
|
-
function runImportArchive(runner, args) {
|
|
300
|
-
const base = invocationCwd(args);
|
|
301
|
-
const archive = optionalString(args.archive || args.path || args.file);
|
|
302
|
-
if (!archive)
|
|
303
|
-
throw new Error("run import requires an archive path (positional, --archive, --path, or --file)");
|
|
304
|
-
const target = node_path_1.default.resolve(base, optionalString(args.target || args.repo || args.cwd) || base);
|
|
305
|
-
const imported = (0, run_export_1.importRun)(node_path_1.default.resolve(base, archive), target);
|
|
306
|
-
const registry = new run_registry_1.RunRegistry(target, runner.withBaseDir(target));
|
|
307
|
-
const registryReport = registry.refresh({ scope: "repo" });
|
|
308
|
-
return { ...imported, registry: registryReport };
|
|
309
|
-
}
|
|
310
|
-
// Read-only: inspect a portable archive's integrity WITHOUT importing it. Routes
|
|
311
|
-
// both surfaces through one shared core entry. The runner is unused (no registry
|
|
312
|
-
// touch — inspection writes nothing) but kept for dispatch-signature symmetry.
|
|
313
|
-
function runInspectArchive(_runner, args) {
|
|
314
|
-
const base = invocationCwd(args);
|
|
315
|
-
const archive = optionalString(args.archive || args.path || args.file);
|
|
316
|
-
if (!archive)
|
|
317
|
-
throw new Error("run inspect-archive requires an archive path (positional, --archive, --path, or --file)");
|
|
318
|
-
return (0, run_export_1.inspectArchive)(node_path_1.default.resolve(base, archive));
|
|
319
|
-
}
|
|
320
|
-
function runVerifyImport(runner, runId, args) {
|
|
321
|
-
return (0, run_export_1.verifyImportedRun)(runner.withBaseDir(optionalString(args.cwd)).loadRun(runId));
|
|
322
|
-
}
|
|
323
|
-
// Fail-closed atomic restore of a portable run archive. `run import` runs a
|
|
324
|
-
// verification (importRun returns it as ImportResult.verification) but does NOT
|
|
325
|
-
// fail on it — it exits 0 even when the telemetry-ledger or trust-audit hash chain
|
|
326
|
-
// does not verify, so a chain-tampered run imports with a fabricated success.
|
|
327
|
-
// `run restore` closes that gap in ONE step: it integrity-INSPECTS the archive
|
|
328
|
-
// FIRST (writing nothing), refuses a bad bundle before any import, then IMPORTS
|
|
329
|
-
// and REUSES importRun's verification verdict — reporting ok:true ONLY when verify
|
|
330
|
-
// passes. Pure composition of the existing functions (inspectArchive + importRun,
|
|
331
|
-
// whose own verification we reuse); no new crypto or IO logic, and no second
|
|
332
|
-
// re-hash. The CLI/MCP surfaces map ok:false to exit 1.
|
|
333
|
-
function runRestoreArchive(runner, args) {
|
|
334
|
-
const base = invocationCwd(args);
|
|
335
|
-
const archive = optionalString(args.archive || args.path || args.file);
|
|
336
|
-
if (!archive)
|
|
337
|
-
throw new Error("run restore requires an archive path (positional, --archive, --path, or --file)");
|
|
338
|
-
const resolvedArchive = node_path_1.default.resolve(base, archive);
|
|
339
|
-
const target = node_path_1.default.resolve(base, optionalString(args.target || args.repo || args.cwd) || base);
|
|
340
|
-
// (1) Integrity-inspect FIRST — read-only, writes nothing. A bad bundle is
|
|
341
|
-
// refused here, before any import touches the target tree (no partial restore).
|
|
342
|
-
const inspect = (0, run_export_1.inspectArchive)(resolvedArchive);
|
|
343
|
-
if (!inspect.ok) {
|
|
344
|
-
return { schemaVersion: 1, ok: false, target, inspect, imported: null, verify: null, registry: null };
|
|
345
|
-
}
|
|
346
|
-
// (2) Intact: import + refresh the target registry (mirrors runImportArchive).
|
|
347
|
-
const imported = (0, run_export_1.importRun)(resolvedArchive, target);
|
|
348
|
-
const registry = new run_registry_1.RunRegistry(target, runner.withBaseDir(target));
|
|
349
|
-
const registryReport = registry.refresh({ scope: "repo" });
|
|
350
|
-
// (3) REUSE the verification importRun already ran — it re-proves the same
|
|
351
|
-
// restored files PLUS the telemetry-ledger and trust-audit hash chains, but
|
|
352
|
-
// returns the verdict WITHOUT throwing. We do NOT re-run verifyImportedRun (that
|
|
353
|
-
// would re-hash every file a second time for no new information). The gap restore
|
|
354
|
-
// closes is exactly that `run import` ships this verdict and exits 0 even when it
|
|
355
|
-
// is false — restore fails-closed on it below (CLI maps ok:false → exit 1).
|
|
356
|
-
const verify = imported.verification;
|
|
357
|
-
return {
|
|
358
|
-
schemaVersion: 1,
|
|
359
|
-
// inspect.ok is guaranteed true here — the corrupt-archive case early-returned
|
|
360
|
-
// above — so the verdict is purely verify.ok (the telemetry/trust-audit chain).
|
|
361
|
-
ok: verify.ok,
|
|
362
|
-
target,
|
|
363
|
-
inspect,
|
|
364
|
-
imported: { ...imported, registry: registryReport },
|
|
365
|
-
verify,
|
|
366
|
-
registry: registryReport
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
// Produce-and-prove: export a run to a portable bundle sealed with the operator's
|
|
370
|
-
// trust key (defaulting to CW_AGENT_ATTEST_PUBKEY, same as `run export`), then
|
|
371
|
-
// IMMEDIATELY verify the artifact offline the way a recipient will. The producer
|
|
372
|
-
// learns now — fail-closed — whether the bundle a client will check is actually
|
|
373
|
-
// verifiable (e.g. an unconfigured attest key yields an unverifiable bundle). Pure
|
|
374
|
-
// composition of runExportArchive + verifyReportBundle; spawns nothing, writes only
|
|
375
|
-
// the archive (and, with --extract-report, the human report) that `run export` would.
|
|
376
|
-
function reportBundle(runner, runId, args) {
|
|
377
|
-
const exported = runExportArchive(runner, runId, args);
|
|
378
|
-
const base = invocationCwd(args);
|
|
379
|
-
const extractReportTo = optionalString(args["extract-report"] || args.extractReport || args.extractReportTo);
|
|
380
|
-
const verification = (0, run_export_1.verifyReportBundle)(exported.path, {
|
|
381
|
-
pubkey: optionalString(args.pubkey || args.pubKey || args.publicKey),
|
|
382
|
-
extractReportTo: extractReportTo ? node_path_1.default.resolve(base, extractReportTo) : undefined,
|
|
383
|
-
strictSignatures: Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs),
|
|
384
|
-
requireSignatures: Boolean(args["require-signatures"] || args.requireSignatures || args.requireSigs)
|
|
385
|
-
});
|
|
386
|
-
return {
|
|
387
|
-
schemaVersion: 1,
|
|
388
|
-
runId,
|
|
389
|
-
archivePath: exported.path,
|
|
390
|
-
trustKeyEmbedded: exported.trustKeyEmbedded,
|
|
391
|
-
reportExtractedTo: verification.reportExtractedTo,
|
|
392
|
-
verification,
|
|
393
|
-
ok: verification.ok
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
// Read-only: verify a portable run bundle OFFLINE and self-contained (archive bytes
|
|
397
|
-
// + telemetry chain + trust-audit chain + embedded-key signatures). The runner is
|
|
398
|
-
// unused — verification restores into its own throwaway tmpdir and writes nothing to
|
|
399
|
-
// any registry — but kept for dispatch-signature symmetry with the other run verbs.
|
|
400
|
-
function runVerifyReportBundle(_runner, args) {
|
|
401
|
-
const base = invocationCwd(args);
|
|
402
|
-
const archive = optionalString(args.archive || args.path || args.file || args.bundle);
|
|
403
|
-
if (!archive)
|
|
404
|
-
throw new Error("report verify-bundle requires a bundle path (positional, --archive, --path, --file, or --bundle)");
|
|
405
|
-
const extractReportTo = optionalString(args["extract-report"] || args.extractReport || args.extractReportTo);
|
|
406
|
-
return (0, run_export_1.verifyReportBundle)(node_path_1.default.resolve(base, archive), {
|
|
407
|
-
pubkey: optionalString(args.pubkey || args.pubKey || args.publicKey),
|
|
408
|
-
extractReportTo: extractReportTo ? node_path_1.default.resolve(base, extractReportTo) : undefined,
|
|
409
|
-
strictSignatures: Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs),
|
|
410
|
-
requireSignatures: Boolean(args["require-signatures"] || args.requireSignatures || args.requireSigs)
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
function queueAdd(reg, args) {
|
|
414
|
-
return reg.queueAdd({
|
|
415
|
-
runId: optionalString(args.runId),
|
|
416
|
-
appId: optionalString(args.appId || args.app),
|
|
417
|
-
workflowId: optionalString(args.workflowId || args.workflow),
|
|
418
|
-
repo: optionalString(args.repo),
|
|
419
|
-
priority: args.priority === undefined ? undefined : Number(args.priority),
|
|
420
|
-
note: optionalString(args.note),
|
|
421
|
-
id: optionalString(args.id)
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
function queueList(reg, args) {
|
|
425
|
-
const status = optionalString(args.status);
|
|
426
|
-
return reg.queueList({
|
|
427
|
-
status: status,
|
|
428
|
-
repo: optionalString(args.repo)
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
function queueDrain(reg, args) {
|
|
432
|
-
return reg.queueDrain({
|
|
433
|
-
limit: args.limit === undefined ? undefined : Number(args.limit),
|
|
434
|
-
repo: optionalString(args.repo)
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
function queueShow(reg, id) {
|
|
438
|
-
return reg.queueShow(id);
|
|
439
|
-
}
|
|
440
|
-
// ---- control-plane scheduling (v0.1.37) -----------------------------------
|
|
441
|
-
function loadSchedulingPolicy(reg) {
|
|
442
|
-
const file = reg.schedulingPolicyPath();
|
|
443
|
-
// Absent policy => conservative DEFAULT (an unconfigured backend, which §4
|
|
444
|
-
// permits). But a PRESENT-but-corrupt policy must fail closed: silently
|
|
445
|
-
// substituting defaults would schedule under settings the operator never
|
|
446
|
-
// chose while their broken file sits on disk. Let readJson's throw surface it.
|
|
447
|
-
if (node_fs_1.default.existsSync(file)) {
|
|
448
|
-
return { policy: (0, scheduling_1.normalizeSchedulingPolicy)((0, state_1.readJson)(file)), source: "file" };
|
|
449
|
-
}
|
|
450
|
-
return { policy: scheduling_1.DEFAULT_SCHEDULING_POLICY, source: "default" };
|
|
451
|
-
}
|
|
452
|
-
function schedNow(args) {
|
|
453
|
-
return optionalString(args.now) || new Date().toISOString();
|
|
454
|
-
}
|
|
455
|
-
function isTrue(value) {
|
|
456
|
-
return value === true || value === "true" || value === "1";
|
|
457
|
-
}
|
|
458
|
-
function schedPlan(reg, args) {
|
|
459
|
-
return (0, scheduling_1.planSchedule)(reg.loadQueueEntries(), loadSchedulingPolicy(reg).policy, schedNow(args));
|
|
460
|
-
}
|
|
461
|
-
function schedLease(reg, args) {
|
|
462
|
-
const now = schedNow(args);
|
|
463
|
-
const policy = loadSchedulingPolicy(reg).policy;
|
|
464
|
-
const limit = args.limit === undefined ? undefined : Number(args.limit);
|
|
465
|
-
const { entries, leases } = (0, scheduling_1.applyLease)(reg.loadQueueEntries(), policy, now, limit);
|
|
466
|
-
reg.saveQueueEntries(entries);
|
|
467
|
-
return { schemaVersion: 1, now, granted: leases.length, leases };
|
|
468
|
-
}
|
|
469
|
-
function schedRelease(reg, args) {
|
|
470
|
-
const now = schedNow(args);
|
|
471
|
-
const failed = isTrue(args.failed);
|
|
472
|
-
const { entries, matched } = (0, scheduling_1.leaseRelease)(reg.loadQueueEntries(), String(args.leaseId || ""), loadSchedulingPolicy(reg).policy, now, {
|
|
473
|
-
failed,
|
|
474
|
-
reason: optionalString(args.reason)
|
|
475
|
-
});
|
|
476
|
-
if (!matched)
|
|
477
|
-
throw new Error(`No active lease to release: ${args.leaseId}`);
|
|
478
|
-
reg.saveQueueEntries(entries);
|
|
479
|
-
return { schemaVersion: 1, released: String(args.leaseId || ""), failed };
|
|
480
|
-
}
|
|
481
|
-
function schedComplete(reg, args) {
|
|
482
|
-
const { entries, matched } = (0, scheduling_1.leaseComplete)(reg.loadQueueEntries(), String(args.leaseId || ""), schedNow(args));
|
|
483
|
-
if (!matched)
|
|
484
|
-
throw new Error(`No active lease to complete: ${args.leaseId}`);
|
|
485
|
-
reg.saveQueueEntries(entries);
|
|
486
|
-
return { schemaVersion: 1, completed: String(args.leaseId || "") };
|
|
487
|
-
}
|
|
488
|
-
function schedReclaim(reg, args) {
|
|
489
|
-
const now = schedNow(args);
|
|
490
|
-
const { entries, reclaimed } = (0, scheduling_1.reclaimExpired)(reg.loadQueueEntries(), loadSchedulingPolicy(reg).policy, now);
|
|
491
|
-
reg.saveQueueEntries(entries);
|
|
492
|
-
return { schemaVersion: 1, now, reclaimed };
|
|
493
|
-
}
|
|
494
|
-
function schedReset(reg, args) {
|
|
495
|
-
const { entries, matched } = (0, scheduling_1.resetEntry)(reg.loadQueueEntries(), String(args.id || ""));
|
|
496
|
-
if (!matched)
|
|
497
|
-
throw new Error(`No parked entry to reset: ${args.id}`);
|
|
498
|
-
reg.saveQueueEntries(entries);
|
|
499
|
-
return { schemaVersion: 1, reset: String(args.id || "") };
|
|
500
|
-
}
|
|
501
|
-
function schedPolicyShow(reg) {
|
|
502
|
-
const { policy, source } = loadSchedulingPolicy(reg);
|
|
503
|
-
return { schemaVersion: 1, policy, source };
|
|
504
|
-
}
|
|
505
|
-
function schedPolicySet(reg, args) {
|
|
506
|
-
const current = loadSchedulingPolicy(reg).policy;
|
|
507
|
-
const patch = {};
|
|
508
|
-
for (const key of ["maxConcurrent", "maxAttempts", "leaseTtlMs", "backoffBaseMs", "backoffFactor", "backoffCapMs"]) {
|
|
509
|
-
if (args[key] === undefined)
|
|
510
|
-
continue;
|
|
511
|
-
// Fail closed on a non-numeric flag instead of letting normalizeSchedulingPolicy
|
|
512
|
-
// silently substitute the DEFAULT (which would report source:"file" + exit 0,
|
|
513
|
-
// so the operator believes they set a value they didn't). Matches the
|
|
514
|
-
// Number.isFinite guard the sibling reclaimPolicyFrom already uses.
|
|
515
|
-
const value = Number(args[key]);
|
|
516
|
-
if (!Number.isFinite(value)) {
|
|
517
|
-
throw new Error(`Invalid --${key} "${String(args[key])}": expected a number (e.g. --${key} 4)`);
|
|
518
|
-
}
|
|
519
|
-
patch[key] = value;
|
|
520
|
-
}
|
|
521
|
-
const policy = (0, scheduling_1.normalizeSchedulingPolicy)({ ...current, ...patch });
|
|
522
|
-
(0, state_1.writeJson)(reg.schedulingPolicyPath(), policy);
|
|
523
|
-
return { schemaVersion: 1, policy, source: "file" };
|
|
524
|
-
}
|
|
525
|
-
// ---- agent delegation drive (v0.1.38) -------------------------------------
|
|
526
|
-
// MECHANISM, ONE SOURCE: both surfaces route drive/preview/config through these.
|
|
527
|
-
// The read-only preview + config-show payloads are deterministic (counts from
|
|
528
|
-
// state, host-stable config path) with NO now-derived numeric field, so
|
|
529
|
-
// `cw <cmd> --json` is byte-identical to `cw_<tool>`.
|
|
530
|
-
/** Read-only, deterministic preview of a run's NEXT drive step. */
|
|
531
|
-
function runDrivePreview(runner, args) {
|
|
532
|
-
return (0, drive_1.drivePreview)(runner, String(args.runId || ""), args);
|
|
533
|
-
}
|
|
534
|
-
const DRIVE_RUNTIME_KEYS = [
|
|
535
|
-
"once",
|
|
536
|
-
"now",
|
|
537
|
-
"preview",
|
|
538
|
-
"step",
|
|
539
|
-
"drive",
|
|
540
|
-
"json",
|
|
541
|
-
"format",
|
|
542
|
-
"run",
|
|
543
|
-
"runId",
|
|
544
|
-
"cwd",
|
|
545
|
-
"agentCommand",
|
|
546
|
-
"agent-command",
|
|
547
|
-
"agentArgs",
|
|
548
|
-
"agent-args",
|
|
549
|
-
"agentEndpoint",
|
|
550
|
-
"agent-endpoint",
|
|
551
|
-
"agentModel",
|
|
552
|
-
"agent-model",
|
|
553
|
-
"agentTimeoutMs",
|
|
554
|
-
"agent-timeout-ms",
|
|
555
|
-
"resume",
|
|
556
|
-
"incremental",
|
|
557
|
-
"concurrency",
|
|
558
|
-
// Remote-source flags (v0.1.91): materialized into a local checkout in the capability
|
|
559
|
-
// layer, never passed to plan as inputs (the resolved sourceUrl/sourceCommit ARE inputs).
|
|
560
|
-
"link",
|
|
561
|
-
"ref",
|
|
562
|
-
"branch",
|
|
563
|
-
"refresh"
|
|
564
|
-
];
|
|
565
|
-
function planInputsFor(args) {
|
|
566
|
-
const copy = withoutRuntimeKeys(args);
|
|
567
|
-
for (const key of DRIVE_RUNTIME_KEYS)
|
|
568
|
-
delete copy[key];
|
|
569
|
-
return copy;
|
|
570
|
-
}
|
|
571
|
-
/** Mutating drive step/run. Plans a fresh run for an app id, or continues a run id.
|
|
572
|
-
* The agent hop goes ONLY through the agent backend; this composes existing verbs.
|
|
573
|
-
*
|
|
574
|
-
* The run lives under its repo's `.cw/` and every runner verb resolves a run from
|
|
575
|
-
* the process cwd, so the drive operates WITH the run's repo as cwd (exactly how
|
|
576
|
-
* the golden path runs each verb from the repo dir) — then restores cwd. This lets
|
|
577
|
-
* `cw run <app> --drive --repo X` work when invoked from anywhere. */
|
|
578
|
-
function runDrive(runner, args) {
|
|
579
|
-
let runId = optionalString(args.runId || args.run);
|
|
580
|
-
let repoCwd = optionalString(args.repo);
|
|
581
|
-
if (!runId) {
|
|
582
|
-
const appId = String(args.appId || args.workflowId || args.app || "");
|
|
583
|
-
if (!appId)
|
|
584
|
-
throw new Error("run --drive requires an app id (or --run <run-id> to continue)");
|
|
585
|
-
const run = runner.plan(appId, planInputsFor(args));
|
|
586
|
-
runId = run.id;
|
|
587
|
-
repoCwd = run.cwd;
|
|
588
|
-
}
|
|
589
|
-
// The runner resolves the run from its baseDir, so the drive must run WITH the
|
|
590
|
-
// run's repo as base. Pass it explicitly via withBaseDir (F7 — no process.chdir).
|
|
591
|
-
const target = repoCwd && node_fs_1.default.existsSync(repoCwd) ? repoCwd : undefined;
|
|
592
|
-
const driveRunId = runId;
|
|
593
|
-
return (0, drive_1.drive)(runner.withBaseDir(target), driveRunId, {
|
|
594
|
-
once: isTrue(args.once),
|
|
595
|
-
now: optionalString(args.now),
|
|
596
|
-
incremental: isTrue(args.incremental),
|
|
597
|
-
concurrency: (0, cli_options_1.numberOption)(args.concurrency),
|
|
598
|
-
args
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
/** The app the one-command quickstart plans when none is named. */
|
|
602
|
-
exports.QUICKSTART_DEFAULT_APP = "architecture-review";
|
|
603
|
-
/** ONE-COMMAND quickstart (v0.1.38+): plan(app) -> run --drive -> report in a single
|
|
604
|
-
* invocation, so a newcomer gets a cited risk report from one command on a target
|
|
605
|
-
* repo. This is a THIN UX wrapper — NOT a new engine: it composes the EXISTING
|
|
606
|
-
* `runDrive` core (which already plans the run, then delegates every worker to the
|
|
607
|
-
* configured agent backend and commits) and then writes the report. It introduces
|
|
608
|
-
* no second executor, queue, or scheduler, and imports no model SDK.
|
|
609
|
-
*
|
|
610
|
-
* RED LINE (DIRECTION.md): worker execution still DELEGATES to the operator's own
|
|
611
|
-
* agent backend (claude -p / codex exec / HTTP endpoint). With no agent configured
|
|
612
|
-
* the drive fails closed (status=blocked) and we never fabricate a completion. */
|
|
613
|
-
/** Collect a COMPACT findings list for the end-of-run summary by re-parsing each completed
|
|
614
|
-
* worker's `result.md` `cw:result` block (the schema source of truth — `normalizeResultEnvelope`).
|
|
615
|
-
* Stderr/human-side ONLY: this NEVER touches the byte-exact stdout payload, so it cannot perturb
|
|
616
|
-
* `--json` or the `cw:result` fence. `baseDir` anchors to the run's OWN repo (quickstart may run
|
|
617
|
-
* cross-directory). Best-effort: a missing/garbled result.md or an unloadable run is skipped, not
|
|
618
|
-
* fatal — the full prose still lives in report.md + each worker transcript. */
|
|
619
|
-
function collectRunFindings(runner, runId, baseDir) {
|
|
620
|
-
const rows = [];
|
|
621
|
-
try {
|
|
622
|
-
const run = runner.withBaseDir(baseDir).loadRun(runId);
|
|
623
|
-
for (const task of run.tasks) {
|
|
624
|
-
if (task.status !== "completed" || !task.resultPath || !node_fs_1.default.existsSync(task.resultPath))
|
|
625
|
-
continue;
|
|
626
|
-
try {
|
|
627
|
-
const envelope = (0, result_normalize_1.normalizeResultEnvelope)(node_fs_1.default.readFileSync(task.resultPath, "utf8"));
|
|
628
|
-
for (const f of envelope.findings) {
|
|
629
|
-
rows.push({ id: f.id, severity: f.severity || "none", classification: f.classification || "unknown" });
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
catch {
|
|
633
|
-
/* skip a garbled result.md — the transcript still holds the full record */
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
catch {
|
|
638
|
-
/* run not loadable on this host — the summary degrades to no findings table */
|
|
639
|
-
}
|
|
640
|
-
return rows;
|
|
641
|
-
}
|
|
642
|
-
function quickstart(runner, args) {
|
|
643
|
-
const appId = String(args.appId || args.app || args.workflowId || exports.QUICKSTART_DEFAULT_APP);
|
|
644
|
-
// Remote source (v0.1.91): a `--link <url>` — or a URL passed to `--repo`/`-dir` — is
|
|
645
|
-
// materialized to a LOCAL checkout in the capability layer (below). Cloning is
|
|
646
|
-
// non-deterministic network I/O and must never enter the replay-deterministic core, so we
|
|
647
|
-
// rewrite `args.repo` to the local path here; everything downstream is a normal local run.
|
|
648
|
-
const linkArg = optionalString(args.link);
|
|
649
|
-
const repoArgRaw = optionalString(args.repo);
|
|
650
|
-
const remoteCandidate = linkArg || (repoArgRaw && (0, remote_source_1.isRemoteUrl)(repoArgRaw) ? repoArgRaw : undefined);
|
|
651
|
-
// Run anywhere (like brew): default the repo-under-review to the caller's cwd when no
|
|
652
|
-
// --repo/--cwd/--link is given. A remote candidate is materialized below, so it must NOT
|
|
653
|
-
// fall through to the cwd default here.
|
|
654
|
-
if (!remoteCandidate && !optionalString(args.repo) && !optionalString(args.cwd)) {
|
|
655
|
-
args.repo = invocationCwd(args);
|
|
656
|
-
}
|
|
657
|
-
const agentConfigured = Boolean((0, agent_config_1.resolveAgentConfig)(args).command || (0, agent_config_1.resolveAgentConfig)(args).endpoint);
|
|
658
|
-
if (isTrue(args.check)) {
|
|
659
|
-
return remoteCandidate
|
|
660
|
-
? remoteQuickstartCheck(runner, appId, args, agentConfigured, remoteCandidate)
|
|
661
|
-
: quickstartCheck(runner, appId, args, agentConfigured);
|
|
662
|
-
}
|
|
663
|
-
// Materialize the remote NOW — after `--check` (which never fetches) and before any
|
|
664
|
-
// plan/preview/drive — so the orchestrator only ever sees the local checkout. Fails closed:
|
|
665
|
-
// a bad URL / blocked scheme / network / auth failure throws here, before any run is planned.
|
|
666
|
-
let remoteSource;
|
|
667
|
-
if (remoteCandidate) {
|
|
668
|
-
remoteSource = (0, remote_source_1.materializeRemote)(remoteCandidate, {
|
|
669
|
-
ref: optionalString(args.ref || args.branch),
|
|
670
|
-
refresh: isTrue(args.refresh)
|
|
671
|
-
});
|
|
672
|
-
args.repo = remoteSource.localPath;
|
|
673
|
-
// Record the origin as plan INPUTS so it rides into run.inputs → the report header.
|
|
674
|
-
args.sourceUrl = remoteSource.url;
|
|
675
|
-
args.sourceCommit = remoteSource.commit;
|
|
676
|
-
if (remoteSource.ref)
|
|
677
|
-
args.sourceRef = remoteSource.ref;
|
|
678
|
-
}
|
|
679
|
-
// `--resume`: a discoverability flag over the existing continuation. With no
|
|
680
|
-
// `--run`, advance exactly ONE step (reuse the `--once` path) and print a
|
|
681
|
-
// copy-pasteable continue line; with `--run <id>`, continue that run to
|
|
682
|
-
// completion (the default drive). It adds no new execution path.
|
|
683
|
-
const resume = isTrue(args.resume);
|
|
684
|
-
const resumeRunId = resume ? optionalString(args.runId || args.run) : undefined;
|
|
685
|
-
// `--preview`: read-only, deterministic next-step projection (no spawn, no commit).
|
|
686
|
-
// Plan a fresh run (the read-only first verb) then project the next drive step.
|
|
687
|
-
if (isTrue(args.preview)) {
|
|
688
|
-
let runId = optionalString(args.runId || args.run);
|
|
689
|
-
let repoCwd = optionalString(args.repo);
|
|
690
|
-
if (!runId) {
|
|
691
|
-
const run = runner.plan(appId, planInputsFor(args));
|
|
692
|
-
runId = run.id;
|
|
693
|
-
repoCwd = run.cwd;
|
|
694
|
-
}
|
|
695
|
-
const previewRunId = runId;
|
|
696
|
-
const target = repoCwd && node_fs_1.default.existsSync(repoCwd) ? repoCwd : undefined;
|
|
697
|
-
return (0, drive_1.drivePreview)(runner.withBaseDir(target), previewRunId, args);
|
|
698
|
-
}
|
|
699
|
-
// Drive end-to-end (or one `--once` step). runDrive plans the run, delegates each
|
|
700
|
-
// worker to the agent backend, and commits — we add only the report write + a
|
|
701
|
-
// single assembled payload. No orchestration is duplicated here.
|
|
702
|
-
// `--resume` with no run id advances a single step so a newcomer WITNESSES the
|
|
703
|
-
// stop-then-resume; with a run id it continues to completion. Non-resume paths
|
|
704
|
-
// are untouched (byte-identical default).
|
|
705
|
-
const result = runDrive(runner, { ...args, appId, ...(resume && !resumeRunId ? { once: true } : {}) });
|
|
706
|
-
// Always (re)write the report so the one command yields a report.md on disk, even
|
|
707
|
-
// when the drive blocked/parked (a partial report is still useful triage).
|
|
708
|
-
//
|
|
709
|
-
// runDrive restored cwd, so the runs root would resolve against the CALLER's cwd
|
|
710
|
-
// here — orphaning the run when quickstart is invoked cross-directory (cwd =
|
|
711
|
-
// plugin dir, --repo elsewhere: the README's headline command). The run's
|
|
712
|
-
// statePath (<repo>/.cw/runs/<id>/state.json) is authoritative however the run
|
|
713
|
-
// was planned or continued; resolve to ITS repo BEFORE any run read, reentrant-safe.
|
|
714
|
-
const runRepoCwd = node_path_1.default.resolve(node_path_1.default.dirname(result.statePath), "..", "..", "..");
|
|
715
|
-
const reportTarget = node_fs_1.default.existsSync(runRepoCwd) ? runRepoCwd : undefined;
|
|
716
|
-
const reportPath = runner.withBaseDir(reportTarget).report(result.runId).path;
|
|
717
|
-
// Tamper-evident provenance: bind the remote origin (url@sha) into the run's hash-chained
|
|
718
|
-
// trust-audit log so `cw audit verify` re-proves where the code came from. metadata is
|
|
719
|
-
// auto-scrubbed of credentials by recordTrustAuditEvent → scrubMetadata. Best-effort: the
|
|
720
|
-
// origin is already in run.inputs/report/the result, so a recording hiccup never fails the
|
|
721
|
-
// review (additive trust evidence, not a gate).
|
|
722
|
-
if (remoteSource) {
|
|
723
|
-
try {
|
|
724
|
-
const provRun = runner.withBaseDir(reportTarget).loadRun(result.runId);
|
|
725
|
-
(0, trust_audit_1.recordTrustAuditEvent)(provRun, {
|
|
726
|
-
kind: remoteSource.kind === "archive" ? "source.download" : "source.clone",
|
|
727
|
-
decision: "recorded",
|
|
728
|
-
source: "operator-recorded",
|
|
729
|
-
metadata: { url: remoteSource.url, commit: remoteSource.commit, ref: remoteSource.ref || null, kind: remoteSource.kind, depth: 1 }
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
catch {
|
|
733
|
-
/* provenance is additive; never fail a completed review over an audit-log hiccup */
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
// --bundle: after a COMPLETE drive, seal the run into a portable, self-verified
|
|
737
|
-
// bundle so the one command yields a client-verifiable artifact. Pure composition
|
|
738
|
-
// of reportBundle() (export sealed + offline self-verify); spawns nothing. Gated on
|
|
739
|
-
// completion: a partial or blocked run is NEVER sealed (you must not ship an
|
|
740
|
-
// uncommitted artifact).
|
|
741
|
-
//
|
|
742
|
-
// Run-state resolution MUST anchor to the run's OWN repo (reportTarget): the README
|
|
743
|
-
// headline runs quickstart cross-directory (caller cwd != --repo), so a caller-cwd
|
|
744
|
-
// loadRun would not find the run. But operator-supplied OUTPUT paths
|
|
745
|
-
// (--output/--extract-report) and the default archive name resolve against the
|
|
746
|
-
// CALLER's cwd — so artifacts land where the operator ran the command (and `&&
|
|
747
|
-
// send out.md` works) and never pollute the analyzed repo's working tree, matching
|
|
748
|
-
// standalone `report bundle`. Pre-resolving to absolute makes path.resolve(base, …)
|
|
749
|
-
// inside reportBundle a no-op, so the run-repo cwd cannot reclaim them.
|
|
750
|
-
let bundle;
|
|
751
|
-
const wantsBundle = flag(args.bundle) === true;
|
|
752
|
-
if (wantsBundle && result.status === "complete") {
|
|
753
|
-
const callerBase = invocationCwd(args);
|
|
754
|
-
const outArg = optionalString(args.output || args.path || args.archive);
|
|
755
|
-
const extractArg = optionalString(args["extract-report"] || args.extractReport || args.extractReportTo);
|
|
756
|
-
bundle = reportBundle(runner, result.runId, {
|
|
757
|
-
...args,
|
|
758
|
-
cwd: reportTarget,
|
|
759
|
-
output: node_path_1.default.resolve(callerBase, outArg || `${result.runId}.cwrun.json`),
|
|
760
|
-
...(extractArg ? { "extract-report": node_path_1.default.resolve(callerBase, extractArg) } : {})
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
let hint;
|
|
764
|
-
if (!agentConfigured) {
|
|
765
|
-
hint =
|
|
766
|
-
"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.";
|
|
767
|
-
}
|
|
768
|
-
else if (result.status === "parked") {
|
|
769
|
-
hint = `a worker parked past its retry budget — inspect: cw run show ${result.runId}`;
|
|
770
|
-
}
|
|
771
|
-
else if (result.status === "blocked") {
|
|
772
|
-
hint = `the drive is blocked — inspect: cw run drive ${result.runId}`;
|
|
773
|
-
}
|
|
774
|
-
else if (result.status === "in-progress") {
|
|
775
|
-
hint = resume
|
|
776
|
-
? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume${wantsBundle ? " --bundle" : ""}`
|
|
777
|
-
: `one step advanced (--once) — continue: cw quickstart ${appId} --run ${result.runId} --once`;
|
|
778
|
-
}
|
|
779
|
-
// --bundle on a run that didn't complete is a NO-OP, not silence: tell the operator
|
|
780
|
-
// why nothing was sealed (Rule of Silence permits a human-facing hint).
|
|
781
|
-
if (wantsBundle && result.status !== "complete") {
|
|
782
|
-
hint = `${hint ? `${hint} ` : ""}--bundle skipped: the run did not complete (status=${result.status}); no bundle was sealed.`;
|
|
783
|
-
}
|
|
784
|
-
return {
|
|
785
|
-
schemaVersion: 1,
|
|
786
|
-
appId,
|
|
787
|
-
runId: result.runId,
|
|
788
|
-
workflowId: result.workflowId,
|
|
789
|
-
status: result.status,
|
|
790
|
-
plannedWorkers: result.plannedWorkers,
|
|
791
|
-
completedWorkers: result.completedWorkers,
|
|
792
|
-
parkedWorkers: result.parkedWorkers,
|
|
793
|
-
commitId: result.commitId,
|
|
794
|
-
reportPath,
|
|
795
|
-
statePath: result.statePath,
|
|
796
|
-
agentConfigured,
|
|
797
|
-
steps: result.steps,
|
|
798
|
-
hint,
|
|
799
|
-
// Stamp resumedFrom ONLY when we continued an explicit run. Conditional spread
|
|
800
|
-
// keeps the key absent on the default/fresh path (own-property absent + omitted
|
|
801
|
-
// by JSON.stringify), so default output is byte-identical.
|
|
802
|
-
...(resumeRunId ? { resumedFrom: resumeRunId } : {}),
|
|
803
|
-
// Same conditional-spread discipline: `bundle` is present only when --bundle ran
|
|
804
|
-
// on a completed drive, so the default (no --bundle) payload is byte-identical.
|
|
805
|
-
...(bundle ? { bundle } : {}),
|
|
806
|
-
// `remote` is present only when the review targeted a --link/URL source, so a local-repo
|
|
807
|
-
// run stays byte-identical. Carries the sanitized origin + resolved commit for provenance.
|
|
808
|
-
...(remoteSource
|
|
809
|
-
? { remote: { url: remoteSource.url, commit: remoteSource.commit, kind: remoteSource.kind, cached: remoteSource.cached, ...(remoteSource.ref ? { ref: remoteSource.ref } : {}) } }
|
|
810
|
-
: {})
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
function quickstartCheck(runner, appId, args, agentConfigured) {
|
|
814
|
-
const base = invocationCwd(args);
|
|
815
|
-
const repoArg = optionalString(args.repo) || base;
|
|
816
|
-
const repo = node_path_1.default.resolve(base, repoArg);
|
|
817
|
-
const checks = [];
|
|
818
|
-
try {
|
|
819
|
-
runner.showApp(appId);
|
|
820
|
-
checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
|
|
821
|
-
}
|
|
822
|
-
catch (error) {
|
|
823
|
-
checks.push({
|
|
824
|
-
name: "app",
|
|
825
|
-
status: "blocked",
|
|
826
|
-
detail: `Workflow app ${appId} is not available.`,
|
|
827
|
-
fix: "Run `cw app list` and choose one of the listed app ids."
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
let repoReadable = false;
|
|
831
|
-
let repoStateWritable = false;
|
|
832
|
-
try {
|
|
833
|
-
const stat = node_fs_1.default.statSync(repo);
|
|
834
|
-
repoReadable = stat.isDirectory();
|
|
835
|
-
if (!repoReadable)
|
|
836
|
-
throw new Error("not a directory");
|
|
837
|
-
node_fs_1.default.accessSync(repo, node_fs_1.default.constants.R_OK);
|
|
838
|
-
checks.push({ name: "repo", status: "ok", detail: `Repository path is readable (${repo}).` });
|
|
839
|
-
}
|
|
840
|
-
catch (error) {
|
|
841
|
-
checks.push({
|
|
842
|
-
name: "repo",
|
|
843
|
-
status: "blocked",
|
|
844
|
-
detail: `Repository path is not readable (${repo}).`,
|
|
845
|
-
fix: "Pass --repo PATH for a readable repository directory."
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
try {
|
|
849
|
-
const cwDir = node_path_1.default.join(repo, ".cw");
|
|
850
|
-
node_fs_1.default.accessSync(node_fs_1.default.existsSync(cwDir) ? cwDir : repo, node_fs_1.default.constants.W_OK);
|
|
851
|
-
repoStateWritable = repoReadable;
|
|
852
|
-
checks.push({ name: "repo-state", status: "ok", detail: "Run state location is writable." });
|
|
853
|
-
}
|
|
854
|
-
catch (error) {
|
|
855
|
-
checks.push({
|
|
856
|
-
name: "repo-state",
|
|
857
|
-
status: "blocked",
|
|
858
|
-
detail: "Run state location is not writable.",
|
|
859
|
-
fix: "Use a writable repo, fix directory permissions, or pass --repo to a writable checkout."
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
if (optionalString(args.question)) {
|
|
863
|
-
checks.push({ name: "question", status: "ok", detail: "Question is set." });
|
|
864
|
-
}
|
|
865
|
-
else {
|
|
866
|
-
checks.push({
|
|
867
|
-
name: "question",
|
|
868
|
-
status: "blocked",
|
|
869
|
-
detail: "Question is missing.",
|
|
870
|
-
fix: "Pass --question TEXT."
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
if (agentConfigured) {
|
|
874
|
-
checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
|
|
875
|
-
}
|
|
876
|
-
else {
|
|
877
|
-
checks.push({
|
|
878
|
-
name: "agent",
|
|
879
|
-
status: "blocked",
|
|
880
|
-
detail: "No agent backend is configured.",
|
|
881
|
-
fix: "Pass --agent-command \"claude -p\", set $CW_AGENT_COMMAND, or use --agent-command builtin:claude."
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
if (flag(args.bundle) === true) {
|
|
885
|
-
const trustKey = optionalString(args["with-trust-key"] || args.withTrustKey || args.trustKey || args.pubkey) || process.env.CW_AGENT_ATTEST_PUBKEY;
|
|
886
|
-
if (trustKey) {
|
|
887
|
-
checks.push({ name: "bundle-trust-key", status: "ok", detail: "Bundle trust public key is configured." });
|
|
888
|
-
}
|
|
889
|
-
else if (Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs)) {
|
|
890
|
-
checks.push({
|
|
891
|
-
name: "bundle-trust-key",
|
|
892
|
-
status: "blocked",
|
|
893
|
-
detail: "Strict signature verification needs a public trust key.",
|
|
894
|
-
fix: "Pass --with-trust-key PATH or set $CW_AGENT_ATTEST_PUBKEY."
|
|
895
|
-
});
|
|
896
|
-
}
|
|
897
|
-
else {
|
|
898
|
-
checks.push({
|
|
899
|
-
name: "bundle-trust-key",
|
|
900
|
-
status: "warn",
|
|
901
|
-
detail: "No public trust key is configured; unsigned or unkeyed bundles may verify with reduced signature proof.",
|
|
902
|
-
fix: "Pass --with-trust-key PATH to embed the public key."
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
const ok = checks.every((check) => check.status !== "blocked") && repoStateWritable;
|
|
907
|
-
return {
|
|
908
|
-
schemaVersion: 1,
|
|
909
|
-
mode: "check",
|
|
910
|
-
ok,
|
|
911
|
-
appId,
|
|
912
|
-
repo,
|
|
913
|
-
checks,
|
|
914
|
-
nextCommand: quickstartNextCommand(appId, repo, args)
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
/** Preflight for a `--link`/URL review: validate the URL SHAPE + tooling WITHOUT fetching
|
|
918
|
-
* (a clone is heavy and side-effecting; --check stays read-only). Mirrors quickstartCheck's
|
|
919
|
-
* app/question/agent sub-checks but swaps the local-repo readability checks for link+tooling.
|
|
920
|
-
* `repo` carries the sanitized URL so the result reports what would be reviewed. */
|
|
921
|
-
function remoteQuickstartCheck(runner, appId, args, agentConfigured, candidate) {
|
|
922
|
-
const validation = (0, remote_source_1.validateRemoteUrl)(candidate);
|
|
923
|
-
const checks = [];
|
|
924
|
-
try {
|
|
925
|
-
runner.showApp(appId);
|
|
926
|
-
checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
|
|
927
|
-
}
|
|
928
|
-
catch (error) {
|
|
929
|
-
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." });
|
|
930
|
-
}
|
|
931
|
-
if (validation.ok) {
|
|
932
|
-
checks.push({ name: "link", status: "ok", detail: `Remote source is a valid ${validation.kind} URL (${validation.url}).` });
|
|
933
|
-
}
|
|
934
|
-
else {
|
|
935
|
-
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)." });
|
|
936
|
-
}
|
|
937
|
-
if ((0, remote_source_1.gitAvailable)()) {
|
|
938
|
-
checks.push({ name: "tooling", status: "ok", detail: "git is available to clone the remote." });
|
|
939
|
-
}
|
|
940
|
-
else {
|
|
941
|
-
checks.push({ name: "tooling", status: "blocked", detail: "git was not found on PATH.", fix: "Install git, then re-run." });
|
|
942
|
-
}
|
|
943
|
-
if (optionalString(args.question)) {
|
|
944
|
-
checks.push({ name: "question", status: "ok", detail: "Question is set." });
|
|
945
|
-
}
|
|
946
|
-
else {
|
|
947
|
-
checks.push({ name: "question", status: "blocked", detail: "Question is missing.", fix: "Pass --question TEXT." });
|
|
948
|
-
}
|
|
949
|
-
if (agentConfigured) {
|
|
950
|
-
checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
|
|
951
|
-
}
|
|
952
|
-
else {
|
|
953
|
-
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." });
|
|
954
|
-
}
|
|
955
|
-
const ok = checks.every((check) => check.status !== "blocked");
|
|
956
|
-
return {
|
|
957
|
-
schemaVersion: 1,
|
|
958
|
-
mode: "check",
|
|
959
|
-
ok,
|
|
960
|
-
appId,
|
|
961
|
-
repo: validation.url,
|
|
962
|
-
checks,
|
|
963
|
-
nextCommand: `cw quickstart ${shellWord(appId)} --link ${shellWord(validation.url)}${optionalString(args.question) ? ` --question ${shellWord(String(args.question))}` : ""}`
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
function quickstartNextCommand(appId, repo, args) {
|
|
967
|
-
const parts = ["cw", "quickstart", shellWord(appId), "--repo", shellWord(repo)];
|
|
968
|
-
const question = optionalString(args.question);
|
|
969
|
-
if (question)
|
|
970
|
-
parts.push("--question", shellWord(question));
|
|
971
|
-
const command = optionalString(args.agentCommand || args["agent-command"]);
|
|
972
|
-
if (command)
|
|
973
|
-
parts.push("--agent-command", shellWord(command));
|
|
974
|
-
if (flag(args.bundle) === true)
|
|
975
|
-
parts.push("--bundle");
|
|
976
|
-
const trustKey = optionalString(args["with-trust-key"] || args.withTrustKey || args.trustKey);
|
|
977
|
-
if (trustKey)
|
|
978
|
-
parts.push("--with-trust-key", shellWord(trustKey));
|
|
979
|
-
if (Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs))
|
|
980
|
-
parts.push("--strict-signatures");
|
|
981
|
-
return parts.join(" ");
|
|
982
|
-
}
|
|
983
|
-
function shellWord(value) {
|
|
984
|
-
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
|
|
985
|
-
return value;
|
|
986
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
987
|
-
}
|
|
988
|
-
/** Read-only, deterministic projection of the effective agent config (secret-stripped). */
|
|
989
|
-
function backendAgentConfigShow(args) {
|
|
990
|
-
return (0, agent_config_1.agentConfigShow)(args);
|
|
991
|
-
}
|
|
992
|
-
/** Persist the durable agent config (secret-stripped) and return the new state. */
|
|
993
|
-
function backendAgentConfigSet(args) {
|
|
994
|
-
(0, agent_config_1.setAgentConfigFile)(args);
|
|
995
|
-
return (0, agent_config_1.agentConfigShow)(args);
|
|
996
|
-
}
|
|
997
|
-
// ---- run retention & provable reclamation (v0.1.39) -----------------------
|
|
998
|
-
// MECHANISM, ONE SOURCE: both surfaces route gc plan/run/verify through these.
|
|
999
|
-
// `gc plan`/`gc verify` are read-only + deterministic (only `generatedAt` is
|
|
1000
|
-
// now-derived ISO, allowed by the parity rule); `gc run` is the disk-freeing tier.
|
|
1001
|
-
function reclaimPolicyFrom(args) {
|
|
1002
|
-
const policy = {};
|
|
1003
|
-
const days = Number(args.reclaimAfterArchiveDays ?? args["reclaim-after-archive-days"] ?? args.olderThanDays ?? args["older-than-days"]);
|
|
1004
|
-
if (Number.isFinite(days))
|
|
1005
|
-
policy.reclaimAfterArchiveDays = days;
|
|
1006
|
-
const keepScratch = flag(args.keepScratch ?? args["keep-scratch"]);
|
|
1007
|
-
if (keepScratch !== undefined)
|
|
1008
|
-
policy.keepScratch = keepScratch;
|
|
1009
|
-
const keepSnapshots = flag(args.keepSnapshots ?? args["keep-snapshots"]);
|
|
1010
|
-
if (keepSnapshots !== undefined)
|
|
1011
|
-
policy.keepSnapshots = keepSnapshots;
|
|
1012
|
-
const maxRuns = Number(args.maxReclaimRuns ?? args["max-reclaim-runs"]);
|
|
1013
|
-
if (Number.isFinite(maxRuns))
|
|
1014
|
-
policy.maxReclaimRuns = maxRuns;
|
|
1015
|
-
const maxBytes = Number(args.maxReclaimBytes ?? args["max-reclaim-bytes"]);
|
|
1016
|
-
if (Number.isFinite(maxBytes))
|
|
1017
|
-
policy.maxReclaimBytes = maxBytes;
|
|
1018
|
-
const states = parseLifecycleList(args.state ?? args.status);
|
|
1019
|
-
if (states.length)
|
|
1020
|
-
policy.reclaimStates = states;
|
|
1021
|
-
return policy;
|
|
1022
|
-
}
|
|
1023
|
-
function gcPlan(reg, runId, args) {
|
|
1024
|
-
return reg.gcPlan({ scope: scopeOf(args, "home"), runId: runId || optionalString(args.runId), policy: reclaimPolicyFrom(args), now: optionalString(args.now) });
|
|
1025
|
-
}
|
|
1026
|
-
function gcRun(reg, runId, args) {
|
|
1027
|
-
return reg.gcRun({
|
|
1028
|
-
scope: scopeOf(args, "home"),
|
|
1029
|
-
runId: runId || optionalString(args.runId),
|
|
1030
|
-
policy: reclaimPolicyFrom(args),
|
|
1031
|
-
now: optionalString(args.now),
|
|
1032
|
-
actor: optionalString(args.actor),
|
|
1033
|
-
limit: args.limit === undefined ? undefined : Number(args.limit)
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
function gcVerify(reg, runId, args) {
|
|
1037
|
-
return reg.gcVerify(runId, { scope: scopeOf(args, "home") });
|
|
1038
|
-
}
|
|
1039
|
-
// Remote-source clone cache (v0.1.91): list/reclaim the `~/.local/state/cool-workflow/clones`
|
|
1040
|
-
// checkouts that `--link`/URL reviews populate. Pure filesystem work; both CLI and MCP route
|
|
1041
|
-
// here so `cw clones …` and `cw_clones_…` are byte-identical.
|
|
1042
|
-
var clones_1 = require("./clones");
|
|
1043
|
-
Object.defineProperty(exports, "listClones", { enumerable: true, get: function () { return clones_1.listClones; } });
|
|
1044
|
-
Object.defineProperty(exports, "gcClones", { enumerable: true, get: function () { return clones_1.gcClones; } });
|
|
1045
|
-
function runHistory(reg, args) {
|
|
1046
|
-
return reg.history({
|
|
1047
|
-
scope: scopeOf(args, "home"),
|
|
1048
|
-
app: optionalString(args.app || args.appId),
|
|
1049
|
-
status: lifecycleOf(args.status),
|
|
1050
|
-
limit: args.limit === undefined ? undefined : Number(args.limit),
|
|
1051
|
-
offset: args.offset === undefined ? undefined : Number(args.offset)
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
// ---- observability + cost accounting (v0.1.31) ----------------------------
|
|
1055
|
-
// MECHANISM, ONE SOURCE: both `cw metrics summary --json` and `cw_metrics_summary`
|
|
1056
|
-
// route through this function. It enumerates the v0.1.28 registry (derived live
|
|
1057
|
-
// from source), loads each run's durable state, and DERIVES the cross-repo
|
|
1058
|
-
// rollup. Runs whose source is unreadable are counted in `unreadableRuns` (fail
|
|
1059
|
-
// closed), never silently dropped. Pricing is POLICY via `--pricing`; `now` is
|
|
1060
|
-
// injectable via `args.now` for eval/replay determinism.
|
|
1061
|
-
function metricsSummary(reg, runner, args) {
|
|
1062
|
-
const scope = scopeOf(args, "repo");
|
|
1063
|
-
const report = reg.show({ scope });
|
|
1064
|
-
const policy = (0, observability_1.loadCostPolicy)(args, runner.pluginRoot);
|
|
1065
|
-
const now = optionalString(args.now) || new Date().toISOString();
|
|
1066
|
-
const inputs = [];
|
|
1067
|
-
let unreadableRuns = 0;
|
|
1068
|
-
for (const record of report.index.records) {
|
|
1069
|
-
try {
|
|
1070
|
-
const loaded = (0, state_1.loadRunStateFile)(record.statePath, { dryRun: true });
|
|
1071
|
-
if (loaded.report.status === "unsupported") {
|
|
1072
|
-
unreadableRuns++;
|
|
1073
|
-
continue;
|
|
1074
|
-
}
|
|
1075
|
-
inputs.push({
|
|
1076
|
-
run: loaded.run,
|
|
1077
|
-
repo: record.repo,
|
|
1078
|
-
persistedFingerprint: (0, observability_1.loadPersistedMetricsFingerprint)(loaded.run)
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
|
-
catch {
|
|
1082
|
-
unreadableRuns++;
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
return (0, observability_1.deriveMetricsSummary)(inputs, { now, scope, policy, unreadableRuns });
|
|
1086
|
-
}
|
|
1087
|
-
function parseLifecycleList(value) {
|
|
1088
|
-
const raw = Array.isArray(value) ? value : value === undefined ? [] : [value];
|
|
1089
|
-
const out = [];
|
|
1090
|
-
for (const item of raw) {
|
|
1091
|
-
if ((0, run_registry_1.isRunLifecycleState)(item))
|
|
1092
|
-
out.push(item);
|
|
1093
|
-
}
|
|
1094
|
-
return out;
|
|
1095
|
-
}
|
|
1096
|
-
// ---- shared argument helpers ----------------------------------------------
|
|
1097
|
-
function sandboxProfileIdFrom(args) {
|
|
1098
|
-
return optionalString(args.sandbox || args.sandboxProfile || args.sandboxProfileId || args.profileId);
|
|
1099
|
-
}
|
|
1100
|
-
function withoutRuntimeKeys(args) {
|
|
1101
|
-
const copy = { ...args };
|
|
1102
|
-
for (const key of ["appId", "workflowId", "inputs", "sandbox", "sandboxProfile", "sandboxProfileId", "profileId"]) {
|
|
1103
|
-
delete copy[key];
|
|
1104
|
-
}
|
|
1105
|
-
return copy;
|
|
1106
|
-
}
|
|
1107
|
-
function optionalString(value) {
|
|
1108
|
-
if (value === undefined || value === null || value === "")
|
|
1109
|
-
return undefined;
|
|
1110
|
-
return String(value);
|
|
1111
|
-
}
|
|
1112
|
-
function isRecord(value) {
|
|
1113
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1114
|
-
}
|
|
1115
|
-
// ---- telemetry attestation: read-only ledger verification (Track 1) --------
|
|
1116
|
-
// Re-prove a run's telemetry chain offline: prevHash linkage + independent per-
|
|
1117
|
-
// record hash recompute (never trusts the stored hash). The auditable claim made
|
|
1118
|
-
// inspectable on demand — anyone can run this; a forged/edited record fails it.
|
|
1119
|
-
function telemetryVerify(runner, args) {
|
|
1120
|
-
const runId = optionalString(args.runId || args.run);
|
|
1121
|
-
if (!runId)
|
|
1122
|
-
throw new Error("telemetry verify requires a run id (cw telemetry verify <run-id>)");
|
|
1123
|
-
const run = runner.loadRun(runId);
|
|
1124
|
-
const v = (0, telemetry_ledger_1.verifyTelemetryLedger)(run);
|
|
1125
|
-
// Opt-in independent signature re-verification. verifyTelemetryLedger re-proves
|
|
1126
|
-
// the chain (so the stored attestation verdicts were not edited); supplying the
|
|
1127
|
-
// trust public key (--pubkey / CW_AGENT_ATTEST_PUBKEY) additionally RE-RUNS the
|
|
1128
|
-
// ed25519 check over each `attested` record's stored raw usage rather than
|
|
1129
|
-
// trusting that verdict, so a forged signature can no longer ride a green chain.
|
|
1130
|
-
const trustPublicKeyInput = optionalString(args.pubkey || args.pubKey || args.publicKey) || process.env.CW_AGENT_ATTEST_PUBKEY;
|
|
1131
|
-
const trustPublicKey = (0, telemetry_attestation_1.resolveTrustPublicKey)(trustPublicKeyInput);
|
|
1132
|
-
const keyChecks = trustPublicKeyInput && !trustPublicKey
|
|
1133
|
-
? [{ name: "signature-key", pass: false, code: "telemetry-pubkey-unreadable" }]
|
|
1134
|
-
: [];
|
|
1135
|
-
const sig = (0, telemetry_attestation_1.verifyTelemetrySignatures)(v.records, trustPublicKey);
|
|
1136
|
-
const failedChecks = [...v.checks.filter((c) => !c.pass), ...keyChecks, ...sig.checks.filter((c) => !c.pass)];
|
|
1137
|
-
return {
|
|
1138
|
-
schemaVersion: 1,
|
|
1139
|
-
runId: run.id,
|
|
1140
|
-
present: v.present,
|
|
1141
|
-
// Chain integrity AND (when a key was supplied) every attested signature must
|
|
1142
|
-
// re-verify. With no key, sig.failed is 0 → unchanged chain-only behavior.
|
|
1143
|
-
verified: v.verified && keyChecks.length === 0 && sig.failed === 0,
|
|
1144
|
-
records: v.records.length,
|
|
1145
|
-
attested: v.attested,
|
|
1146
|
-
unattested: v.unattested,
|
|
1147
|
-
absent: v.absent,
|
|
1148
|
-
signatureKeyProvided: sig.keyProvided,
|
|
1149
|
-
signaturesChecked: sig.checked,
|
|
1150
|
-
signaturesReverified: sig.reverified,
|
|
1151
|
-
signaturesFailed: sig.failed,
|
|
1152
|
-
failedChecks: failedChecks.map((c) => ({ name: c.name, code: c.code }))
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
// audit.verify — fail-closed re-prove of a run's trust-audit hash chain. The peer
|
|
1156
|
-
// of telemetry.verify for the sandbox/policy/commit-gate decision log: recomputes
|
|
1157
|
-
// every event hash from genesis, checks chain linkage, and catches the
|
|
1158
|
-
// unchained-event forgery. Exposed as a verb (not just embedded in `audit summary`,
|
|
1159
|
-
// which always exits 0) so `cw audit verify <run> && deploy` can gate on the exit
|
|
1160
|
-
// code. POLA: a run with no audit log is present:false / verified:true / exit 0.
|
|
1161
|
-
function auditVerify(runner, args) {
|
|
1162
|
-
const runId = optionalString(args.runId || args.run);
|
|
1163
|
-
if (!runId)
|
|
1164
|
-
throw new Error("audit verify requires a run id (cw audit verify <run-id>)");
|
|
1165
|
-
const run = runner.loadRun(runId);
|
|
1166
|
-
const v = (0, trust_audit_1.verifyTrustAudit)(run);
|
|
1167
|
-
return {
|
|
1168
|
-
schemaVersion: 1,
|
|
1169
|
-
runId: run.id,
|
|
1170
|
-
present: v.present,
|
|
1171
|
-
verified: v.verified,
|
|
1172
|
-
eventCount: v.eventCount,
|
|
1173
|
-
chained: v.chained,
|
|
1174
|
-
unchained: v.unchained,
|
|
1175
|
-
corruptLines: v.corruptLines,
|
|
1176
|
-
failedChecks: v.checks.filter((c) => !c.pass).map((c) => ({ name: c.name, code: c.code }))
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
// ---- demo: tamper-evidence (the one-command proof) -------------------------
|
|
1180
|
-
// Hermetic, deterministic-shape: builds a real ed25519-signed telemetry ledger,
|
|
1181
|
-
// then forges it three ways and shows all three tamper-evidence layers (ledger,
|
|
1182
|
-
// signature, result) catch it. CLI-only
|
|
1183
|
-
// (a human-facing demonstration; the underlying verify is the telemetry.verify verb).
|
|
1184
|
-
function demoTamper(_runner, _args = {}) {
|
|
1185
|
-
return (0, telemetry_demo_1.runTamperDemo)();
|
|
1186
|
-
}
|
|
1187
|
-
function demoBundle(_runner, _args = {}) {
|
|
1188
|
-
return (0, telemetry_demo_1.runBundleDemo)();
|
|
1189
|
-
}
|