@oisincoveney/pipeline 1.27.17 → 1.27.18

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.
@@ -8,6 +8,7 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
8
8
  eventAuthSecretKey: z.ZodOptional<z.ZodString>;
9
9
  eventAuthSecretName: z.ZodOptional<z.ZodString>;
10
10
  generateName: z.ZodOptional<z.ZodString>;
11
+ gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
11
12
  githubAuthSecretName: z.ZodOptional<z.ZodString>;
12
13
  image: z.ZodOptional<z.ZodString>;
13
14
  imagePullPolicy: z.ZodOptional<z.ZodEnum<{
@@ -18,7 +19,7 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
18
19
  imagePullSecretName: z.ZodOptional<z.ZodString>;
19
20
  kubeconfigPath: z.ZodOptional<z.ZodString>;
20
21
  name: z.ZodOptional<z.ZodString>;
21
- namespace: z.ZodDefault<z.ZodString>;
22
+ namespace: z.ZodString;
22
23
  opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
23
24
  payloadJson: z.ZodString;
24
25
  queueName: z.ZodOptional<z.ZodString>;
@@ -24,6 +24,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
24
24
  eventAuthSecretKey: z.string().min(1).optional(),
25
25
  eventAuthSecretName: z.string().min(1).optional(),
26
26
  generateName: z.string().min(1).optional(),
27
+ gitCredentialsSecretName: z.string().min(1).optional(),
27
28
  githubAuthSecretName: z.string().min(1).optional(),
28
29
  image: z.string().min(1).optional(),
29
30
  imagePullPolicy: z.enum([
@@ -34,7 +35,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
34
35
  imagePullSecretName: z.string().min(1).optional(),
35
36
  kubeconfigPath: z.string().min(1).optional(),
36
37
  name: z.string().min(1).optional(),
37
- namespace: z.string().min(1).default("momokaya-pipeline"),
38
+ namespace: z.string().min(1),
38
39
  opencodeAuthSecretName: z.string().min(1).optional(),
39
40
  payloadJson: z.string().min(1),
40
41
  queueName: z.string().min(1).optional(),
@@ -72,6 +73,7 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
72
73
  eventAuthSecretKey: options.eventAuthSecretKey,
73
74
  eventAuthSecretName: options.eventAuthSecretName,
74
75
  generateName: options.generateName,
76
+ gitCredentialsSecretName: options.gitCredentialsSecretName,
75
77
  githubAuthSecretName: options.githubAuthSecretName,
76
78
  image: options.image,
77
79
  imagePullPolicy: options.imagePullPolicy,
@@ -102,6 +102,7 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
102
102
  key: z.ZodString;
103
103
  path: z.ZodString;
104
104
  }, z.core.$strict>>>;
105
+ optional: z.ZodOptional<z.ZodBoolean>;
105
106
  secretName: z.ZodString;
106
107
  }, z.core.$strict>>;
107
108
  }, z.core.$strict>>;
@@ -112,6 +113,7 @@ declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
112
113
  eventAuthSecretKey: z.ZodOptional<z.ZodString>;
113
114
  eventAuthSecretName: z.ZodOptional<z.ZodString>;
114
115
  generateName: z.ZodOptional<z.ZodString>;
116
+ gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
115
117
  githubAuthSecretName: z.ZodOptional<z.ZodString>;
116
118
  annotations: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
117
119
  image: z.ZodDefault<z.ZodString>;
@@ -10,6 +10,7 @@ const RUNNER_WORKFLOW_SERVICE_ACCOUNT = "pipeline-runner";
10
10
  const RUNNER_WORKFLOW_ENTRYPOINT = "pipeline";
11
11
  const RUNNER_WORKFLOW_PAYLOAD_PATH = "/etc/pipeline/payload.json";
12
12
  const RUNNER_WORKFLOW_SCHEDULE_PATH = "/etc/pipeline/schedule.yaml";
13
+ const RUNNER_GIT_CREDENTIALS_PATH = "/etc/pipeline/git-credentials";
13
14
  const kubernetesNameSchema = z.string().min(1);
14
15
  const labelValueSchema = z.string().min(1);
15
16
  const stringMapSchema = z.record(z.string().min(1), z.string().min(1));
@@ -26,6 +27,7 @@ const secretVolumeSchema = z.object({
26
27
  key: z.string().min(1),
27
28
  path: z.string().min(1)
28
29
  }).strict()).optional(),
30
+ optional: z.boolean().optional(),
29
31
  secretName: kubernetesNameSchema
30
32
  }).strict();
31
33
  const argoWorkflowVolumeSchema = z.object({
@@ -110,6 +112,7 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
110
112
  eventAuthSecretKey: z.string().min(1).optional(),
111
113
  eventAuthSecretName: kubernetesNameSchema.optional(),
112
114
  generateName: z.string().min(1).optional(),
115
+ gitCredentialsSecretName: kubernetesNameSchema.optional(),
113
116
  githubAuthSecretName: kubernetesNameSchema.optional(),
114
117
  annotations: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
115
118
  image: z.string().min(1).default(RUNNER_WORKFLOW_IMAGE),
@@ -270,38 +273,51 @@ function runnerWorkflowStorage(options, tasks) {
270
273
  subPath: "auth.json"
271
274
  });
272
275
  }
273
- if (options.githubAuthSecretName) {
276
+ if (options.gitCredentialsSecretName) {
274
277
  volumes.push({
275
- name: "github-auth",
278
+ name: "runner-git-credentials",
276
279
  secret: {
280
+ defaultMode: 256,
277
281
  items: [
278
282
  {
279
- key: "gitconfig",
280
- path: "gitconfig"
283
+ key: "username",
284
+ path: "username"
281
285
  },
282
286
  {
283
- key: "git-credentials",
284
- path: "git-credentials"
287
+ key: "password",
288
+ path: "password"
285
289
  },
286
290
  {
287
- key: "hosts.yml",
288
- path: "hosts.yml"
291
+ key: "identity",
292
+ path: "identity"
293
+ },
294
+ {
295
+ key: "known_hosts",
296
+ path: "known_hosts"
289
297
  }
290
298
  ],
291
- secretName: options.githubAuthSecretName
299
+ optional: true,
300
+ secretName: options.gitCredentialsSecretName
292
301
  }
293
302
  });
294
303
  volumeMounts.push({
295
- mountPath: "/root/.gitconfig",
296
- name: "github-auth",
297
- readOnly: true,
298
- subPath: "gitconfig"
299
- }, {
300
- mountPath: "/root/.git-credentials",
304
+ mountPath: RUNNER_GIT_CREDENTIALS_PATH,
305
+ name: "runner-git-credentials",
306
+ readOnly: true
307
+ });
308
+ }
309
+ if (options.githubAuthSecretName) {
310
+ volumes.push({
301
311
  name: "github-auth",
302
- readOnly: true,
303
- subPath: "git-credentials"
304
- }, {
312
+ secret: {
313
+ items: [{
314
+ key: "hosts.yml",
315
+ path: "hosts.yml"
316
+ }],
317
+ secretName: options.githubAuthSecretName
318
+ }
319
+ });
320
+ volumeMounts.push({
305
321
  mountPath: "/root/.config/gh/hosts.yml",
306
322
  name: "github-auth",
307
323
  readOnly: true,
package/dist/config.d.ts CHANGED
@@ -362,8 +362,8 @@ declare const configSchema: z.ZodObject<{
362
362
  policy: z.ZodOptional<z.ZodObject<{
363
363
  commands: z.ZodOptional<z.ZodEnum<{
364
364
  allow: "allow";
365
- "trusted-only": "trusted-only";
366
365
  deny: "deny";
366
+ "trusted-only": "trusted-only";
367
367
  }>>;
368
368
  modules: z.ZodOptional<z.ZodEnum<{
369
369
  allow: "allow";
@@ -387,8 +387,8 @@ declare const configSchema: z.ZodObject<{
387
387
  }, z.core.$strict>>>;
388
388
  default_profile: z.ZodOptional<z.ZodString>;
389
389
  mode: z.ZodEnum<{
390
- hosted: "hosted";
391
390
  local: "local";
391
+ hosted: "hosted";
392
392
  }>;
393
393
  provider: z.ZodLiteral<"toolhive">;
394
394
  authorization_env: z.ZodDefault<z.ZodString>;
@@ -431,10 +431,10 @@ declare const configSchema: z.ZodObject<{
431
431
  }, z.core.$strict>>;
432
432
  output: z.ZodOptional<z.ZodObject<{
433
433
  format: z.ZodEnum<{
434
+ json_schema: "json_schema";
434
435
  text: "text";
435
436
  json: "json";
436
437
  jsonl: "jsonl";
437
- json_schema: "json_schema";
438
438
  }>;
439
439
  repair: z.ZodOptional<z.ZodObject<{
440
440
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -485,8 +485,8 @@ declare const configSchema: z.ZodObject<{
485
485
  rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
486
486
  path: z.ZodString;
487
487
  source_root: z.ZodDefault<z.ZodEnum<{
488
- package: "package";
489
488
  project: "project";
489
+ package: "package";
490
490
  }>>;
491
491
  }, z.core.$strict>>>;
492
492
  runners: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -503,10 +503,10 @@ declare const configSchema: z.ZodObject<{
503
503
  disabled: "disabled";
504
504
  }>>>;
505
505
  output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
506
+ json_schema: "json_schema";
506
507
  text: "text";
507
508
  json: "json";
508
509
  jsonl: "jsonl";
509
- json_schema: "json_schema";
510
510
  }>>>;
511
511
  rules: z.ZodOptional<z.ZodBoolean>;
512
512
  skills: z.ZodOptional<z.ZodBoolean>;
@@ -613,8 +613,8 @@ declare const configSchema: z.ZodObject<{
613
613
  schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
614
614
  description: z.ZodOptional<z.ZodString>;
615
615
  baseline: z.ZodEnum<{
616
- execute: "execute";
617
616
  quick: "quick";
617
+ execute: "execute";
618
618
  }>;
619
619
  max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
620
620
  node_catalog: z.ZodOptional<z.ZodString>;
@@ -626,8 +626,8 @@ declare const configSchema: z.ZodObject<{
626
626
  skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
627
627
  path: z.ZodString;
628
628
  source_root: z.ZodDefault<z.ZodEnum<{
629
- package: "package";
630
629
  project: "project";
630
+ package: "package";
631
631
  }>>;
632
632
  }, z.core.$strict>>>;
633
633
  task_context: z.ZodOptional<z.ZodObject<{
package/dist/hooks.d.ts CHANGED
@@ -13,8 +13,8 @@ declare const hookResultSchema: z.ZodObject<{
13
13
  taskContext: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
14
14
  }, z.core.$strict>>;
15
15
  status: z.ZodEnum<{
16
- fail: "fail";
17
16
  pass: "pass";
17
+ fail: "fail";
18
18
  skip: "skip";
19
19
  }>;
20
20
  summary: z.ZodOptional<z.ZodString>;
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { resolvePackageAssetPath } from "./package-assets.js";
10
10
  import { formatConfigError, runPipelineFromConfig } from "./pipeline-runtime.js";
11
11
  import { registerRunnerCommandCommand } from "./commands/runner-command-command.js";
12
12
  import { formatInstallCommandsResult, installCommands, parseCommandHost } from "./install-commands.js";
13
+ import { loadMokaGlobalConfig } from "./moka-global-config.js";
13
14
  import { submitMoka } from "./moka-submit.js";
14
15
  import { formatPipelineInitResult, initPipelineProject } from "./pipeline-init.js";
15
16
  import { existsSync, readFileSync, realpathSync } from "node:fs";
@@ -313,31 +314,40 @@ function addMokaSubmitOptions(command) {
313
314
  }
314
315
  function runMokaSubmitFromCli(input, flags) {
315
316
  const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
317
+ const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
318
+ const globalConfig = loadMokaGlobalConfig();
316
319
  const commonOptions = mokaCommonSubmitOptions({
317
- config: loadPipelineConfig(cwd, { allowMissingLintFileReferences: true }),
320
+ config,
318
321
  cwd,
319
- eventUrl: resolveMokaEventUrl(flags),
320
- flags
322
+ eventUrl: resolveMokaEventUrl(flags, globalConfig),
323
+ flags,
324
+ globalConfig
321
325
  });
322
326
  if (flags.command) return submitMokaCommandFromCli(input, flags, commonOptions);
323
327
  return submitMokaGraphFromCli(input, flags, commonOptions);
324
328
  }
325
- function resolveMokaEventUrl(flags) {
326
- return flags.eventUrl ?? process.env.PIPELINE_EVENT_URL ?? "https://pipeline-console.momokaya.ee/api/pipeline/runner-events";
329
+ function resolveMokaEventUrl(flags, globalConfig) {
330
+ return flags.eventUrl ?? globalConfig?.momokaya.submit.eventUrl;
327
331
  }
328
332
  function mokaCommonSubmitOptions(input) {
333
+ const momokaya = input.globalConfig?.momokaya;
329
334
  return {
330
335
  config: input.config,
331
336
  eventUrl: input.eventUrl,
337
+ eventAuthSecretKey: momokaya?.submit.eventAuthSecretKey,
338
+ eventAuthSecretName: momokaya?.submit.eventAuthSecretName,
332
339
  generateName: input.flags.generateName,
340
+ gitCredentialsSecretName: momokaya?.submit.gitCredentialsSecretName,
341
+ githubAuthSecretName: momokaya?.submit.githubAuthSecretName,
333
342
  image: input.flags.image,
334
343
  imagePullPolicy: parseImagePullPolicy(input.flags.imagePullPolicy),
335
- imagePullSecretName: input.flags.imagePullSecret,
336
- kubeconfigPath: input.flags.kubeconfig,
344
+ imagePullSecretName: input.flags.imagePullSecret ?? momokaya?.submit.imagePullSecretName,
345
+ kubeconfigPath: input.flags.kubeconfig ?? momokaya?.kubernetes.kubeconfig,
337
346
  name: input.flags.name,
338
- namespace: input.flags.namespace,
339
- queueName: input.flags.queueName,
340
- serviceAccountName: input.flags.serviceAccount,
347
+ namespace: input.flags.namespace ?? momokaya?.kubernetes.namespace,
348
+ opencodeAuthSecretName: momokaya?.submit.opencodeAuthSecretName,
349
+ queueName: input.flags.queueName ?? momokaya?.submit.queueName,
350
+ serviceAccountName: input.flags.serviceAccount ?? momokaya?.submit.serviceAccountName,
341
351
  worktreePath: input.cwd
342
352
  };
343
353
  }
@@ -377,7 +387,7 @@ function parseGatewayHostScope(value) {
377
387
  throw new Error("scope must be project or global");
378
388
  }
379
389
  function addRunnerArgoOptions(command, options = {}) {
380
- command.option("--name <name>", "Workflow metadata.name").option("--generate-name <prefix>", "Workflow metadata.generateName").option("--namespace <namespace>", "Workflow namespace", "momokaya-pipeline");
390
+ command.option("--name <name>", "Workflow metadata.name").option("--generate-name <prefix>", "Workflow metadata.generateName").option("--namespace <namespace>", "Workflow namespace");
381
391
  if (options.kubeconfig) command.option("--kubeconfig <path>", "kubeconfig path");
382
392
  return command.option("--queue-name <name>", "Kueue LocalQueue label for Workflow pods").option("--service-account <name>", "Workflow service account").option("--image <image>", "runner image").addOption(new Option("--image-pull-policy <policy>", "runner image pull policy").choices([
383
393
  "Always",
@@ -172,7 +172,7 @@ function entrypointDispatchBlock(host, config, id, entrypoint) {
172
172
  `The schedule policy is \`${entrypoint.schedule}\`.`,
173
173
  id === "quick" ? "Run `moka submit --quick <task description>` to submit the graph as an Argo Workflow." : `Run \`moka submit <task description>\` to submit the \`${id}\` graph as an Argo Workflow.`,
174
174
  "The pipeline runtime executes as Argo DAG tasks using the package-owned runner image.",
175
- "Use `--kubeconfig <path>` and `--namespace <namespace>` to target a local or remote Kubernetes cluster.",
175
+ "Configure the target in `~/.config/moka/config.yaml`; use `--kubeconfig <path>` and `--namespace <namespace>` only for explicit command overrides.",
176
176
  "Use `moka submit --schedule <schedule.yaml> <task description>` only when rerunning an existing schedule artifact."
177
177
  ].join("\n");
178
178
  }
@@ -446,7 +446,8 @@ function selectedHosts(host) {
446
446
  const GENERATED_RESOURCE_ROOTS = { opencode: [
447
447
  ".opencode/commands",
448
448
  ".opencode/agents",
449
- ".opencode/plugins"
449
+ ".opencode/plugins",
450
+ ".opencode/skills"
450
451
  ] };
451
452
  async function listFiles(root) {
452
453
  if (!existsSync(root)) return [];
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/moka-global-config.d.ts
4
+ declare const MOKA_GLOBAL_CONFIG_PATH = ".config/moka/config.yaml";
5
+ declare const mokaGlobalConfigSchema: z.ZodObject<{
6
+ momokaya: z.ZodObject<{
7
+ kubernetes: z.ZodObject<{
8
+ kubeconfig: z.ZodOptional<z.ZodString>;
9
+ namespace: z.ZodString;
10
+ }, z.core.$strict>;
11
+ submit: z.ZodObject<{
12
+ eventAuthSecretKey: z.ZodString;
13
+ eventAuthSecretName: z.ZodString;
14
+ eventUrl: z.ZodString;
15
+ gitCredentialsSecretName: z.ZodString;
16
+ githubAuthSecretName: z.ZodString;
17
+ imagePullSecretName: z.ZodString;
18
+ opencodeAuthSecretName: z.ZodString;
19
+ queueName: z.ZodString;
20
+ serviceAccountName: z.ZodString;
21
+ }, z.core.$strict>;
22
+ }, z.core.$strict>;
23
+ }, z.core.$strict>;
24
+ type MokaGlobalConfig = z.infer<typeof mokaGlobalConfigSchema>;
25
+ declare function mokaGlobalConfigPath(homeDir?: string): string;
26
+ declare function loadMokaGlobalConfig(): MokaGlobalConfig | null;
27
+ declare function parseMokaGlobalConfig(source: string, sourcePath: string): MokaGlobalConfig;
28
+ //#endregion
29
+ export { MOKA_GLOBAL_CONFIG_PATH, MokaGlobalConfig, loadMokaGlobalConfig, mokaGlobalConfigPath, mokaGlobalConfigSchema, parseMokaGlobalConfig };
@@ -0,0 +1,56 @@
1
+ import { PipelineConfigError } from "./config.js";
2
+ import { parseDocument } from "yaml";
3
+ import { z } from "zod";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { homedir } from "node:os";
7
+ //#region src/moka-global-config.ts
8
+ const MOKA_GLOBAL_CONFIG_PATH = ".config/moka/config.yaml";
9
+ const mokaSubmitGlobalConfigSchema = z.object({
10
+ eventAuthSecretKey: z.string().min(1),
11
+ eventAuthSecretName: z.string().min(1),
12
+ eventUrl: z.string().url(),
13
+ gitCredentialsSecretName: z.string().min(1),
14
+ githubAuthSecretName: z.string().min(1),
15
+ imagePullSecretName: z.string().min(1),
16
+ opencodeAuthSecretName: z.string().min(1),
17
+ queueName: z.string().min(1),
18
+ serviceAccountName: z.string().min(1)
19
+ }).strict();
20
+ const mokaKubernetesGlobalConfigSchema = z.object({
21
+ kubeconfig: z.string().min(1).optional(),
22
+ namespace: z.string().min(1)
23
+ }).strict();
24
+ const mokaGlobalConfigSchema = z.object({ momokaya: z.object({
25
+ kubernetes: mokaKubernetesGlobalConfigSchema,
26
+ submit: mokaSubmitGlobalConfigSchema
27
+ }).strict() }).strict();
28
+ function mokaGlobalConfigPath(homeDir = homedir()) {
29
+ return join(homeDir, MOKA_GLOBAL_CONFIG_PATH);
30
+ }
31
+ function loadMokaGlobalConfig() {
32
+ const configPath = mokaGlobalConfigPath();
33
+ if (!existsSync(configPath)) return null;
34
+ return parseMokaGlobalConfig(readFileSync(configPath, "utf8"), configPath);
35
+ }
36
+ function parseMokaGlobalConfig(source, sourcePath) {
37
+ const document = parseDocument(source, {
38
+ prettyErrors: false,
39
+ uniqueKeys: true
40
+ });
41
+ if (document.errors.length > 0) throw new PipelineConfigError("PIPELINE_CONFIG_PARSE_ERROR", `Failed to parse ${sourcePath}`, document.errors.map((err) => ({
42
+ message: err.message,
43
+ path: sourcePath
44
+ })));
45
+ const parsed = mokaGlobalConfigSchema.safeParse(document.toJS());
46
+ if (!parsed.success) {
47
+ const issues = parsed.error.issues.map((issue) => ({
48
+ path: issue.path.join("."),
49
+ message: issue.message
50
+ }));
51
+ throw new PipelineConfigError("PIPELINE_CONFIG_VALIDATION_ERROR", [`Invalid ${sourcePath}:`, ...issues.map((issue) => issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`)].join("\n"), issues);
52
+ }
53
+ return parsed.data;
54
+ }
55
+ //#endregion
56
+ export { MOKA_GLOBAL_CONFIG_PATH, loadMokaGlobalConfig, mokaGlobalConfigPath, mokaGlobalConfigSchema, parseMokaGlobalConfig };
@@ -73,16 +73,17 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
73
73
  authTokenFile: z.ZodOptional<z.ZodString>;
74
74
  url: z.ZodString;
75
75
  }, z.core.$strict>>;
76
- eventAuthSecretKey: z.ZodDefault<z.ZodString>;
77
- eventAuthSecretName: z.ZodDefault<z.ZodString>;
78
- eventUrl: z.ZodDefault<z.ZodString>;
76
+ eventAuthSecretKey: z.ZodOptional<z.ZodString>;
77
+ eventAuthSecretName: z.ZodOptional<z.ZodString>;
78
+ eventUrl: z.ZodOptional<z.ZodString>;
79
79
  events: z.ZodOptional<z.ZodObject<{
80
80
  authHeader: z.ZodDefault<z.ZodString>;
81
81
  authTokenFile: z.ZodOptional<z.ZodString>;
82
82
  url: z.ZodString;
83
83
  }, z.core.$strict>>;
84
84
  generateName: z.ZodOptional<z.ZodString>;
85
- githubAuthSecretName: z.ZodDefault<z.ZodString>;
85
+ gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
86
+ githubAuthSecretName: z.ZodOptional<z.ZodString>;
86
87
  hookPolicy: z.ZodOptional<z.ZodObject<{
87
88
  allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
88
89
  allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
@@ -142,12 +143,12 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
142
143
  IfNotPresent: "IfNotPresent";
143
144
  Never: "Never";
144
145
  }>>;
145
- imagePullSecretName: z.ZodDefault<z.ZodString>;
146
+ imagePullSecretName: z.ZodOptional<z.ZodString>;
146
147
  kubeconfigPath: z.ZodOptional<z.ZodString>;
147
148
  name: z.ZodOptional<z.ZodString>;
148
- namespace: z.ZodDefault<z.ZodString>;
149
- opencodeAuthSecretName: z.ZodDefault<z.ZodString>;
150
- queueName: z.ZodDefault<z.ZodString>;
149
+ namespace: z.ZodOptional<z.ZodString>;
150
+ opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
151
+ queueName: z.ZodOptional<z.ZodString>;
151
152
  repository: z.ZodOptional<z.ZodObject<{
152
153
  baseBranch: z.ZodString;
153
154
  sha: z.ZodOptional<z.ZodString>;
@@ -158,10 +159,10 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
158
159
  project: z.ZodString;
159
160
  requestedBy: z.ZodOptional<z.ZodString>;
160
161
  }, z.core.$strict>>;
161
- serviceAccountName: z.ZodDefault<z.ZodString>;
162
+ serviceAccountName: z.ZodOptional<z.ZodString>;
162
163
  mode: z.ZodEnum<{
163
- quick: "quick";
164
164
  full: "full";
165
+ quick: "quick";
165
166
  }>;
166
167
  schedulePath: z.ZodOptional<z.ZodString>;
167
168
  scheduleYaml: z.ZodOptional<z.ZodString>;
@@ -185,16 +186,17 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
185
186
  authTokenFile: z.ZodOptional<z.ZodString>;
186
187
  url: z.ZodString;
187
188
  }, z.core.$strict>>;
188
- eventAuthSecretKey: z.ZodDefault<z.ZodString>;
189
- eventAuthSecretName: z.ZodDefault<z.ZodString>;
190
- eventUrl: z.ZodDefault<z.ZodString>;
189
+ eventAuthSecretKey: z.ZodOptional<z.ZodString>;
190
+ eventAuthSecretName: z.ZodOptional<z.ZodString>;
191
+ eventUrl: z.ZodOptional<z.ZodString>;
191
192
  events: z.ZodOptional<z.ZodObject<{
192
193
  authHeader: z.ZodDefault<z.ZodString>;
193
194
  authTokenFile: z.ZodOptional<z.ZodString>;
194
195
  url: z.ZodString;
195
196
  }, z.core.$strict>>;
196
197
  generateName: z.ZodOptional<z.ZodString>;
197
- githubAuthSecretName: z.ZodDefault<z.ZodString>;
198
+ gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
199
+ githubAuthSecretName: z.ZodOptional<z.ZodString>;
198
200
  hookPolicy: z.ZodOptional<z.ZodObject<{
199
201
  allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
200
202
  allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
@@ -254,12 +256,12 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
254
256
  IfNotPresent: "IfNotPresent";
255
257
  Never: "Never";
256
258
  }>>;
257
- imagePullSecretName: z.ZodDefault<z.ZodString>;
259
+ imagePullSecretName: z.ZodOptional<z.ZodString>;
258
260
  kubeconfigPath: z.ZodOptional<z.ZodString>;
259
261
  name: z.ZodOptional<z.ZodString>;
260
- namespace: z.ZodDefault<z.ZodString>;
261
- opencodeAuthSecretName: z.ZodDefault<z.ZodString>;
262
- queueName: z.ZodDefault<z.ZodString>;
262
+ namespace: z.ZodOptional<z.ZodString>;
263
+ opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
264
+ queueName: z.ZodOptional<z.ZodString>;
263
265
  repository: z.ZodOptional<z.ZodObject<{
264
266
  baseBranch: z.ZodString;
265
267
  sha: z.ZodOptional<z.ZodString>;
@@ -270,7 +272,7 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
270
272
  project: z.ZodString;
271
273
  requestedBy: z.ZodOptional<z.ZodString>;
272
274
  }, z.core.$strict>>;
273
- serviceAccountName: z.ZodDefault<z.ZodString>;
275
+ serviceAccountName: z.ZodOptional<z.ZodString>;
274
276
  commandArgv: z.ZodArray<z.ZodString>;
275
277
  task: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
276
278
  kind: z.ZodLiteral<"prompt">;
@@ -315,13 +317,14 @@ interface MokaWorkflowSubmitOptions {
315
317
  eventAuthSecretKey?: string;
316
318
  eventAuthSecretName?: string;
317
319
  generateName?: string;
320
+ gitCredentialsSecretName?: string;
318
321
  githubAuthSecretName?: string;
319
322
  image?: string;
320
323
  imagePullPolicy?: "Always" | "IfNotPresent" | "Never";
321
324
  imagePullSecretName?: string;
322
325
  kubeconfigPath?: string;
323
326
  name?: string;
324
- namespace?: string;
327
+ namespace: string;
325
328
  opencodeAuthSecretName?: string;
326
329
  payloadJson: string;
327
330
  queueName?: string;
@@ -9,14 +9,6 @@ import parseGitUrl from "git-url-parse";
9
9
  import { resolve } from "node:path";
10
10
  import { simpleGit } from "simple-git";
11
11
  //#region src/moka-submit.ts
12
- const MOMOKAYA_EVENT_AUTH_SECRET_KEY = "OISIN_PIPELINE_EVENT_AUTH_TOKEN";
13
- const MOMOKAYA_EVENT_AUTH_SECRET_NAME = "pipeline-runner-event-auth";
14
- const MOMOKAYA_EVENT_URL = "https://pipeline-console.momokaya.ee/api/pipeline/runner-events";
15
- const MOMOKAYA_GITHUB_AUTH_SECRET_NAME = "oisin-bot-github-auth";
16
- const MOMOKAYA_IMAGE_PULL_SECRET_NAME = "ghcr-pull-secret";
17
- const MOMOKAYA_OPENCODE_AUTH_SECRET_NAME = "opencode-auth-1";
18
- const MOMOKAYA_QUEUE_NAME = "momokaya-pipeline";
19
- const MOMOKAYA_RUNNER_SERVICE_ACCOUNT_NAME = "pipeline-runner";
20
12
  const imagePullPolicySchema = z.enum([
21
13
  "Always",
22
14
  "IfNotPresent",
@@ -69,25 +61,26 @@ const mokaSubmitResultSchema = workflowSubmitResultSchema;
69
61
  const mokaSubmitBaseOptionsSchema = z.object({
70
62
  delivery: runnerDeliverySchema.default({ pullRequest: false }),
71
63
  eventSink: mokaSubmitEventsSchema.optional(),
72
- eventAuthSecretKey: z.string().min(1).default(MOMOKAYA_EVENT_AUTH_SECRET_KEY),
73
- eventAuthSecretName: z.string().min(1).default(MOMOKAYA_EVENT_AUTH_SECRET_NAME),
74
- eventUrl: z.string().url().default(MOMOKAYA_EVENT_URL),
64
+ eventAuthSecretKey: z.string().min(1).optional(),
65
+ eventAuthSecretName: z.string().min(1).optional(),
66
+ eventUrl: z.string().url().optional(),
75
67
  events: mokaSubmitEventsSchema.optional(),
76
68
  generateName: z.string().min(1).optional(),
77
- githubAuthSecretName: z.string().min(1).default(MOMOKAYA_GITHUB_AUTH_SECRET_NAME),
69
+ gitCredentialsSecretName: z.string().min(1).optional(),
70
+ githubAuthSecretName: z.string().min(1).optional(),
78
71
  hookPolicy: mokaSubmitHookPolicySchema.optional(),
79
72
  hooks: mokaSubmitDirectHooksSchema.optional(),
80
73
  image: z.string().min(1).optional(),
81
74
  imagePullPolicy: imagePullPolicySchema,
82
- imagePullSecretName: z.string().min(1).default(MOMOKAYA_IMAGE_PULL_SECRET_NAME),
75
+ imagePullSecretName: z.string().min(1).optional(),
83
76
  kubeconfigPath: z.string().min(1).optional(),
84
77
  name: z.string().min(1).optional(),
85
- namespace: z.string().min(1).default("momokaya-pipeline"),
86
- opencodeAuthSecretName: z.string().min(1).default(MOMOKAYA_OPENCODE_AUTH_SECRET_NAME),
87
- queueName: z.string().min(1).default(MOMOKAYA_QUEUE_NAME),
78
+ namespace: z.string().min(1).optional(),
79
+ opencodeAuthSecretName: z.string().min(1).optional(),
80
+ queueName: z.string().min(1).optional(),
88
81
  repository: runnerRepositoryContextSchema.optional(),
89
82
  run: runnerRunIdentitySchema.optional(),
90
- serviceAccountName: z.string().min(1).default(MOMOKAYA_RUNNER_SERVICE_ACCOUNT_NAME)
83
+ serviceAccountName: z.string().min(1).optional()
91
84
  }).strict();
92
85
  const mokaGraphSubmitOptionsSchema = mokaSubmitBaseOptionsSchema.extend({
93
86
  mode: z.enum(["full", "quick"]),
@@ -107,6 +100,11 @@ const mokaSubmitOptionsSchema = z.discriminatedUnion("type", [mokaGraphSubmitOpt
107
100
  message: "Choose either eventSink or events, not both",
108
101
  path: ["eventSink"]
109
102
  });
103
+ if (data.eventSink === void 0 && data.events === void 0 && data.eventUrl === void 0) ctx.addIssue({
104
+ code: "custom",
105
+ message: "eventUrl is required unless eventSink or events is provided",
106
+ path: ["eventUrl"]
107
+ });
110
108
  });
111
109
  const submitHookId = (event) => `moka-submit-${event.replaceAll(".", "-")}`;
112
110
  function objectWithoutUndefined(value) {
@@ -284,18 +282,23 @@ function workflowSubmitOptions(options) {
284
282
  return {
285
283
  eventAuthSecretKey: options.eventAuthSecretKey,
286
284
  eventAuthSecretName: options.eventAuthSecretName,
285
+ gitCredentialsSecretName: options.gitCredentialsSecretName,
287
286
  githubAuthSecretName: options.githubAuthSecretName,
288
287
  image: options.image,
289
288
  imagePullPolicy: options.imagePullPolicy,
290
289
  imagePullSecretName: options.imagePullSecretName,
291
290
  kubeconfigPath: options.kubeconfigPath,
292
291
  name: options.name,
293
- namespace: options.namespace,
292
+ namespace: requireSubmitOption(options.namespace, "namespace"),
294
293
  opencodeAuthSecretName: options.opencodeAuthSecretName,
295
294
  queueName: options.queueName,
296
295
  serviceAccountName: options.serviceAccountName
297
296
  };
298
297
  }
298
+ function requireSubmitOption(value, name) {
299
+ if (!value) throw new Error(`${name} is required for moka submit`);
300
+ return value;
301
+ }
299
302
  function runnerPayloadJson(input) {
300
303
  return JSON.stringify(buildRunnerCommandPayload({
301
304
  delivery: input.options.delivery,
@@ -323,6 +326,7 @@ function runnerEvents(options) {
323
326
  authTokenFile: eventSink.authTokenFile ?? eventAuthTokenFile(options),
324
327
  url: eventSink.url
325
328
  };
329
+ if (!options.eventUrl) throw new Error("eventUrl is required unless eventSink or events is provided");
326
330
  return {
327
331
  authHeader: "Authorization",
328
332
  authTokenFile: eventAuthTokenFile(options),
@@ -330,19 +334,23 @@ function runnerEvents(options) {
330
334
  };
331
335
  }
332
336
  function eventAuthTokenFile(options) {
337
+ if (!options.eventAuthSecretKey) throw new Error("eventAuthSecretKey is required unless eventSink.authTokenFile is provided");
333
338
  return `/etc/pipeline/event-auth/${options.eventAuthSecretKey}`;
334
339
  }
335
340
  async function resolveSubmissionContext(options, dependencies, runId) {
336
341
  const explicitContext = explicitSubmissionContext(options);
337
342
  if (explicitContext) return explicitContext;
338
343
  const git = await resolveRequiredGit(options, dependencies);
344
+ const repository = repositoryContext(options, git);
345
+ assertRepositoryCredentialConfiguration(options);
339
346
  return {
340
- repository: repositoryContext(options, git),
347
+ repository,
341
348
  run: runContext(options, git, runId)
342
349
  };
343
350
  }
344
351
  function explicitSubmissionContext(options) {
345
352
  if (!(options.repository && options.run)) return null;
353
+ assertRepositoryCredentialConfiguration(options);
346
354
  return {
347
355
  repository: options.repository,
348
356
  run: options.run
@@ -359,6 +367,9 @@ function repositoryContext(options, git) {
359
367
  url: git.url
360
368
  };
361
369
  }
370
+ function assertRepositoryCredentialConfiguration(options) {
371
+ if (!options.gitCredentialsSecretName) throw new Error("gitCredentialsSecretName is required for runner git clone, fetch, and push operations");
372
+ }
362
373
  function runContext(options, git, runId) {
363
374
  return options.run ?? {
364
375
  id: runId,
@@ -1,14 +1,15 @@
1
- import { chmodSync, copyFileSync, existsSync, mkdirSync } from "node:fs";
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
  import { execFile } from "node:child_process";
5
5
  import { promisify } from "node:util";
6
6
  //#region src/run-state/git-refs.ts
7
7
  const DEFAULT_WORKSPACE_PATH = "/workspace";
8
- const DEFAULT_GIT_CREDENTIAL_STORE = "/root/.git-credentials";
8
+ const DEFAULT_GIT_CREDENTIALS_DIR = "/etc/pipeline/git-credentials";
9
9
  const WRITABLE_GIT_CREDENTIAL_STORE = resolve(tmpdir(), "pipeline-git-credentials");
10
+ const SCP_LIKE_SSH_REMOTE_RE = /^[^@\s]+@[^:\s]+:.+/u;
10
11
  const execGit = promisify(execFile);
11
- let preparedCredentialStore;
12
+ let preparedBasicAuthCredentialStore;
12
13
  function runnerGitRefs(payload, nodeId) {
13
14
  const prefix = `refs/heads/pipeline/runs/${payload.run.id}/${payload.workflow.id}`;
14
15
  return {
@@ -102,21 +103,19 @@ async function configureGitCommitter(worktreePath, committer) {
102
103
  ]);
103
104
  }
104
105
  function runnerGitCommandArgs(args) {
105
- return [...gitCredentialConfigArgs(), ...args];
106
+ return [...gitCredentialConfigArgs(remoteUrlFromGitArgs(args)), ...args];
106
107
  }
107
108
  async function runGit(cwd, args) {
109
+ const remoteUrl = remoteUrlFromGitArgs(args);
108
110
  const { stdout } = await execGit("git", runnerGitCommandArgs(args), {
109
111
  cwd,
110
112
  encoding: "utf8",
111
- env: {
112
- ...process.env,
113
- GIT_TERMINAL_PROMPT: "0"
114
- }
113
+ env: runnerGitEnv(remoteUrl)
115
114
  });
116
115
  return stdout;
117
116
  }
118
- function gitCredentialConfigArgs() {
119
- const writablePath = prepareWritableGitCredentialStore();
117
+ function gitCredentialConfigArgs(remoteUrl) {
118
+ const writablePath = prepareWritableGitCredentialStore(remoteUrl);
120
119
  if (!writablePath) return [];
121
120
  return [
122
121
  "-c",
@@ -125,26 +124,102 @@ function gitCredentialConfigArgs() {
125
124
  `credential.helper=store --file=${writablePath}`
126
125
  ];
127
126
  }
128
- function prepareWritableGitCredentialStore() {
129
- const sourcePath = availableGitCredentialStore();
130
- if (!sourcePath) return;
127
+ function prepareWritableGitCredentialStore(remoteUrl) {
131
128
  const writablePath = writableGitCredentialStore();
132
- copyGitCredentialStore(sourcePath, writablePath);
133
- return writablePath;
129
+ const basicAuth = availableBasicAuthCredentials();
130
+ if (basicAuth) return prepareBasicAuthCredentialStore(basicAuth, writablePath, remoteUrl);
131
+ }
132
+ function availableBasicAuthCredentials() {
133
+ const credentialsDir = gitCredentialsDir();
134
+ const usernamePath = resolve(credentialsDir, "username");
135
+ const passwordPath = resolve(credentialsDir, "password");
136
+ if (!(existsSync(usernamePath) && existsSync(passwordPath))) return;
137
+ return {
138
+ password: readCredentialFile(passwordPath),
139
+ username: readCredentialFile(usernamePath)
140
+ };
134
141
  }
135
- function availableGitCredentialStore() {
136
- const sourcePath = process.env.PIPELINE_GIT_CREDENTIAL_STORE ?? DEFAULT_GIT_CREDENTIAL_STORE;
137
- return existsSync(sourcePath) ? sourcePath : void 0;
142
+ function gitCredentialsDir() {
143
+ return process.env.PIPELINE_GIT_CREDENTIALS_DIR ?? DEFAULT_GIT_CREDENTIALS_DIR;
138
144
  }
139
145
  function writableGitCredentialStore() {
140
146
  return process.env.PIPELINE_WRITABLE_GIT_CREDENTIAL_STORE ?? WRITABLE_GIT_CREDENTIAL_STORE;
141
147
  }
142
- function copyGitCredentialStore(sourcePath, writablePath) {
143
- if (preparedCredentialStore === writablePath) return;
148
+ function writeGitCredentialStore(credentials, writablePath, host) {
144
149
  mkdirSync(dirname(writablePath), { recursive: true });
145
- copyFileSync(sourcePath, writablePath);
150
+ writeFileSync(writablePath, `https://${encodeURIComponent(credentials.username)}:${encodeURIComponent(credentials.password)}@${host}\n`, { mode: 384 });
146
151
  chmodSync(writablePath, 384);
147
- preparedCredentialStore = writablePath;
152
+ }
153
+ function prepareBasicAuthCredentialStore(credentials, writablePath, remoteUrl) {
154
+ const host = remoteUrl ? credentialHost(remoteUrl) : void 0;
155
+ if (!host) return existingPreparedBasicAuthCredentialStore(writablePath);
156
+ if (isPreparedBasicAuthCredentialStore(writablePath, host)) return writablePath;
157
+ writeGitCredentialStore(credentials, writablePath, host);
158
+ preparedBasicAuthCredentialStore = {
159
+ host,
160
+ path: writablePath
161
+ };
162
+ return writablePath;
163
+ }
164
+ function existingPreparedBasicAuthCredentialStore(writablePath) {
165
+ return preparedBasicAuthCredentialStore?.path === writablePath ? writablePath : void 0;
166
+ }
167
+ function isPreparedBasicAuthCredentialStore(writablePath, host) {
168
+ return preparedBasicAuthCredentialStore?.path === writablePath && preparedBasicAuthCredentialStore.host === host;
169
+ }
170
+ function runnerGitEnv(remoteUrl) {
171
+ const env = {
172
+ ...process.env,
173
+ GIT_TERMINAL_PROMPT: "0"
174
+ };
175
+ if (remoteUrl && isSshRemote(remoteUrl)) {
176
+ const sshCommand = gitSshCommand();
177
+ if (sshCommand) env.GIT_SSH_COMMAND = sshCommand;
178
+ }
179
+ return env;
180
+ }
181
+ function gitSshCommand() {
182
+ const credentialsDir = gitCredentialsDir();
183
+ const identityPath = resolve(credentialsDir, "identity");
184
+ const knownHostsPath = resolve(credentialsDir, "known_hosts");
185
+ if (!(existsSync(identityPath) && existsSync(knownHostsPath))) return;
186
+ chmodSync(identityPath, 256);
187
+ return [
188
+ "ssh",
189
+ "-i",
190
+ shellQuote(identityPath),
191
+ "-o",
192
+ "IdentitiesOnly=yes",
193
+ "-o",
194
+ `UserKnownHostsFile=${shellQuote(knownHostsPath)}`,
195
+ "-o",
196
+ "StrictHostKeyChecking=yes"
197
+ ].join(" ");
198
+ }
199
+ function readCredentialFile(path) {
200
+ return readFileSync(path, "utf8").trim();
201
+ }
202
+ function remoteUrlFromGitArgs(args) {
203
+ if (args[0] === "clone") return args.find((arg) => isRemoteUrl(arg));
204
+ }
205
+ function isRemoteUrl(value) {
206
+ return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("ssh://") || isScpLikeSshRemote(value);
207
+ }
208
+ function isSshRemote(value) {
209
+ return value.startsWith("ssh://") || isScpLikeSshRemote(value);
210
+ }
211
+ function isScpLikeSshRemote(value) {
212
+ return SCP_LIKE_SSH_REMOTE_RE.test(value);
213
+ }
214
+ function credentialHost(remoteUrl) {
215
+ try {
216
+ return new URL(remoteUrl).host || void 0;
217
+ } catch {
218
+ return;
219
+ }
220
+ }
221
+ function shellQuote(value) {
222
+ return `'${value.replaceAll("'", `'\\''`)}'`;
148
223
  }
149
224
  //#endregion
150
225
  export { commitAndPushNodeRef, mergeDependencyRefs, prepareRunnerGitWorkspace, promoteFinalRef };
@@ -43,8 +43,8 @@ declare const runnerDeliverySchema: z.ZodObject<{
43
43
  declare const mokaSubmissionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
44
44
  kind: z.ZodLiteral<"graph">;
45
45
  mode: z.ZodEnum<{
46
- quick: "quick";
47
46
  full: "full";
47
+ quick: "quick";
48
48
  }>;
49
49
  }, z.core.$strict>, z.ZodObject<{
50
50
  argv: z.ZodArray<z.ZodString>;
@@ -104,8 +104,8 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
104
104
  submission: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
105
105
  kind: z.ZodLiteral<"graph">;
106
106
  mode: z.ZodEnum<{
107
- quick: "quick";
108
107
  full: "full";
108
+ quick: "quick";
109
109
  }>;
110
110
  }, z.core.$strict>, z.ZodObject<{
111
111
  argv: z.ZodArray<z.ZodString>;
@@ -129,11 +129,27 @@ description and current git context, creates payload/schedule ConfigMaps, and
129
129
  submits an Argo Workflow that runs the graph as DAG tasks using the package-owned
130
130
  runner image.
131
131
 
132
- `moka submit` uses the Momokaya default event sink unless overridden with
133
- `PIPELINE_EVENT_URL` or `--event-url`.
132
+ `moka submit` reads the private Momokaya target from
133
+ `~/.config/moka/config.yaml`.
134
+
135
+ ```yaml
136
+ momokaya:
137
+ kubernetes:
138
+ kubeconfig: /path/to/cluster.kubeconfig
139
+ namespace: <workflow-namespace>
140
+ submit:
141
+ eventAuthSecretKey: <event-auth-secret-key>
142
+ eventAuthSecretName: <event-auth-secret-name>
143
+ eventUrl: <runner-event-sink-url>
144
+ gitCredentialsSecretName: <git-credentials-secret-name>
145
+ githubAuthSecretName: <github-auth-secret-name>
146
+ imagePullSecretName: <image-pull-secret-name>
147
+ opencodeAuthSecretName: <opencode-auth-secret-name>
148
+ queueName: <local-queue-name>
149
+ serviceAccountName: <runner-service-account-name>
150
+ ```
134
151
 
135
152
  ```shell
136
- export PIPELINE_EVENT_URL="https://console.example.com/api/pipeline/runner-events"
137
153
  moka submit "fix the login bug" --quick
138
154
  moka submit "Implement PIPE-54"
139
155
  ```
@@ -142,7 +158,7 @@ For a local cluster, point the same commands at that cluster with
142
158
  `--kubeconfig <path>` and `--namespace <namespace>`:
143
159
 
144
160
  ```shell
145
- moka submit "fix the login bug" --quick --kubeconfig ~/.kube/config --namespace momokaya-pipeline
161
+ moka submit "fix the login bug" --quick --kubeconfig ~/.kube/config --namespace <workflow-namespace>
146
162
  ```
147
163
 
148
164
  There is no separate workstation-local `submit` path; local submission means
@@ -169,7 +185,7 @@ apiVersion: argoproj.io/v1alpha1
169
185
  kind: Workflow
170
186
  metadata:
171
187
  generateName: pipeline-run-alpha-
172
- namespace: momokaya-pipeline
188
+ namespace: <workflow-namespace>
173
189
  spec:
174
190
  entrypoint: pipeline
175
191
  onExit: pipeline-finalizer
@@ -211,17 +227,28 @@ Secret volume at the path configured in `events.authTokenFile`.
211
227
 
212
228
  Expected namespace resources:
213
229
 
214
- - ServiceAccount `pipeline-runner` with the required RBAC
215
- - Secret `opencode-auth-1` with key `auth.json`
216
- - Secret `pipeline-runner-event-auth` with key
217
- `OISIN_PIPELINE_EVENT_AUTH_TOKEN`
218
- - Secret `oisin-bot-github-auth` with keys `gitconfig`, `git-credentials`, and
219
- `hosts.yml`
230
+ - The ServiceAccount named by `submit.serviceAccountName` with the required RBAC
231
+ - The OpenCode auth Secret named by `submit.opencodeAuthSecretName` with key
232
+ `auth.json`
233
+ - The event auth Secret named by `submit.eventAuthSecretName` with the key named
234
+ by `submit.eventAuthSecretKey`
235
+ - The git credentials Secret named by `submit.gitCredentialsSecretName` using
236
+ the same key conventions as Flux `GitRepository` credentials: `username` and
237
+ `password` for HTTPS remotes, or `identity` and `known_hosts` for SSH remotes
238
+ - The GitHub CLI auth Secret named by `submit.githubAuthSecretName` with key
239
+ `hosts.yml`; this Secret is for `gh` and pull request delivery, not git
240
+ clone/fetch/push authentication
220
241
  - A pipeline-console event sink reachable from the pod
221
242
 
222
- Credential rotation is owned by the infra repository scripts. `moka submit`
223
- references the managed Momokaya Secret names; it does not accept per-run auth
224
- Secret overrides.
243
+ Credential issuance and rotation are owned by the cluster/infra layer, not by
244
+ runner payloads; existing infra repository scripts can continue to own the
245
+ operator-facing lifecycle. Recommended production setups use External Secrets
246
+ Operator or Secrets Store CSI Driver. For GitHub HTTPS, prefer a GitHub App
247
+ installation token materialized by External Secrets Operator's
248
+ `GithubAccessToken` generator with a refresh interval below the token lifetime,
249
+ then template it as `username`/`password`. For SSH, materialize `identity` and
250
+ `known_hosts` from the external secret manager. `moka submit` references
251
+ configured Secret names; it does not accept per-run secret values.
225
252
 
226
253
  ## Payload Contract
227
254
 
@@ -43,7 +43,7 @@ validation, tests, and docs.
43
43
  "events": {
44
44
  "url": "https://console.example/api/pipeline/runner-events",
45
45
  "authHeader": "Authorization",
46
- "authTokenFile": "/etc/pipeline/event-auth/OISIN_PIPELINE_EVENT_AUTH_TOKEN"
46
+ "authTokenFile": "/etc/pipeline/event-auth/<event-auth-secret-key>"
47
47
  }
48
48
  }
49
49
  ```
@@ -160,11 +160,16 @@ environment and mounted secrets.
160
160
 
161
161
  Expected runner Job secrets and mounts include:
162
162
 
163
- - `opencode-auth-1` mounted at `/root/.local/share/opencode/auth.json`
164
- - `pipeline-runner-event-auth` mounted at
165
- `/etc/pipeline/event-auth/OISIN_PIPELINE_EVENT_AUTH_TOKEN`
163
+ - The configured OpenCode auth Secret mounted at
164
+ `/root/.local/share/opencode/auth.json`
165
+ - The configured event auth Secret mounted at
166
+ `/etc/pipeline/event-auth/<event-auth-secret-key>`
166
167
  - MCP gateway auth Secret mounted at a path referenced by the runner
167
- - `oisin-bot-github-auth` mounted for GitHub auth usable by both `git` and `gh`
168
+ - The configured git credentials Secret mounted for `git`; HTTPS remotes use
169
+ `username` and `password`, and SSH remotes use `identity` and `known_hosts`
170
+ following Flux `GitRepository` credential conventions
171
+ - The configured GitHub CLI auth Secret mounted for `gh` when pull request
172
+ delivery is enabled
168
173
 
169
174
  The runner image sets `HOME=/root`; Kubernetes must project each numbered auth
170
175
  Secret with the key `auth.json` into the target directory. The runner does not
@@ -172,6 +177,12 @@ read event auth tokens or payloads from environment variables. No
172
177
  `OISIN_PIPELINE_RUNNER_PAYLOAD_JSON`, `PIPELINE_EVENT_API_TOKEN`, or
173
178
  `OPENCODE_AUTH_JSON` env vars are used.
174
179
 
180
+ Git credential issuance and rotation are cluster concerns. For GitHub HTTPS,
181
+ operators should prefer GitHub App installation tokens generated by External
182
+ Secrets Operator or equivalent control-plane automation. For SSH, operators
183
+ must provide both the private identity and pinned `known_hosts` data. Runner
184
+ payloads never carry credential values or Secret names.
185
+
175
186
  ## Boundary
176
187
 
177
188
  The `moka` command is its own user-facing command and runtime. Argo
package/package.json CHANGED
@@ -70,6 +70,10 @@
70
70
  "types": "./dist/moka-submit.d.ts",
71
71
  "import": "./dist/moka-submit.js"
72
72
  },
73
+ "./moka-global-config": {
74
+ "types": "./dist/moka-global-config.d.ts",
75
+ "import": "./dist/moka-global-config.js"
76
+ },
73
77
  "./planner": {
74
78
  "types": "./dist/workflow-planner.d.ts",
75
79
  "import": "./dist/workflow-planner.js"
@@ -118,7 +122,7 @@
118
122
  "prepack": "bun run build:cli"
119
123
  },
120
124
  "type": "module",
121
- "version": "1.27.17",
125
+ "version": "1.27.18",
122
126
  "description": "Config-driven multi-agent pipeline runner for repository work",
123
127
  "main": "./dist/index.js",
124
128
  "types": "./dist/index.d.ts",