@oisincoveney/pipeline 3.15.2 → 3.15.4

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 (107) hide show
  1. package/defaults/profiles.yaml +2 -2
  2. package/dist/argo-graph.js +1 -1
  3. package/dist/argo-submit.js +2 -2
  4. package/dist/argo-workflow.d.ts +2 -40
  5. package/dist/argo-workflow.js +53 -434
  6. package/dist/cli/bootstrap-commands.js +34 -0
  7. package/dist/cli/loop-commands.js +49 -0
  8. package/dist/cli/mcp-gateway-commands.js +60 -0
  9. package/dist/cli/plan-commands.js +156 -0
  10. package/dist/cli/program.d.ts +1 -26
  11. package/dist/cli/program.js +39 -595
  12. package/dist/cli/run-commands.js +58 -0
  13. package/dist/cli/run-service.d.ts +29 -0
  14. package/dist/cli/run-service.js +349 -0
  15. package/dist/commands/ticket/create.js +1 -1
  16. package/dist/config/defaults.js +1 -1
  17. package/dist/config/schemas.d.ts +1 -1
  18. package/dist/config/schemas.js +1 -1
  19. package/dist/{broker-auth.d.ts → credentials/broker.d.ts} +2 -3
  20. package/dist/credentials/broker.js +34 -0
  21. package/dist/credentials/codex-config.js +32 -0
  22. package/dist/credentials/file-targets.js +26 -0
  23. package/dist/{codex-auth-sync.js → credentials/local-codex-auth-sync.js} +21 -9
  24. package/dist/credentials/opencode-config.js +65 -0
  25. package/dist/credentials/runner.js +31 -0
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.js +2 -1
  28. package/dist/install-commands/claude-code.js +1 -1
  29. package/dist/install-commands/host-selection.js +13 -0
  30. package/dist/install-commands/opencode.js +2 -2
  31. package/dist/install-commands/planner.js +299 -0
  32. package/dist/install-commands/result-format.js +6 -0
  33. package/dist/install-commands/writer.js +45 -0
  34. package/dist/install-commands.js +22 -268
  35. package/dist/install-hooks.js +1 -1
  36. package/dist/loop/gh-checks.js +1 -1
  37. package/dist/moka-global-config.js +2 -2
  38. package/dist/moka-submit.d.ts +410 -44
  39. package/dist/moka-submit.js +15 -328
  40. package/dist/pipeline-runtime.d.ts +7 -27
  41. package/dist/pipeline-runtime.js +11 -869
  42. package/dist/planning/generate.js +3 -3
  43. package/dist/remote/argo/model.d.ts +44 -0
  44. package/dist/remote/argo/model.js +160 -0
  45. package/dist/remote/argo/policy.js +77 -0
  46. package/dist/remote/argo/storage.js +110 -0
  47. package/dist/remote/argo/templates.js +82 -0
  48. package/dist/remote/submit/argo-submission.d.ts +26 -0
  49. package/dist/remote/submit/argo-submission.js +62 -0
  50. package/dist/remote/submit/compilation.d.ts +8 -0
  51. package/dist/remote/submit/compilation.js +106 -0
  52. package/dist/remote/submit/event-boundary.js +118 -0
  53. package/dist/remote/submit/hook-events.js +14 -0
  54. package/dist/remote/submit/io.d.ts +13 -0
  55. package/dist/remote/submit/io.js +66 -0
  56. package/dist/remote/submit/service.d.ts +10 -0
  57. package/dist/remote/submit/service.js +26 -0
  58. package/dist/run-control/next-node.js +2 -21
  59. package/dist/run-control/runtime-reporter.js +1 -1
  60. package/dist/run-control/submit-result.js +1 -1
  61. package/dist/run-state/git-refs.js +28 -10
  62. package/dist/runner/subprocess.js +1 -1
  63. package/dist/runner-command/finalize.js +1 -1
  64. package/dist/runner-command/lifecycle-context.js +1 -1
  65. package/dist/runner-command/lifecycle.js +1 -1
  66. package/dist/runner-command/run.js +2 -2
  67. package/dist/runner-command/task-descriptor.js +1 -1
  68. package/dist/runner-command-contract.d.ts +2 -2
  69. package/dist/runner-command-contract.js +1 -1
  70. package/dist/runner-event-schema.d.ts +6 -6
  71. package/dist/runtime/agent-node/agent-node.js +83 -545
  72. package/dist/runtime/agent-node/handoff-finalization.js +55 -0
  73. package/dist/runtime/agent-node/model-selection.js +38 -0
  74. package/dist/runtime/agent-node/output-finalization.js +263 -0
  75. package/dist/runtime/agent-node/prompt-rendering.js +302 -0
  76. package/dist/runtime/agent-node/session-execution.js +48 -0
  77. package/dist/runtime/builtins/builtins.js +1 -1
  78. package/dist/runtime/config-error.js +6 -0
  79. package/dist/runtime/context/context.js +1 -1
  80. package/dist/runtime/durable-store/acquisition.js +10 -0
  81. package/dist/runtime/events/events.js +244 -89
  82. package/dist/runtime/hooks/command-hook.js +87 -0
  83. package/dist/runtime/hooks/context.js +38 -0
  84. package/dist/runtime/hooks/events.js +131 -0
  85. package/dist/runtime/hooks/execution.js +29 -0
  86. package/dist/runtime/hooks/hooks.js +33 -365
  87. package/dist/runtime/hooks/invocation.js +49 -0
  88. package/dist/runtime/hooks/module-hook.js +52 -0
  89. package/dist/runtime/hooks/policy.js +36 -0
  90. package/dist/runtime/hooks/results.js +81 -0
  91. package/dist/runtime/journal-acquisition.d.ts +9 -0
  92. package/dist/runtime/journal-acquisition.js +28 -0
  93. package/dist/runtime/node-execution.js +560 -0
  94. package/dist/runtime/opencode-runtime.js +47 -1
  95. package/dist/runtime/runtime-results.js +75 -0
  96. package/dist/runtime/scheduled-dependencies.d.ts +4 -0
  97. package/dist/runtime/scheduled-dependencies.js +98 -0
  98. package/dist/runtime/services/backlog-service.js +2 -3
  99. package/dist/runtime/services/runner-command-io-service.js +1 -1
  100. package/dist/runtime/workflow-execution.js +84 -0
  101. package/dist/tickets/backlog-task-store.d.ts +2 -2
  102. package/dist/tickets/backlog-task-store.js +1 -1
  103. package/dist/tickets/ticket-graph-dto.js +1 -1
  104. package/dist/tickets/ticket-plan.js +1 -1
  105. package/package.json +1 -2
  106. package/dist/broker-auth.js +0 -173
  107. package/dist/run-state/opencode-accounts.js +0 -47
@@ -155,7 +155,7 @@ profiles:
155
155
  scheduling_roles: [implementation]
156
156
  description: Add focused failing tests for the requested behavior.
157
157
  timeout_ms: 900000
158
- instructions: { inline: "Add focused failing tests for the requested behavior only. Do not change production code. Only edit files matching test paths such as **/*.test.*, **/*.spec.*, **/*_test.*, **/__tests__/**, test/**, or tests/**. NEVER silence lint, type, complexity, or dead-code findings with suppression comments (no // fallow-ignore, // biome-ignore, eslint-disable, oxlint-disable, @ts-ignore, or @ts-expect-error); fix the underlying cause if a gate flags your test, restructure the test (e.g. move restricted imports into shared support/fixture helpers) rather than suppressing it. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
158
+ instructions: { inline: "This scheduled node is already dispatched by Moka; do direct repository work inside the current workspace. Do not invoke `moka run`, `moka submit`, `$dispatch`, `$scope`, `$execute`, or any nested Moka/workflow supervisor from this node. Add focused failing tests for the requested behavior only. Do not change production code. Only edit files matching test paths such as **/*.test.*, **/*.spec.*, **/*_test.*, **/__tests__/**, test/**, or tests/**. Tool findings about lint, types, complexity, or dead code must be resolved at source; comments must not hide them. If a gate flags your test, restructure the test (e.g. move restricted imports into shared support/fixture helpers). Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
159
159
  skills: [test]
160
160
  mcp_servers: [pipeline-gateway]
161
161
  tools: [read, list, grep, glob, bash, edit, write]
@@ -170,7 +170,7 @@ profiles:
170
170
  scheduling_roles: [implementation]
171
171
  description: Implement production code until the failing tests pass.
172
172
  timeout_ms: 900000
173
- instructions: { inline: "Implement the smallest production change that satisfies the failing tests. NEVER silence lint, type, complexity, or dead-code findings with suppression comments (no // fallow-ignore, // biome-ignore, eslint-disable, oxlint-disable, @ts-ignore, or @ts-expect-error); fix the underlying cause reduce complexity by extracting helpers, remove genuinely dead code, and migrate off deprecated APIs rather than suppressing the warning. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
173
+ instructions: { inline: "This scheduled node is already dispatched by Moka; do direct repository work inside the current workspace. Do not invoke `moka run`, `moka submit`, `$dispatch`, `$scope`, `$execute`, or any nested Moka/workflow supervisor from this node. Implement the smallest production change that makes the failing tests pass. Tool findings about lint, types, complexity, or dead code must be resolved at source; comments must not hide them. Reduce complexity by extracting helpers, remove genuinely dead code, and migrate off deprecated APIs rather than hiding the warning. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
174
174
  skills: [trace, test, fix, library-first-development]
175
175
  mcp_servers: [pipeline-gateway]
176
176
  tools: [read, list, grep, glob, bash, edit, write]
@@ -1,8 +1,8 @@
1
1
  import { uniqueStrings } from "./strings.js";
2
2
  import { resolveExecutableDependencyIds } from "./planning/dependency-refs.js";
3
3
  import { terminalDependencyItems } from "./planning/graph.js";
4
- import { z } from "zod";
5
4
  import { Data } from "effect";
5
+ import { z } from "zod";
6
6
  //#region src/argo-graph.ts
7
7
  const argoExecutableTaskSchema = z.object({
8
8
  dependencies: z.array(z.string().min(1)),
@@ -1,5 +1,5 @@
1
1
  import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
2
- import { brokerAuthOptionSchema } from "./broker-auth.js";
2
+ import { brokerAuthOptionSchema } from "./credentials/broker.js";
3
3
  import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
4
4
  import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
5
5
  import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
@@ -8,8 +8,8 @@ import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.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";
11
- import { z } from "zod";
12
11
  import { Effect } from "effect";
12
+ import { z } from "zod";
13
13
  import { randomBytes } from "node:crypto";
14
14
  //#region src/argo-submit.ts
15
15
  const scheduleIdSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
@@ -1,4 +1,5 @@
1
1
  import { WorkflowExecutionPlan } from "./planning/compile.js";
2
+ import { buildRunnerArgoWorkflowOptionsSchema } from "./remote/argo/model.js";
2
3
  import { z } from "zod";
3
4
 
4
5
  //#region src/argo-workflow.d.ts
@@ -59,7 +60,6 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
59
60
  }, z.core.$strict>>;
60
61
  dag: z.ZodOptional<z.ZodObject<{
61
62
  tasks: z.ZodArray<z.ZodObject<{
62
- dependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
63
63
  arguments: z.ZodOptional<z.ZodObject<{
64
64
  artifacts: z.ZodOptional<z.ZodArray<z.ZodObject<{
65
65
  from: z.ZodOptional<z.ZodString>;
@@ -67,6 +67,7 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
67
67
  path: z.ZodOptional<z.ZodString>;
68
68
  }, z.core.$strict>>>;
69
69
  }, z.core.$strict>>;
70
+ dependencies: z.ZodOptional<z.ZodArray<z.ZodString>>;
70
71
  name: z.ZodString;
71
72
  template: z.ZodString;
72
73
  }, z.core.$strict>>;
@@ -127,45 +128,6 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
127
128
  }, z.core.$strict>>;
128
129
  }, z.core.$strict>;
129
130
  }, z.core.$strict>;
130
- declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
131
- activeDeadlineSeconds: z.ZodOptional<z.ZodNumber>;
132
- eventAuthSecretKey: z.ZodOptional<z.ZodString>;
133
- eventAuthSecretName: z.ZodOptional<z.ZodString>;
134
- generateName: z.ZodOptional<z.ZodString>;
135
- gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
136
- githubAuthSecretName: z.ZodOptional<z.ZodString>;
137
- annotations: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
138
- image: z.ZodDefault<z.ZodString>;
139
- imagePullPolicy: z.ZodDefault<z.ZodEnum<{
140
- Always: "Always";
141
- IfNotPresent: "IfNotPresent";
142
- Never: "Never";
143
- }>>;
144
- imagePullSecretName: z.ZodOptional<z.ZodString>;
145
- labels: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
146
- name: z.ZodOptional<z.ZodString>;
147
- namespace: z.ZodString;
148
- brokerAuth: z.ZodObject<{
149
- secretKey: z.ZodDefault<z.ZodString>;
150
- secretName: z.ZodString;
151
- url: z.ZodDefault<z.ZodString>;
152
- }, z.core.$strict>;
153
- payloadConfigMapKey: z.ZodDefault<z.ZodString>;
154
- payloadConfigMapName: z.ZodString;
155
- resources: z.ZodOptional<z.ZodObject<{
156
- limits: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
157
- requests: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
158
- }, z.core.$strict>>;
159
- scheduleConfigMapKey: z.ZodDefault<z.ZodString>;
160
- scheduleConfigMapName: z.ZodString;
161
- serviceAccountName: z.ZodDefault<z.ZodString>;
162
- taskDescriptorConfigMapName: z.ZodString;
163
- ttlStrategy: z.ZodOptional<z.ZodObject<{
164
- secondsAfterCompletion: z.ZodOptional<z.ZodNumber>;
165
- secondsAfterFailure: z.ZodOptional<z.ZodNumber>;
166
- secondsAfterSuccess: z.ZodOptional<z.ZodNumber>;
167
- }, z.core.$strict>>;
168
- }, z.core.$strict>;
169
131
  type ArgoWorkflowManifest = z.infer<typeof runnerArgoWorkflowManifestSchema>;
170
132
  type BuildRunnerArgoWorkflowOptions = z.input<typeof buildRunnerArgoWorkflowOptionsSchema> & {
171
133
  plan: WorkflowExecutionPlan;
@@ -1,457 +1,76 @@
1
1
  import { compileArgoExecutionGraph } from "./argo-graph.js";
2
- import { DEFAULT_RUNNER_TASK_DESCRIPTOR_PATH } from "./runner-command/task-descriptor.js";
2
+ import { RUNNER_WORKFLOW_ENTRYPOINT, RUNNER_WORKFLOW_START_TASK } from "./remote/argo/policy.js";
3
+ import { buildRunnerArgoWorkflowOptionsSchema, createRunnerArgoWorkflowManifestSchema } from "./remote/argo/model.js";
4
+ import { runnerWorkflowStorage } from "./remote/argo/storage.js";
5
+ import { runnerCommandTemplate, runnerFinalizerTemplate, runnerLifecycleTemplate } from "./remote/argo/templates.js";
3
6
  import { stringify } from "yaml";
4
- import { z } from "zod";
5
7
  //#region src/argo-workflow.ts
6
- const ARGO_WORKFLOW_API_VERSION = "argoproj.io/v1alpha1";
7
- const ARGO_WORKFLOW_KIND = "Workflow";
8
- const RUNNER_WORKFLOW_IMAGE = "ghcr.io/oisin-ee/pipeline-runner:latest";
9
- const RUNNER_WORKFLOW_SERVICE_ACCOUNT = "pipeline-runner";
10
- const RUNNER_WORKFLOW_ENTRYPOINT = "pipeline";
11
- const RUNNER_WORKFLOW_START_TASK = "workflow-start";
12
- const RUNNER_WORKFLOW_PAYLOAD_PATH = "/etc/pipeline/payload.json";
13
- const RUNNER_WORKFLOW_SCHEDULE_PATH = "/etc/pipeline/schedule.yaml";
14
- const RUNNER_GIT_CREDENTIALS_PATH = "/etc/pipeline/git-credentials";
15
- const RUNNER_RETRY_STRATEGY = {
16
- expression: "lastRetry.status == 'Error' || (lastRetry.exitCode != '0' && lastRetry.exitCode != '1')",
17
- limit: "3",
18
- retryPolicy: "Always"
19
- };
20
- const RUNNER_OPENCODE_ENV = [
21
- {
22
- name: "CODEX_AUTH_PER_PROJECT_ACCOUNTS",
23
- value: "0"
24
- },
25
- {
26
- name: "PIPELINE_AGENT_TIMEOUT_MS",
27
- value: "600000"
28
- },
29
- {
30
- name: "PIPELINE_AGENT_IDLE_TIMEOUT_MS",
31
- value: "180000"
32
- },
33
- {
34
- name: "PIPELINE_DISABLED_MODELS",
35
- value: "opencode-go/qwen3.7-max"
36
- }
37
- ];
38
- const DEFAULT_RUNNER_RESOURCES = {
39
- limits: {
40
- cpu: "4",
41
- memory: "12Gi"
42
- },
43
- requests: {
44
- cpu: "1",
45
- memory: "5Gi"
46
- }
47
- };
48
- const DEFAULT_RUNNER_DEADLINE_SECONDS = 5400;
49
- const kubernetesNameSchema = z.string().min(1);
50
- const labelValueSchema = z.string().min(1);
51
- const stringMapSchema = z.record(z.string().min(1), z.string().min(1));
52
- const configMapVolumeSchema = z.object({
53
- items: z.array(z.object({
54
- key: z.string().min(1),
55
- path: z.string().min(1)
56
- }).strict()),
57
- name: kubernetesNameSchema
58
- }).strict();
59
- const secretVolumeSchema = z.object({
60
- defaultMode: z.number().int().positive().optional(),
61
- items: z.array(z.object({
62
- key: z.string().min(1),
63
- path: z.string().min(1)
64
- }).strict()).optional(),
65
- optional: z.boolean().optional(),
66
- secretName: kubernetesNameSchema
67
- }).strict();
68
- const argoWorkflowVolumeSchema = z.object({
69
- configMap: configMapVolumeSchema.optional(),
70
- name: kubernetesNameSchema,
71
- secret: secretVolumeSchema.optional()
72
- }).strict().refine((volume) => volume.configMap !== void 0 || volume.secret !== void 0, { message: "Workflow volumes must declare configMap or secret" });
73
- const argoWorkflowVolumeMountSchema = z.object({
74
- mountPath: z.string().min(1),
75
- name: kubernetesNameSchema,
76
- readOnly: z.boolean().optional(),
77
- subPath: z.string().min(1).optional()
78
- }).strict();
79
- const argoWorkflowResourceRequirementsSchema = z.object({
80
- limits: stringMapSchema.optional(),
81
- requests: stringMapSchema.optional()
82
- }).strict();
83
- const argoWorkflowArtifactSchema = z.object({
84
- from: z.string().min(1).optional(),
85
- name: z.string().min(1),
86
- path: z.string().min(1).optional()
87
- }).strict();
88
- const argoWorkflowRetryStrategySchema = z.object({
89
- expression: z.string().min(1).optional(),
90
- limit: z.string().min(1).optional(),
91
- retryPolicy: z.enum([
92
- "Always",
93
- "OnError",
94
- "OnFailure",
95
- "OnTransientError"
96
- ])
97
- }).strict();
98
- const argoWorkflowEnvVarSchema = z.union([z.object({
99
- name: z.string().min(1),
100
- value: z.string()
101
- }).strict(), z.object({
102
- name: z.string().min(1),
103
- valueFrom: z.object({ secretKeyRef: z.object({
104
- key: z.string().min(1),
105
- name: kubernetesNameSchema
106
- }).strict() }).strict()
107
- }).strict()]);
108
- const argoWorkflowTemplateSchema = z.object({
109
- container: z.object({
110
- args: z.array(z.string().min(1)).min(1),
111
- command: z.array(z.string().min(1)).min(1).optional(),
112
- env: z.array(argoWorkflowEnvVarSchema).optional(),
113
- image: z.string().min(1),
114
- imagePullPolicy: z.enum([
115
- "Always",
116
- "IfNotPresent",
117
- "Never"
118
- ]),
119
- name: z.string().min(1).optional(),
120
- resources: argoWorkflowResourceRequirementsSchema.optional(),
121
- volumeMounts: z.array(argoWorkflowVolumeMountSchema)
122
- }).strict().optional(),
123
- dag: z.object({ tasks: z.array(z.object({
124
- dependencies: z.array(z.string().min(1)).optional(),
125
- arguments: z.object({ artifacts: z.array(argoWorkflowArtifactSchema).optional() }).strict().optional(),
126
- name: z.string().min(1),
127
- template: z.string().min(1)
128
- }).strict()).min(1) }).strict().optional(),
129
- inputs: z.object({
130
- artifacts: z.array(argoWorkflowArtifactSchema).optional(),
131
- parameters: z.array(z.object({ name: z.string().min(1) }).strict()).optional()
132
- }).strict().optional(),
133
- outputs: z.object({ artifacts: z.array(argoWorkflowArtifactSchema) }).strict().optional(),
134
- activeDeadlineSeconds: z.number().int().positive().optional(),
135
- name: z.string().min(1),
136
- retryStrategy: argoWorkflowRetryStrategySchema.optional()
137
- }).strict().refine((template) => template.container !== void 0 || template.dag !== void 0, { message: "Workflow templates must declare container or dag" });
138
- const runnerArgoWorkflowManifestSchema = z.object({
139
- apiVersion: z.literal(ARGO_WORKFLOW_API_VERSION),
140
- kind: z.literal(ARGO_WORKFLOW_KIND),
141
- metadata: z.object({
142
- annotations: z.record(z.string().min(1), z.string().min(1)).optional(),
143
- generateName: z.string().min(1).optional(),
144
- labels: z.record(z.string().min(1), labelValueSchema).optional(),
145
- name: z.string().min(1).optional(),
146
- namespace: kubernetesNameSchema
147
- }).strict().refine((metadata) => metadata.name !== void 0 || metadata.generateName !== void 0, { message: "Workflow metadata must declare name or generateName" }),
148
- spec: z.object({
149
- activeDeadlineSeconds: z.number().int().positive().optional(),
150
- entrypoint: z.literal(RUNNER_WORKFLOW_ENTRYPOINT),
151
- imagePullSecrets: z.array(z.object({ name: kubernetesNameSchema }).strict()).optional(),
152
- podMetadata: z.object({ labels: z.record(z.string().min(1), labelValueSchema).optional() }).strict().optional(),
153
- serviceAccountName: kubernetesNameSchema,
154
- onExit: z.string().min(1).optional(),
155
- templates: z.array(argoWorkflowTemplateSchema).min(2),
156
- ttlStrategy: z.object({
157
- secondsAfterCompletion: z.number().int().positive().optional(),
158
- secondsAfterFailure: z.number().int().positive().optional(),
159
- secondsAfterSuccess: z.number().int().positive().optional()
160
- }).strict().optional(),
161
- volumes: z.array(argoWorkflowVolumeSchema).min(1)
162
- }).strict()
163
- }).strict();
164
- const buildRunnerArgoWorkflowOptionsSchema = z.object({
165
- activeDeadlineSeconds: z.number().int().positive().optional(),
166
- eventAuthSecretKey: z.string().min(1).optional(),
167
- eventAuthSecretName: kubernetesNameSchema.optional(),
168
- generateName: z.string().min(1).optional(),
169
- gitCredentialsSecretName: kubernetesNameSchema.optional(),
170
- githubAuthSecretName: kubernetesNameSchema.optional(),
171
- annotations: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
172
- image: z.string().min(1).default(RUNNER_WORKFLOW_IMAGE),
173
- imagePullPolicy: z.enum([
174
- "Always",
175
- "IfNotPresent",
176
- "Never"
177
- ]).default("Always"),
178
- imagePullSecretName: kubernetesNameSchema.optional(),
179
- labels: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
180
- name: z.string().min(1).optional(),
181
- namespace: kubernetesNameSchema,
182
- brokerAuth: z.object({
183
- secretKey: z.string().min(1).default("api-key"),
184
- secretName: kubernetesNameSchema,
185
- url: z.string().min(1).default("https://cliproxy.momokaya.ee")
186
- }).strict(),
187
- payloadConfigMapKey: z.string().min(1).default("payload.json"),
188
- payloadConfigMapName: kubernetesNameSchema,
189
- resources: argoWorkflowResourceRequirementsSchema.optional(),
190
- scheduleConfigMapKey: z.string().min(1).default("schedule.yaml"),
191
- scheduleConfigMapName: kubernetesNameSchema,
192
- serviceAccountName: kubernetesNameSchema.default(RUNNER_WORKFLOW_SERVICE_ACCOUNT),
193
- taskDescriptorConfigMapName: kubernetesNameSchema,
194
- ttlStrategy: z.object({
195
- secondsAfterCompletion: z.number().int().positive().optional(),
196
- secondsAfterFailure: z.number().int().positive().optional(),
197
- secondsAfterSuccess: z.number().int().positive().optional()
198
- }).strict().optional()
199
- }).strict().refine((options) => options.name !== void 0 || options.generateName !== void 0, { message: "Runner Workflow options must declare name or generateName" });
8
+ const runnerArgoWorkflowManifestSchema = createRunnerArgoWorkflowManifestSchema();
200
9
  function buildRunnerArgoWorkflowManifest(rawOptions) {
201
- const { plan, ...schemaOptions } = rawOptions;
202
- const options = {
203
- ...buildRunnerArgoWorkflowOptionsSchema.parse(schemaOptions),
204
- plan
205
- };
206
- const graph = compileArgoExecutionGraph(plan);
207
- const { volumeMounts, volumes } = runnerWorkflowStorage(options, graph.tasks);
10
+ const options = parsedBuildOptions(rawOptions);
11
+ const graph = compileArgoExecutionGraph(options.plan);
12
+ const storage = runnerWorkflowStorage(options, graph.tasks);
208
13
  return runnerArgoWorkflowManifestSchema.parse({
209
- apiVersion: ARGO_WORKFLOW_API_VERSION,
210
- kind: ARGO_WORKFLOW_KIND,
211
- metadata: {
212
- annotations: compactRecord(options.annotations),
213
- ...options.name ? { name: options.name } : {},
214
- ...options.generateName ? { generateName: options.generateName } : {},
215
- labels: compactRecord({
216
- "pipeline.oisin.dev/source": "argo-workflow",
217
- "pipeline.oisin.dev/workflow": plan.workflowId,
218
- ...options.labels
219
- }),
220
- namespace: options.namespace
221
- },
222
- spec: {
223
- ...options.activeDeadlineSeconds ? { activeDeadlineSeconds: options.activeDeadlineSeconds } : {},
224
- entrypoint: RUNNER_WORKFLOW_ENTRYPOINT,
225
- ...options.imagePullSecretName ? { imagePullSecrets: [{ name: options.imagePullSecretName }] } : {},
226
- onExit: "pipeline-finalizer",
227
- serviceAccountName: options.serviceAccountName,
228
- templates: [
229
- {
230
- dag: { tasks: [{
231
- name: RUNNER_WORKFLOW_START_TASK,
232
- template: RUNNER_WORKFLOW_START_TASK
233
- }, ...graph.tasks.map((task) => ({
234
- dependencies: [RUNNER_WORKFLOW_START_TASK, ...task.dependencies],
235
- name: task.taskName,
236
- template: task.templateName
237
- }))] },
238
- name: RUNNER_WORKFLOW_ENTRYPOINT
239
- },
240
- runnerLifecycleTemplate(options, volumeMounts),
241
- ...graph.tasks.map((task) => runnerCommandTemplate(task, options, volumeMounts)),
242
- runnerFinalizerTemplate(options, volumeMounts)
243
- ],
244
- ...options.ttlStrategy ? { ttlStrategy: options.ttlStrategy } : {},
245
- volumes
246
- }
14
+ apiVersion: "argoproj.io/v1alpha1",
15
+ kind: "Workflow",
16
+ metadata: workflowMetadata(options),
17
+ spec: workflowSpec(options, graph.tasks, storage)
247
18
  });
248
19
  }
249
20
  function stringifyRunnerArgoWorkflow(workflow) {
250
21
  return stringify(runnerArgoWorkflowManifestSchema.parse(workflow));
251
22
  }
252
- function runnerWorkflowStorage(options, tasks) {
253
- const volumes = [
254
- {
255
- configMap: {
256
- items: [{
257
- key: options.payloadConfigMapKey,
258
- path: "payload.json"
259
- }],
260
- name: options.payloadConfigMapName
261
- },
262
- name: "runner-payload"
263
- },
264
- {
265
- configMap: {
266
- items: [{
267
- key: options.scheduleConfigMapKey,
268
- path: "schedule.yaml"
269
- }],
270
- name: options.scheduleConfigMapName
271
- },
272
- name: "runner-schedule"
273
- },
274
- {
275
- configMap: {
276
- items: tasks.map((task) => ({
277
- key: `${task.taskName}.json`,
278
- path: `${task.taskName}.json`
279
- })),
280
- name: options.taskDescriptorConfigMapName
281
- },
282
- name: "runner-task-descriptor"
283
- }
284
- ];
285
- const volumeMounts = [{
286
- mountPath: RUNNER_WORKFLOW_PAYLOAD_PATH,
287
- name: "runner-payload",
288
- readOnly: true,
289
- subPath: "payload.json"
290
- }, {
291
- mountPath: RUNNER_WORKFLOW_SCHEDULE_PATH,
292
- name: "runner-schedule",
293
- readOnly: true,
294
- subPath: "schedule.yaml"
295
- }];
296
- if (options.eventAuthSecretName) {
297
- volumes.push({
298
- name: "runner-event-auth",
299
- secret: {
300
- ...options.eventAuthSecretKey ? { items: [{
301
- key: options.eventAuthSecretKey,
302
- path: options.eventAuthSecretKey
303
- }] } : {},
304
- secretName: options.eventAuthSecretName
305
- }
306
- });
307
- volumeMounts.push({
308
- mountPath: "/etc/pipeline/event-auth",
309
- name: "runner-event-auth",
310
- readOnly: true
311
- });
312
- }
313
- if (options.gitCredentialsSecretName) {
314
- volumes.push({
315
- name: "runner-git-credentials",
316
- secret: {
317
- defaultMode: 256,
318
- secretName: options.gitCredentialsSecretName
319
- }
320
- });
321
- volumeMounts.push({
322
- mountPath: RUNNER_GIT_CREDENTIALS_PATH,
323
- name: "runner-git-credentials",
324
- readOnly: true
325
- });
326
- }
327
- if (options.githubAuthSecretName) {
328
- volumes.push({
329
- name: "github-auth",
330
- secret: {
331
- items: [{
332
- key: "hosts.yml",
333
- path: "hosts.yml"
334
- }],
335
- secretName: options.githubAuthSecretName
336
- }
337
- });
338
- volumeMounts.push({
339
- mountPath: "/root/.config/gh/hosts.yml",
340
- name: "github-auth",
341
- readOnly: true,
342
- subPath: "hosts.yml"
343
- });
344
- }
23
+ function parsedBuildOptions(rawOptions) {
24
+ const { plan, ...schemaOptions } = rawOptions;
345
25
  return {
346
- volumeMounts: z.array(argoWorkflowVolumeMountSchema).parse(volumeMounts),
347
- volumes: z.array(argoWorkflowVolumeSchema).parse(volumes)
26
+ ...buildRunnerArgoWorkflowOptionsSchema.parse(schemaOptions),
27
+ plan
348
28
  };
349
29
  }
350
- /**
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).
354
- */
355
- function runnerContainerEnv(options) {
356
- return [
357
- ...RUNNER_OPENCODE_ENV,
358
- {
359
- name: "BROKER_URL",
360
- value: options.brokerAuth.url
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
- },
370
- {
371
- name: "BROKER_API_KEY",
372
- valueFrom: { secretKeyRef: {
373
- key: options.brokerAuth.secretKey,
374
- name: options.brokerAuth.secretName
375
- } }
376
- }
377
- ];
378
- }
379
- function runnerLifecycleTemplate(options, volumeMounts) {
30
+ function workflowMetadata(options) {
380
31
  return {
381
- container: {
382
- args: [
383
- "runner-lifecycle",
384
- "--phase",
385
- "workflow.start",
386
- "--payload-file",
387
- RUNNER_WORKFLOW_PAYLOAD_PATH,
388
- "--schedule-file",
389
- RUNNER_WORKFLOW_SCHEDULE_PATH
390
- ],
391
- command: ["moka"],
392
- env: runnerContainerEnv(options),
393
- image: options.image,
394
- imagePullPolicy: options.imagePullPolicy,
395
- name: "runner",
396
- resources: options.resources ?? DEFAULT_RUNNER_RESOURCES,
397
- volumeMounts
398
- },
399
- name: RUNNER_WORKFLOW_START_TASK,
400
- retryStrategy: { ...RUNNER_RETRY_STRATEGY },
401
- activeDeadlineSeconds: DEFAULT_RUNNER_DEADLINE_SECONDS
32
+ annotations: compactRecord(options.annotations),
33
+ ...options.name ? { name: options.name } : {},
34
+ ...options.generateName ? { generateName: options.generateName } : {},
35
+ labels: compactRecord({
36
+ "pipeline.oisin.dev/source": "argo-workflow",
37
+ "pipeline.oisin.dev/workflow": options.plan.workflowId,
38
+ ...options.labels
39
+ }),
40
+ namespace: options.namespace
402
41
  };
403
42
  }
404
- function runnerCommandTemplate(task, options, volumeMounts) {
405
- const taskVolumeMount = {
406
- mountPath: DEFAULT_RUNNER_TASK_DESCRIPTOR_PATH,
407
- name: "runner-task-descriptor",
408
- readOnly: true,
409
- subPath: `${task.taskName}.json`
410
- };
43
+ function workflowSpec(options, tasks, storage) {
411
44
  return {
412
- container: {
413
- args: [
414
- "runner-command",
415
- "--payload-file",
416
- RUNNER_WORKFLOW_PAYLOAD_PATH,
417
- "--schedule-file",
418
- RUNNER_WORKFLOW_SCHEDULE_PATH
419
- ],
420
- command: ["moka"],
421
- env: runnerContainerEnv(options),
422
- image: options.image,
423
- imagePullPolicy: options.imagePullPolicy,
424
- name: "runner",
425
- resources: options.resources ?? DEFAULT_RUNNER_RESOURCES,
426
- volumeMounts: [...volumeMounts, taskVolumeMount]
427
- },
428
- name: task.templateName,
429
- retryStrategy: { ...RUNNER_RETRY_STRATEGY },
430
- activeDeadlineSeconds: DEFAULT_RUNNER_DEADLINE_SECONDS
45
+ ...options.activeDeadlineSeconds ? { activeDeadlineSeconds: options.activeDeadlineSeconds } : {},
46
+ entrypoint: RUNNER_WORKFLOW_ENTRYPOINT,
47
+ ...options.imagePullSecretName ? { imagePullSecrets: [{ name: options.imagePullSecretName }] } : {},
48
+ serviceAccountName: options.serviceAccountName,
49
+ onExit: "pipeline-finalizer",
50
+ templates: workflowTemplates(options, tasks, storage.volumeMounts),
51
+ ...options.ttlStrategy ? { ttlStrategy: options.ttlStrategy } : {},
52
+ volumes: storage.volumes
431
53
  };
432
54
  }
433
- function runnerFinalizerTemplate(options, volumeMounts) {
55
+ function workflowTemplates(options, tasks, volumeMounts) {
56
+ return [
57
+ workflowDagTemplate(tasks),
58
+ runnerLifecycleTemplate(options, volumeMounts),
59
+ ...tasks.map((task) => runnerCommandTemplate(task, options, volumeMounts)),
60
+ runnerFinalizerTemplate(options, volumeMounts)
61
+ ];
62
+ }
63
+ function workflowDagTemplate(tasks) {
434
64
  return {
435
- container: {
436
- args: [
437
- "runner-finalize",
438
- "--payload-file",
439
- RUNNER_WORKFLOW_PAYLOAD_PATH,
440
- "--schedule-file",
441
- RUNNER_WORKFLOW_SCHEDULE_PATH,
442
- "--argo-status",
443
- "{{workflow.status}}"
444
- ],
445
- command: ["moka"],
446
- env: runnerContainerEnv(options),
447
- image: options.image,
448
- imagePullPolicy: options.imagePullPolicy,
449
- name: "runner",
450
- resources: options.resources ?? DEFAULT_RUNNER_RESOURCES,
451
- volumeMounts
452
- },
453
- name: "pipeline-finalizer",
454
- activeDeadlineSeconds: DEFAULT_RUNNER_DEADLINE_SECONDS
65
+ dag: { tasks: [{
66
+ name: RUNNER_WORKFLOW_START_TASK,
67
+ template: RUNNER_WORKFLOW_START_TASK
68
+ }, ...tasks.map((task) => ({
69
+ dependencies: [RUNNER_WORKFLOW_START_TASK, ...task.dependencies],
70
+ name: task.taskName,
71
+ template: task.templateName
72
+ }))] },
73
+ name: RUNNER_WORKFLOW_ENTRYPOINT
455
74
  };
456
75
  }
457
76
  function compactRecord(input) {
@@ -0,0 +1,34 @@
1
+ import { formatCodexAuthSyncResult, syncLocalCodexAuth } from "../credentials/local-codex-auth-sync.js";
2
+ import { formatPipelineInitResult, initPipelineProject } from "../pipeline-init.js";
3
+ import { runDoctor } from "./doctor.js";
4
+ import { formatDoctorResult } from "./format.js";
5
+ import { resolve } from "node:path";
6
+ //#region src/cli/bootstrap-commands.ts
7
+ function registerBootstrapCommands(program) {
8
+ program.command("doctor").description("Check local prerequisites for pipeline init and execution").option("--cluster [namespace]", "also check runner-job Kubernetes prerequisites").option("--json", "print machine-readable readiness results").option("--kube-context <context>", "kubectl context for cluster checks").option("--kubeconfig <path>", "kubeconfig path for cluster checks").action(async (flags) => {
9
+ const result = await runDoctor(process.env.PIPELINE_TARGET_PATH ?? process.cwd(), flags);
10
+ console.log(flags.json ? JSON.stringify(result) : formatDoctorResult(result));
11
+ if (!result.passed) throw new Error("Doctor checks failed.");
12
+ });
13
+ program.command("init").description("Install or refresh package-owned pipeline support: per-machine harness (skills + slash-command adapters + agent hooks + global instruction files) installed globally to ~/.claude, ~/.config/opencode, ~/.codex with no repo-local config").option("--check", "verify the generated harness is current; fail if stale").option("--dry-run", "show planned changes without writing files").option("--force", "overwrite manually edited harness files").action(async (flags) => {
14
+ const result = await initPipelineProject({
15
+ ...flags,
16
+ cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd()
17
+ });
18
+ console.log(formatPipelineInitResult(result, {
19
+ check: flags.check,
20
+ dryRun: flags.dryRun
21
+ }));
22
+ });
23
+ 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) => {
24
+ const result = syncLocalCodexAuth({
25
+ check: flags.check,
26
+ dryRun: flags.dryRun,
27
+ root: resolve(flags.root ?? process.env.PIPELINE_TARGET_PATH ?? process.cwd())
28
+ });
29
+ console.log(formatCodexAuthSyncResult(result));
30
+ if (!result.ok) process.exitCode = 1;
31
+ });
32
+ }
33
+ //#endregion
34
+ export { registerBootstrapCommands };