@oisincoveney/pipeline 3.24.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/README.md +14 -2
  2. package/defaults/pipeline.yaml +23 -5
  3. package/defaults/profiles.yaml +104 -21
  4. package/dist/argo-graph.js +90 -88
  5. package/dist/argo-submit.d.ts +110 -138
  6. package/dist/argo-submit.js +363 -378
  7. package/dist/argo-workflow.d.ts +156 -177
  8. package/dist/argo-workflow.js +96 -138
  9. package/dist/backlog.js +5 -4
  10. package/dist/bench/eval-report.js +21 -12
  11. package/dist/claude-settings-config.js +23 -31
  12. package/dist/claude-user-config.js +6 -16
  13. package/dist/cli/bootstrap-commands.js +3 -3
  14. package/dist/cli/doctor.d.ts +1 -1
  15. package/dist/cli/doctor.js +176 -171
  16. package/dist/cli/factory-commands.js +13 -13
  17. package/dist/cli/format.js +92 -86
  18. package/dist/cli/loop-commands.js +21 -21
  19. package/dist/cli/mcp-gateway-commands.js +12 -12
  20. package/dist/cli/plan-commands.js +85 -116
  21. package/dist/cli/program.d.ts +3 -3
  22. package/dist/cli/program.js +41 -43
  23. package/dist/cli/run-command.d.ts +1 -1
  24. package/dist/cli/run-command.js +25 -25
  25. package/dist/cli/run-commands.js +31 -29
  26. package/dist/cli/run-resolver.d.ts +1 -1
  27. package/dist/cli/run-resolver.js +30 -32
  28. package/dist/cli/run-service.d.ts +2 -3
  29. package/dist/cli/run-service.js +229 -239
  30. package/dist/cli/submit-options.js +85 -83
  31. package/dist/cluster-doctor.js +147 -175
  32. package/dist/codex-config.js +24 -30
  33. package/dist/commands/bench-command.js +9 -7
  34. package/dist/commands/coordinator-command.js +35 -0
  35. package/dist/commands/pipeline-command.js +13 -7
  36. package/dist/commands/runner-command-command.js +39 -24
  37. package/dist/commands/runner-node-command.js +31 -0
  38. package/dist/commands/ticket/complete.js +29 -33
  39. package/dist/commands/ticket/create.js +84 -82
  40. package/dist/commands/ticket/graph-check.js +11 -11
  41. package/dist/commands/ticket/next.js +36 -36
  42. package/dist/commands/ticket/sequence.js +11 -12
  43. package/dist/commands/ticket/shared.d.ts +5 -2
  44. package/dist/commands/ticket/shared.js +63 -66
  45. package/dist/commands/ticket/start.js +58 -67
  46. package/dist/commands/ticket-command.js +2 -2
  47. package/dist/config/defaults.d.ts +139 -140
  48. package/dist/config/defaults.js +81 -80
  49. package/dist/config/lint.js +52 -64
  50. package/dist/config/load.d.ts +4 -5
  51. package/dist/config/load.js +64 -74
  52. package/dist/config/schema/catalog.d.ts +2 -1
  53. package/dist/config/schema/catalog.js +3 -2
  54. package/dist/config/schema/mcp.js +46 -52
  55. package/dist/config/schemas.d.ts +382 -528
  56. package/dist/config/schemas.js +400 -398
  57. package/dist/config/validate.d.ts +1 -2
  58. package/dist/config/validate.js +236 -201
  59. package/dist/config.d.ts +2 -2
  60. package/dist/context/repo-map.js +105 -119
  61. package/dist/coordinator/composition.js +169 -0
  62. package/dist/coordinator/config.js +71 -0
  63. package/dist/coordinator/run-owner.js +64 -0
  64. package/dist/coordinator/wave-advance.js +148 -0
  65. package/dist/coordinator/wave-scheduler.js +50 -0
  66. package/dist/credentials/broker.d.ts +6 -8
  67. package/dist/credentials/broker.js +17 -16
  68. package/dist/credentials/codex-config.js +15 -19
  69. package/dist/credentials/file-targets.js +9 -15
  70. package/dist/credentials/local-codex-auth-sync.js +55 -58
  71. package/dist/credentials/opencode-config.js +28 -32
  72. package/dist/credentials/runner.js +13 -13
  73. package/dist/effect-sync-errors.js +28 -0
  74. package/dist/factory/create-experiment.js +285 -121
  75. package/dist/factory/exec.js +10 -12
  76. package/dist/factory/git-credentials.js +4 -4
  77. package/dist/factory/stamp-answers.js +10 -11
  78. package/dist/factory/template-update.js +65 -44
  79. package/dist/factory-lane.d.ts +26 -40
  80. package/dist/factory-lane.js +26 -25
  81. package/dist/gates.js +3 -3
  82. package/dist/git-remote-url.js +17 -19
  83. package/dist/hooks.d.ts +26 -26
  84. package/dist/hooks.js +20 -22
  85. package/dist/index.d.ts +4 -3
  86. package/dist/index.js +27 -36
  87. package/dist/install-commands/claude-code.js +100 -92
  88. package/dist/install-commands/host-selection.js +3 -6
  89. package/dist/install-commands/opencode.js +264 -259
  90. package/dist/install-commands/planner.js +163 -181
  91. package/dist/install-commands/result-format.js +1 -3
  92. package/dist/install-commands/shared.js +25 -42
  93. package/dist/install-commands/writer.js +22 -25
  94. package/dist/install-commands.js +8 -8
  95. package/dist/jscpd-output.js +29 -0
  96. package/dist/json-config-merge.js +14 -20
  97. package/dist/json-line-values.js +17 -6
  98. package/dist/loop/argo-poll.d.ts +0 -1
  99. package/dist/loop/argo-poll.js +15 -95
  100. package/dist/loop/backlog-records.js +1 -3
  101. package/dist/loop/controller-deps.js +150 -161
  102. package/dist/loop/controller.js +164 -186
  103. package/dist/loop/gh-checks.js +62 -61
  104. package/dist/loop/gh-runner.js +22 -25
  105. package/dist/loop/loop-command.js +66 -58
  106. package/dist/loop/loop-controller-entrypoint.js +29 -30
  107. package/dist/loop/merge.js +36 -40
  108. package/dist/loop/run-terminal-poll.js +69 -0
  109. package/dist/mcp/gateway-config.js +25 -29
  110. package/dist/mcp/gateway-doctor.js +77 -119
  111. package/dist/mcp/gateway-reconcile.js +17 -22
  112. package/dist/mcp/gateway-runtime.js +1 -3
  113. package/dist/mcp/host-config.js +37 -40
  114. package/dist/mcp/host-renderers.js +10 -18
  115. package/dist/mcp/repo-local-backends.js +51 -54
  116. package/dist/mcp/toolhive-vmcp.js +39 -39
  117. package/dist/model-resolver.js +29 -33
  118. package/dist/moka-global-config.d.ts +78 -55
  119. package/dist/moka-global-config.js +93 -92
  120. package/dist/moka-submit.d.ts +2179 -866
  121. package/dist/moka-submit.js +97 -99
  122. package/dist/opencode-project-config.js +34 -40
  123. package/dist/package-assets.js +1 -3
  124. package/dist/path-refs.js +2 -2
  125. package/dist/pipeline-init.js +25 -25
  126. package/dist/pipeline-runtime.d.ts +15 -14
  127. package/dist/pipeline-runtime.js +51 -50
  128. package/dist/planned-node.js +6 -3
  129. package/dist/planning/compile.d.ts +5 -5
  130. package/dist/planning/compile.js +140 -139
  131. package/dist/planning/dependency-refs.js +5 -5
  132. package/dist/planning/generate.d.ts +1296 -1296
  133. package/dist/planning/generate.js +307 -314
  134. package/dist/planning/graph.d.ts +0 -1
  135. package/dist/planning/graph.js +131 -149
  136. package/dist/remote/argo/model.d.ts +104 -122
  137. package/dist/remote/argo/model.js +205 -197
  138. package/dist/remote/argo/policy.js +39 -47
  139. package/dist/remote/argo/storage.js +81 -85
  140. package/dist/remote/jobs/execution-identity.js +70 -0
  141. package/dist/remote/jobs/job-outcome.js +108 -0
  142. package/dist/remote/jobs/manifest.js +96 -0
  143. package/dist/remote/jobs/model.d.ts +158 -0
  144. package/dist/remote/jobs/model.js +162 -0
  145. package/dist/remote/jobs/node-bootstrap.js +213 -0
  146. package/dist/remote/jobs/policy.js +123 -0
  147. package/dist/remote/jobs/reconcile.js +100 -0
  148. package/dist/remote/jobs/run-spec-client.js +64 -0
  149. package/dist/remote/jobs/run-spec-path.js +31 -0
  150. package/dist/remote/jobs/run-spec-server.d.ts +34 -0
  151. package/dist/remote/jobs/run-spec-server.js +116 -0
  152. package/dist/remote/jobs/submit.d.ts +23 -0
  153. package/dist/remote/jobs/submit.js +116 -0
  154. package/dist/remote/jobs/watch.js +59 -0
  155. package/dist/remote/submit/argo-submission.d.ts +3 -1
  156. package/dist/remote/submit/argo-submission.js +53 -58
  157. package/dist/remote/submit/compilation.d.ts +2 -0
  158. package/dist/remote/submit/compilation.js +46 -49
  159. package/dist/remote/submit/event-boundary.js +62 -76
  160. package/dist/remote/submit/hook-events.d.ts +6 -0
  161. package/dist/remote/submit/io.d.ts +7 -1
  162. package/dist/remote/submit/io.js +40 -43
  163. package/dist/remote/submit/jobs-submission.d.ts +59 -0
  164. package/dist/remote/submit/jobs-submission.js +97 -0
  165. package/dist/remote/submit/jobs-submit-result.d.ts +25 -0
  166. package/dist/remote/submit/jobs-submit-result.js +34 -0
  167. package/dist/remote/submit/options.d.ts +49 -0
  168. package/dist/remote/submit/options.js +27 -0
  169. package/dist/remote/submit/service.d.ts +40 -1
  170. package/dist/remote/submit/service.js +67 -49
  171. package/dist/run-control/command-context.js +6 -8
  172. package/dist/run-control/commands.js +10 -10
  173. package/dist/run-control/contracts.d.ts +52 -101
  174. package/dist/run-control/contracts.js +83 -69
  175. package/dist/run-control/detach.js +66 -72
  176. package/dist/run-control/execution-store.js +0 -0
  177. package/dist/run-control/file-errors.js +1 -3
  178. package/dist/run-control/logical-segment.js +3 -3
  179. package/dist/run-control/next-node.js +67 -78
  180. package/dist/run-control/postgres/postgres-run-control-store.js +196 -225
  181. package/dist/run-control/resume-command.js +34 -41
  182. package/dist/run-control/run-artifacts-command.js +84 -105
  183. package/dist/run-control/run-command-domain.js +7 -11
  184. package/dist/run-control/run-control-store.js +9 -9
  185. package/dist/run-control/run-query-command.js +73 -100
  186. package/dist/run-control/run-record.js +5 -3
  187. package/dist/run-control/run-state-lock.js +5 -5
  188. package/dist/run-control/runtime-event-projection.js +38 -50
  189. package/dist/run-control/runtime-reporter.js +86 -102
  190. package/dist/run-control/stop-command.js +42 -53
  191. package/dist/run-control/store-fs-effects.js +3 -1
  192. package/dist/run-control/store-manifest.js +38 -13
  193. package/dist/run-control/store-paths.js +3 -3
  194. package/dist/run-control/store.js +1 -1
  195. package/dist/run-control/submit-result.js +21 -21
  196. package/dist/run-control/supervisor.js +78 -68
  197. package/dist/run-control/workspace.js +2 -2
  198. package/dist/run-state/git-refs.js +258 -307
  199. package/dist/runner/opencode-excludes.js +4 -4
  200. package/dist/runner/subprocess-result.js +28 -37
  201. package/dist/runner/subprocess.js +41 -41
  202. package/dist/runner/timeouts.js +14 -13
  203. package/dist/runner-command/dynamic-command.js +46 -39
  204. package/dist/runner-command/environment-commands.js +91 -0
  205. package/dist/runner-command/finalize.js +174 -153
  206. package/dist/runner-command/lifecycle-context.js +115 -130
  207. package/dist/runner-command/lifecycle.js +78 -72
  208. package/dist/runner-command/pre-schedule.js +203 -223
  209. package/dist/runner-command/run.js +450 -448
  210. package/dist/runner-command/schedule-source-options.js +8 -10
  211. package/dist/runner-command/select-ready-wave.js +39 -30
  212. package/dist/runner-command/task-descriptor.js +11 -17
  213. package/dist/runner-command-contract.d.ts +147 -157
  214. package/dist/runner-command-contract.js +403 -411
  215. package/dist/runner-event-schema.d.ts +364 -505
  216. package/dist/runner-event-schema.js +187 -175
  217. package/dist/runner-event-sink.js +37 -43
  218. package/dist/runner-output.js +6 -6
  219. package/dist/runner.d.ts +57 -37
  220. package/dist/runner.js +176 -127
  221. package/dist/runtime/actor-ids.d.ts +1 -0
  222. package/dist/runtime/actor-ids.js +2 -2
  223. package/dist/runtime/agent-node/agent-node.js +99 -110
  224. package/dist/runtime/agent-node/handoff-finalization.js +23 -24
  225. package/dist/runtime/agent-node/model-selection.js +19 -22
  226. package/dist/runtime/agent-node/output-finalization.js +174 -204
  227. package/dist/runtime/agent-node/prompt-rendering.js +208 -251
  228. package/dist/runtime/agent-node/session-execution.js +43 -39
  229. package/dist/runtime/builtins/builtins.js +218 -284
  230. package/dist/runtime/changed-files/changed-files.js +29 -34
  231. package/dist/runtime/command-executor/command-executor.js +31 -23
  232. package/dist/runtime/config-error.js +1 -3
  233. package/dist/runtime/context/context.js +35 -37
  234. package/dist/runtime/contracts/contracts.d.ts +3 -0
  235. package/dist/runtime/contracts/index.d.ts +1 -1
  236. package/dist/runtime/detached-race.js +11 -14
  237. package/dist/runtime/drain-merge/drain-merge.js +139 -164
  238. package/dist/runtime/durable-store/acquisition.js +5 -3
  239. package/dist/runtime/durable-store/durable-store.d.ts +3 -1
  240. package/dist/runtime/durable-store/postgres/client.js +29 -0
  241. package/dist/runtime/durable-store/postgres/migrate-substrate.js +34 -29
  242. package/dist/runtime/durable-store/postgres/postgres-store.js +18 -20
  243. package/dist/runtime/events/events.js +273 -331
  244. package/dist/runtime/events/index.js +2 -2
  245. package/dist/runtime/execution-envelope.js +44 -0
  246. package/dist/runtime/gates/adjudication/llm-judge.js +21 -24
  247. package/dist/runtime/gates/adjudication/structured-claim.js +15 -16
  248. package/dist/runtime/gates/adjudicator/adjudicator.js +27 -27
  249. package/dist/runtime/gates/adjudicator/index.js +2 -2
  250. package/dist/runtime/gates/contract.js +5 -9
  251. package/dist/runtime/gates/gates.js +9 -8
  252. package/dist/runtime/gates/kinds/acceptance/acceptance.js +68 -71
  253. package/dist/runtime/gates/kinds/acceptance/index.js +2 -2
  254. package/dist/runtime/gates/kinds/artifact/artifact.js +4 -4
  255. package/dist/runtime/gates/kinds/artifact/index.js +2 -2
  256. package/dist/runtime/gates/kinds/builtin/builtin.js +4 -4
  257. package/dist/runtime/gates/kinds/builtin/index.js +2 -2
  258. package/dist/runtime/gates/kinds/changed-files/changed-files.js +31 -35
  259. package/dist/runtime/gates/kinds/changed-files/index.js +2 -2
  260. package/dist/runtime/gates/kinds/command/command.js +3 -3
  261. package/dist/runtime/gates/kinds/command/index.js +2 -2
  262. package/dist/runtime/gates/kinds/json-schema/index.js +2 -2
  263. package/dist/runtime/gates/kinds/json-schema/json-schema.js +7 -6
  264. package/dist/runtime/gates/kinds/verdict/index.js +2 -2
  265. package/dist/runtime/gates/kinds/verdict/verdict.js +3 -3
  266. package/dist/runtime/gates/orchestrator.js +72 -86
  267. package/dist/runtime/gates/registry.js +1 -3
  268. package/dist/runtime/handoff.d.ts +2 -1
  269. package/dist/runtime/handoff.js +43 -46
  270. package/dist/runtime/hooks/command-hook.js +57 -71
  271. package/dist/runtime/hooks/context.js +21 -25
  272. package/dist/runtime/hooks/events.js +84 -100
  273. package/dist/runtime/hooks/execution.js +6 -12
  274. package/dist/runtime/hooks/hooks.js +34 -23
  275. package/dist/runtime/hooks/invocation.js +23 -29
  276. package/dist/runtime/hooks/module-hook.js +36 -35
  277. package/dist/runtime/hooks/policy.js +32 -29
  278. package/dist/runtime/hooks/results.js +56 -66
  279. package/dist/runtime/journal-acquisition.d.ts +3 -2
  280. package/dist/runtime/journal-acquisition.js +14 -14
  281. package/dist/runtime/json-validation/json-validation.js +51 -72
  282. package/dist/runtime/launch-plan-args.js +39 -0
  283. package/dist/runtime/local-scheduler.js +46 -48
  284. package/dist/runtime/node-execution.js +376 -445
  285. package/dist/runtime/node-protocol/node-protocol.js +36 -79
  286. package/dist/runtime/node-state-store.d.ts +4 -1
  287. package/dist/runtime/node-state-store.js +13 -57
  288. package/dist/runtime/node-state-tracker.js +46 -54
  289. package/dist/runtime/open-pull-request/open-pull-request.js +201 -187
  290. package/dist/runtime/opencode-adapter.js +23 -24
  291. package/dist/runtime/opencode-agent-name.js +6 -6
  292. package/dist/runtime/opencode-runtime.js +115 -93
  293. package/dist/runtime/opencode-server.js +39 -53
  294. package/dist/runtime/opencode-session-executor.js +312 -344
  295. package/dist/runtime/parallel-node/parallel-node.js +123 -134
  296. package/dist/runtime/parallel-worktrees/parallel-worktrees.js +90 -73
  297. package/dist/runtime/protected-paths/protected-paths.js +70 -71
  298. package/dist/runtime/remediation/remediation.js +202 -231
  299. package/dist/runtime/retry.js +8 -10
  300. package/dist/runtime/run-journal.d.ts +2 -0
  301. package/dist/runtime/run-journal.js +7 -7
  302. package/dist/runtime/runtime-results.js +54 -69
  303. package/dist/runtime/scheduled-dependencies.d.ts +2 -1
  304. package/dist/runtime/scheduled-dependencies.js +61 -75
  305. package/dist/runtime/scheduler.js +179 -202
  306. package/dist/runtime/services/agent-node-runtime-service.js +3 -3
  307. package/dist/runtime/services/backlog-service.d.ts +1 -2
  308. package/dist/runtime/services/backlog-service.js +8 -14
  309. package/dist/runtime/services/command-executor-service.js +1 -1
  310. package/dist/runtime/services/config-io-service.js +24 -21
  311. package/dist/runtime/services/drain-merge-git-service.js +1 -1
  312. package/dist/runtime/services/file-system-service.js +9 -7
  313. package/dist/runtime/services/git-porcelain-service.js +6 -6
  314. package/dist/runtime/services/kubernetes-argo-service.d.ts +10 -3
  315. package/dist/runtime/services/kubernetes-argo-service.js +90 -84
  316. package/dist/runtime/services/kubernetes-jobs-read-service.js +115 -0
  317. package/dist/runtime/services/kubernetes-jobs-service.d.ts +37 -0
  318. package/dist/runtime/services/kubernetes-jobs-service.js +45 -0
  319. package/dist/runtime/services/kubernetes-jobs-watch-service.js +61 -0
  320. package/dist/runtime/services/kubernetes-lease-service.js +21 -0
  321. package/dist/runtime/services/mcp-gateway-service.js +143 -94
  322. package/dist/runtime/services/open-pull-request-git-service.js +1 -3
  323. package/dist/runtime/services/opencode-runtime-server-service.js +10 -12
  324. package/dist/runtime/services/opencode-sdk-service.js +7 -7
  325. package/dist/runtime/services/repo-io-service.js +10 -12
  326. package/dist/runtime/services/run-journal-file-service.js +2 -2
  327. package/dist/runtime/services/runner-command-io-service.js +48 -52
  328. package/dist/runtime/services/runner-event-sink-http-service.js +32 -39
  329. package/dist/runtime/step/step-node.js +15 -15
  330. package/dist/runtime/workflow-execution.js +56 -57
  331. package/dist/runtime/workflow-lifecycle.js +82 -101
  332. package/dist/runtime/yeet-event-record.js +104 -0
  333. package/dist/runtime/yeet-executor.js +184 -0
  334. package/dist/safe-json.js +16 -16
  335. package/dist/schedule/backlog-context.js +122 -116
  336. package/dist/schedule/baseline.js +249 -255
  337. package/dist/schedule/passes/coverage.js +63 -68
  338. package/dist/schedule/passes/drain-merge.js +34 -40
  339. package/dist/schedule/passes/ids.js +15 -17
  340. package/dist/schedule/passes/models.js +40 -37
  341. package/dist/schedule/passes/open-pull-request.js +16 -20
  342. package/dist/schedule/passes/references.js +18 -22
  343. package/dist/schedule/prompts.js +135 -141
  344. package/dist/schedule/scheduling-roles.js +8 -14
  345. package/dist/schema-boundary.d.ts +4 -0
  346. package/dist/schema-boundary.js +145 -0
  347. package/dist/serialized-write-queue.js +2 -2
  348. package/dist/standard-output-schemas.js +61 -59
  349. package/dist/strings.js +12 -12
  350. package/dist/task-ref.js +5 -5
  351. package/dist/tickets/apply-ticket-plan.js +52 -61
  352. package/dist/tickets/backlog-task-store.d.ts +15 -23
  353. package/dist/tickets/backlog-task-store.js +82 -94
  354. package/dist/tickets/completion/complete-ticket.js +53 -59
  355. package/dist/tickets/ticket-graph-dto.d.ts +24 -48
  356. package/dist/tickets/ticket-graph-dto.js +40 -40
  357. package/dist/tickets/ticket-graph.d.ts +7 -2
  358. package/dist/tickets/ticket-graph.js +36 -50
  359. package/dist/tickets/ticket-plan-command-args.js +6 -10
  360. package/dist/tickets/ticket-plan-render.js +10 -16
  361. package/dist/tickets/ticket-plan.d.ts +32 -41
  362. package/dist/tickets/ticket-plan.js +97 -77
  363. package/dist/tickets/ticket-selection.d.ts +3 -1
  364. package/dist/tickets/ticket-selection.js +37 -52
  365. package/dist/tickets/ticket-task-index.js +13 -17
  366. package/dist/tickets/validation-error-format.js +3 -10
  367. package/dist/token-estimator.js +12 -7
  368. package/dist/unknown-fields.js +21 -0
  369. package/dist/workflow-submit-contract.d.ts +9 -10
  370. package/dist/workflow-submit-contract.js +10 -9
  371. package/docs/adr-moka-postgres-schema-isolation.md +48 -0
  372. package/docs/adr-opencode-first-goal-loop-runtime.md +11 -11
  373. package/docs/config-architecture.md +13 -13
  374. package/docs/mcp-host-isolation.md +1 -0
  375. package/docs/moka-orchestrator-design.md +3 -3
  376. package/docs/operator-guide.md +1 -1
  377. package/docs/pipeline-console-runner-contract.md +10 -6
  378. package/docs/runtime-actor-model.md +18 -18
  379. package/docs/runtime-test-subsystems.md +200 -0
  380. package/docs/slash-command-adapter-contract.md +3 -3
  381. package/package.json +49 -85
  382. package/dist/config/schema/reference-validation.js +0 -83
@@ -1,121 +1,324 @@
1
+ import { nonEmptyMutableArray, parseStrictWithSchema, parseWithSchema, requiredString, stringRecord, struct, withDefault } from "./schema-boundary.js";
1
2
  import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
2
- import { argoWorkflowActiveDeadlineSecondsSchema, argoWorkflowPodGcSchema, argoWorkflowTtlStrategySchema, dbAuthOptionSchema, mcpGatewayAuthOptionSchema } from "./remote/argo/model.js";
3
- import { brokerAuthOptionSchema } from "./credentials/broker.js";
4
3
  import { appendPullRequestDelivery } from "./schedule/passes/open-pull-request.js";
5
4
  import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
6
5
  import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
7
6
  import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
8
7
  import { buildDynamicRunnerArgoWorkflowManifest, buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
9
8
  import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.js";
9
+ import { runnerPodSubmitOptionShape } from "./remote/submit/options.js";
10
10
  import { KubernetesArgoService, KubernetesArgoServiceLive } from "./runtime/services/kubernetes-argo-service.js";
11
11
  import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
12
+ import * as DateTime from "effect/DateTime";
13
+ import * as Effect from "effect/Effect";
14
+ import * as ManagedRuntime from "effect/ManagedRuntime";
15
+ import * as Option$1 from "effect/Option";
16
+ import * as R from "effect/Record";
17
+ import * as Schema from "effect/Schema";
12
18
  import { stringify } from "yaml";
13
- import { Effect } from "effect";
14
- import { z } from "zod";
15
- import { randomBytes } from "node:crypto";
16
19
  //#region src/argo-submit.ts
17
- const scheduleIdSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
18
- const configMapSchema = z.object({
19
- apiVersion: z.literal("v1"),
20
- data: z.record(z.string().min(1), z.string()),
21
- kind: z.literal("ConfigMap"),
22
- metadata: z.object({
23
- labels: z.record(z.string().min(1), z.string().min(1)).optional(),
24
- name: z.string().min(1),
25
- namespace: z.string().min(1)
26
- }).strict()
27
- }).strict();
28
- const workflowOwnerReferenceSchema = z.object({
29
- apiVersion: z.literal("argoproj.io/v1alpha1"),
30
- kind: z.literal("Workflow"),
31
- name: z.string().min(1),
32
- uid: z.string().min(1)
33
- }).strict();
34
- const configMapOwnerReferencesPatchSchema = z.object({ metadata: z.object({ ownerReferences: z.tuple([workflowOwnerReferenceSchema]) }).strict() }).strict();
35
- const createdWorkflowSchema = z.object({ metadata: z.object({
36
- name: z.string().min(1).optional(),
37
- uid: z.string().min(1).optional()
38
- }).passthrough() }).passthrough();
20
+ const scheduleId = Schema.String.check(Schema.isPattern(/^[a-z][a-z0-9-]*$/u));
21
+ const configMapSchema = struct({
22
+ apiVersion: Schema.Literal("v1"),
23
+ data: stringRecord,
24
+ kind: Schema.Literal("ConfigMap"),
25
+ metadata: struct({
26
+ labels: Schema.optional(stringRecord),
27
+ name: requiredString,
28
+ namespace: requiredString
29
+ })
30
+ });
31
+ const workflowOwnerReferenceSchema = struct({
32
+ apiVersion: Schema.Literal("argoproj.io/v1alpha1"),
33
+ kind: Schema.Literal("Workflow"),
34
+ name: requiredString,
35
+ uid: requiredString
36
+ });
37
+ const configMapOwnerReferencesPatchSchema = struct({ metadata: struct({ ownerReferences: Schema.mutable(Schema.Tuple([workflowOwnerReferenceSchema])) }) });
38
+ const createdWorkflowSchema = struct({ metadata: struct({
39
+ name: Schema.optional(requiredString),
40
+ uid: Schema.optional(requiredString)
41
+ }) });
42
+ const runnerTaskDescriptorJson = Schema.fromJsonString(struct({ nodeId: requiredString }));
43
+ const encodeRunnerTaskDescriptorJson = Schema.encodeSync(runnerTaskDescriptorJson);
44
+ const runnerCommandPayloadJson = Schema.fromJsonString(runnerCommandPayloadSchema);
45
+ const encodeRunnerCommandPayloadJson = Schema.encodeSync(runnerCommandPayloadJson);
46
+ const errorMessageShape = struct({ message: Schema.String });
47
+ const randomHex = (byteLength) => globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, byteLength * 2);
48
+ var ArgoSubmitError = class extends Schema.TaggedErrorClass()("ArgoSubmitError", { message: Schema.String }) {};
39
49
  const submitRunnerArgoWorkflowBaseOptionShape = {
40
- activeDeadlineSeconds: argoWorkflowActiveDeadlineSecondsSchema.optional(),
41
- brokerAuth: brokerAuthOptionSchema,
42
- dbAuth: dbAuthOptionSchema.optional(),
43
- eventAuthSecretKey: z.string().min(1).optional(),
44
- eventAuthSecretName: z.string().min(1).optional(),
45
- generateName: z.string().min(1).optional(),
46
- gitCredentialsSecretName: z.string().min(1).optional(),
47
- githubAuthSecretName: z.string().min(1).optional(),
48
- image: z.string().min(1).optional(),
49
- imagePullPolicy: z.enum([
50
+ ...runnerPodSubmitOptionShape,
51
+ imagePullPolicy: Schema.optional(Schema.Literals([
50
52
  "Always",
51
53
  "IfNotPresent",
52
54
  "Never"
53
- ]).optional(),
54
- imagePullSecretName: z.string().min(1).optional(),
55
- kubeContext: z.string().min(1).optional(),
56
- kubeconfigPath: z.string().min(1).optional(),
57
- mcpGatewayAuth: mcpGatewayAuthOptionSchema.optional(),
58
- name: z.string().min(1).optional(),
59
- namespace: z.string().min(1),
60
- npmRegistryAuthSecretName: z.string().min(1).optional(),
61
- payloadJson: z.string().min(1),
62
- podGC: argoWorkflowPodGcSchema.optional(),
63
- serviceAccountName: z.string().min(1).optional(),
64
- ttlStrategy: argoWorkflowTtlStrategySchema.optional()
55
+ ])),
56
+ namespace: requiredString,
57
+ payloadJson: requiredString
65
58
  };
66
- const submitRunnerArgoWorkflowOptionsSchema = z.object({
59
+ const commandScheduleOptionsSchema = struct({
60
+ command: nonEmptyMutableArray(requiredString),
61
+ deliverPullRequest: withDefault(Schema.Boolean, false),
62
+ generatedAt: Schema.optional(Schema.DateTimeUtcFromDate),
63
+ scheduleId: Schema.optional(scheduleId),
64
+ task: requiredString
65
+ });
66
+ const hasWorkflowName = (options) => options.name !== void 0 || options.generateName !== void 0;
67
+ const submitRunnerArgoWorkflowOptionsSchema = struct({
67
68
  ...submitRunnerArgoWorkflowBaseOptionShape,
68
- scheduleYaml: z.string().min(1)
69
- }).strict().refine(hasWorkflowName, { message: "Argo submit options must declare name or generateName" });
70
- const submitDynamicRunnerArgoWorkflowOptionsSchema = z.object({
69
+ scheduleYaml: requiredString
70
+ }).check(Schema.makeFilter((options) => hasWorkflowName(options) || "Argo submit options must declare name or generateName", {
71
+ description: "Static Argo submit options must include name or generateName.",
72
+ identifier: "SubmitRunnerArgoWorkflowName",
73
+ title: "Submit runner Argo workflow name"
74
+ }));
75
+ const submitDynamicRunnerArgoWorkflowOptionsSchema = struct({
71
76
  ...submitRunnerArgoWorkflowBaseOptionShape,
72
- workflowId: z.string().min(1)
73
- }).strict().refine(hasWorkflowName, { message: "Argo submit options must declare name or generateName" });
74
- const commandScheduleOptionsSchema = z.object({
75
- command: z.array(z.string().min(1)).min(1),
76
- deliverPullRequest: z.boolean().default(false),
77
- generatedAt: z.date().default(() => /* @__PURE__ */ new Date()),
78
- scheduleId: scheduleIdSchema.optional(),
79
- task: z.string().min(1)
80
- }).strict();
81
- function hasWorkflowName(options) {
82
- return options.name !== void 0 || options.generateName !== void 0;
83
- }
84
- function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
85
- return Effect.runPromise(Effect.provide(Effect.suspend(() => submitRunnerArgoWorkflowEffect(rawOptions, dependencies)), KubernetesArgoServiceLive));
86
- }
87
- function submitDynamicRunnerArgoWorkflow(rawOptions, dependencies = {}) {
88
- return Effect.runPromise(Effect.provide(Effect.suspend(() => submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies)), KubernetesArgoServiceLive));
89
- }
90
- function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
91
- const { config, ...schemaOptions } = rawOptions;
92
- const options = submitRunnerArgoWorkflowOptionsSchema.parse(schemaOptions);
77
+ workflowId: requiredString
78
+ }).check(Schema.makeFilter((options) => hasWorkflowName(options) || "Argo submit options must declare name or generateName", {
79
+ description: "Dynamic Argo submit options must include name or generateName.",
80
+ identifier: "SubmitDynamicRunnerArgoWorkflowName",
81
+ title: "Submit dynamic runner Argo workflow name"
82
+ }));
83
+ const kubernetesArgoRuntime = ManagedRuntime.make(KubernetesArgoServiceLive);
84
+ const runKubernetesArgoEffect = async (effect) => await kubernetesArgoRuntime.runPromise(effect);
85
+ const runConfigMap = (input) => {
86
+ return {
87
+ body: parseStrictWithSchema(configMapSchema, {
88
+ apiVersion: "v1",
89
+ data: input.data,
90
+ kind: "ConfigMap",
91
+ metadata: {
92
+ labels: input.labels,
93
+ name: input.name,
94
+ namespace: input.namespace
95
+ }
96
+ }),
97
+ name: input.name
98
+ };
99
+ };
100
+ const staticRunConfigMaps = (input) => [
101
+ runConfigMap({
102
+ data: { "payload.json": input.payloadJson },
103
+ labels: input.labels,
104
+ name: input.payloadConfigMapName,
105
+ namespace: input.namespace
106
+ }),
107
+ runConfigMap({
108
+ data: R.fromEntries(input.tasks.map((task) => [`${task.taskName}.json`, `${encodeRunnerTaskDescriptorJson(buildRunnerTaskDescriptor(task.nodeId))}\n`])),
109
+ labels: input.labels,
110
+ name: input.taskDescriptorConfigMapName,
111
+ namespace: input.namespace
112
+ }),
113
+ runConfigMap({
114
+ data: { "schedule.yaml": input.scheduleYaml },
115
+ labels: input.labels,
116
+ name: input.scheduleConfigMapName,
117
+ namespace: input.namespace
118
+ })
119
+ ];
120
+ const dynamicRunConfigMaps = (input) => [runConfigMap({
121
+ data: { "payload.json": input.payloadJson },
122
+ labels: input.labels,
123
+ name: input.payloadConfigMapName,
124
+ namespace: input.namespace
125
+ })];
126
+ const runConfigMapEffects = (configMapNames, effectForName) => Effect.all(configMapNames.map(effectForName), { concurrency: "unbounded" });
127
+ const patchRunConfigMapOwnerReferences = (input) => Effect.gen(function* effectBody() {
128
+ const service = yield* KubernetesArgoService;
129
+ yield* runConfigMapEffects(input.configMapNames, (name) => service.patchConfigMapOwnerReferences({
130
+ body: input.body,
131
+ dependencies: input.dependencies,
132
+ name,
133
+ namespace: input.namespace,
134
+ options: input.options
135
+ }));
136
+ });
137
+ const deleteRunConfigMaps = (input) => Effect.gen(function* effectBody() {
138
+ const service = yield* KubernetesArgoService;
139
+ yield* runConfigMapEffects(input.configMapNames, (name) => service.deleteConfigMap({
140
+ dependencies: input.dependencies,
141
+ name,
142
+ namespace: input.namespace,
143
+ options: input.options
144
+ }));
145
+ });
146
+ const workflowOwnerReference = (result) => {
147
+ if (result.workflowUid === void 0) return Option$1.none();
148
+ return Option$1.some(parseStrictWithSchema(workflowOwnerReferenceSchema, {
149
+ apiVersion: "argoproj.io/v1alpha1",
150
+ kind: "Workflow",
151
+ name: result.workflowName,
152
+ uid: result.workflowUid
153
+ }));
154
+ };
155
+ const missingWorkflowUidMessage = (result) => `Created Argo Workflow '${result.workflowName}' did not include metadata.uid; cannot own ConfigMaps`;
156
+ const configMapOwnerReferencesPatch = (ownerReference) => parseStrictWithSchema(configMapOwnerReferencesPatchSchema, { metadata: { ownerReferences: [ownerReference] } });
157
+ const errorMessage = (error) => Option$1.match(Schema.decodeUnknownOption(errorMessageShape)(error), {
158
+ onNone: () => String(error),
159
+ onSome: (value) => value.message
160
+ });
161
+ const warnRunConfigMapOwnershipSkipped = (input) => Effect.sync(() => {
162
+ const reason = errorMessage(input.error);
163
+ const message = `moka submit: failed to set Workflow ownerReference for ConfigMaps after Argo Workflow '${input.result.workflowName}' was created; leaving ConfigMaps for TTL/sweeper cleanup: ${reason}\n`;
164
+ process.stderr.write(message);
165
+ });
166
+ const ownRunConfigMaps = (input) => {
167
+ const ownerReference = workflowOwnerReference(input.result);
168
+ if (Option$1.isNone(ownerReference)) return warnRunConfigMapOwnershipSkipped({
169
+ error: new ArgoSubmitError({ message: missingWorkflowUidMessage(input.result) }),
170
+ result: input.result
171
+ }).pipe(Effect.as(input.result));
172
+ const body = configMapOwnerReferencesPatch(ownerReference.value);
173
+ return patchRunConfigMapOwnerReferences({
174
+ body,
175
+ configMapNames: input.configMapNames,
176
+ dependencies: input.dependencies,
177
+ namespace: input.namespace,
178
+ options: input.options
179
+ }).pipe(Effect.as(input.result));
180
+ };
181
+ const ownRunConfigMapsBestEffort = (input) => ownRunConfigMaps(input).pipe(Effect.catch((error) => warnRunConfigMapOwnershipSkipped({
182
+ error,
183
+ result: input.result
184
+ }).pipe(Effect.as(input.result))));
185
+ const cleanupRunConfigMapsOnFailure = (input) => deleteRunConfigMaps({
186
+ configMapNames: input.configMapNames,
187
+ dependencies: input.dependencies,
188
+ namespace: input.namespace,
189
+ options: input.options
190
+ }).pipe(Effect.catch((cleanupError) => Effect.fail(new ArgoSubmitError({ message: `Failed to clean up ConfigMaps after submit failure: ${errorMessage(input.error)}; cleanup failed: ${errorMessage(cleanupError)}` }))), Effect.flatMap(() => Effect.fail(input.error)));
191
+ const createRunConfigMaps = (input) => {
192
+ const createdConfigMapNames = [];
193
+ return Effect.gen(function* effectBody() {
194
+ const service = yield* KubernetesArgoService;
195
+ yield* Effect.all(input.configMaps.map((configMap) => service.createConfigMap({
196
+ body: configMap.body,
197
+ dependencies: input.dependencies,
198
+ namespace: input.namespace,
199
+ options: input.options
200
+ }).pipe(Effect.tap(() => Effect.sync(() => {
201
+ createdConfigMapNames.push(configMap.name);
202
+ })))), { concurrency: "unbounded" });
203
+ return createdConfigMapNames;
204
+ }).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
205
+ configMapNames: createdConfigMapNames,
206
+ dependencies: input.dependencies,
207
+ error,
208
+ namespace: input.namespace,
209
+ options: input.options
210
+ })));
211
+ };
212
+ const applyWorkflowFieldOverrides = (workflow, overrides = {}) => {
213
+ if (overrides.podGC === void 0) return workflow;
214
+ return parseStrictWithSchema(runnerArgoWorkflowManifestSchema, {
215
+ ...workflow,
216
+ spec: {
217
+ ...workflow.spec,
218
+ podGC: overrides.podGC
219
+ }
220
+ });
221
+ };
222
+ const workflowSubmitResult = (response, workflow, base) => {
223
+ const created = parseWithSchema(createdWorkflowSchema, response, { onExcessProperty: "preserve" });
224
+ return parseStrictWithSchema(workflowSubmitResultSchema, {
225
+ ...base,
226
+ workflowName: created.metadata.name ?? workflow.metadata.name,
227
+ workflowUid: created.metadata.uid
228
+ });
229
+ };
230
+ const submitWorkflowManifest = (input) => Effect.gen(function* effectBody() {
231
+ const service = yield* KubernetesArgoService;
232
+ const workflow = applyWorkflowFieldOverrides(input.workflow, input.workflowFieldOverrides);
233
+ const response = yield* service.createWorkflow({
234
+ body: parseStrictWithSchema(runnerArgoWorkflowManifestSchema, workflow),
235
+ dependencies: input.dependencies,
236
+ namespace: input.namespace,
237
+ options: input.options
238
+ });
239
+ return workflowSubmitResult(response, workflow, {
240
+ namespace: input.namespace,
241
+ ...input.resultExtras
242
+ });
243
+ });
244
+ const submitWorkflowWithRunConfigMaps = (input) => submitWorkflowManifest({
245
+ dependencies: input.dependencies,
246
+ namespace: input.namespace,
247
+ options: input.options,
248
+ resultExtras: input.resultExtras,
249
+ workflow: input.workflow,
250
+ workflowFieldOverrides: input.workflowFieldOverrides
251
+ }).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
252
+ configMapNames: input.configMapNames,
253
+ dependencies: input.dependencies,
254
+ error,
255
+ namespace: input.namespace,
256
+ options: input.options
257
+ })), Effect.flatMap((result) => ownRunConfigMapsBestEffort({
258
+ configMapNames: input.configMapNames,
259
+ dependencies: input.dependencies,
260
+ namespace: input.namespace,
261
+ options: input.options,
262
+ result
263
+ })));
264
+ const buildCommandScheduleYaml = (rawOptions) => {
265
+ const options = parseStrictWithSchema(commandScheduleOptionsSchema, rawOptions);
266
+ const scheduleId = options.scheduleId ?? `custom-${randomHex(8)}`;
267
+ const generatedAt = options.generatedAt ?? DateTime.nowUnsafe();
268
+ const artifact = {
269
+ generated_at: DateTime.formatIso(generatedAt),
270
+ kind: "pipeline-schedule",
271
+ root_workflow: "root",
272
+ schedule_id: scheduleId,
273
+ source_entrypoint: "custom",
274
+ task: options.task,
275
+ version: 1,
276
+ workflows: { root: { nodes: [{
277
+ command: options.command,
278
+ id: "command",
279
+ kind: "command"
280
+ }] } }
281
+ };
282
+ return stringify(appendPullRequestDelivery(options.deliverPullRequest, artifact));
283
+ };
284
+ const normalizeRunnerPayloadForSubmit = (input) => {
285
+ const repository = normalizeRunnerRepositoryForSubmit(input.payload.repository);
286
+ if (repository === input.payload.repository) return input;
287
+ const payload = parseStrictWithSchema(runnerCommandPayloadSchema, {
288
+ ...input.payload,
289
+ repository
290
+ });
291
+ return {
292
+ payload,
293
+ payloadJson: encodeRunnerCommandPayloadJson(payload)
294
+ };
295
+ };
296
+ const submitDynamicRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
297
+ const { config: _config, ...schemaOptions } = rawOptions;
298
+ const options = parseStrictWithSchema(submitDynamicRunnerArgoWorkflowOptionsSchema, schemaOptions);
299
+ const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
93
300
  const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
94
- payload: runnerCommandPayloadSchema.parse(parseRunnerCommandPayload(options.payloadJson)),
301
+ payload: parsedPayload,
95
302
  payloadJson: options.payloadJson
96
303
  });
97
- const compiled = compileScheduleArtifact(config, parseScheduleArtifact(options.scheduleYaml, "schedule.yaml"));
98
- const payloadConfigMapName = `pipeline-payload-${randomBytes(6).toString("hex")}`;
99
- const scheduleArtifactConfigMapName = `pipeline-schedule-${randomBytes(6).toString("hex")}`;
100
- const taskDescriptorConfigMapName = `pipeline-task-descriptors-${randomBytes(6).toString("hex")}`;
101
- if (payload.workflow.id !== compiled.workflowId) throw new Error(`Runner payload workflow '${payload.workflow.id}' does not match schedule workflow '${compiled.workflowId}'`);
102
- const graphEffect = compileSubmitArgoGraph(compiled).pipe(Effect.mapError((error) => /* @__PURE__ */ new Error(`Schedule '${compiled.workflowId}' cannot be submitted: ${error.message}`)));
304
+ if (payload.workflow.id !== options.workflowId) return Effect.fail(new ArgoSubmitError({ message: `Runner payload workflow '${payload.workflow.id}' does not match dynamic workflow '${options.workflowId}'` }));
305
+ const payloadConfigMapName = `pipeline-payload-${randomHex(6)}`;
103
306
  const labels = {
104
307
  "pipeline.oisin.dev/project": payload.run.project,
105
308
  "pipeline.oisin.dev/run-id": payload.run.id,
106
309
  "pipeline.oisin.dev/source": "argo-workflow",
107
- "pipeline.oisin.dev/workflow": compiled.workflowId
310
+ "pipeline.oisin.dev/workflow": options.workflowId
108
311
  };
109
- const workflow = buildRunnerArgoWorkflowManifest({
110
- annotations: payload.task.kind === "ticket" ? {
111
- "pipeline.oisin.dev/ticket-id": payload.task.id,
112
- "pipeline.oisin.dev/ticket-project": payload.run.project,
113
- "pipeline.oisin.dev/ticket-title": payload.task.title
114
- } : {},
312
+ const annotations = payload.task.kind === "ticket" ? {
313
+ "pipeline.oisin.dev/ticket-id": payload.task.id,
314
+ "pipeline.oisin.dev/ticket-project": payload.run.project,
315
+ "pipeline.oisin.dev/ticket-title": payload.task.title
316
+ } : {};
317
+ const workflow = buildDynamicRunnerArgoWorkflowManifest({
115
318
  activeDeadlineSeconds: options.activeDeadlineSeconds,
319
+ annotations,
116
320
  brokerAuth: options.brokerAuth,
117
321
  dbAuth: options.dbAuth,
118
- mcpGatewayAuth: options.mcpGatewayAuth,
119
322
  eventAuthSecretKey: options.eventAuthSecretKey,
120
323
  eventAuthSecretName: options.eventAuthSecretName,
121
324
  generateName: options.generateName,
@@ -125,68 +328,73 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
125
328
  imagePullPolicy: options.imagePullPolicy,
126
329
  imagePullSecretName: options.imagePullSecretName,
127
330
  labels,
331
+ mcpGatewayAuth: options.mcpGatewayAuth,
128
332
  name: options.name,
129
333
  namespace: options.namespace,
130
334
  npmRegistryAuthSecretName: options.npmRegistryAuthSecretName,
131
335
  payloadConfigMapName,
132
- plan: compiled.plan,
133
- scheduleConfigMapName: scheduleArtifactConfigMapName,
134
336
  serviceAccountName: options.serviceAccountName,
135
- taskDescriptorConfigMapName,
136
- ttlStrategy: options.ttlStrategy
337
+ ttlStrategy: options.ttlStrategy,
338
+ workflowId: options.workflowId
137
339
  });
138
- return Effect.gen(function* () {
139
- const graph = yield* graphEffect;
340
+ return Effect.gen(function* effectBody() {
341
+ const configMaps = dynamicRunConfigMaps({
342
+ labels,
343
+ namespace: options.namespace,
344
+ payloadConfigMapName,
345
+ payloadJson
346
+ });
347
+ const createdConfigMapNames = yield* createRunConfigMaps({
348
+ configMaps,
349
+ dependencies,
350
+ namespace: options.namespace,
351
+ options
352
+ });
140
353
  return yield* submitWorkflowWithRunConfigMaps({
141
- configMapNames: yield* createRunConfigMaps({
142
- configMaps: staticRunConfigMaps({
143
- labels,
144
- namespace: options.namespace,
145
- payloadConfigMapName,
146
- payloadJson,
147
- scheduleConfigMapName: scheduleArtifactConfigMapName,
148
- scheduleYaml: options.scheduleYaml,
149
- taskDescriptorConfigMapName,
150
- tasks: graph.tasks
151
- }),
152
- dependencies,
153
- namespace: options.namespace,
154
- options
155
- }),
354
+ configMapNames: createdConfigMapNames,
156
355
  dependencies,
157
356
  namespace: options.namespace,
158
357
  options,
159
- resultExtras: {
160
- payloadConfigMapName,
161
- scheduleConfigMapName: scheduleArtifactConfigMapName,
162
- taskDescriptorConfigMapName
163
- },
358
+ resultExtras: { payloadConfigMapName },
164
359
  workflow,
165
360
  workflowFieldOverrides: { podGC: options.podGC }
166
361
  });
167
362
  });
168
- }
169
- function submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies) {
170
- const { config: _config, ...schemaOptions } = rawOptions;
171
- const options = submitDynamicRunnerArgoWorkflowOptionsSchema.parse(schemaOptions);
363
+ };
364
+ const submitDynamicRunnerArgoWorkflow = async (rawOptions, dependencies = {}) => await runKubernetesArgoEffect(Effect.suspend(() => submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies)));
365
+ const compileSubmitArgoGraph = (compiled) => Effect.try({
366
+ catch: (error) => {
367
+ if (error instanceof ArgoGraphCompilerError) return error;
368
+ throw error;
369
+ },
370
+ try: () => compileArgoExecutionGraph(compiled.plan)
371
+ });
372
+ const submitRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
373
+ const { config, ...schemaOptions } = rawOptions;
374
+ const options = parseStrictWithSchema(submitRunnerArgoWorkflowOptionsSchema, schemaOptions);
375
+ const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
172
376
  const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
173
- payload: runnerCommandPayloadSchema.parse(parseRunnerCommandPayload(options.payloadJson)),
377
+ payload: parsedPayload,
174
378
  payloadJson: options.payloadJson
175
379
  });
176
- if (payload.workflow.id !== options.workflowId) throw new Error(`Runner payload workflow '${payload.workflow.id}' does not match dynamic workflow '${options.workflowId}'`);
177
- const payloadConfigMapName = `pipeline-payload-${randomBytes(6).toString("hex")}`;
380
+ const compiled = compileScheduleArtifact(config, parseScheduleArtifact(options.scheduleYaml, "schedule.yaml"));
381
+ const payloadConfigMapName = `pipeline-payload-${randomHex(6)}`;
382
+ const scheduleArtifactConfigMapName = `pipeline-schedule-${randomHex(6)}`;
383
+ const taskDescriptorConfigMapName = `pipeline-task-descriptors-${randomHex(6)}`;
384
+ if (payload.workflow.id !== compiled.workflowId) return Effect.fail(new ArgoSubmitError({ message: `Runner payload workflow '${payload.workflow.id}' does not match schedule workflow '${compiled.workflowId}'` }));
385
+ const graphEffect = compileSubmitArgoGraph(compiled).pipe(Effect.mapError((error) => new ArgoSubmitError({ message: `Schedule '${compiled.workflowId}' cannot be submitted: ${error.message}` })));
178
386
  const labels = {
179
387
  "pipeline.oisin.dev/project": payload.run.project,
180
388
  "pipeline.oisin.dev/run-id": payload.run.id,
181
389
  "pipeline.oisin.dev/source": "argo-workflow",
182
- "pipeline.oisin.dev/workflow": options.workflowId
390
+ "pipeline.oisin.dev/workflow": compiled.workflowId
183
391
  };
184
392
  const annotations = payload.task.kind === "ticket" ? {
185
393
  "pipeline.oisin.dev/ticket-id": payload.task.id,
186
394
  "pipeline.oisin.dev/ticket-project": payload.run.project,
187
395
  "pipeline.oisin.dev/ticket-title": payload.task.title
188
396
  } : {};
189
- const workflow = buildDynamicRunnerArgoWorkflowManifest({
397
+ const workflow = buildRunnerArgoWorkflowManifest({
190
398
  activeDeadlineSeconds: options.activeDeadlineSeconds,
191
399
  annotations,
192
400
  brokerAuth: options.brokerAuth,
@@ -199,274 +407,51 @@ function submitDynamicRunnerArgoWorkflowEffect(rawOptions, dependencies) {
199
407
  image: options.image,
200
408
  imagePullPolicy: options.imagePullPolicy,
201
409
  imagePullSecretName: options.imagePullSecretName,
202
- mcpGatewayAuth: options.mcpGatewayAuth,
203
410
  labels,
411
+ mcpGatewayAuth: options.mcpGatewayAuth,
204
412
  name: options.name,
205
413
  namespace: options.namespace,
206
414
  npmRegistryAuthSecretName: options.npmRegistryAuthSecretName,
207
415
  payloadConfigMapName,
416
+ plan: compiled.plan,
417
+ scheduleConfigMapName: scheduleArtifactConfigMapName,
208
418
  serviceAccountName: options.serviceAccountName,
209
- ttlStrategy: options.ttlStrategy,
210
- workflowId: options.workflowId
419
+ taskDescriptorConfigMapName,
420
+ ttlStrategy: options.ttlStrategy
211
421
  });
212
- return Effect.gen(function* () {
422
+ return Effect.gen(function* effectBody() {
423
+ const graph = yield* graphEffect;
424
+ const configMaps = staticRunConfigMaps({
425
+ labels,
426
+ namespace: options.namespace,
427
+ payloadConfigMapName,
428
+ payloadJson,
429
+ scheduleConfigMapName: scheduleArtifactConfigMapName,
430
+ scheduleYaml: options.scheduleYaml,
431
+ taskDescriptorConfigMapName,
432
+ tasks: graph.tasks
433
+ });
434
+ const createdConfigMapNames = yield* createRunConfigMaps({
435
+ configMaps,
436
+ dependencies,
437
+ namespace: options.namespace,
438
+ options
439
+ });
213
440
  return yield* submitWorkflowWithRunConfigMaps({
214
- configMapNames: yield* createRunConfigMaps({
215
- configMaps: dynamicRunConfigMaps({
216
- labels,
217
- namespace: options.namespace,
218
- payloadConfigMapName,
219
- payloadJson
220
- }),
221
- dependencies,
222
- namespace: options.namespace,
223
- options
224
- }),
441
+ configMapNames: createdConfigMapNames,
225
442
  dependencies,
226
443
  namespace: options.namespace,
227
444
  options,
228
- resultExtras: { payloadConfigMapName },
445
+ resultExtras: {
446
+ payloadConfigMapName,
447
+ scheduleConfigMapName: scheduleArtifactConfigMapName,
448
+ taskDescriptorConfigMapName
449
+ },
229
450
  workflow,
230
451
  workflowFieldOverrides: { podGC: options.podGC }
231
452
  });
232
453
  });
233
- }
234
- function staticRunConfigMaps(input) {
235
- return [
236
- runConfigMap({
237
- data: { "payload.json": input.payloadJson },
238
- labels: input.labels,
239
- name: input.payloadConfigMapName,
240
- namespace: input.namespace
241
- }),
242
- runConfigMap({
243
- data: Object.fromEntries(input.tasks.map((task) => [`${task.taskName}.json`, `${JSON.stringify(buildRunnerTaskDescriptor(task.nodeId))}\n`])),
244
- labels: input.labels,
245
- name: input.taskDescriptorConfigMapName,
246
- namespace: input.namespace
247
- }),
248
- runConfigMap({
249
- data: { "schedule.yaml": input.scheduleYaml },
250
- labels: input.labels,
251
- name: input.scheduleConfigMapName,
252
- namespace: input.namespace
253
- })
254
- ];
255
- }
256
- function dynamicRunConfigMaps(input) {
257
- return [runConfigMap({
258
- data: { "payload.json": input.payloadJson },
259
- labels: input.labels,
260
- name: input.payloadConfigMapName,
261
- namespace: input.namespace
262
- })];
263
- }
264
- function runConfigMap(input) {
265
- return {
266
- body: configMapSchema.parse({
267
- apiVersion: "v1",
268
- data: input.data,
269
- kind: "ConfigMap",
270
- metadata: {
271
- labels: input.labels,
272
- name: input.name,
273
- namespace: input.namespace
274
- }
275
- }),
276
- name: input.name
277
- };
278
- }
279
- function createRunConfigMaps(input) {
280
- const createdConfigMapNames = [];
281
- return Effect.gen(function* () {
282
- const service = yield* KubernetesArgoService;
283
- yield* Effect.forEach(input.configMaps, (configMap) => service.createConfigMap({
284
- body: configMap.body,
285
- dependencies: input.dependencies,
286
- namespace: input.namespace,
287
- options: input.options
288
- }).pipe(Effect.tap(() => Effect.sync(() => {
289
- createdConfigMapNames.push(configMap.name);
290
- }))));
291
- return createdConfigMapNames;
292
- }).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
293
- configMapNames: createdConfigMapNames,
294
- dependencies: input.dependencies,
295
- error,
296
- namespace: input.namespace,
297
- options: input.options
298
- })));
299
- }
300
- function ownRunConfigMaps(input) {
301
- const ownerReference = workflowOwnerReference(input.result);
302
- if (ownerReference === void 0) return warnRunConfigMapOwnershipSkipped({
303
- error: new Error(missingWorkflowUidMessage(input.result)),
304
- result: input.result
305
- }).pipe(Effect.as(input.result));
306
- return patchRunConfigMapOwnerReferences({
307
- body: configMapOwnerReferencesPatch(ownerReference),
308
- configMapNames: input.configMapNames,
309
- dependencies: input.dependencies,
310
- namespace: input.namespace,
311
- options: input.options
312
- }).pipe(Effect.as(input.result));
313
- }
314
- function ownRunConfigMapsBestEffort(input) {
315
- return ownRunConfigMaps(input).pipe(Effect.catch((error) => warnRunConfigMapOwnershipSkipped({
316
- error,
317
- result: input.result
318
- }).pipe(Effect.as(input.result))));
319
- }
320
- function warnRunConfigMapOwnershipSkipped(input) {
321
- return Effect.sync(() => {
322
- const reason = errorMessage(input.error);
323
- const message = `moka submit: failed to set Workflow ownerReference for ConfigMaps after Argo Workflow '${input.result.workflowName}' was created; leaving ConfigMaps for TTL/sweeper cleanup: ${reason}\n`;
324
- process.stderr.write(message);
325
- });
326
- }
327
- function cleanupRunConfigMapsOnFailure(input) {
328
- return deleteRunConfigMaps({
329
- configMapNames: input.configMapNames,
330
- dependencies: input.dependencies,
331
- namespace: input.namespace,
332
- options: input.options
333
- }).pipe(Effect.catch((cleanupError) => Effect.fail(/* @__PURE__ */ new Error(`Failed to clean up ConfigMaps after submit failure: ${errorMessage(input.error)}; cleanup failed: ${errorMessage(cleanupError)}`))), Effect.flatMap(() => Effect.fail(input.error)));
334
- }
335
- function patchRunConfigMapOwnerReferences(input) {
336
- return Effect.gen(function* () {
337
- const service = yield* KubernetesArgoService;
338
- return yield* Effect.all(input.configMapNames.map((name) => service.patchConfigMapOwnerReferences({
339
- body: input.body,
340
- dependencies: input.dependencies,
341
- name,
342
- namespace: input.namespace,
343
- options: input.options
344
- })), { concurrency: "unbounded" });
345
- });
346
- }
347
- function deleteRunConfigMaps(input) {
348
- return Effect.gen(function* () {
349
- const service = yield* KubernetesArgoService;
350
- return yield* Effect.all(input.configMapNames.map((name) => service.deleteConfigMap({
351
- dependencies: input.dependencies,
352
- name,
353
- namespace: input.namespace,
354
- options: input.options
355
- })), { concurrency: "unbounded" });
356
- });
357
- }
358
- function workflowOwnerReference(result) {
359
- if (result.workflowUid === void 0) return;
360
- return workflowOwnerReferenceSchema.parse({
361
- apiVersion: "argoproj.io/v1alpha1",
362
- kind: "Workflow",
363
- name: result.workflowName,
364
- uid: result.workflowUid
365
- });
366
- }
367
- function missingWorkflowUidMessage(result) {
368
- return `Created Argo Workflow '${result.workflowName}' did not include metadata.uid; cannot own ConfigMaps`;
369
- }
370
- function configMapOwnerReferencesPatch(ownerReference) {
371
- return configMapOwnerReferencesPatchSchema.parse({ metadata: { ownerReferences: [ownerReference] } });
372
- }
373
- function errorMessage(error) {
374
- return error instanceof Error ? error.message : String(error);
375
- }
376
- function submitWorkflowManifest(input) {
377
- return Effect.gen(function* () {
378
- const service = yield* KubernetesArgoService;
379
- const workflow = applyWorkflowFieldOverrides(input.workflow, input.workflowFieldOverrides);
380
- return workflowSubmitResult(yield* service.createWorkflow({
381
- body: runnerArgoWorkflowManifestSchema.parse(workflow),
382
- dependencies: input.dependencies,
383
- namespace: input.namespace,
384
- options: input.options
385
- }), workflow, {
386
- namespace: input.namespace,
387
- ...input.resultExtras
388
- });
389
- });
390
- }
391
- function submitWorkflowWithRunConfigMaps(input) {
392
- return submitWorkflowManifest({
393
- dependencies: input.dependencies,
394
- namespace: input.namespace,
395
- options: input.options,
396
- resultExtras: input.resultExtras,
397
- workflow: input.workflow,
398
- workflowFieldOverrides: input.workflowFieldOverrides
399
- }).pipe(Effect.catch((error) => cleanupRunConfigMapsOnFailure({
400
- configMapNames: input.configMapNames,
401
- dependencies: input.dependencies,
402
- error,
403
- namespace: input.namespace,
404
- options: input.options
405
- })), Effect.flatMap((result) => ownRunConfigMapsBestEffort({
406
- configMapNames: input.configMapNames,
407
- dependencies: input.dependencies,
408
- namespace: input.namespace,
409
- options: input.options,
410
- result
411
- })));
412
- }
413
- function applyWorkflowFieldOverrides(workflow, overrides) {
414
- if (overrides?.podGC === void 0) return workflow;
415
- return runnerArgoWorkflowManifestSchema.parse({
416
- ...workflow,
417
- spec: {
418
- ...workflow.spec,
419
- podGC: overrides.podGC
420
- }
421
- });
422
- }
423
- function workflowSubmitResult(response, workflow, base) {
424
- const created = createdWorkflowSchema.parse(response);
425
- return workflowSubmitResultSchema.parse({
426
- ...base,
427
- workflowName: created.metadata.name ?? workflow.metadata.name,
428
- workflowUid: created.metadata.uid
429
- });
430
- }
431
- function buildCommandScheduleYaml(rawOptions) {
432
- const options = commandScheduleOptionsSchema.parse(rawOptions);
433
- const scheduleId = options.scheduleId ?? `custom-${randomBytes(8).toString("hex")}`;
434
- const artifact = {
435
- generated_at: options.generatedAt.toISOString(),
436
- kind: "pipeline-schedule",
437
- root_workflow: "root",
438
- schedule_id: scheduleId,
439
- source_entrypoint: "custom",
440
- task: options.task,
441
- version: 1,
442
- workflows: { root: { nodes: [{
443
- command: options.command,
444
- id: "command",
445
- kind: "command"
446
- }] } }
447
- };
448
- return stringify(appendPullRequestDelivery(options.deliverPullRequest, artifact));
449
- }
450
- function normalizeRunnerPayloadForSubmit(input) {
451
- const repository = normalizeRunnerRepositoryForSubmit(input.payload.repository);
452
- if (repository === input.payload.repository) return input;
453
- const payload = runnerCommandPayloadSchema.parse({
454
- ...input.payload,
455
- repository
456
- });
457
- return {
458
- payload,
459
- payloadJson: JSON.stringify(payload)
460
- };
461
- }
462
- function compileSubmitArgoGraph(compiled) {
463
- return Effect.try({
464
- try: () => compileArgoExecutionGraph(compiled.plan),
465
- catch: (error) => {
466
- if (error instanceof ArgoGraphCompilerError) return error;
467
- throw error;
468
- }
469
- });
470
- }
454
+ };
455
+ const submitRunnerArgoWorkflow = async (rawOptions, dependencies = {}) => await runKubernetesArgoEffect(Effect.suspend(() => submitRunnerArgoWorkflowEffect(rawOptions, dependencies)));
471
456
  //#endregion
472
457
  export { buildCommandScheduleYaml, submitDynamicRunnerArgoWorkflow, submitRunnerArgoWorkflow };