@oisincoveney/pipeline 1.22.6 → 1.23.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.
@@ -20,9 +20,11 @@ hooks: generated-defaults-audit
20
20
  Instructions:
21
21
  Orchestrate package-owned pipeline config.
22
22
 
23
+ Submit Kubernetes runner Jobs by default through `oisin-pipeline quick` and `oisin-pipeline execute`.
24
+
23
25
  Generate a schedule for entrypoint `execute` and the user task.
24
26
  The schedule policy is `execute-schedule`.
25
- Run `oisin-pipeline run --entrypoint execute <task description>` to generate and execute the schedule artifact.
26
- The pipeline CLI runtime is the deterministic graph scheduler for scheduled entrypoints.
27
- It launches configured Codex/OpenCode agent subprocesses as soon as their dependencies pass.
27
+ Run `oisin-pipeline execute <task description>` to submit the pipeline as a k8s job.
28
+ The pipeline runtime executes inside a Kubernetes pod using the package-owned runner image.
29
+ Use `oisin-pipeline execute --local <task description>` for local execution instead.
28
30
  Use `oisin-pipeline run --schedule <schedule.yaml>` only when rerunning an existing schedule artifact.
@@ -20,9 +20,11 @@ hooks: generated-defaults-audit
20
20
  Instructions:
21
21
  Orchestrate package-owned pipeline config.
22
22
 
23
+ Submit Kubernetes runner Jobs by default through `oisin-pipeline quick` and `oisin-pipeline execute`.
24
+
23
25
  Generate a schedule for entrypoint `quick` and the user task.
24
26
  The schedule policy is `quick-schedule`.
25
- Run `oisin-pipeline run --entrypoint quick <task description>` to generate and execute the schedule artifact.
26
- The pipeline CLI runtime is the deterministic graph scheduler for scheduled entrypoints.
27
- It launches configured Codex/OpenCode agent subprocesses as soon as their dependencies pass.
27
+ Run `oisin-pipeline quick <task description>` to submit the pipeline as a k8s job.
28
+ The pipeline runtime executes inside a Kubernetes pod using the package-owned runner image.
29
+ Use `oisin-pipeline quick --local <task description>` for local execution instead.
28
30
  Use `oisin-pipeline run --schedule <schedule.yaml>` only when rerunning an existing schedule artifact.
@@ -59,6 +59,15 @@ ecosystem_code:
59
59
  role: Codex-style 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
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
62
71
  - id: opencode-snip
63
72
  name: opencode-snip
64
73
  role: prompt snippet and command-discipline helper patterns
@@ -14,8 +14,8 @@ function registerConfiguredEntrypointCommands(program, config, runEntrypoint) {
14
14
  const reservedCommands = new Set(program.commands.map((command) => command.name()));
15
15
  for (const [id, entrypoint] of Object.entries(config.entrypoints)) {
16
16
  if (reservedCommands.has(id)) continue;
17
- program.command(id).description(entrypoint.description ?? `Run the ${id} workflow`).argument("<description...>", "task description").action(async (descriptionParts) => {
18
- await runEntrypoint(id, descriptionParts.join(" "));
17
+ program.command(id).description(entrypoint.description ?? `Run the ${id} workflow`).argument("<description...>", "task description").option("--local", "run locally instead of submitting as a k8s job").action(async (descriptionParts, flags) => {
18
+ await runEntrypoint(id, descriptionParts.join(" "), flags);
19
19
  });
20
20
  registered.add(id);
21
21
  reservedCommands.add(id);
package/dist/config.d.ts CHANGED
@@ -461,6 +461,7 @@ declare const configSchema: z.ZodObject<{
461
461
  skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
462
462
  timeout_ms: z.ZodOptional<z.ZodNumber>;
463
463
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
464
+ task: "task";
464
465
  read: "read";
465
466
  list: "list";
466
467
  grep: "grep";
@@ -468,7 +469,6 @@ declare const configSchema: z.ZodObject<{
468
469
  bash: "bash";
469
470
  edit: "edit";
470
471
  write: "write";
471
- task: "task";
472
472
  }>>>;
473
473
  }, z.core.$strict>>>;
474
474
  runner_job: z.ZodDefault<z.ZodObject<{
@@ -494,8 +494,8 @@ declare const configSchema: z.ZodObject<{
494
494
  rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
495
495
  path: z.ZodString;
496
496
  source_root: z.ZodDefault<z.ZodEnum<{
497
- package: "package";
498
497
  project: "project";
498
+ package: "package";
499
499
  }>>;
500
500
  }, z.core.$strict>>>;
501
501
  runners: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -520,6 +520,7 @@ declare const configSchema: z.ZodObject<{
520
520
  rules: z.ZodOptional<z.ZodBoolean>;
521
521
  skills: z.ZodOptional<z.ZodBoolean>;
522
522
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
523
+ task: "task";
523
524
  read: "read";
524
525
  list: "list";
525
526
  grep: "grep";
@@ -527,15 +528,14 @@ declare const configSchema: z.ZodObject<{
527
528
  bash: "bash";
528
529
  edit: "edit";
529
530
  write: "write";
530
- task: "task";
531
531
  }>>>;
532
532
  }, z.core.$strict>;
533
533
  command: z.ZodOptional<z.ZodString>;
534
534
  host_models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
535
535
  model: z.ZodOptional<z.ZodString>;
536
536
  type: z.ZodEnum<{
537
- opencode: "opencode";
538
537
  command: "command";
538
+ opencode: "opencode";
539
539
  codex: "codex";
540
540
  }>;
541
541
  }, z.core.$strict>>>;
@@ -636,8 +636,8 @@ declare const configSchema: z.ZodObject<{
636
636
  skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
637
637
  path: z.ZodString;
638
638
  source_root: z.ZodDefault<z.ZodEnum<{
639
- package: "package";
640
639
  project: "project";
640
+ package: "package";
641
641
  }>>;
642
642
  }, z.core.$strict>>>;
643
643
  task_context: z.ZodOptional<z.ZodObject<{
package/dist/gates.js CHANGED
@@ -75,7 +75,11 @@ async function resolvePackageBinaryCommand(worktreePath, binary, args) {
75
75
  };
76
76
  default: return {
77
77
  command: "npx",
78
- args: [binary, ...args]
78
+ args: [
79
+ "--yes",
80
+ binary,
81
+ ...args
82
+ ]
79
83
  };
80
84
  }
81
85
  }
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { formatConfigError, runPipelineFromConfig } from "./pipeline-runtime.js"
10
10
  import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "./schedule-planner.js";
11
11
  import { registerRunnerJobCommand } from "./commands/runner-job-command.js";
12
12
  import { formatInstallCommandsResult, installCommands, parseCommandHost } from "./install-commands.js";
13
+ import { submitK8sRunnerJob } from "./k8s-submit.js";
13
14
  import { formatPipelineInitResult, initPipelineProject } from "./pipeline-init.js";
14
15
  import { existsSync, readFileSync, realpathSync } from "node:fs";
15
16
  import { resolve } from "node:path";
@@ -296,7 +297,16 @@ function createCliProgram() {
296
297
  console.log(formatInstallCommandsResult(result));
297
298
  });
298
299
  registerRunnerJobCommand(program);
299
- const configuredEntrypointCommands = registerConfiguredEntrypointCommands(program, configuredPipeline, (entrypoint, task) => execute(task, { entrypoint }));
300
+ const configuredEntrypointCommands = registerConfiguredEntrypointCommands(program, configuredPipeline, async (entrypoint, task, opts) => {
301
+ if (!opts.local && (entrypoint === "quick" || entrypoint === "execute")) {
302
+ const result = await submitK8sRunnerJob({
303
+ entrypoint,
304
+ task,
305
+ eventUrl: process.env.PIPELINE_EVENT_URL ?? ""
306
+ });
307
+ console.log(`Job submitted: ${result.jobName} in ${result.namespace}`);
308
+ } else await execute(task, { entrypoint });
309
+ });
300
310
  if (configuredEntrypointCommands.size > 0) program.configureHelp({ subcommandTerm(command) {
301
311
  if (configuredEntrypointCommands.has(command.name())) return command.name();
302
312
  return Help.prototype.subcommandTerm.call(this, command);
@@ -385,7 +395,8 @@ async function runDoctor(cwd) {
385
395
  const commandChecks = await Promise.all([
386
396
  checkCommand("npx", ["--version"], cwd),
387
397
  checkCommand("codex", ["--version"], cwd),
388
- checkCommand("opencode", ["--version"], cwd)
398
+ checkCommand("opencode", ["--version"], cwd),
399
+ checkCommand("fallow", ["--version"], cwd)
389
400
  ]);
390
401
  const configCheck = checkPipelineConfig(cwd);
391
402
  const checks = [...commandChecks, configCheck];
@@ -175,12 +175,16 @@ function entrypointDispatchBlock(host, config, id, entrypoint) {
175
175
  return [
176
176
  `Generate a schedule for entrypoint \`${id}\` and the user task.`,
177
177
  `The schedule policy is \`${entrypoint.schedule}\`.`,
178
- `Run \`oisin-pipeline run --entrypoint ${id} <task description>\` to generate and execute the schedule artifact.`,
179
- "The pipeline CLI runtime is the deterministic graph scheduler for scheduled entrypoints.",
180
- "It launches configured Codex/OpenCode agent subprocesses as soon as their dependencies pass.",
178
+ `Run \`oisin-pipeline ${id} <task description>\` to submit the pipeline as a k8s job.`,
179
+ "The pipeline runtime executes inside a Kubernetes pod using the package-owned runner image.",
180
+ `Use \`oisin-pipeline ${id} --local <task description>\` for local execution instead.`,
181
181
  "Use `oisin-pipeline run --schedule <schedule.yaml>` only when rerunning an existing schedule artifact."
182
182
  ].join("\n");
183
183
  }
184
+ function scheduledEntrypointK8sNote(entrypoint) {
185
+ if ("workflow" in entrypoint) return;
186
+ return "Submit Kubernetes runner Jobs by default through `oisin-pipeline quick` and `oisin-pipeline execute`.";
187
+ }
184
188
  function orchestratorEntrypointDispatchBlock(host, config) {
185
189
  const scheduledEntrypoints = entrypointEntries(config).filter(([, entrypoint]) => !("workflow" in entrypoint));
186
190
  if (scheduledEntrypoints.length === 0) return dispatchBlock(host, config);
@@ -379,6 +383,8 @@ function opencodeDefinitions(config, cwd) {
379
383
  "",
380
384
  orchestratorBlock(config),
381
385
  "",
386
+ scheduledEntrypointK8sNote(entrypoint),
387
+ scheduledEntrypointK8sNote(entrypoint) ? "" : void 0,
382
388
  entrypointDispatchBlock("opencode", config, id, entrypoint)
383
389
  ]).join("\n")),
384
390
  host: "opencode",
@@ -447,6 +453,8 @@ function codexDefinitions(config, cwd) {
447
453
  "",
448
454
  orchestratorBlock(config),
449
455
  "",
456
+ scheduledEntrypointK8sNote(entrypoint),
457
+ scheduledEntrypointK8sNote(entrypoint) ? "" : void 0,
450
458
  entrypointDispatchBlock("codex", config, id, entrypoint)
451
459
  ]).join("\n")),
452
460
  host: "codex",
@@ -466,6 +474,8 @@ function codexDefinitions(config, cwd) {
466
474
  "",
467
475
  orchestratorBlock(config),
468
476
  "",
477
+ scheduledEntrypointK8sNote(entrypoint),
478
+ scheduledEntrypointK8sNote(entrypoint) ? "" : void 0,
469
479
  entrypointDispatchBlock("codex", config, id, entrypoint)
470
480
  ]).join("\n")),
471
481
  host: "codex",
@@ -0,0 +1,267 @@
1
+ import { z } from "zod";
2
+ import { simpleGit } from "simple-git";
3
+ import { randomBytes } from "node:crypto";
4
+ import { BatchV1Api, CoreV1Api, KubeConfig } from "@kubernetes/client-node";
5
+ //#region src/k8s-submit.ts
6
+ const GIT_SUFFIX_RE = /\.git$/;
7
+ const k8sSecretRefSchema = z.object({
8
+ key: z.string().min(1),
9
+ name: z.string().min(1)
10
+ }).strict();
11
+ const k8sSubmitOptionsSchema = z.object({
12
+ codexAuth: k8sSecretRefSchema.default({
13
+ key: "auth.json",
14
+ name: "codex-auth-1"
15
+ }),
16
+ entrypoint: z.enum(["execute", "quick"]),
17
+ eventAuth: k8sSecretRefSchema.default({
18
+ key: "token",
19
+ name: "pipeline-runner-event-auth"
20
+ }),
21
+ eventUrl: z.string().url(),
22
+ githubAuth: k8sSecretRefSchema.default({
23
+ key: "hosts.yml",
24
+ name: "pipeline-runner-github-auth"
25
+ }),
26
+ jobName: z.string().min(1).optional(),
27
+ kubeconfigPath: z.string().min(1).optional(),
28
+ namespace: z.string().min(1).default("momokaya-pipeline"),
29
+ opencodeAuth: k8sSecretRefSchema.default({
30
+ key: "auth.json",
31
+ name: "opencode-auth-1"
32
+ }),
33
+ opencodeOpenaiAccounts: k8sSecretRefSchema.optional(),
34
+ orchestrator: z.enum(["codex", "opencode"]).default("opencode"),
35
+ serviceAccountName: z.string().min(1).default("pipeline-runner"),
36
+ task: z.string().min(1)
37
+ }).strict();
38
+ z.object({
39
+ jobName: z.string().min(1),
40
+ namespace: z.string().min(1)
41
+ }).strict();
42
+ async function resolveGitContext() {
43
+ const git = simpleGit();
44
+ const [branchResult, sha, remoteConfig] = await Promise.all([
45
+ git.branch(),
46
+ git.revparse(["HEAD"]),
47
+ git.getConfig("remote.origin.url")
48
+ ]);
49
+ const url = remoteConfig.value;
50
+ if (!url) throw new Error("Could not resolve git remote origin URL. Ensure the repository has a remote configured.");
51
+ const project = projectNameFromUrl(url);
52
+ return {
53
+ baseBranch: branchResult.current,
54
+ project,
55
+ sha: sha.trim(),
56
+ url
57
+ };
58
+ }
59
+ function projectNameFromUrl(url) {
60
+ return url.replace(GIT_SUFFIX_RE, "").split("/").at(-1) ?? "unknown";
61
+ }
62
+ function generateRunId() {
63
+ return `run-${randomBytes(8).toString("hex")}`;
64
+ }
65
+ function generatePayloadConfigMapName() {
66
+ return `pipeline-payload-${randomBytes(6).toString("hex")}`;
67
+ }
68
+ async function submitK8sRunnerJob(rawOptions) {
69
+ const options = k8sSubmitOptionsSchema.parse(rawOptions);
70
+ const git = await resolveGitContext();
71
+ const kc = new KubeConfig();
72
+ if (options.kubeconfigPath) kc.loadFromFile(options.kubeconfigPath);
73
+ else kc.loadFromDefault();
74
+ const coreApi = kc.makeApiClient(CoreV1Api);
75
+ const batchApi = kc.makeApiClient(BatchV1Api);
76
+ const payloadConfigMapName = generatePayloadConfigMapName();
77
+ const jobName = options.jobName ?? `pipeline-run-${options.entrypoint}-${randomBytes(4).toString("hex")}`;
78
+ const namespace = options.namespace;
79
+ const payload = {
80
+ command: options.entrypoint,
81
+ contractVersion: "1",
82
+ events: {
83
+ authTokenFile: `/etc/pipeline/event-auth/${options.eventAuth.key}`,
84
+ url: options.eventUrl
85
+ },
86
+ repository: {
87
+ baseBranch: git.baseBranch,
88
+ sha: git.sha,
89
+ url: git.url
90
+ },
91
+ run: {
92
+ id: generateRunId(),
93
+ project: git.project
94
+ },
95
+ task: {
96
+ kind: "prompt",
97
+ prompt: options.task
98
+ }
99
+ };
100
+ const configMapBody = {
101
+ apiVersion: "v1",
102
+ kind: "ConfigMap",
103
+ metadata: {
104
+ name: payloadConfigMapName,
105
+ namespace
106
+ },
107
+ data: { "payload.json": JSON.stringify(payload) }
108
+ };
109
+ await coreApi.createNamespacedConfigMap(namespace, configMapBody);
110
+ const volumes = [
111
+ {
112
+ name: payloadConfigMapName,
113
+ configMap: {
114
+ name: payloadConfigMapName,
115
+ items: [{
116
+ key: "payload.json",
117
+ path: "payload.json"
118
+ }]
119
+ }
120
+ },
121
+ {
122
+ name: options.eventAuth.name,
123
+ secret: {
124
+ secretName: options.eventAuth.name,
125
+ items: [{
126
+ key: options.eventAuth.key,
127
+ path: options.eventAuth.key
128
+ }]
129
+ }
130
+ },
131
+ {
132
+ name: options.codexAuth.name,
133
+ secret: {
134
+ secretName: options.codexAuth.name,
135
+ defaultMode: 256
136
+ }
137
+ },
138
+ {
139
+ name: options.opencodeAuth.name,
140
+ secret: {
141
+ secretName: options.opencodeAuth.name,
142
+ defaultMode: 256
143
+ }
144
+ },
145
+ ...options.opencodeOpenaiAccounts ? [{
146
+ name: options.opencodeOpenaiAccounts.name,
147
+ secret: {
148
+ secretName: options.opencodeOpenaiAccounts.name,
149
+ defaultMode: 256
150
+ }
151
+ }] : [],
152
+ {
153
+ name: options.githubAuth.name,
154
+ secret: {
155
+ secretName: options.githubAuth.name,
156
+ items: [
157
+ {
158
+ key: "gitconfig",
159
+ path: "gitconfig"
160
+ },
161
+ {
162
+ key: "git-credentials",
163
+ path: "git-credentials"
164
+ },
165
+ {
166
+ key: "hosts.yml",
167
+ path: "hosts.yml"
168
+ }
169
+ ]
170
+ }
171
+ }
172
+ ];
173
+ const volumeMounts = [
174
+ {
175
+ name: payloadConfigMapName,
176
+ mountPath: "/etc/pipeline/payload.json",
177
+ subPath: "payload.json",
178
+ readOnly: true
179
+ },
180
+ {
181
+ name: options.eventAuth.name,
182
+ mountPath: "/etc/pipeline/event-auth",
183
+ readOnly: true
184
+ },
185
+ {
186
+ name: options.codexAuth.name,
187
+ mountPath: `/root/.codex/${options.codexAuth.key}`,
188
+ subPath: options.codexAuth.key,
189
+ readOnly: true
190
+ },
191
+ {
192
+ name: options.opencodeAuth.name,
193
+ mountPath: `/root/.local/share/opencode/${options.opencodeAuth.key}`,
194
+ subPath: options.opencodeAuth.key,
195
+ readOnly: true
196
+ },
197
+ ...options.opencodeOpenaiAccounts ? [{
198
+ name: options.opencodeOpenaiAccounts.name,
199
+ mountPath: "/root/.opencode/oc-codex-multi-auth-accounts.json",
200
+ subPath: options.opencodeOpenaiAccounts.key,
201
+ readOnly: true
202
+ }] : [],
203
+ {
204
+ name: options.githubAuth.name,
205
+ mountPath: "/root/.gitconfig",
206
+ subPath: "gitconfig",
207
+ readOnly: true
208
+ },
209
+ {
210
+ name: options.githubAuth.name,
211
+ mountPath: "/root/.git-credentials",
212
+ subPath: "git-credentials",
213
+ readOnly: true
214
+ },
215
+ {
216
+ name: options.githubAuth.name,
217
+ mountPath: "/root/.config/gh/hosts.yml",
218
+ subPath: "hosts.yml",
219
+ readOnly: true
220
+ }
221
+ ];
222
+ const env = options.opencodeOpenaiAccounts ? [{
223
+ name: "CODEX_AUTH_PER_PROJECT_ACCOUNTS",
224
+ value: "false"
225
+ }] : [];
226
+ const jobBody = {
227
+ apiVersion: "batch/v1",
228
+ kind: "Job",
229
+ metadata: {
230
+ name: jobName,
231
+ namespace
232
+ },
233
+ spec: {
234
+ backoffLimit: 0,
235
+ template: { spec: {
236
+ serviceAccountName: options.serviceAccountName,
237
+ restartPolicy: "Never",
238
+ containers: [{
239
+ name: "runner",
240
+ image: "ghcr.io/oisin-ee/pipeline-runner:latest",
241
+ imagePullPolicy: "Always",
242
+ args: [
243
+ "runner-job",
244
+ "--payload-file",
245
+ "/etc/pipeline/payload.json",
246
+ options.orchestrator
247
+ ],
248
+ ...env.length > 0 ? { env } : {},
249
+ volumeMounts
250
+ }],
251
+ volumes
252
+ } }
253
+ }
254
+ };
255
+ try {
256
+ await batchApi.createNamespacedJob(namespace, jobBody);
257
+ } catch (err) {
258
+ const message = err instanceof Error ? err.message : String(err);
259
+ throw new Error(`Kubernetes cluster unreachable or Job creation failed: ${message}`);
260
+ }
261
+ return {
262
+ jobName,
263
+ namespace
264
+ };
265
+ }
266
+ //#endregion
267
+ export { submitK8sRunnerJob };
@@ -250,6 +250,48 @@ The runner does not own the console database, event store, Job builder, Kueue
250
250
  watcher, or UI. Do not add a runner-side Kubernetes API kind, database, console
251
251
  deployment per run, or separate language stack for this integration.
252
252
 
253
+
254
+
255
+ ### K8s-native pipeline execution (quick and execute)
256
+
257
+ The `quick` and `execute` entrypoints submit Kubernetes runner Jobs by default. Each command builds a runner job payload from the task description
258
+ and current git context, creates a ConfigMap, and submits a `batch/v1` Job that
259
+ runs the pipeline inside a pod using the package-owned runner image.
260
+
261
+ Set `PIPELINE_EVENT_URL` to configure the runner event sink. Without it, the
262
+ command fails with a validation error.
263
+
264
+ #### Prerequisites
265
+
266
+ The following must exist in the target namespace:
267
+
268
+ - **ServiceAccount** `pipeline-runner` with RBAC to read pods/logs (for
269
+ debugging).
270
+ - **Secret** `codex-auth-1` with key `auth.json` (Codex authentication).
271
+ - **Secret** `opencode-auth-1` with key `auth.json` (OpenCode authentication).
272
+ - **Secret** `pipeline-runner-event-auth` with key `token` (event sink bearer
273
+ token, mounted at `/etc/pipeline/event-auth/token`).
274
+ - **Secret** `pipeline-runner-github-auth` with keys `gitconfig`,
275
+ `git-credentials`, `hosts.yml` (GitHub authentication for `git` and `gh`).
276
+ - A pipeline-console event sink endpoint reachable from the pod.
277
+
278
+ #### Usage
279
+
280
+ ```shell
281
+ export PIPELINE_EVENT_URL="https://console.example.com/api/pipeline/runner-events"
282
+ oisin-pipeline quick "fix the login bug"
283
+ oisin-pipeline execute "Implement PIPE-53"
284
+ ```
285
+
286
+ #### Local execution fallback
287
+
288
+ Use the `--local` flag for workstation-local execution:
289
+
290
+ ```shell
291
+ oisin-pipeline quick --local "fix the login bug"
292
+ oisin-pipeline execute --local "Implement PIPE-53"
293
+ ```
294
+
253
295
  Generated invocations include:
254
296
 
255
297
  ```text
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "@dagrejs/graphlib": "^4.0.1",
4
+ "@kubernetes/client-node": "^1.4.0",
4
5
  "ajv": "^8.20.0",
5
6
  "ajv-formats": "^3.0.1",
6
7
  "commander": "^14.0.3",
@@ -111,7 +112,7 @@
111
112
  "prepack": "bun run build:cli"
112
113
  },
113
114
  "type": "module",
114
- "version": "1.22.6",
115
+ "version": "1.23.0",
115
116
  "description": "Config-driven multi-agent pipeline runner for repository work",
116
117
  "main": "./dist/index.js",
117
118
  "types": "./dist/index.d.ts",