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.
Files changed (315) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +18 -7
  4. package/apps/architecture-review/app.json +2 -2
  5. package/apps/architecture-review/workflow.js +12 -12
  6. package/apps/architecture-review-fast/app.json +1 -1
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/cli/dispatch.js +236 -0
  12. package/dist/cli/entry.js +120 -0
  13. package/dist/cli/io.js +21 -4
  14. package/dist/cli/parseargv.js +157 -0
  15. package/dist/cli.js +6 -33
  16. package/dist/core/capability-table.js +3518 -0
  17. package/dist/core/format/help.js +314 -0
  18. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  19. package/dist/core/hash.js +137 -0
  20. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  21. package/dist/core/multi-agent/collaboration.js +481 -0
  22. package/dist/core/multi-agent/coordinator.js +515 -0
  23. package/dist/core/multi-agent/eval-replay.js +306 -0
  24. package/dist/core/multi-agent/runtime.js +929 -0
  25. package/dist/core/multi-agent/topology.js +298 -0
  26. package/dist/core/multi-agent/trust-policy.js +197 -0
  27. package/dist/core/pipeline/commit-gate.js +320 -0
  28. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  29. package/dist/core/pipeline/dispatch.js +103 -0
  30. package/dist/core/pipeline/drive-decide.js +227 -0
  31. package/dist/core/pipeline/error-feedback.js +161 -0
  32. package/dist/core/pipeline/loop-expansion.js +124 -0
  33. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  34. package/dist/core/pipeline/runner.js +230 -0
  35. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  36. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  37. package/dist/core/state/node-projection.js +95 -0
  38. package/dist/core/state/node-snapshot.js +230 -0
  39. package/dist/core/state/run-paths.js +93 -0
  40. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  41. package/dist/core/state/schema.js +50 -0
  42. package/dist/core/state/state-explosion/digest.js +243 -0
  43. package/dist/core/state/state-explosion/graph.js +527 -0
  44. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  45. package/dist/core/state/state-explosion/report.js +187 -0
  46. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  47. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  48. package/dist/core/state/types.js +19 -0
  49. package/dist/{validation.js → core/state/validation.js} +64 -131
  50. package/dist/core/trust/evidence-grounding.js +134 -0
  51. package/dist/core/trust/ledger.js +199 -0
  52. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  53. package/dist/core/trust/telemetry-ledger.js +127 -0
  54. package/dist/core/types.js +20 -0
  55. package/dist/core/version.js +16 -0
  56. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  57. package/dist/mcp/dispatch.js +104 -0
  58. package/dist/mcp/server.js +144 -0
  59. package/dist/mcp-server.js +6 -84
  60. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  61. package/dist/shell/app-run-cli.js +88 -0
  62. package/dist/shell/audit-cli.js +259 -0
  63. package/dist/shell/audit-provenance.js +83 -0
  64. package/dist/shell/candidate-scoring-io.js +459 -0
  65. package/dist/shell/collaboration-io.js +264 -0
  66. package/dist/shell/commit-summary.js +104 -0
  67. package/dist/shell/commit.js +290 -0
  68. package/dist/shell/coordinator-io.js +476 -0
  69. package/dist/shell/demo-cli.js +19 -0
  70. package/dist/shell/dispatch.js +162 -0
  71. package/dist/shell/doctor.js +292 -0
  72. package/dist/shell/drive.js +885 -0
  73. package/dist/shell/error-feedback-io.js +305 -0
  74. package/dist/shell/eval-io.js +473 -0
  75. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  76. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  77. package/dist/shell/exec-backend-cli.js +88 -0
  78. package/dist/shell/execution-backend/agent.js +507 -0
  79. package/dist/shell/execution-backend/ci.js +15 -0
  80. package/dist/shell/execution-backend/container.js +69 -0
  81. package/dist/shell/execution-backend/envelopes.js +55 -0
  82. package/dist/shell/execution-backend/local.js +113 -0
  83. package/dist/shell/execution-backend/probes.js +175 -0
  84. package/dist/shell/execution-backend/registry.js +402 -0
  85. package/dist/shell/execution-backend/remote.js +128 -0
  86. package/dist/shell/execution-backend/types.js +11 -0
  87. package/dist/shell/feedback-cli.js +81 -0
  88. package/dist/shell/feedback-operations.js +48 -0
  89. package/dist/shell/fs-atomic.js +276 -0
  90. package/dist/shell/harness.js +98 -0
  91. package/dist/shell/ledger-cli.js +212 -0
  92. package/dist/shell/ledger-io.js +169 -0
  93. package/dist/shell/man-cli.js +89 -0
  94. package/dist/shell/metrics-cli.js +100 -0
  95. package/dist/shell/multi-agent-cli.js +1002 -0
  96. package/dist/shell/multi-agent-host.js +563 -0
  97. package/dist/shell/multi-agent-io.js +387 -0
  98. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  99. package/dist/shell/node-store.js +124 -0
  100. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  101. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  102. package/dist/{observability.js → shell/observability.js} +159 -332
  103. package/dist/{onramp.js → shell/onramp.js} +11 -0
  104. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  105. package/dist/shell/operator-ux.js +431 -0
  106. package/dist/shell/orchestrator.js +231 -0
  107. package/dist/shell/pipeline-cli.js +667 -0
  108. package/dist/shell/pipeline.js +217 -0
  109. package/dist/shell/reclamation-io.js +1366 -0
  110. package/dist/shell/registry-cli.js +344 -0
  111. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  112. package/dist/shell/report-cli.js +101 -0
  113. package/dist/shell/report-view-cli.js +117 -0
  114. package/dist/{orchestrator → shell}/report.js +289 -282
  115. package/dist/shell/reporter.js +62 -0
  116. package/dist/shell/run-export-cli.js +106 -0
  117. package/dist/shell/run-export.js +680 -0
  118. package/dist/shell/run-registry-io.js +1014 -0
  119. package/dist/shell/run-store.js +164 -0
  120. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  121. package/dist/{scheduler.js → shell/scheduler-io.js} +257 -48
  122. package/dist/shell/scheduling-io.js +321 -0
  123. package/dist/shell/state-cli.js +181 -0
  124. package/dist/shell/state-explosion-cli.js +197 -0
  125. package/dist/shell/telemetry-cli.js +85 -0
  126. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  127. package/dist/shell/telemetry-ledger-io.js +132 -0
  128. package/dist/{term.js → shell/term.js} +36 -46
  129. package/dist/shell/topology-io.js +361 -0
  130. package/dist/shell/trust-audit.js +472 -0
  131. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  132. package/dist/shell/verifier.js +48 -0
  133. package/dist/shell/workbench-host.js +258 -0
  134. package/dist/shell/workbench-text.js +18 -0
  135. package/dist/shell/workbench.js +170 -0
  136. package/dist/shell/worker-cli.js +124 -0
  137. package/dist/shell/worker-isolation.js +852 -0
  138. package/dist/shell/workflow-app-loader.js +650 -0
  139. package/docs/agent-delegation-drive.7.md +4 -0
  140. package/docs/cli-mcp-parity.7.md +282 -219
  141. package/docs/contract-migration-tooling.7.md +4 -0
  142. package/docs/control-plane-scheduling.7.md +4 -0
  143. package/docs/durable-state-and-locking.7.md +4 -0
  144. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  145. package/docs/execution-backends.7.md +4 -0
  146. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  147. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  148. package/docs/multi-agent-operator-ux.7.md +4 -0
  149. package/docs/node-snapshot-diff-replay.7.md +4 -0
  150. package/docs/observability-cost-accounting.7.md +6 -1
  151. package/docs/project-index.md +137 -71
  152. package/docs/real-execution-backends.7.md +4 -0
  153. package/docs/release-and-migration.7.md +4 -0
  154. package/docs/release-tooling.7.md +4 -0
  155. package/docs/remote-source-review.7.md +4 -4
  156. package/docs/report-verifiable-bundle.7.md +1 -1
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/security-trust-hardening.7.md +3 -1
  160. package/docs/state-explosion-management.7.md +4 -0
  161. package/docs/team-collaboration.7.md +4 -0
  162. package/docs/web-desktop-workbench.7.md +15 -1
  163. package/manifest/plugin.manifest.json +1 -1
  164. package/manifest/source-context-profiles.json +9 -13
  165. package/package.json +1 -1
  166. package/scripts/agents/agent-adapter-core.js +22 -2
  167. package/scripts/agents/claude-p-agent.js +8 -3
  168. package/scripts/agents/cw-attest-wrap.js +2 -2
  169. package/scripts/agents/gemini-agent.js +5 -1
  170. package/scripts/agents/opencode-agent.js +5 -1
  171. package/scripts/bump-version.js +4 -3
  172. package/scripts/canonical-apps.js +4 -4
  173. package/scripts/dogfood-architecture-review.js +2 -3
  174. package/scripts/dogfood-release.js +3 -3
  175. package/scripts/gen-parity-doc.js +15 -2
  176. package/scripts/golden-path.js +4 -4
  177. package/scripts/onramp-check.js +1 -1
  178. package/scripts/parity-check.js +38 -21
  179. package/scripts/release-flow.js +2 -2
  180. package/scripts/sync-project-index.js +51 -27
  181. package/scripts/validate-run-state-schema.js +2 -2
  182. package/scripts/version-sync-check.js +30 -30
  183. package/dist/candidate-scoring.js +0 -729
  184. package/dist/capability-core.js +0 -1189
  185. package/dist/capability-registry.js +0 -885
  186. package/dist/cli/command-surface.js +0 -494
  187. package/dist/cli/format.js +0 -56
  188. package/dist/cli/handlers/audit.js +0 -82
  189. package/dist/cli/handlers/blackboard.js +0 -81
  190. package/dist/cli/handlers/candidate.js +0 -40
  191. package/dist/cli/handlers/clones.js +0 -34
  192. package/dist/cli/handlers/collaboration.js +0 -61
  193. package/dist/cli/handlers/eval.js +0 -40
  194. package/dist/cli/handlers/ledger.js +0 -169
  195. package/dist/cli/handlers/maintenance.js +0 -107
  196. package/dist/cli/handlers/multi-agent.js +0 -165
  197. package/dist/cli/handlers/node.js +0 -41
  198. package/dist/cli/handlers/operational.js +0 -155
  199. package/dist/cli/handlers/operator.js +0 -146
  200. package/dist/cli/handlers/registry.js +0 -68
  201. package/dist/cli/handlers/run.js +0 -153
  202. package/dist/cli/handlers/scheduling.js +0 -132
  203. package/dist/cli/handlers/workbench.js +0 -41
  204. package/dist/cli/handlers/worker.js +0 -45
  205. package/dist/cli/run-summary.js +0 -45
  206. package/dist/clones.js +0 -162
  207. package/dist/collaboration.js +0 -726
  208. package/dist/commit.js +0 -592
  209. package/dist/compare.js +0 -18
  210. package/dist/coordinator/classify.js +0 -45
  211. package/dist/coordinator/paths.js +0 -42
  212. package/dist/coordinator/util.js +0 -126
  213. package/dist/coordinator.js +0 -990
  214. package/dist/daemon.js +0 -44
  215. package/dist/dispatch.js +0 -250
  216. package/dist/doctor.js +0 -212
  217. package/dist/drive.js +0 -864
  218. package/dist/error-feedback.js +0 -419
  219. package/dist/evidence-grounding.js +0 -184
  220. package/dist/execution-backend/agent.js +0 -354
  221. package/dist/execution-backend/probes.js +0 -112
  222. package/dist/execution-backend/util.js +0 -47
  223. package/dist/execution-backend.js +0 -961
  224. package/dist/gates.js +0 -48
  225. package/dist/harness.js +0 -61
  226. package/dist/ledger.js +0 -313
  227. package/dist/loop-expansion.js +0 -60
  228. package/dist/mcp/tool-call.js +0 -470
  229. package/dist/mcp/tool-definitions.js +0 -1066
  230. package/dist/mcp-surface.js +0 -30
  231. package/dist/multi-agent/graph.js +0 -84
  232. package/dist/multi-agent/helpers.js +0 -141
  233. package/dist/multi-agent/ids.js +0 -20
  234. package/dist/multi-agent/paths.js +0 -22
  235. package/dist/multi-agent-eval/normalize.js +0 -51
  236. package/dist/multi-agent-eval.js +0 -678
  237. package/dist/multi-agent-host.js +0 -777
  238. package/dist/multi-agent.js +0 -984
  239. package/dist/node-projection.js +0 -59
  240. package/dist/node-snapshot.js +0 -260
  241. package/dist/operator-ux.js +0 -631
  242. package/dist/orchestrator/app-operations.js +0 -211
  243. package/dist/orchestrator/audit-operations.js +0 -182
  244. package/dist/orchestrator/candidate-operations.js +0 -117
  245. package/dist/orchestrator/cli-options.js +0 -294
  246. package/dist/orchestrator/collaboration-operations.js +0 -86
  247. package/dist/orchestrator/feedback-operations.js +0 -81
  248. package/dist/orchestrator/host-operations.js +0 -78
  249. package/dist/orchestrator/lifecycle-operations.js +0 -650
  250. package/dist/orchestrator/migration-operations.js +0 -44
  251. package/dist/orchestrator/multi-agent-operations.js +0 -362
  252. package/dist/orchestrator/topology-operations.js +0 -84
  253. package/dist/orchestrator.js +0 -925
  254. package/dist/pipeline-runner.js +0 -285
  255. package/dist/reclamation/hash.js +0 -72
  256. package/dist/reclamation.js +0 -812
  257. package/dist/reporter.js +0 -67
  258. package/dist/run-export.js +0 -815
  259. package/dist/run-registry/derive.js +0 -175
  260. package/dist/run-registry/format.js +0 -124
  261. package/dist/run-registry/gc.js +0 -251
  262. package/dist/run-registry/policy.js +0 -16
  263. package/dist/run-registry/queue.js +0 -115
  264. package/dist/run-registry.js +0 -850
  265. package/dist/run-state-schema.js +0 -68
  266. package/dist/scheduling.js +0 -184
  267. package/dist/state-explosion.js +0 -1014
  268. package/dist/state.js +0 -367
  269. package/dist/telemetry-ledger.js +0 -196
  270. package/dist/topology.js +0 -565
  271. package/dist/triggers.js +0 -184
  272. package/dist/trust-audit.js +0 -644
  273. package/dist/types/blackboard.js +0 -2
  274. package/dist/types/candidate.js +0 -2
  275. package/dist/types/collaboration.js +0 -2
  276. package/dist/types/core.js +0 -2
  277. package/dist/types/drive.js +0 -10
  278. package/dist/types/error-feedback.js +0 -2
  279. package/dist/types/evidence-reasoning.js +0 -2
  280. package/dist/types/execution-backend.js +0 -2
  281. package/dist/types/multi-agent.js +0 -2
  282. package/dist/types/observability.js +0 -2
  283. package/dist/types/pipeline.js +0 -2
  284. package/dist/types/reclamation.js +0 -8
  285. package/dist/types/report-bundle.js +0 -6
  286. package/dist/types/result.js +0 -2
  287. package/dist/types/run-registry.js +0 -2
  288. package/dist/types/run.js +0 -2
  289. package/dist/types/sandbox.js +0 -2
  290. package/dist/types/schedule.js +0 -2
  291. package/dist/types/state-node.js +0 -2
  292. package/dist/types/topology.js +0 -2
  293. package/dist/types/trust.js +0 -2
  294. package/dist/types/workbench.js +0 -2
  295. package/dist/types/worker.js +0 -2
  296. package/dist/types/workflow-app.js +0 -2
  297. package/dist/types.js +0 -44
  298. package/dist/util/fingerprint.js +0 -19
  299. package/dist/util/fingerprint.test.js +0 -27
  300. package/dist/verifier.js +0 -78
  301. package/dist/version.js +0 -8
  302. package/dist/workbench-host.js +0 -192
  303. package/dist/workbench.js +0 -192
  304. package/dist/worker-accept/acceptance.js +0 -114
  305. package/dist/worker-accept/blackboard-fanout.js +0 -80
  306. package/dist/worker-accept/blackboard-linkage.js +0 -19
  307. package/dist/worker-accept/context.js +0 -2
  308. package/dist/worker-accept/telemetry-ledger.js +0 -126
  309. package/dist/worker-accept/validation.js +0 -77
  310. package/dist/worker-accept/verifier-completion.js +0 -73
  311. package/dist/worker-isolation/helpers.js +0 -51
  312. package/dist/worker-isolation/paths.js +0 -46
  313. package/dist/worker-isolation.js +0 -656
  314. package/dist/workflow-api.js +0 -131
  315. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,680 @@
1
+ "use strict";
2
+ // shell/run-export.ts — portable run archive format: exportRun,
3
+ // importRun, inspectArchive, verifyImportedRun, verifyReportBundle.
4
+ //
5
+ // MILESTONE 8. Byte-exact port of the old build's src/run-export.ts.
6
+ // Impure (fs, tmpdir, os) by nature — the archive/bundle mechanism.
7
+ //
8
+ // Evidence: SPEC/ledger-trust.md "`cw report verify-bundle` JSON", "Files
9
+ // on disk" (bundle shape), invariants 16-17, rebuild risk 8;
10
+ // plugins/cool-workflow/src/run-export.ts:1-925 (byte-exact source).
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.exportRun = exportRun;
46
+ exports.importRun = importRun;
47
+ exports.verifyImportedRun = verifyImportedRun;
48
+ exports.inspectArchive = inspectArchive;
49
+ exports.importManifestPath = importManifestPath;
50
+ exports.verifyReportBundle = verifyReportBundle;
51
+ const fs = __importStar(require("node:fs"));
52
+ const os = __importStar(require("node:os"));
53
+ const path = __importStar(require("node:path"));
54
+ const hash_1 = require("../core/hash");
55
+ const version_1 = require("../core/version");
56
+ const fs_atomic_1 = require("./fs-atomic");
57
+ const run_paths_1 = require("../core/state/run-paths");
58
+ const run_store_1 = require("./run-store");
59
+ const telemetry_ledger_io_1 = require("./telemetry-ledger-io");
60
+ const telemetry_attestation_1 = require("../core/trust/telemetry-attestation");
61
+ const trust_audit_1 = require("./trust-audit");
62
+ /** Export a run to a portable JSON archive with run-local bytes and
63
+ * digests. */
64
+ function exportRun(run, outputPath, options = {}) {
65
+ const exportedAt = new Date().toISOString();
66
+ const files = collectArchiveFiles(run);
67
+ const manifestSha256 = digestManifest(files);
68
+ const trustPublicKeyPem = (0, telemetry_attestation_1.resolveTrustPublicKey)(options.trustPublicKey);
69
+ const exported = {
70
+ schemaVersion: 1,
71
+ exportedAt,
72
+ sourceVersion: version_1.CURRENT_COOL_WORKFLOW_VERSION,
73
+ run,
74
+ files,
75
+ integrity: { fileCount: files.length, manifestSha256 },
76
+ ...(trustPublicKeyPem ? { trust: { publicKeyPem: trustPublicKeyPem, algorithm: "ed25519" } } : {}),
77
+ artifacts: files
78
+ .filter((file) => file.role === "artifact")
79
+ .map((file) => ({ path: file.relativePath, contentBase64: file.contentBase64, sha256: file.sha256, sizeBytes: file.sizeBytes })),
80
+ audit: files.filter((file) => file.role === "audit").map((file) => file.relativePath),
81
+ };
82
+ (0, fs_atomic_1.writeJson)(outputPath, exported);
83
+ const archiveSha256 = (0, hash_1.sha256Bytes)(fs.readFileSync(outputPath));
84
+ return {
85
+ runId: run.id,
86
+ exportedAt,
87
+ path: outputPath,
88
+ taskCount: run.tasks.length,
89
+ commitCount: run.commits.length,
90
+ fileCount: files.length,
91
+ artifactCount: files.filter((file) => file.role === "artifact").length,
92
+ auditFileCount: files.filter((file) => file.role === "audit").length,
93
+ telemetryIncluded: files.some((file) => file.role === "telemetry"),
94
+ trustKeyEmbedded: Boolean(trustPublicKeyPem),
95
+ manifestSha256,
96
+ archiveSha256,
97
+ };
98
+ }
99
+ /** Import a run from a portable JSON file into a target directory. */
100
+ function importRun(exportPath, targetDir) {
101
+ const raw = (0, fs_atomic_1.readJson)(exportPath);
102
+ if (raw.schemaVersion !== 1)
103
+ throw new Error(`Unsupported export schema version: ${raw.schemaVersion}`);
104
+ const archiveSha256 = (0, hash_1.sha256Bytes)(fs.readFileSync(exportPath));
105
+ const files = normalizeArchiveFiles(raw);
106
+ verifyArchiveFileDigests(files, raw.integrity);
107
+ if (!raw.run || typeof raw.run !== "object") {
108
+ throw new Error("Invalid run export: missing run object");
109
+ }
110
+ const runId = (0, fs_atomic_1.assertSafeRunId)(raw.run.id);
111
+ const runsRoot = path.join(targetDir, ".cw", "runs");
112
+ const runDir = path.join(runsRoot, runId);
113
+ if (!(0, fs_atomic_1.isContainedPath)(runDir, runsRoot)) {
114
+ throw new Error(`Run id escapes the runs directory: ${JSON.stringify(raw.run.id)}`);
115
+ }
116
+ const oldRunDir = raw.run.paths.runDir;
117
+ const oldCwd = raw.run.cwd;
118
+ const paths = (0, run_paths_1.createRunPaths)(runDir);
119
+ (0, run_paths_1.ensureRunDirs)(paths);
120
+ for (const file of files) {
121
+ const destination = path.join(runDir, file.relativePath);
122
+ if (!(0, fs_atomic_1.isContainedPath)(destination, runDir)) {
123
+ throw new Error(`Archive file escapes restore directory: ${file.relativePath}`);
124
+ }
125
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
126
+ fs.writeFileSync(destination, decodeBase64Strict(file.contentBase64, file.relativePath));
127
+ }
128
+ const externalPathMap = new Map();
129
+ for (const file of files) {
130
+ if (file.sourcePath)
131
+ externalPathMap.set(file.sourcePath, path.join(runDir, file.relativePath));
132
+ }
133
+ const run = rebaseRun(raw.run, { oldRunDir, newRunDir: runDir, oldCwd, newCwd: targetDir, paths, externalPathMap });
134
+ (0, run_store_1.saveCheckpoint)(run);
135
+ const manifest = {
136
+ schemaVersion: 1,
137
+ runId: run.id,
138
+ importedAt: new Date().toISOString(),
139
+ sourceVersion: raw.sourceVersion,
140
+ archiveSha256,
141
+ manifestSha256: digestManifest(files),
142
+ files: files.map(({ contentBase64: _contentBase64, ...file }) => file),
143
+ };
144
+ const manifestPath = importManifestPath(run);
145
+ (0, fs_atomic_1.writeJson)(manifestPath, manifest, { durable: true });
146
+ const verification = verifyImportedRun(run);
147
+ return {
148
+ run,
149
+ runDir,
150
+ statePath: paths.state,
151
+ manifestPath,
152
+ verifyCommand: `cw run verify-import ${run.id} --cwd ${targetDir} --json`,
153
+ verification,
154
+ };
155
+ }
156
+ /** Verify an imported run against its restore manifest and telemetry
157
+ * chain. */
158
+ function verifyImportedRun(run) {
159
+ const manifestPath = importManifestPath(run);
160
+ const checks = [];
161
+ if (!fs.existsSync(manifestPath)) {
162
+ return {
163
+ runId: run.id,
164
+ ok: false,
165
+ manifestPath,
166
+ checkedFiles: 0,
167
+ checks: [{ name: "import-manifest", pass: false, code: "missing-import-manifest", path: manifestPath }],
168
+ };
169
+ }
170
+ let manifest;
171
+ try {
172
+ manifest = (0, fs_atomic_1.readJson)(manifestPath);
173
+ }
174
+ catch (error) {
175
+ return {
176
+ runId: run.id,
177
+ ok: false,
178
+ manifestPath,
179
+ checkedFiles: 0,
180
+ checks: [{ name: "import-manifest", pass: false, code: "invalid-import-manifest", path: manifestPath, actual: messageOf(error) }],
181
+ };
182
+ }
183
+ const currentManifestDigest = digestManifest(manifest.files.map((file) => ({ ...file, contentBase64: "" })));
184
+ checks.push({
185
+ name: "import-manifest",
186
+ pass: manifest.runId === run.id && manifest.manifestSha256 === currentManifestDigest,
187
+ code: manifest.runId !== run.id ? "run-id-mismatch" : manifest.manifestSha256 === currentManifestDigest ? undefined : "manifest-digest-mismatch",
188
+ expected: manifest.manifestSha256,
189
+ actual: currentManifestDigest,
190
+ });
191
+ let filesOk = true;
192
+ for (const file of manifest.files) {
193
+ const restoredPath = path.join(run.paths.runDir, file.relativePath);
194
+ if (!(0, fs_atomic_1.isContainedPath)(restoredPath, run.paths.runDir)) {
195
+ filesOk = false;
196
+ checks.push({ name: "archive-file", pass: false, code: "path-escape", path: file.relativePath });
197
+ continue;
198
+ }
199
+ if (!fs.existsSync(restoredPath)) {
200
+ filesOk = false;
201
+ checks.push({ name: "archive-file", pass: false, code: "missing-file", path: file.relativePath, expected: file.sha256 });
202
+ continue;
203
+ }
204
+ const actual = (0, hash_1.sha256Bytes)(fs.readFileSync(restoredPath));
205
+ const pass = actual === file.sha256;
206
+ if (!pass)
207
+ filesOk = false;
208
+ checks.push({ name: "archive-file", pass, code: pass ? undefined : "digest-mismatch", path: file.relativePath, expected: file.sha256, actual });
209
+ }
210
+ checks.push({ name: "archive-files", pass: filesOk, code: filesOk ? undefined : "archive-files-invalid" });
211
+ const telemetry = (0, telemetry_ledger_io_1.verifyTelemetryLedger)(run);
212
+ checks.push({ name: "telemetry-ledger", pass: telemetry.verified, code: telemetry.verified ? undefined : "telemetry-ledger-invalid" });
213
+ const audit = (0, trust_audit_1.verifyTrustAudit)(run);
214
+ checks.push({ name: "trust-audit", pass: audit.verified, code: audit.verified ? undefined : "trust-audit-invalid" });
215
+ return { runId: run.id, ok: checks.every((check) => check.pass), manifestPath, checkedFiles: manifest.files.length, checks };
216
+ }
217
+ /** Read-only integrity inspection of a portable archive WITHOUT
218
+ * importing it. Never throws. */
219
+ function inspectArchive(archivePath) {
220
+ const base = {
221
+ schemaVersion: 1,
222
+ archivePath,
223
+ ok: false,
224
+ schemaSupported: false,
225
+ runId: null,
226
+ fileCount: 0,
227
+ manifestSha256: null,
228
+ archiveSha256: null,
229
+ checks: [],
230
+ };
231
+ let bytes;
232
+ try {
233
+ bytes = fs.readFileSync(archivePath);
234
+ }
235
+ catch (error) {
236
+ return { ...base, checks: [{ name: "archive", pass: false, code: "archive-unreadable", path: archivePath, actual: messageOf(error) }] };
237
+ }
238
+ base.archiveSha256 = (0, hash_1.sha256Bytes)(bytes);
239
+ let raw;
240
+ try {
241
+ raw = JSON.parse(bytes.toString("utf8"));
242
+ }
243
+ catch (error) {
244
+ return { ...base, checks: [{ name: "archive", pass: false, code: "archive-invalid-json", path: archivePath, actual: messageOf(error) }] };
245
+ }
246
+ if (raw.schemaVersion !== 1) {
247
+ return {
248
+ ...base,
249
+ schemaVersion: typeof raw.schemaVersion === "number" ? raw.schemaVersion : base.schemaVersion,
250
+ checks: [{ name: "schema", pass: false, code: "unsupported-schema", expected: "1", actual: String(raw.schemaVersion) }],
251
+ };
252
+ }
253
+ try {
254
+ const files = normalizeArchiveFiles(raw);
255
+ const { checks } = collectArchiveDigestChecks(files, raw.integrity);
256
+ if (!raw.integrity && /^(1|true|yes|on)$/i.test(process.env.CW_REQUIRE_ARCHIVE_INTEGRITY || "")) {
257
+ checks.push({ name: "archive-integrity", pass: false, code: "archive-integrity-required" });
258
+ }
259
+ return {
260
+ schemaVersion: 1,
261
+ archivePath,
262
+ ok: checks.every((c) => c.pass),
263
+ schemaSupported: true,
264
+ runId: raw.run && raw.run.id ? raw.run.id : null,
265
+ fileCount: files.length,
266
+ manifestSha256: raw.integrity ? digestManifest(files) : null,
267
+ archiveSha256: base.archiveSha256,
268
+ checks,
269
+ };
270
+ }
271
+ catch (error) {
272
+ return { ...base, schemaSupported: true, checks: [{ name: "archive", pass: false, code: "archive-malformed", path: archivePath, actual: messageOf(error) }] };
273
+ }
274
+ }
275
+ function importManifestPath(run) {
276
+ return path.join(run.paths.runDir, "import-manifest.json");
277
+ }
278
+ /** True when report.md embeds `expected` (the trimmed result) at the
279
+ * task's OWN section: a `### <taskId>` heading, a `Result: <path>`
280
+ * line, then the result body — and the body STARTS WITH `expected`. */
281
+ function reportSectionEmbedsResult(reportMd, taskId, expected) {
282
+ const needle = `### ${taskId}\n`;
283
+ for (let from = reportMd.indexOf(needle); from >= 0; from = reportMd.indexOf(needle, from + 1)) {
284
+ const after = reportMd.slice(from);
285
+ const prefix = after.match(/^### [^\n]*\n\nResult: [^\n]*\n\n/);
286
+ if (prefix && after.slice(prefix[0].length).startsWith(expected))
287
+ return true;
288
+ }
289
+ return false;
290
+ }
291
+ /** Verify a portable run bundle OFFLINE and SELF-CONTAINED: prove the
292
+ * archive bytes, the telemetry hash chain, the trust-audit chain, and
293
+ * (with the bundle's embedded public key) the ed25519 signatures.
294
+ * Never throws — every failure is a structured check and a false `ok`.
295
+ *
296
+ * Key precedence is bundle > argument > environment. */
297
+ function verifyReportBundle(archivePath, options = {}) {
298
+ const inspect = inspectArchive(archivePath);
299
+ const failedChecks = inspect.checks.filter((check) => !check.pass).map((check) => ({ name: check.name, code: check.code }));
300
+ const base = {
301
+ schemaVersion: 1,
302
+ archivePath,
303
+ runId: inspect.runId,
304
+ ok: false,
305
+ archiveOk: inspect.ok,
306
+ telemetryVerified: false,
307
+ trustAuditVerified: false,
308
+ trustKeySource: "none",
309
+ signatureKeyProvided: false,
310
+ signaturesChecked: 0,
311
+ signaturesReverified: 0,
312
+ signaturesFailed: 0,
313
+ trustLevel: "unsigned",
314
+ reportFindingsVerified: false,
315
+ failedChecks,
316
+ };
317
+ if (!inspect.schemaSupported)
318
+ return base;
319
+ let bundleKey;
320
+ let reportContent;
321
+ try {
322
+ const raw = JSON.parse(fs.readFileSync(archivePath, "utf8"));
323
+ bundleKey = raw.trust?.publicKeyPem;
324
+ if (options.extractReportTo) {
325
+ const reportFile = (raw.files || []).find((file) => file.relativePath === "report.md");
326
+ if (reportFile) {
327
+ const decoded = decodeBase64StrictResult(reportFile.contentBase64, reportFile.relativePath);
328
+ if (decoded.ok)
329
+ reportContent = decoded.bytes.toString("utf8");
330
+ }
331
+ }
332
+ }
333
+ catch {
334
+ /* inspect already recorded the parse failure; treat key as absent */
335
+ }
336
+ const trustKeySource = bundleKey ? "bundle" : options.pubkey ? "argument" : process.env.CW_AGENT_ATTEST_PUBKEY ? "environment" : "none";
337
+ const trustKey = (0, telemetry_attestation_1.resolveTrustPublicKey)(bundleKey || options.pubkey || process.env.CW_AGENT_ATTEST_PUBKEY);
338
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "cw-verify-bundle-"));
339
+ let telemetryVerified = false;
340
+ let trustAuditVerified = false;
341
+ let signaturesChecked = 0;
342
+ let signaturesReverified = 0;
343
+ let signaturesFailed = 0;
344
+ let signaturesResultBound = 0;
345
+ let reportFindingsOk = true;
346
+ let reportExtractedTo;
347
+ try {
348
+ const imported = importRun(archivePath, tmpDir);
349
+ for (const check of imported.verification.checks) {
350
+ if (check.name === "telemetry-ledger")
351
+ telemetryVerified = check.pass;
352
+ if (check.name === "trust-audit")
353
+ trustAuditVerified = check.pass;
354
+ if (!check.pass)
355
+ failedChecks.push({ name: check.name, code: check.code });
356
+ }
357
+ const ledger = (0, telemetry_ledger_io_1.verifyTelemetryLedger)(imported.run);
358
+ const sig = (0, telemetry_attestation_1.verifyTelemetrySignatures)(ledger.records, trustKey);
359
+ signaturesChecked = sig.checked;
360
+ signaturesReverified = sig.reverified;
361
+ signaturesFailed = sig.failed;
362
+ signaturesResultBound = sig.resultBound.length;
363
+ for (const check of sig.checks)
364
+ if (!check.pass)
365
+ failedChecks.push({ name: check.name, code: check.code });
366
+ // Report ⇄ result ⇄ signature cross-check — driven by sig.resultBound
367
+ // (records whose signature actually COVERED the result), NEVER
368
+ // run.tasks (unbound data an attacker can edit).
369
+ const reportPath = imported.run.paths.report;
370
+ const reportMd = reportPath && fs.existsSync(reportPath) ? fs.readFileSync(reportPath, "utf8") : "";
371
+ const completedById = new Map(imported.run.tasks.filter((task) => task.status === "completed").map((task) => [task.id, task]));
372
+ for (const bound of sig.resultBound) {
373
+ const failBound = (code) => {
374
+ reportFindingsOk = false;
375
+ failedChecks.push({ name: "report-findings", code: `${code}:${bound.taskId}` });
376
+ };
377
+ const task = completedById.get(bound.taskId);
378
+ if (!task || !task.resultPath || !fs.existsSync(task.resultPath)) {
379
+ failBound("result-missing");
380
+ continue;
381
+ }
382
+ const resultRaw = fs.readFileSync(task.resultPath, "utf8");
383
+ if ((0, hash_1.sha256)(resultRaw) !== bound.resultDigest) {
384
+ failBound("result-digest-mismatch");
385
+ continue;
386
+ }
387
+ if (!reportSectionEmbedsResult(reportMd, bound.taskId, resultRaw.trim())) {
388
+ failBound("report-result-mismatch");
389
+ }
390
+ }
391
+ if (options.extractReportTo && reportContent !== undefined) {
392
+ reportExtractedTo = path.resolve(options.extractReportTo);
393
+ if (options.cwd) {
394
+ const baseCwd = path.resolve(options.cwd);
395
+ if (!(0, fs_atomic_1.isContainedPath)(reportExtractedTo, baseCwd)) {
396
+ failedChecks.push({ name: "extract-report", code: "path-outside-working-directory" });
397
+ reportExtractedTo = undefined;
398
+ }
399
+ }
400
+ if (reportExtractedTo)
401
+ fs.writeFileSync(reportExtractedTo, reportContent);
402
+ }
403
+ }
404
+ catch (error) {
405
+ failedChecks.push({ name: "restore", code: messageOf(error) });
406
+ }
407
+ finally {
408
+ fs.rmSync(tmpDir, { recursive: true, force: true });
409
+ }
410
+ const strictShortfall = Boolean(options.strictSignatures) && signaturesChecked > 0 && !trustKey;
411
+ if (strictShortfall)
412
+ failedChecks.push({ name: "signatures", code: "signature-key-required" });
413
+ const trustLevel = signaturesResultBound > 0 && signaturesFailed === 0 && reportFindingsOk ? "signed" : "unsigned";
414
+ const unsignedShortfall = Boolean(options.requireSignatures) && trustLevel === "unsigned";
415
+ if (unsignedShortfall)
416
+ failedChecks.push({ name: "signatures", code: "signatures-required" });
417
+ const extractShortfall = Boolean(options.extractReportTo) && !reportExtractedTo;
418
+ if (extractShortfall)
419
+ failedChecks.push({ name: "extract-report", code: "report-md-unavailable" });
420
+ return {
421
+ schemaVersion: 1,
422
+ archivePath,
423
+ runId: inspect.runId,
424
+ ok: inspect.ok && telemetryVerified && trustAuditVerified && signaturesFailed === 0 && reportFindingsOk && !strictShortfall && !extractShortfall && !unsignedShortfall,
425
+ archiveOk: inspect.ok,
426
+ telemetryVerified,
427
+ trustAuditVerified,
428
+ trustKeySource,
429
+ signatureKeyProvided: Boolean(trustKey),
430
+ signaturesChecked,
431
+ signaturesReverified,
432
+ signaturesFailed,
433
+ trustLevel,
434
+ reportFindingsVerified: reportFindingsOk,
435
+ reportExtractedTo,
436
+ failedChecks,
437
+ };
438
+ }
439
+ function collectArchiveFiles(run) {
440
+ const entries = new Map();
441
+ for (const file of walkFiles(run.paths.runDir)) {
442
+ const relativePath = toArchivePath(path.relative(run.paths.runDir, file));
443
+ if (!relativePath || relativePath === "state.json" || relativePath === "import-manifest.json" || relativePath.endsWith(".lock"))
444
+ continue;
445
+ addFile(entries, file, roleForRelativePath(relativePath), run.paths.runDir);
446
+ }
447
+ for (const artifactPath of collectReferencedArtifactPaths(run)) {
448
+ if (!artifactPath || !fs.existsSync(artifactPath) || !fs.statSync(artifactPath).isFile())
449
+ continue;
450
+ if ((0, fs_atomic_1.isContainedPath)(artifactPath, run.paths.runDir)) {
451
+ addFile(entries, artifactPath, "artifact", run.paths.runDir);
452
+ continue;
453
+ }
454
+ if ((0, fs_atomic_1.isContainedPath)(artifactPath, run.cwd))
455
+ addExternalArtifactFile(entries, artifactPath);
456
+ }
457
+ return [...entries.values()].sort((left, right) => (left.relativePath < right.relativePath ? -1 : left.relativePath > right.relativePath ? 1 : 0));
458
+ }
459
+ function addFile(entries, file, role, runDir) {
460
+ const relativePath = toArchivePath(path.relative(runDir, file));
461
+ if (relativePath === "state.json" || relativePath === "import-manifest.json")
462
+ return;
463
+ if (!relativePath || relativePath.startsWith("../"))
464
+ return;
465
+ const bytes = fs.readFileSync(file);
466
+ entries.set(relativePath, { relativePath, role, contentBase64: bytes.toString("base64"), sha256: (0, hash_1.sha256Bytes)(bytes), sizeBytes: bytes.length });
467
+ }
468
+ function addExternalArtifactFile(entries, file) {
469
+ const sourcePath = path.resolve(file);
470
+ const bytes = fs.readFileSync(sourcePath);
471
+ const relativePath = `external-artifacts/${(0, hash_1.sha256Bytes)(Buffer.from(sourcePath, "utf8")).slice(0, 16)}-${safeArchiveBasename(path.basename(sourcePath))}`;
472
+ entries.set(relativePath, { relativePath, role: "artifact", contentBase64: bytes.toString("base64"), sha256: (0, hash_1.sha256Bytes)(bytes), sizeBytes: bytes.length, sourcePath });
473
+ }
474
+ function collectReferencedArtifactPaths(run) {
475
+ const paths = new Set();
476
+ for (const node of run.nodes || []) {
477
+ for (const artifact of node.artifacts || [])
478
+ addArtifactPath(paths, run, artifact.path);
479
+ }
480
+ const candidates = (run.candidates || []);
481
+ for (const candidate of candidates) {
482
+ for (const artifact of candidate.artifacts || [])
483
+ addArtifactPath(paths, run, artifact.path);
484
+ }
485
+ const selections = (run.candidateSelections || []);
486
+ for (const selection of selections) {
487
+ for (const artifact of selection.artifacts || [])
488
+ addArtifactPath(paths, run, artifact.path);
489
+ }
490
+ const bbArtifacts = (run.blackboard?.artifacts || []);
491
+ for (const artifact of bbArtifacts)
492
+ addArtifactPath(paths, run, artifact.path);
493
+ return [...paths].sort();
494
+ }
495
+ function addArtifactPath(paths, run, value) {
496
+ if (!value)
497
+ return;
498
+ paths.add(path.isAbsolute(value) ? value : path.resolve(run.cwd, value));
499
+ }
500
+ function walkFiles(root) {
501
+ if (!fs.existsSync(root))
502
+ return [];
503
+ const found = [];
504
+ for (const name of fs.readdirSync(root)) {
505
+ const file = path.join(root, name);
506
+ const stat = fs.lstatSync(file);
507
+ if (stat.isSymbolicLink())
508
+ continue;
509
+ if (stat.isDirectory())
510
+ found.push(...walkFiles(file));
511
+ else if (stat.isFile())
512
+ found.push(file);
513
+ }
514
+ return found;
515
+ }
516
+ function roleForRelativePath(relativePath) {
517
+ if (relativePath === "telemetry.json")
518
+ return "telemetry";
519
+ if (relativePath === "audit" || relativePath.startsWith("audit/"))
520
+ return "audit";
521
+ if (relativePath === "artifacts" || relativePath.startsWith("artifacts/"))
522
+ return "artifact";
523
+ return "run-file";
524
+ }
525
+ function normalizeArchiveFiles(raw) {
526
+ const modern = raw.files || [];
527
+ if (modern.length) {
528
+ return modern.map((file) => ({
529
+ relativePath: cleanArchiveRelativePath(file.relativePath),
530
+ role: file.role,
531
+ contentBase64: file.contentBase64,
532
+ sha256: file.sha256,
533
+ sizeBytes: file.sizeBytes,
534
+ sourcePath: file.sourcePath,
535
+ }));
536
+ }
537
+ return (raw.artifacts || []).map((artifact) => {
538
+ const contentBase64 = artifact.contentBase64 || Buffer.from("", "utf8").toString("base64");
539
+ const bytes = Buffer.from(contentBase64, "base64");
540
+ return {
541
+ relativePath: cleanArchiveRelativePath(artifact.path),
542
+ role: "artifact",
543
+ contentBase64,
544
+ sha256: artifact.sha256 || (0, hash_1.sha256Bytes)(bytes),
545
+ sizeBytes: artifact.sizeBytes ?? bytes.length,
546
+ };
547
+ });
548
+ }
549
+ function decodeBase64StrictResult(value, relativePath) {
550
+ if (typeof value !== "string") {
551
+ return { ok: false, check: { name: "archive-file", pass: false, code: "archive-bad-base64", path: relativePath, actual: "contentBase64 is not a string" } };
552
+ }
553
+ const compact = value.replace(/\s+/g, "");
554
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(compact)) {
555
+ return { ok: false, check: { name: "archive-file", pass: false, code: "archive-bad-base64", path: relativePath, actual: "invalid base64 encoding" } };
556
+ }
557
+ const bytes = Buffer.from(compact, "base64");
558
+ const expected = compact.replace(/=+$/, "");
559
+ const actual = bytes.toString("base64").replace(/=+$/, "");
560
+ if (actual !== expected) {
561
+ return { ok: false, check: { name: "archive-file", pass: false, code: "archive-bad-base64", path: relativePath, actual: "non-canonical base64 encoding" } };
562
+ }
563
+ return { ok: true, bytes };
564
+ }
565
+ function decodeBase64Strict(value, relativePath) {
566
+ const decoded = decodeBase64StrictResult(value, relativePath);
567
+ if (!decoded.ok)
568
+ throw new Error(archiveCheckMessage(decoded.check));
569
+ return decoded.bytes;
570
+ }
571
+ function collectArchiveDigestChecks(files, integrity) {
572
+ const checks = [];
573
+ for (const file of files) {
574
+ const decoded = decodeBase64StrictResult(file.contentBase64, file.relativePath);
575
+ if (!decoded.ok) {
576
+ checks.push(decoded.check);
577
+ continue;
578
+ }
579
+ const bytes = decoded.bytes;
580
+ const actual = (0, hash_1.sha256Bytes)(bytes);
581
+ const digestOk = actual === file.sha256;
582
+ checks.push(digestOk ? { name: "archive-file", pass: true, path: file.relativePath } : { name: "archive-file", pass: false, code: "digest-mismatch", path: file.relativePath, expected: file.sha256, actual });
583
+ const sizeOk = bytes.length === file.sizeBytes;
584
+ checks.push(sizeOk ? { name: "archive-file", pass: true, path: file.relativePath } : { name: "archive-file", pass: false, code: "size-mismatch", path: file.relativePath, expected: String(file.sizeBytes), actual: String(bytes.length) });
585
+ }
586
+ if (integrity) {
587
+ const countOk = integrity.fileCount === files.length;
588
+ checks.push(countOk ? { name: "archive-file-count", pass: true } : { name: "archive-file-count", pass: false, code: "file-count-mismatch", expected: String(integrity.fileCount), actual: String(files.length) });
589
+ const actualManifest = digestManifest(files);
590
+ const manifestOk = integrity.manifestSha256 === actualManifest;
591
+ checks.push(manifestOk ? { name: "archive-manifest", pass: true } : { name: "archive-manifest", pass: false, code: "manifest-digest-mismatch", expected: integrity.manifestSha256, actual: actualManifest });
592
+ }
593
+ return { checks, ok: checks.every((c) => c.pass) };
594
+ }
595
+ function archiveCheckMessage(check) {
596
+ switch (check.code) {
597
+ case "digest-mismatch":
598
+ return `Archive digest mismatch for ${check.path}: expected ${check.expected}, got ${check.actual}`;
599
+ case "size-mismatch":
600
+ return `Archive size mismatch for ${check.path}: expected ${check.expected}, got ${check.actual}`;
601
+ case "file-count-mismatch":
602
+ return `Archive file count mismatch: expected ${check.expected}, got ${check.actual}`;
603
+ case "manifest-digest-mismatch":
604
+ return `Archive manifest digest mismatch: expected ${check.expected}, got ${check.actual}`;
605
+ case "archive-bad-base64":
606
+ return `Archive base64 invalid for ${check.path}: ${check.actual}`;
607
+ default:
608
+ return `Archive verification failed: ${check.name}`;
609
+ }
610
+ }
611
+ function verifyArchiveFileDigests(files, integrity) {
612
+ if (!integrity && /^(1|true|yes|on)$/i.test(process.env.CW_REQUIRE_ARCHIVE_INTEGRITY || "")) {
613
+ throw new Error("Archive integrity block required but absent (CW_REQUIRE_ARCHIVE_INTEGRITY=1)");
614
+ }
615
+ const failed = collectArchiveDigestChecks(files, integrity).checks.find((c) => !c.pass);
616
+ if (failed)
617
+ throw new Error(archiveCheckMessage(failed));
618
+ }
619
+ function digestManifest(files) {
620
+ const manifest = files
621
+ .map((file) => ({ relativePath: file.relativePath, role: file.role, sha256: file.sha256, sizeBytes: file.sizeBytes }))
622
+ .sort((left, right) => (left.relativePath < right.relativePath ? -1 : left.relativePath > right.relativePath ? 1 : 0));
623
+ return (0, hash_1.sha256Bytes)(Buffer.from(JSON.stringify(manifest), "utf8"));
624
+ }
625
+ function rebaseRun(source, context) {
626
+ const cloned = deepRebase(JSON.parse(JSON.stringify(source)), context);
627
+ cloned.cwd = context.newCwd;
628
+ cloned.paths = context.paths;
629
+ cloned.updatedAt = new Date().toISOString();
630
+ cloned.audit = cloned.audit
631
+ ? {
632
+ schemaVersion: 1,
633
+ eventLogPath: path.join(context.paths.auditDir || path.join(context.paths.runDir, "audit"), "events.jsonl"),
634
+ summaryPath: path.join(context.paths.auditDir || path.join(context.paths.runDir, "audit"), "summary.json"),
635
+ indexPath: path.join(context.paths.auditDir || path.join(context.paths.runDir, "audit"), "index.json"),
636
+ }
637
+ : cloned.audit;
638
+ return cloned;
639
+ }
640
+ function deepRebase(value, context) {
641
+ if (typeof value === "string")
642
+ return rebaseString(value, context);
643
+ if (Array.isArray(value))
644
+ return value.map((entry) => deepRebase(entry, context));
645
+ if (value && typeof value === "object") {
646
+ const out = {};
647
+ for (const [key, entry] of Object.entries(value))
648
+ out[key] = deepRebase(entry, context);
649
+ return out;
650
+ }
651
+ return value;
652
+ }
653
+ function rebaseString(value, context) {
654
+ const archivedExternal = context.externalPathMap?.get(value);
655
+ if (archivedExternal)
656
+ return archivedExternal;
657
+ if (value === context.oldRunDir || value.startsWith(context.oldRunDir + path.sep)) {
658
+ return context.newRunDir + value.slice(context.oldRunDir.length);
659
+ }
660
+ if (value === context.oldCwd || value.startsWith(context.oldCwd + path.sep)) {
661
+ return context.newCwd + value.slice(context.oldCwd.length);
662
+ }
663
+ return value;
664
+ }
665
+ function cleanArchiveRelativePath(value) {
666
+ const cleaned = toArchivePath(value).replace(/^\/+/, "");
667
+ if (!cleaned || cleaned === "." || cleaned.startsWith("../") || cleaned.includes("/../")) {
668
+ throw new Error(`Invalid archive relative path: ${value}`);
669
+ }
670
+ return cleaned;
671
+ }
672
+ function toArchivePath(value) {
673
+ return value.split(path.sep).join("/");
674
+ }
675
+ function safeArchiveBasename(value) {
676
+ return value.replace(/[^A-Za-z0-9._-]/g, "_") || "artifact";
677
+ }
678
+ function messageOf(error) {
679
+ return error instanceof Error ? error.message : String(error);
680
+ }