@oisincoveney/pipeline 3.13.0 → 3.15.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 (113) hide show
  1. package/defaults/opencode-ecosystem.yaml +2 -12
  2. package/defaults/pipeline.yaml +0 -4
  3. package/dist/argo-submit.d.ts +2 -4
  4. package/dist/argo-submit.js +2 -6
  5. package/dist/argo-workflow.d.ts +2 -7
  6. package/dist/argo-workflow.js +16 -48
  7. package/dist/broker-auth.js +4 -3
  8. package/dist/cli/doctor.js +2 -2
  9. package/dist/cli/program.d.ts +1 -1
  10. package/dist/cli/program.js +72 -59
  11. package/dist/cli/submit-options.js +7 -4
  12. package/dist/cluster-doctor.js +4 -5
  13. package/dist/codex-auth-sync.js +16 -51
  14. package/dist/commands/ticket/complete.js +61 -0
  15. package/dist/commands/ticket/create.js +115 -0
  16. package/dist/commands/ticket/graph-check.js +16 -0
  17. package/dist/commands/ticket/next.js +43 -0
  18. package/dist/commands/ticket/sequence.js +18 -0
  19. package/dist/commands/ticket/shared.d.ts +14 -0
  20. package/dist/commands/ticket/shared.js +77 -0
  21. package/dist/commands/ticket/start.js +94 -0
  22. package/dist/commands/ticket-command.d.ts +2 -15
  23. package/dist/commands/ticket-command.js +14 -319
  24. package/dist/config/load.js +28 -7
  25. package/dist/config/schemas.d.ts +18 -21
  26. package/dist/config/schemas.js +2 -7
  27. package/dist/gates.js +1 -1
  28. package/dist/install-commands/claude-code.js +1 -1
  29. package/dist/install-commands/opencode.js +20 -2
  30. package/dist/install-commands.js +2 -2
  31. package/dist/install-hooks.js +2 -2
  32. package/dist/install-rules.js +1 -1
  33. package/dist/loop/controller-deps.js +1 -2
  34. package/dist/loop/loop-command.js +0 -2
  35. package/dist/loop/loop-controller-entrypoint.js +7 -4
  36. package/dist/moka-global-config.d.ts +19 -5
  37. package/dist/moka-global-config.js +43 -6
  38. package/dist/moka-submit.d.ts +6 -12
  39. package/dist/moka-submit.js +2 -6
  40. package/dist/pipeline-runtime.d.ts +22 -1
  41. package/dist/pipeline-runtime.js +55 -20
  42. package/dist/planning/generate.d.ts +20 -20
  43. package/dist/planning/generate.js +2 -2
  44. package/dist/run-control/commands.js +78 -40
  45. package/dist/run-control/contracts.js +1 -0
  46. package/dist/run-control/detach.js +1 -1
  47. package/dist/run-control/next-node.js +118 -0
  48. package/dist/run-control/postgres/postgres-run-control-store.js +244 -0
  49. package/dist/run-control/postgres/schema.js +52 -0
  50. package/dist/run-control/run-control-store.js +62 -0
  51. package/dist/run-control/runtime-reporter.js +10 -12
  52. package/dist/run-control/store.js +20 -8
  53. package/dist/run-control/submit-result.js +59 -0
  54. package/dist/run-control/supervisor.js +20 -16
  55. package/dist/run-state/opencode-accounts.js +8 -94
  56. package/dist/runner-command/lifecycle-context.js +1 -1
  57. package/dist/runner-command/run.js +30 -14
  58. package/dist/runner-command-contract.d.ts +2 -2
  59. package/dist/runner-event-schema.d.ts +4 -4
  60. package/dist/runner.d.ts +7 -0
  61. package/dist/runner.js +53 -19
  62. package/dist/runtime/agent-node/agent-node.js +2 -2
  63. package/dist/runtime/context/context.js +1 -1
  64. package/dist/runtime/contracts/contracts.d.ts +20 -1
  65. package/dist/runtime/contracts/index.d.ts +1 -1
  66. package/dist/runtime/durable-store/durable-store.js +53 -0
  67. package/dist/runtime/durable-store/postgres/postgres-store.js +133 -0
  68. package/dist/runtime/durable-store/postgres/schema.js +31 -0
  69. package/dist/runtime/events/events.js +1 -1
  70. package/dist/runtime/gates/adjudication/llm-judge.js +56 -0
  71. package/dist/runtime/gates/adjudication/structured-claim.js +52 -0
  72. package/dist/runtime/gates/adjudicator/adjudicator.js +69 -0
  73. package/dist/runtime/gates/adjudicator/index.js +39 -0
  74. package/dist/runtime/gates/contract.js +25 -0
  75. package/dist/runtime/gates/gates.js +15 -368
  76. package/dist/runtime/gates/index.js +3 -0
  77. package/dist/runtime/gates/kinds/acceptance/acceptance.js +109 -0
  78. package/dist/runtime/gates/kinds/acceptance/index.js +9 -0
  79. package/dist/runtime/gates/kinds/artifact/artifact.js +20 -0
  80. package/dist/runtime/gates/kinds/artifact/index.js +9 -0
  81. package/dist/runtime/gates/kinds/builtin/builtin.js +21 -0
  82. package/dist/runtime/gates/kinds/builtin/index.js +9 -0
  83. package/dist/runtime/gates/kinds/changed-files/changed-files.js +61 -0
  84. package/dist/runtime/gates/kinds/changed-files/index.js +9 -0
  85. package/dist/runtime/gates/kinds/command/command.js +21 -0
  86. package/dist/runtime/gates/kinds/command/index.js +9 -0
  87. package/dist/runtime/gates/kinds/json-schema/index.js +9 -0
  88. package/dist/runtime/gates/kinds/json-schema/json-schema.js +32 -0
  89. package/dist/runtime/gates/kinds/verdict/index.js +9 -0
  90. package/dist/runtime/gates/kinds/verdict/verdict.js +32 -0
  91. package/dist/runtime/gates/orchestrator.js +161 -0
  92. package/dist/runtime/gates/registry.js +47 -0
  93. package/dist/runtime/json-validation/json-validation.js +1 -1
  94. package/dist/runtime/node-protocol/node-protocol.js +83 -0
  95. package/dist/runtime/opencode-session-executor.js +48 -8
  96. package/dist/runtime/protected-paths/protected-paths.js +97 -0
  97. package/dist/runtime/run-journal.d.ts +21 -0
  98. package/dist/runtime/scheduler.js +13 -8
  99. package/dist/runtime/services/opencode-sdk-service.js +4 -0
  100. package/dist/runtime/services/repo-io-service.d.ts +2 -0
  101. package/dist/runtime/services/runner-command-io-service.js +1 -1
  102. package/dist/tickets/backlog-task-store.d.ts +2 -0
  103. package/dist/tickets/completion/complete-ticket.js +96 -0
  104. package/dist/tickets/ticket-graph.d.ts +2 -0
  105. package/dist/tickets/ticket-selection.d.ts +1 -0
  106. package/docs/mcp-gateway.md +6 -6
  107. package/docs/moka-orchestrator-design.md +79 -0
  108. package/docs/okteto-runner.md +1 -1
  109. package/docs/operator-guide.md +8 -5
  110. package/docs/pipeline-console-runner-contract.md +8 -7
  111. package/package.json +5 -2
  112. package/dist/runtime/run-journal.js +0 -28
  113. package/dist/runtime/services/run-journal-file-service.js +0 -20
@@ -59,15 +59,6 @@ ecosystem_code:
59
59
  role: long-running goal mode with /goal slash command, persistent state, evidence-gated completion, and TUI sidebar
60
60
  default_stack: true
61
61
  source: https://www.npmjs.com/package/@prevalentware/opencode-goal-plugin
62
- - id: oc-codex-multi-auth
63
- name: oc-codex-multi-auth
64
- package: oc-codex-multi-auth
65
- plugin:
66
- kind: npm
67
- package: oc-codex-multi-auth@6.3.2
68
- role: ChatGPT Plus/Pro OAuth with multi-account rotation, health checks, and Codex/GPT-5 routing for OpenCode
69
- default_stack: true
70
- source: https://github.com/ndycode/oc-codex-multi-auth
71
62
  - id: opencode-snip
72
63
  name: opencode-snip
73
64
  role: prompt snippet and command-discipline helper patterns
@@ -86,9 +77,8 @@ ecosystem_code:
86
77
  # Reasoning effort is no longer expressed as synthetic per-effort model ids.
87
78
  # Pipeline config carries `reasoning_effort` as data on each node/profile and the
88
79
  # runner applies it as the opencode model variant (`--variant` / prompt body
89
- # `variant`) for the selected GPT-5 model. The oc-codex-multi-auth plugin owns
90
- # the base-model templates and the Codex request options (store:false,
91
- # reasoning.encrypted_content), so moka registers no provider models here.
80
+ # `variant`) for the selected GPT-5 model. Broker auth owns Codex request
81
+ # options, so moka registers no provider models here.
92
82
  mcp_backends:
93
83
  - id: pipeline-gateway
94
84
  locality: project-remote
@@ -11,10 +11,6 @@ context_handoff:
11
11
  # seeded by the node's task + handoff artifacts, within token_budget.
12
12
  repo_map:
13
13
  enabled: true
14
- # durability: journal each terminal node result so a killed run resumes from the
15
- # last passed node without re-running (or re-spending tokens on) finished work.
16
- durability:
17
- enabled: true
18
14
  # parallel_worktrees: opt-in git-worktree isolation for parallel child nodes —
19
15
  # each parallel child runs on its own branch with its own per-worktree opencode
20
16
  # server so concurrent edits and sessions can't collide. OFF by default; enable
@@ -20,13 +20,11 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
20
20
  kubeconfigPath: z.ZodOptional<z.ZodString>;
21
21
  name: z.ZodOptional<z.ZodString>;
22
22
  namespace: z.ZodString;
23
- brokerAuth: z.ZodOptional<z.ZodObject<{
23
+ brokerAuth: z.ZodObject<{
24
24
  secretKey: z.ZodDefault<z.ZodString>;
25
25
  secretName: z.ZodString;
26
26
  url: z.ZodDefault<z.ZodString>;
27
- }, z.core.$strict>>;
28
- opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
29
- opencodeOpenaiAccountsSecretName: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$strict>;
30
28
  payloadJson: z.ZodString;
31
29
  scheduleYaml: z.ZodString;
32
30
  serviceAccountName: z.ZodOptional<z.ZodString>;
@@ -1,10 +1,10 @@
1
1
  import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
2
2
  import { brokerAuthOptionSchema } from "./broker-auth.js";
3
+ import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
3
4
  import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
4
5
  import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
5
6
  import { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
6
7
  import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.js";
7
- import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
8
8
  import { KubernetesArgoService, KubernetesArgoServiceLive } from "./runtime/services/kubernetes-argo-service.js";
9
9
  import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
10
10
  import { stringify } from "yaml";
@@ -39,9 +39,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
39
39
  kubeconfigPath: z.string().min(1).optional(),
40
40
  name: z.string().min(1).optional(),
41
41
  namespace: z.string().min(1),
42
- brokerAuth: brokerAuthOptionSchema.optional(),
43
- opencodeAuthSecretName: z.string().min(1).optional(),
44
- opencodeOpenaiAccountsSecretName: z.string().min(1).optional(),
42
+ brokerAuth: brokerAuthOptionSchema,
45
43
  payloadJson: z.string().min(1),
46
44
  scheduleYaml: z.string().min(1),
47
45
  serviceAccountName: z.string().min(1).optional()
@@ -92,8 +90,6 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
92
90
  name: options.name,
93
91
  namespace: options.namespace,
94
92
  brokerAuth: options.brokerAuth,
95
- opencodeAuthSecretName: options.opencodeAuthSecretName,
96
- opencodeOpenaiAccountsSecret: options.opencodeOpenaiAccountsSecretName ? { name: options.opencodeOpenaiAccountsSecretName } : void 0,
97
93
  payloadConfigMapName,
98
94
  plan: compiled.plan,
99
95
  scheduleConfigMapName: scheduleArtifactConfigMapName,
@@ -145,16 +145,11 @@ declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
145
145
  labels: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
146
146
  name: z.ZodOptional<z.ZodString>;
147
147
  namespace: z.ZodString;
148
- brokerAuth: z.ZodOptional<z.ZodObject<{
148
+ brokerAuth: z.ZodObject<{
149
149
  secretKey: z.ZodDefault<z.ZodString>;
150
150
  secretName: z.ZodString;
151
151
  url: z.ZodDefault<z.ZodString>;
152
- }, z.core.$strict>>;
153
- opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
154
- opencodeOpenaiAccountsSecret: z.ZodOptional<z.ZodObject<{
155
- key: z.ZodOptional<z.ZodString>;
156
- name: z.ZodString;
157
- }, z.core.$strict>>;
152
+ }, z.core.$strict>;
158
153
  payloadConfigMapKey: z.ZodDefault<z.ZodString>;
159
154
  payloadConfigMapName: z.ZodString;
160
155
  resources: z.ZodOptional<z.ZodObject<{
@@ -1,5 +1,4 @@
1
1
  import { compileArgoExecutionGraph } from "./argo-graph.js";
2
- import { OPENCODE_AUTH_STAGING_DIR, OPENCODE_OPENAI_ACCOUNTS_STAGING_DIR } from "./run-state/opencode-accounts.js";
3
2
  import { DEFAULT_RUNNER_TASK_DESCRIPTOR_PATH } from "./runner-command/task-descriptor.js";
4
3
  import { stringify } from "yaml";
5
4
  import { z } from "zod";
@@ -27,6 +26,10 @@ const RUNNER_OPENCODE_ENV = [
27
26
  name: "PIPELINE_AGENT_TIMEOUT_MS",
28
27
  value: "600000"
29
28
  },
29
+ {
30
+ name: "PIPELINE_AGENT_IDLE_TIMEOUT_MS",
31
+ value: "180000"
32
+ },
30
33
  {
31
34
  name: "PIPELINE_DISABLED_MODELS",
32
35
  value: "opencode-go/qwen3.7-max"
@@ -180,12 +183,7 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
180
183
  secretKey: z.string().min(1).default("api-key"),
181
184
  secretName: kubernetesNameSchema,
182
185
  url: z.string().min(1).default("https://cliproxy.momokaya.ee")
183
- }).strict().optional(),
184
- opencodeAuthSecretName: kubernetesNameSchema.optional(),
185
- opencodeOpenaiAccountsSecret: z.object({
186
- key: z.string().min(1).optional(),
187
- name: kubernetesNameSchema
188
- }).strict().optional(),
186
+ }).strict(),
189
187
  payloadConfigMapKey: z.string().min(1).default("payload.json"),
190
188
  payloadConfigMapName: kubernetesNameSchema,
191
189
  resources: argoWorkflowResourceRequirementsSchema.optional(),
@@ -312,43 +310,6 @@ function runnerWorkflowStorage(options, tasks) {
312
310
  readOnly: true
313
311
  });
314
312
  }
315
- if (options.opencodeAuthSecretName) {
316
- volumes.push({
317
- name: "opencode-auth",
318
- secret: {
319
- defaultMode: 256,
320
- items: [{
321
- key: "auth.json",
322
- path: "auth.json"
323
- }],
324
- secretName: options.opencodeAuthSecretName
325
- }
326
- });
327
- volumeMounts.push({
328
- mountPath: OPENCODE_AUTH_STAGING_DIR,
329
- name: "opencode-auth",
330
- readOnly: true
331
- });
332
- }
333
- if (options.opencodeOpenaiAccountsSecret) {
334
- const accountsKey = options.opencodeOpenaiAccountsSecret.key ?? "accounts.json";
335
- volumes.push({
336
- name: "opencode-openai-accounts",
337
- secret: {
338
- defaultMode: 256,
339
- items: [{
340
- key: accountsKey,
341
- path: "accounts.json"
342
- }],
343
- secretName: options.opencodeOpenaiAccountsSecret.name
344
- }
345
- });
346
- volumeMounts.push({
347
- mountPath: OPENCODE_OPENAI_ACCOUNTS_STAGING_DIR,
348
- name: "opencode-openai-accounts",
349
- readOnly: true
350
- });
351
- }
352
313
  if (options.gitCredentialsSecretName) {
353
314
  volumes.push({
354
315
  name: "runner-git-credentials",
@@ -387,18 +348,25 @@ function runnerWorkflowStorage(options, tasks) {
387
348
  };
388
349
  }
389
350
  /**
390
- * The runner container env: the static opencode/agent tuning plus, when broker
391
- * auth is configured, BROKER_URL (literal) and BROKER_API_KEY (sourced from the
392
- * broker secret key, never inlined into the manifest).
351
+ * The runner container env: static opencode/agent tuning plus BROKER_URL
352
+ * (literal) and BROKER_API_KEY (sourced from the broker secret key, never
353
+ * inlined into the manifest).
393
354
  */
394
355
  function runnerContainerEnv(options) {
395
- if (!options.brokerAuth) return [...RUNNER_OPENCODE_ENV];
396
356
  return [
397
357
  ...RUNNER_OPENCODE_ENV,
398
358
  {
399
359
  name: "BROKER_URL",
400
360
  value: options.brokerAuth.url
401
361
  },
362
+ {
363
+ name: "PIPELINE_BROKER_SECRET_NAME",
364
+ value: options.brokerAuth.secretName
365
+ },
366
+ {
367
+ name: "PIPELINE_BROKER_SECRET_KEY",
368
+ value: options.brokerAuth.secretKey
369
+ },
402
370
  {
403
371
  name: "BROKER_API_KEY",
404
372
  valueFrom: { secretKeyRef: {
@@ -39,9 +39,10 @@ const brokerAuthOptionSchema = z.object({
39
39
  url: z.string().min(1).default(DEFAULT_BROKER_URL)
40
40
  }).strict();
41
41
  /**
42
- * Resolve broker credentials from the environment, or `undefined` when the
43
- * runner is not broker-authenticated (local dev, non-broker fallback). The
44
- * `BROKER_URL` env is optional and defaults to the production broker origin.
42
+ * Resolve broker credentials from the environment, or `undefined` when a
43
+ * non-runner caller has not supplied broker auth. Remote runner manifests always
44
+ * inject BROKER_API_KEY. The `BROKER_URL` env is optional and defaults to the
45
+ * production broker origin.
45
46
  */
46
47
  function resolveBrokerCredentials(env = process.env) {
47
48
  const apiKey = env[BROKER_API_KEY_ENV];
@@ -1,12 +1,12 @@
1
1
  import { PipelineConfigError } from "../config/schemas.js";
2
2
  import { loadPipelineConfig } from "../config/load.js";
3
3
  import "../config.js";
4
- import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
5
4
  import { loadMokaGlobalConfig } from "../moka-global-config.js";
5
+ import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
6
6
  import { defaultClusterDoctorNamespace, runClusterDoctor } from "../cluster-doctor.js";
7
7
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
8
- import { join } from "node:path";
9
8
  import { execa } from "execa";
9
+ import { join } from "node:path";
10
10
  import matter from "gray-matter";
11
11
  //#region src/cli/doctor.ts
12
12
  const HEADLESS_AGENT_PERMISSION_VALUES = new Set(["ask"]);
@@ -1,6 +1,6 @@
1
1
  import { RunEffort, RunMode, RunTarget } from "../run-control/contracts.js";
2
2
  import { RunCommand } from "./run-command.js";
3
- import { TicketCommandOptions } from "../commands/ticket-command.js";
3
+ import { TicketCommandOptions } from "../commands/ticket/shared.js";
4
4
  import { runPipelineFromConfig } from "../pipeline-runtime.js";
5
5
  import { Effect } from "effect";
6
6
  import { Command } from "commander";
@@ -1,13 +1,15 @@
1
1
  import { loadPipelineConfig } from "../config/load.js";
2
2
  import "../config.js";
3
+ import { loadMokaGlobalConfig } from "../moka-global-config.js";
3
4
  import { flattenNodes } from "../planning/graph.js";
4
- import { configureGatewayHosts, localGatewayStatus, reconcileGateway, renderGatewayConfig, runGatewayDoctor, startLocalGateway } from "../mcp/gateway.js";
5
5
  import { createOrchestratorLaunchPlan, createRunnerLaunchPlan } from "../runner.js";
6
6
  import { compileWorkflowPlan } from "../planning/compile.js";
7
+ import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "../planning/generate.js";
8
+ import { withRunControlStoreScoped } from "../run-control/run-control-store.js";
9
+ import { configureGatewayHosts, localGatewayStatus, reconcileGateway, renderGatewayConfig, runGatewayDoctor, startLocalGateway } from "../mcp/gateway.js";
7
10
  import { generateRuntimeRunId, resolveWorkflowSelection } from "../runtime/context/context.js";
8
11
  import "../runtime/context/index.js";
9
12
  import { runPipelineFromConfig } from "../pipeline-runtime.js";
10
- import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "../planning/generate.js";
11
13
  import { formatCodexAuthSyncResult, syncLocalCodexAuth } from "../codex-auth-sync.js";
12
14
  import { registerBenchCommand } from "../commands/bench-command.js";
13
15
  import { registerConfiguredEntrypointCommands } from "../commands/pipeline-command.js";
@@ -17,9 +19,7 @@ import { registerTicketCommand } from "../commands/ticket-command.js";
17
19
  import { formatConfigLintWarning, lintPipelineConfig } from "../config/lint.js";
18
20
  import { parseLoopFlags, runLoopSubmit } from "../loop/loop-command.js";
19
21
  import { runLoopControllerEntrypoint } from "../loop/loop-controller-entrypoint.js";
20
- import { loadMokaGlobalConfig } from "../moka-global-config.js";
21
22
  import { formatPipelineInitResult, initPipelineProject } from "../pipeline-init.js";
22
- import { createRun, runControlStatusPaths, updateRunController } from "../run-control/store.js";
23
23
  import { registerRunControlCommands } from "../run-control/commands.js";
24
24
  import { startDetachedRunController } from "../run-control/detach.js";
25
25
  import { createRunStoreRuntimeReporter } from "../run-control/runtime-reporter.js";
@@ -78,10 +78,12 @@ async function runConfiguredPipeline(rawInputs) {
78
78
  const inputs = withRunId(rawInputs);
79
79
  const config = loadPipelineConfig(inputs.worktreePath, { allowMissingLintFileReferences: true });
80
80
  if (inputs.schedule) {
81
- const compiled = compileScheduleArtifact(config, parseScheduleArtifact(readFileSync(inputs.schedule, "utf8"), inputs.schedule), inputs.worktreePath);
81
+ const scheduleYaml = readFileSync(inputs.schedule, "utf8");
82
+ const compiled = compileScheduleArtifact(config, parseScheduleArtifact(scheduleYaml, inputs.schedule), inputs.worktreePath);
82
83
  await runAndPrintPipeline({
83
84
  ...inputs,
84
85
  config: compiled.config,
86
+ scheduleArtifact: scheduleYaml,
85
87
  workflow: compiled.workflowId
86
88
  });
87
89
  return;
@@ -103,10 +105,12 @@ async function runConfiguredPipeline(rawInputs) {
103
105
  worktreePath: inputs.worktreePath
104
106
  });
105
107
  console.log(`Schedule generated: ${result.path}`);
106
- const compiled = compileScheduleArtifact(config, parseScheduleArtifact(readFileSync(resolve(inputs.worktreePath, result.path), "utf8"), result.path), inputs.worktreePath);
108
+ const scheduleYaml = readFileSync(resolve(inputs.worktreePath, result.path), "utf8");
109
+ const compiled = compileScheduleArtifact(config, parseScheduleArtifact(scheduleYaml, result.path), inputs.worktreePath);
107
110
  await runAndPrintPipeline({
108
111
  ...inputs,
109
112
  config: compiled.config,
113
+ scheduleArtifact: scheduleYaml,
110
114
  workflow: compiled.workflowId
111
115
  });
112
116
  return;
@@ -117,8 +121,14 @@ async function runConfiguredPipeline(rawInputs) {
117
121
  });
118
122
  }
119
123
  async function runAndPrintPipeline(inputs) {
124
+ await Effect.runPromise(withRunControlStoreScoped(inputs.worktreePath, (store) => Effect.tryPromise({
125
+ catch: (error) => error,
126
+ try: () => runAndPrintPipelineWithStore(inputs, store)
127
+ })));
128
+ }
129
+ async function runAndPrintPipelineWithStore(inputs, store) {
120
130
  const runner = inputs.pipelineRunner ?? runPipelineFromConfig;
121
- const runStoreReporter = await createRunStoreReporter(inputs, createTerminalRuntimeReporter());
131
+ const runStoreReporter = await createRunStoreReporter(inputs, createTerminalRuntimeReporter(), store);
122
132
  if (inputs.supervised) console.log(formatSupervisedRunFollowUp(requireRunId(inputs.runId)));
123
133
  let result;
124
134
  try {
@@ -144,27 +154,29 @@ async function runWithFlushedReporter(flush, run) {
144
154
  await flush();
145
155
  }
146
156
  }
147
- async function createLocalRunStoreRuntimeReporter(inputs, reporter) {
157
+ async function createLocalRunStoreRuntimeReporter(inputs, reporter, store) {
148
158
  const runId = requireRunId(inputs.runId);
149
- await createRun({
159
+ await Effect.runPromise(store.createRun({
150
160
  ...resolvedRunControlOptions(inputs.runControl),
151
161
  nodeIds: plannedRunStoreNodeIds(inputs),
152
162
  runId,
153
- workspaceRoot: inputs.worktreePath
154
- });
163
+ ...inputs.scheduleArtifact ? { schedule: inputs.scheduleArtifact } : {}
164
+ }));
155
165
  return createRunStoreRuntimeReporter({
156
166
  reporter,
157
167
  runId,
168
+ store,
158
169
  workspaceRoot: inputs.worktreePath
159
170
  });
160
171
  }
161
- function createRunStoreReporter(inputs, reporter) {
172
+ function createRunStoreReporter(inputs, reporter, store) {
162
173
  if (inputs.runStoreMode === "reuse") {
163
174
  const runId = requireRunId(inputs.runId);
164
175
  if (inputs.supervisor) {
165
176
  const supervisor = createRunControlSupervisor({
166
177
  reporter,
167
178
  runId,
179
+ store,
168
180
  workspaceRoot: inputs.worktreePath
169
181
  });
170
182
  supervisor.start();
@@ -176,10 +188,11 @@ function createRunStoreReporter(inputs, reporter) {
176
188
  return createRunStoreRuntimeReporter({
177
189
  reporter,
178
190
  runId,
191
+ store,
179
192
  workspaceRoot: inputs.worktreePath
180
193
  });
181
194
  }
182
- return createLocalRunStoreRuntimeReporter(inputs, reporter);
195
+ return createLocalRunStoreRuntimeReporter(inputs, reporter, store);
183
196
  }
184
197
  function requireRunId(runId) {
185
198
  if (!runId) throw new Error("Run id is required for local run-control persistence.");
@@ -343,7 +356,7 @@ function createCliProgram(options = {}) {
343
356
  dryRun: flags.dryRun
344
357
  }));
345
358
  });
346
- program.command("codex-auth").description("Manage local Codex multi-auth integration").command("sync-local").description("Use one local oc-codex account pool and declare the plugin in dev repos").option("--root <path>", "directory containing repositories to sync").option("--dry-run", "show planned changes without writing files").option("--check", "fail if local Codex auth config is not synced").action((flags) => {
359
+ program.command("codex-auth").description("Manage local Codex broker auth integration").command("sync-local").description("Point local dev repos' opencode openai provider at the central CLIProxyAPI broker").option("--root <path>", "directory containing repositories to sync").option("--dry-run", "show planned changes without writing files").option("--check", "fail if local Codex auth config is not synced").action((flags) => {
347
360
  const result = syncLocalCodexAuth({
348
361
  check: flags.check,
349
362
  dryRun: flags.dryRun,
@@ -352,12 +365,7 @@ function createCliProgram(options = {}) {
352
365
  console.log(formatCodexAuthSyncResult(result));
353
366
  if (!result.ok) process.exitCode = 1;
354
367
  });
355
- addMokaSubmitOptions(program.command("submit").description([
356
- "Submit work to Momokaya as an Argo Workflow.",
357
- "Compatibility alias for `moka run \"<task>\" --target remote --effort thorough`.",
358
- "Quick equivalent: `moka run \"<task>\" --target remote --effort quick`.",
359
- "Command equivalent: `moka run --target remote --command -- <argv...>`."
360
- ].join("\n")).argument("[input...]", "task description, or command argv with --command")).action(async (input, flags) => {
368
+ addMokaSubmitOptions(program.command("submit").description("Submit work to Momokaya as an Argo Workflow.").argument("[input...]", "task description, or command argv with --command")).action(async (input, flags) => {
361
369
  printMokaSubmitResult(await runMokaSubmitFromCli(input, flags));
362
370
  });
363
371
  registerLoopCommand(program);
@@ -407,7 +415,10 @@ function buildLoopSubmitInput(options) {
407
415
  const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
408
416
  const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
409
417
  const momokaya = loadMokaGlobalConfig()?.momokaya;
418
+ const brokerAuth = momokaya?.submit.brokerAuth;
419
+ if (!brokerAuth) throw new Error("momokaya.submit.brokerAuth is required for moka loop submit");
410
420
  return {
421
+ brokerAuth,
411
422
  config,
412
423
  eventUrl: momokaya?.submit.eventUrl,
413
424
  flags: parseLoopFlags(options),
@@ -415,9 +426,6 @@ function buildLoopSubmitInput(options) {
415
426
  githubAuthSecretName: momokaya?.submit.githubAuthSecretName,
416
427
  kubeconfigPath: momokaya?.kubernetes.kubeconfig,
417
428
  namespace: momokaya?.kubernetes.namespace,
418
- brokerAuth: momokaya?.submit.brokerAuth,
419
- opencodeAuthSecretName: momokaya?.submit.opencodeAuthSecretName,
420
- opencodeOpenaiAccountsSecretName: momokaya?.submit.opencodeOpenaiAccountsSecretName,
421
429
  serviceAccountName: momokaya?.submit.serviceAccountName,
422
430
  worktreePath: cwd
423
431
  };
@@ -441,52 +449,55 @@ async function runDetachedResolvedTask(task, execution, runControl) {
441
449
  task,
442
450
  worktreePath
443
451
  });
444
- await createRun({
445
- ...resolvedRunControlOptions(runControl),
446
- nodeIds: plannedRunStoreNodeIds({
447
- config: prepared.config,
448
- entrypoint: prepared.entrypoint,
452
+ await Effect.runPromise(withRunControlStoreScoped(worktreePath, (store) => Effect.gen(function* () {
453
+ yield* store.createRun({
454
+ ...resolvedRunControlOptions(runControl),
455
+ nodeIds: plannedRunStoreNodeIds({
456
+ config: prepared.config,
457
+ entrypoint: prepared.entrypoint,
458
+ runId,
459
+ runControl,
460
+ schedule: prepared.schedule,
461
+ task,
462
+ workflow: prepared.workflow,
463
+ worktreePath
464
+ }),
449
465
  runId,
450
- runControl,
451
- schedule: prepared.schedule,
452
- task,
453
- workflow: prepared.workflow,
454
- worktreePath
455
- }),
456
- runId,
457
- workspaceRoot: worktreePath
458
- });
459
- const launch = await startDetachedRunController({
460
- entrypoint: prepared.entrypoint,
461
- runId,
462
- schedule: prepared.schedule,
463
- task,
464
- workflow: prepared.workflow,
465
- workspaceRoot: worktreePath
466
- });
467
- await updateRunController({
468
- controller: {
469
- argv: launch.argv,
470
- cwd: worktreePath,
471
- paths: runControlStatusPaths({
466
+ ...prepared.scheduleArtifact ? { schedule: prepared.scheduleArtifact } : {}
467
+ });
468
+ const launch = yield* Effect.tryPromise({
469
+ catch: (error) => error,
470
+ try: () => startDetachedRunController({
471
+ entrypoint: prepared.entrypoint,
472
472
  runId,
473
+ schedule: prepared.schedule,
474
+ task,
475
+ workflow: prepared.workflow,
473
476
  workspaceRoot: worktreePath
474
- }),
475
- pid: launch.pid,
476
- startedAt: launch.startedAt
477
- },
478
- runId,
479
- workspaceRoot: worktreePath
480
- });
477
+ })
478
+ });
479
+ yield* store.updateRunController({
480
+ controller: {
481
+ argv: launch.argv,
482
+ cwd: worktreePath,
483
+ paths: store.statusPaths({ runId }),
484
+ pid: launch.pid,
485
+ startedAt: launch.startedAt
486
+ },
487
+ runId
488
+ });
489
+ })));
481
490
  console.log(formatDetachedRunFollowUp(runId));
482
491
  }
483
492
  async function prepareDetachedRun(input) {
484
493
  if (input.execution.schedule) {
485
494
  const schedule = resolve(input.execution.schedule);
486
- const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(readFileSync(schedule, "utf8"), schedule), input.worktreePath);
495
+ const scheduleYaml = readFileSync(schedule, "utf8");
496
+ const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(scheduleYaml, schedule), input.worktreePath);
487
497
  return {
488
498
  config: compiled.config,
489
499
  schedule,
500
+ scheduleArtifact: scheduleYaml,
490
501
  workflow: compiled.workflowId
491
502
  };
492
503
  }
@@ -505,10 +516,12 @@ async function prepareDetachedRun(input) {
505
516
  });
506
517
  console.log(`Schedule generated: ${result.path}`);
507
518
  const schedule = resolve(input.worktreePath, result.path);
508
- const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(readFileSync(schedule, "utf8"), result.path), input.worktreePath);
519
+ const scheduleYaml = readFileSync(schedule, "utf8");
520
+ const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(scheduleYaml, result.path), input.worktreePath);
509
521
  return {
510
522
  config: compiled.config,
511
523
  schedule,
524
+ scheduleArtifact: scheduleYaml,
512
525
  workflow: compiled.workflowId
513
526
  };
514
527
  }
@@ -1,7 +1,7 @@
1
1
  import { loadPipelineConfig } from "../config/load.js";
2
2
  import "../config.js";
3
- import { submitMoka } from "../moka-submit.js";
4
3
  import { loadMokaGlobalConfig } from "../moka-global-config.js";
4
+ import { submitMoka } from "../moka-submit.js";
5
5
  import { Option } from "commander";
6
6
  //#region src/cli/submit-options.ts
7
7
  function addMokaSubmitOptions(command) {
@@ -34,6 +34,7 @@ function resolveMokaEventUrl(flags, globalConfig) {
34
34
  function mokaCommonSubmitOptions(input) {
35
35
  const momokaya = input.globalConfig?.momokaya;
36
36
  return {
37
+ brokerAuth: resolveMokaBrokerAuth(input.globalConfig),
37
38
  config: input.config,
38
39
  delivery: { pullRequest: input.flags.openPr === true },
39
40
  eventUrl: input.eventUrl,
@@ -48,13 +49,15 @@ function mokaCommonSubmitOptions(input) {
48
49
  kubeconfigPath: input.flags.kubeconfig ?? momokaya?.kubernetes.kubeconfig,
49
50
  name: input.flags.name,
50
51
  namespace: input.flags.namespace ?? momokaya?.kubernetes.namespace,
51
- brokerAuth: momokaya?.submit.brokerAuth,
52
- opencodeAuthSecretName: momokaya?.submit.opencodeAuthSecretName,
53
- opencodeOpenaiAccountsSecretName: momokaya?.submit.opencodeOpenaiAccountsSecretName,
54
52
  serviceAccountName: input.flags.serviceAccount ?? momokaya?.submit.serviceAccountName,
55
53
  worktreePath: input.cwd
56
54
  };
57
55
  }
56
+ function resolveMokaBrokerAuth(globalConfig) {
57
+ const brokerAuth = globalConfig?.momokaya.submit.brokerAuth;
58
+ if (!brokerAuth) throw new Error("momokaya.submit.brokerAuth is required for remote submit");
59
+ return brokerAuth;
60
+ }
58
61
  function submitMokaCommandInput(input, flags, commonOptions) {
59
62
  if (flags.quick || flags.schedule) throw new Error("--command cannot be combined with --quick or --schedule");
60
63
  if (input.length === 0) throw new Error("Command argv is required when --command is set");
@@ -1,16 +1,16 @@
1
- import { KubernetesArgoService, KubernetesArgoServiceLive } from "./runtime/services/kubernetes-argo-service.js";
2
1
  import { loadMokaGlobalConfig } from "./moka-global-config.js";
2
+ import { KubernetesArgoService, KubernetesArgoServiceLive } from "./runtime/services/kubernetes-argo-service.js";
3
3
  import { Effect } from "effect";
4
4
  //#region src/cluster-doctor.ts
5
5
  const DEFAULT_NAMESPACE = "momokaya-pipeline";
6
6
  const DEFAULT_RESOURCES = {
7
+ brokerAuthSecretName: "broker-api-key",
7
8
  eventAuthSecretName: "pipeline-runner-event-auth",
8
9
  eventAuthExternalSecretName: "pipeline-runner-event-auth",
9
10
  externalSecretRemoteRef: "agent-runtime/pipeline-runner/event-auth",
10
11
  gitCredentialsSecretName: "oisin-bot-git-credentials",
11
12
  githubAuthSecretName: "oisin-bot-github-auth",
12
13
  imagePullSecretName: "ghcr-pull-secret",
13
- opencodeAuthSecretName: "opencode-auth-1",
14
14
  serviceAccountName: "pipeline-runner"
15
15
  };
16
16
  const FORBIDDEN_RE = /forbidden/i;
@@ -62,17 +62,16 @@ function clusterResources() {
62
62
  const configured = loadMokaGlobalConfig()?.momokaya.submit;
63
63
  return configured ? {
64
64
  ...DEFAULT_RESOURCES,
65
- brokerAuthSecretName: configured.brokerAuth?.secretName,
65
+ brokerAuthSecretName: configured.brokerAuth?.secretName ?? DEFAULT_RESOURCES.brokerAuthSecretName,
66
66
  eventAuthSecretName: configured.eventAuthSecretName,
67
67
  gitCredentialsSecretName: configured.gitCredentialsSecretName,
68
68
  githubAuthSecretName: configured.githubAuthSecretName,
69
69
  imagePullSecretName: configured.imagePullSecretName,
70
- opencodeAuthSecretName: configured.opencodeAuthSecretName ?? DEFAULT_RESOURCES.opencodeAuthSecretName,
71
70
  serviceAccountName: configured.serviceAccountName
72
71
  } : DEFAULT_RESOURCES;
73
72
  }
74
73
  function secretChecks(namespace, kubectlOptions, resources) {
75
- const authSecretCheck = resources.brokerAuthSecretName ? [resources.brokerAuthSecretName, `Secret ${resources.brokerAuthSecretName} missing in ${namespace}; expected broker api-key mount by name.`] : [resources.opencodeAuthSecretName, `Secret ${resources.opencodeAuthSecretName} missing in ${namespace}; expected OpenCode auth mount by name.`];
74
+ const authSecretCheck = [resources.brokerAuthSecretName, `Secret ${resources.brokerAuthSecretName} missing in ${namespace}; expected broker api-key mount by name.`];
76
75
  return [
77
76
  [resources.eventAuthSecretName, eventAuthMissingDetail(namespace)],
78
77
  [resources.imagePullSecretName, `Secret ${resources.imagePullSecretName} missing in ${namespace}; expected imagePullSecret for ghcr.io/oisin-ee/pipeline-runner.`],