@oisincoveney/pipeline 3.15.3 → 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 (55) hide show
  1. package/defaults/profiles.yaml +2 -2
  2. package/dist/argo-graph.js +1 -1
  3. package/dist/argo-submit.js +1 -1
  4. package/dist/argo-workflow.d.ts +2 -40
  5. package/dist/argo-workflow.js +53 -434
  6. package/dist/config/defaults.js +1 -1
  7. package/dist/config/schemas.d.ts +4 -4
  8. package/dist/config/schemas.js +1 -1
  9. package/dist/install-commands/host-selection.js +13 -0
  10. package/dist/install-commands/planner.js +299 -0
  11. package/dist/install-commands/result-format.js +6 -0
  12. package/dist/install-commands/writer.js +45 -0
  13. package/dist/install-commands.js +22 -268
  14. package/dist/loop/gh-checks.js +1 -1
  15. package/dist/moka-global-config.js +1 -1
  16. package/dist/moka-submit.d.ts +409 -43
  17. package/dist/moka-submit.js +14 -327
  18. package/dist/planning/generate.js +1 -1
  19. package/dist/remote/argo/model.d.ts +44 -0
  20. package/dist/remote/argo/model.js +160 -0
  21. package/dist/remote/argo/policy.js +77 -0
  22. package/dist/remote/argo/storage.js +110 -0
  23. package/dist/remote/argo/templates.js +82 -0
  24. package/dist/remote/submit/argo-submission.d.ts +26 -0
  25. package/dist/remote/submit/argo-submission.js +62 -0
  26. package/dist/remote/submit/compilation.d.ts +8 -0
  27. package/dist/remote/submit/compilation.js +106 -0
  28. package/dist/remote/submit/event-boundary.js +118 -0
  29. package/dist/remote/submit/hook-events.js +14 -0
  30. package/dist/remote/submit/io.d.ts +13 -0
  31. package/dist/remote/submit/io.js +66 -0
  32. package/dist/remote/submit/service.d.ts +10 -0
  33. package/dist/remote/submit/service.js +26 -0
  34. package/dist/runner-command/finalize.js +1 -1
  35. package/dist/runner-command/lifecycle.js +1 -1
  36. package/dist/runner-command/run.js +1 -1
  37. package/dist/runner-command/task-descriptor.js +1 -1
  38. package/dist/runner-command-contract.js +1 -1
  39. package/dist/runner-event-schema.d.ts +6 -6
  40. package/dist/runtime/events/events.js +244 -89
  41. package/dist/runtime/hooks/command-hook.js +87 -0
  42. package/dist/runtime/hooks/context.js +38 -0
  43. package/dist/runtime/hooks/events.js +131 -0
  44. package/dist/runtime/hooks/execution.js +29 -0
  45. package/dist/runtime/hooks/hooks.js +33 -365
  46. package/dist/runtime/hooks/invocation.js +49 -0
  47. package/dist/runtime/hooks/module-hook.js +52 -0
  48. package/dist/runtime/hooks/policy.js +36 -0
  49. package/dist/runtime/hooks/results.js +81 -0
  50. package/dist/runtime/services/backlog-service.js +2 -3
  51. package/dist/tickets/backlog-task-store.d.ts +2 -2
  52. package/dist/tickets/backlog-task-store.js +1 -1
  53. package/dist/tickets/ticket-graph-dto.js +1 -1
  54. package/dist/tickets/ticket-plan.js +1 -1
  55. package/package.json +1 -2
@@ -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)),
@@ -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) {
@@ -1,6 +1,6 @@
1
1
  import { ConfigIoService, parseConfigYamlAs, runConfigIoSync } from "../runtime/services/config-io-service.js";
2
- import { z } from "zod";
3
2
  import { Effect } from "effect";
3
+ import { z } from "zod";
4
4
  //#region src/config/defaults.ts
5
5
  const PIPELINE_CONFIG_PATH = ".pipeline/pipeline.yaml";
6
6
  const RUNNERS_CONFIG_PATH = ".pipeline/runners.yaml";
@@ -226,8 +226,8 @@ declare const configSchema: z.ZodObject<{
226
226
  policy: z.ZodOptional<z.ZodObject<{
227
227
  commands: z.ZodOptional<z.ZodEnum<{
228
228
  allow: "allow";
229
- "trusted-only": "trusted-only";
230
229
  deny: "deny";
230
+ "trusted-only": "trusted-only";
231
231
  }>>;
232
232
  modules: z.ZodOptional<z.ZodEnum<{
233
233
  allow: "allow";
@@ -255,8 +255,8 @@ declare const configSchema: z.ZodObject<{
255
255
  global: "global";
256
256
  }>>;
257
257
  mode: z.ZodEnum<{
258
- hosted: "hosted";
259
258
  local: "local";
259
+ hosted: "hosted";
260
260
  }>;
261
261
  provider: z.ZodLiteral<"toolhive">;
262
262
  authorization_env: z.ZodDefault<z.ZodString>;
@@ -300,10 +300,10 @@ declare const configSchema: z.ZodObject<{
300
300
  }, z.core.$strict>>;
301
301
  output: z.ZodOptional<z.ZodObject<{
302
302
  format: z.ZodEnum<{
303
+ json_schema: "json_schema";
303
304
  text: "text";
304
305
  json: "json";
305
306
  jsonl: "jsonl";
306
- json_schema: "json_schema";
307
307
  }>;
308
308
  repair: z.ZodOptional<z.ZodObject<{
309
309
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -379,10 +379,10 @@ declare const configSchema: z.ZodObject<{
379
379
  disabled: "disabled";
380
380
  }>>>;
381
381
  output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
382
+ json_schema: "json_schema";
382
383
  text: "text";
383
384
  json: "json";
384
385
  jsonl: "jsonl";
385
- json_schema: "json_schema";
386
386
  }>>>;
387
387
  rules: z.ZodOptional<z.ZodBoolean>;
388
388
  skills: z.ZodOptional<z.ZodBoolean>;