@oisincoveney/pipeline 1.25.0 → 1.26.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.
package/README.md CHANGED
@@ -163,6 +163,13 @@ Submitters can import the executable contract from
163
163
  `@oisincoveney/pipeline/runner-command-contract` for payload construction,
164
164
  validation, contract-version checks, and JSON Schema generation.
165
165
 
166
+ Pipeline Console submits hosted runs through `@oisincoveney/pipeline/moka-submit`.
167
+ The subpath exports `mokaSubmitOptionsSchema`, `mokaSubmitResultSchema`,
168
+ `MokaSubmitOptionsInput`, `MokaSubmitOptionsOutput`, `MokaSubmitInput`,
169
+ `MokaSubmitOutput`, and `submitMoka` so callers can validate prompt or ticket
170
+ tasks with explicit repository, run, delivery, event sink, and runner settings
171
+ without importing Argo, Kubernetes, or runner-command internals.
172
+
166
173
  ## Configuration Model
167
174
 
168
175
  Runtime execution uses package-owned defaults. Tests and advanced embedding code
@@ -1,4 +1,5 @@
1
1
  import { PipelineConfig } from "./config.js";
2
+ import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
2
3
  import { z } from "zod";
3
4
  import { CoreV1Api, CustomObjectsApi, KubeConfig } from "@kubernetes/client-node";
4
5
 
@@ -24,14 +25,6 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
24
25
  scheduleYaml: z.ZodString;
25
26
  serviceAccountName: z.ZodOptional<z.ZodString>;
26
27
  }, z.core.$strict>;
27
- declare const submitRunnerArgoWorkflowResultSchema: z.ZodObject<{
28
- namespace: z.ZodString;
29
- payloadConfigMapName: z.ZodString;
30
- scheduleConfigMapName: z.ZodString;
31
- taskDescriptorConfigMapName: z.ZodString;
32
- workflowName: z.ZodString;
33
- workflowUid: z.ZodOptional<z.ZodString>;
34
- }, z.core.$strict>;
35
28
  declare const commandScheduleOptionsSchema: z.ZodObject<{
36
29
  command: z.ZodArray<z.ZodString>;
37
30
  generatedAt: z.ZodDefault<z.ZodDate>;
@@ -41,7 +34,7 @@ declare const commandScheduleOptionsSchema: z.ZodObject<{
41
34
  type SubmitRunnerArgoWorkflowOptions = z.input<typeof submitRunnerArgoWorkflowOptionsSchema> & {
42
35
  config: PipelineConfig;
43
36
  };
44
- type SubmitRunnerArgoWorkflowResult = z.infer<typeof submitRunnerArgoWorkflowResultSchema>;
37
+ type SubmitRunnerArgoWorkflowResult = z.infer<typeof workflowSubmitResultSchema>;
45
38
  type CommandScheduleOptions = z.input<typeof commandScheduleOptionsSchema>;
46
39
  type CoreApi = Pick<CoreV1Api, "createNamespacedConfigMap">;
47
40
  type WorkflowApi = Pick<CustomObjectsApi, "createNamespacedCustomObject">;
@@ -3,6 +3,7 @@ import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
3
3
  import { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
4
4
  import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
5
5
  import { compileScheduleArtifact, parseScheduleArtifact } from "./schedule-planner.js";
6
+ import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
6
7
  import { stringify } from "yaml";
7
8
  import { z } from "zod";
8
9
  import { randomBytes } from "node:crypto";
@@ -40,14 +41,6 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
40
41
  scheduleYaml: z.string().min(1),
41
42
  serviceAccountName: z.string().min(1).optional()
42
43
  }).strict().refine((options) => options.name !== void 0 || options.generateName !== void 0, { message: "Argo submit options must declare name or generateName" });
43
- const submitRunnerArgoWorkflowResultSchema = z.object({
44
- namespace: z.string().min(1),
45
- payloadConfigMapName: z.string().min(1),
46
- scheduleConfigMapName: z.string().min(1),
47
- taskDescriptorConfigMapName: z.string().min(1),
48
- workflowName: z.string().min(1),
49
- workflowUid: z.string().min(1).optional()
50
- }).strict();
51
44
  const commandScheduleOptionsSchema = z.object({
52
45
  command: z.array(z.string().min(1)).min(1),
53
46
  generatedAt: z.date().default(() => /* @__PURE__ */ new Date()),
@@ -140,7 +133,7 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
140
133
  name: z.string().min(1).optional(),
141
134
  uid: z.string().min(1).optional()
142
135
  }).passthrough() }).passthrough().parse(response);
143
- return submitRunnerArgoWorkflowResultSchema.parse({
136
+ return workflowSubmitResultSchema.parse({
144
137
  namespace: options.namespace,
145
138
  payloadConfigMapName,
146
139
  scheduleConfigMapName: scheduleArtifactConfigMapName,
package/dist/config.d.ts CHANGED
@@ -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<{
@@ -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>;
@@ -3,6 +3,7 @@ import { DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST, loadPipelineConfig } from "./confi
3
3
  import { compileWorkflowPlan } from "./workflow-planner.js";
4
4
  import { renderOpenCodeGatewayConfig } from "./mcp/gateway.js";
5
5
  import { resolvePackageAssetPath } from "./package-assets.js";
6
+ import { mergeOpenCodeProjectConfig } from "./opencode-project-config.js";
6
7
  import { existsSync, readFileSync, statSync } from "node:fs";
7
8
  import { basename, dirname, join, relative } from "node:path";
8
9
  import matter from "gray-matter";
@@ -17,6 +18,7 @@ const OWNER_YAML_MARKER_PREFIX = "# @oisincoveney/pipeline:";
17
18
  const AGENTS_MD_START = "<!-- @oisincoveney/pipeline:agents:start -->";
18
19
  const AGENTS_MD_END = "<!-- @oisincoveney/pipeline:agents:end -->";
19
20
  const SINGLE_OPENCODE_PLUGIN_ARRAY_RE = /\n {2}"plugin": \[\n {4}("[^"]+")\n {2}\]/;
21
+ const OPENCODE_PROJECT_CONFIG_PATH = ".opencode/opencode.json";
20
22
  const ENTRYPOINT_PATH_PATTERNS = { opencode: [/^\.opencode\/commands\/([^/]+)\.md$/] };
21
23
  const COMMAND_HOSTS = ["opencode"];
22
24
  function header(host) {
@@ -517,6 +519,22 @@ function entrypointIdFromGeneratedPath(host, path) {
517
519
  function invocationForHost(host, entrypointId = "execute") {
518
520
  return `${{ opencode: "/" }[host]}${entrypointId} <task description>`;
519
521
  }
522
+ function resolveDefinitionContent(definition, target) {
523
+ if (definition.path !== OPENCODE_PROJECT_CONFIG_PATH || !existsSync(target)) return {
524
+ conflict: false,
525
+ content: definition.content
526
+ };
527
+ const projection = JSON.parse(definition.content);
528
+ const merged = mergeOpenCodeProjectConfig(readFileSync(target, "utf8"), projection);
529
+ if (!merged.ok) return {
530
+ conflict: true,
531
+ content: definition.content
532
+ };
533
+ return {
534
+ conflict: false,
535
+ content: merged.content
536
+ };
537
+ }
520
538
  function actionFor(path, content, force, block) {
521
539
  if (!existsSync(path)) return "create";
522
540
  const current = readFileSync(path, "utf8");
@@ -540,37 +558,90 @@ function upsertGeneratedBlock(current, content, block) {
540
558
  const separator = current.trimEnd().length > 0 ? "\n\n" : "";
541
559
  return `${current.trimEnd()}${separator}${content}`;
542
560
  }
543
- async function installCommands(options = {}) {
561
+ function installActionForDefinition(definition, target, resolved, force) {
562
+ if (resolved.conflict) return "conflict";
563
+ return actionFor(target, resolved.content, force || definition.path === OPENCODE_PROJECT_CONFIG_PATH, definition.block);
564
+ }
565
+ async function writeDefinition(definition, target, content) {
566
+ await mkdir(dirname(target), { recursive: true });
567
+ if (definition.block && existsSync(target)) {
568
+ await writeFile(target, upsertGeneratedBlock(readFileSync(target, "utf8"), content, definition.block));
569
+ return;
570
+ }
571
+ await writeFile(target, content);
572
+ }
573
+ function shouldSkipInstallWrite(options, action) {
574
+ return Boolean(options.check || options.dryRun || action === "unchanged" || action === "conflict");
575
+ }
576
+ async function installDefinition(cwd, definition, options) {
577
+ const target = join(cwd, definition.path);
578
+ const resolved = resolveDefinitionContent(definition, target);
579
+ const action = installActionForDefinition(definition, target, resolved, Boolean(options.force));
580
+ const item = commandInstallPlanItem(definition, action);
581
+ if (!shouldSkipInstallWrite(options, action)) await writeDefinition(definition, target, resolved.content);
582
+ return item;
583
+ }
584
+ function commandInstallPlanItem(definition, action) {
585
+ return {
586
+ action,
587
+ host: definition.host,
588
+ invocation: definition.invocation,
589
+ path: definition.path
590
+ };
591
+ }
592
+ function installCommandsContext(options) {
544
593
  const cwd = options.cwd ?? process.cwd();
545
594
  const host = options.host ?? "all";
546
- const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
595
+ const definitions = definitionsFor(host, loadPipelineConfig(cwd, { allowMissingLintFileReferences: true }), cwd);
596
+ return {
597
+ cwd,
598
+ definitions,
599
+ host,
600
+ wantedPaths: new Set(definitions.map((definition) => definition.path))
601
+ };
602
+ }
603
+ async function installDefinitions(cwd, definitions, options) {
547
604
  const items = [];
548
- const definitions = definitionsFor(host, config, cwd);
549
- const wantedPaths = new Set(definitions.map((definition) => definition.path));
550
- for (const definition of definitions) {
551
- const target = join(cwd, definition.path);
552
- const action = actionFor(target, definition.content, Boolean(options.force), definition.block);
553
- items.push({
554
- action,
555
- host: definition.host,
556
- invocation: definition.invocation,
557
- path: definition.path
558
- });
559
- if (options.check || options.dryRun || action === "unchanged") continue;
560
- if (action === "conflict") continue;
561
- await mkdir(dirname(target), { recursive: true });
562
- if (definition.block && existsSync(target)) await writeFile(target, upsertGeneratedBlock(readFileSync(target, "utf8"), definition.content, definition.block));
563
- else await writeFile(target, definition.content);
564
- }
565
- const obsoleteItems = await obsoleteGeneratedItems(cwd, host, wantedPaths);
566
- items.push(...obsoleteItems);
567
- if (!(options.check || options.dryRun)) for (const item of obsoleteItems) await rm(join(cwd, item.path), { force: true });
568
- if (!options.dryRun && items.some((item) => item.action === "conflict")) throw new Error([
605
+ for (const definition of definitions) items.push(await installDefinition(cwd, definition, options));
606
+ return items;
607
+ }
608
+ function shouldRemoveObsoleteItems(options) {
609
+ return !(options.check || options.dryRun);
610
+ }
611
+ async function removeObsoleteItems(cwd, items, options) {
612
+ if (!shouldRemoveObsoleteItems(options)) return;
613
+ for (const item of items) await rm(join(cwd, item.path), { force: true });
614
+ }
615
+ function actionIsConflict(item) {
616
+ return item.action === "conflict";
617
+ }
618
+ function actionIsChanged(item) {
619
+ return item.action !== "unchanged";
620
+ }
621
+ function assertNoInstallConflicts(options, items) {
622
+ if (options.dryRun) return;
623
+ const conflicts = items.filter(actionIsConflict);
624
+ if (conflicts.length === 0) return;
625
+ throw new Error([
569
626
  "Refusing to overwrite manually edited command files.",
570
- ...items.filter((item) => item.action === "conflict").map((item) => `- ${item.path}`),
627
+ ...conflicts.map((item) => `- ${item.path}`),
571
628
  "Re-run with --force to overwrite them."
572
629
  ].join("\n"));
573
- if (options.check && items.some((item) => item.action !== "unchanged")) throw new Error(["Installed command files are not up to date.", ...items.filter((item) => item.action !== "unchanged").map((item) => `- ${item.path}: ${item.action}`)].join("\n"));
630
+ }
631
+ function assertInstallCheckCurrent(options, items) {
632
+ if (!options.check) return;
633
+ const changedItems = items.filter(actionIsChanged);
634
+ if (changedItems.length === 0) return;
635
+ throw new Error(["Installed command files are not up to date.", ...changedItems.map((item) => `- ${item.path}: ${item.action}`)].join("\n"));
636
+ }
637
+ async function installCommands(options = {}) {
638
+ const context = installCommandsContext(options);
639
+ const items = await installDefinitions(context.cwd, context.definitions, options);
640
+ const obsoleteItems = await obsoleteGeneratedItems(context.cwd, context.host, context.wantedPaths);
641
+ items.push(...obsoleteItems);
642
+ await removeObsoleteItems(context.cwd, obsoleteItems, options);
643
+ assertNoInstallConflicts(options, items);
644
+ assertInstallCheckCurrent(options, items);
574
645
  return { items };
575
646
  }
576
647
  function parseCommandHost(value) {
@@ -0,0 +1,160 @@
1
+ import { PipelineConfig } from "./config.js";
2
+ import { generateScheduleArtifact } from "./schedule-planner.js";
3
+ import { z } from "zod";
4
+
5
+ //#region src/moka-submit.d.ts
6
+ declare const mokaSubmitResultSchema: z.ZodObject<{
7
+ namespace: z.ZodString;
8
+ payloadConfigMapName: z.ZodString;
9
+ scheduleConfigMapName: z.ZodString;
10
+ taskDescriptorConfigMapName: z.ZodString;
11
+ workflowName: z.ZodString;
12
+ workflowUid: z.ZodOptional<z.ZodString>;
13
+ }, z.core.$strict>;
14
+ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
15
+ delivery: z.ZodDefault<z.ZodObject<{
16
+ pullRequest: z.ZodDefault<z.ZodBoolean>;
17
+ }, z.core.$strict>>;
18
+ eventAuthSecretKey: z.ZodDefault<z.ZodString>;
19
+ eventAuthSecretName: z.ZodDefault<z.ZodString>;
20
+ eventUrl: z.ZodDefault<z.ZodString>;
21
+ events: z.ZodOptional<z.ZodObject<{
22
+ authHeader: z.ZodDefault<z.ZodString>;
23
+ authTokenFile: z.ZodOptional<z.ZodString>;
24
+ url: z.ZodString;
25
+ }, z.core.$strict>>;
26
+ generateName: z.ZodOptional<z.ZodString>;
27
+ githubAuthSecretName: z.ZodDefault<z.ZodString>;
28
+ image: z.ZodOptional<z.ZodString>;
29
+ imagePullPolicy: z.ZodDefault<z.ZodEnum<{
30
+ Always: "Always";
31
+ IfNotPresent: "IfNotPresent";
32
+ Never: "Never";
33
+ }>>;
34
+ imagePullSecretName: z.ZodDefault<z.ZodString>;
35
+ kubeconfigPath: z.ZodOptional<z.ZodString>;
36
+ name: z.ZodOptional<z.ZodString>;
37
+ namespace: z.ZodDefault<z.ZodString>;
38
+ opencodeAuthSecretName: z.ZodDefault<z.ZodString>;
39
+ queueName: z.ZodDefault<z.ZodString>;
40
+ repository: z.ZodOptional<z.ZodObject<{
41
+ baseBranch: z.ZodString;
42
+ sha: z.ZodOptional<z.ZodString>;
43
+ url: z.ZodString;
44
+ }, z.core.$strict>>;
45
+ run: z.ZodOptional<z.ZodObject<{
46
+ id: z.ZodString;
47
+ project: z.ZodString;
48
+ requestedBy: z.ZodOptional<z.ZodString>;
49
+ }, z.core.$strict>>;
50
+ serviceAccountName: z.ZodDefault<z.ZodString>;
51
+ mode: z.ZodEnum<{
52
+ full: "full";
53
+ quick: "quick";
54
+ }>;
55
+ schedulePath: z.ZodOptional<z.ZodString>;
56
+ scheduleYaml: z.ZodOptional<z.ZodString>;
57
+ task: z.ZodUnion<readonly [z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
58
+ kind: z.ZodLiteral<"prompt">;
59
+ prompt: z.ZodString;
60
+ title: z.ZodOptional<z.ZodString>;
61
+ }, z.core.$strict>, z.ZodObject<{
62
+ id: z.ZodString;
63
+ kind: z.ZodLiteral<"ticket">;
64
+ path: z.ZodOptional<z.ZodString>;
65
+ title: z.ZodOptional<z.ZodString>;
66
+ }, z.core.$strict>], "kind">]>;
67
+ type: z.ZodLiteral<"graph">;
68
+ }, z.core.$strict>, z.ZodObject<{
69
+ delivery: z.ZodDefault<z.ZodObject<{
70
+ pullRequest: z.ZodDefault<z.ZodBoolean>;
71
+ }, z.core.$strict>>;
72
+ eventAuthSecretKey: z.ZodDefault<z.ZodString>;
73
+ eventAuthSecretName: z.ZodDefault<z.ZodString>;
74
+ eventUrl: z.ZodDefault<z.ZodString>;
75
+ events: z.ZodOptional<z.ZodObject<{
76
+ authHeader: z.ZodDefault<z.ZodString>;
77
+ authTokenFile: z.ZodOptional<z.ZodString>;
78
+ url: z.ZodString;
79
+ }, z.core.$strict>>;
80
+ generateName: z.ZodOptional<z.ZodString>;
81
+ githubAuthSecretName: z.ZodDefault<z.ZodString>;
82
+ image: z.ZodOptional<z.ZodString>;
83
+ imagePullPolicy: z.ZodDefault<z.ZodEnum<{
84
+ Always: "Always";
85
+ IfNotPresent: "IfNotPresent";
86
+ Never: "Never";
87
+ }>>;
88
+ imagePullSecretName: z.ZodDefault<z.ZodString>;
89
+ kubeconfigPath: z.ZodOptional<z.ZodString>;
90
+ name: z.ZodOptional<z.ZodString>;
91
+ namespace: z.ZodDefault<z.ZodString>;
92
+ opencodeAuthSecretName: z.ZodDefault<z.ZodString>;
93
+ queueName: z.ZodDefault<z.ZodString>;
94
+ repository: z.ZodOptional<z.ZodObject<{
95
+ baseBranch: z.ZodString;
96
+ sha: z.ZodOptional<z.ZodString>;
97
+ url: z.ZodString;
98
+ }, z.core.$strict>>;
99
+ run: z.ZodOptional<z.ZodObject<{
100
+ id: z.ZodString;
101
+ project: z.ZodString;
102
+ requestedBy: z.ZodOptional<z.ZodString>;
103
+ }, z.core.$strict>>;
104
+ serviceAccountName: z.ZodDefault<z.ZodString>;
105
+ commandArgv: z.ZodArray<z.ZodString>;
106
+ task: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
107
+ kind: z.ZodLiteral<"prompt">;
108
+ prompt: z.ZodString;
109
+ title: z.ZodOptional<z.ZodString>;
110
+ }, z.core.$strict>, z.ZodObject<{
111
+ id: z.ZodString;
112
+ kind: z.ZodLiteral<"ticket">;
113
+ path: z.ZodOptional<z.ZodString>;
114
+ title: z.ZodOptional<z.ZodString>;
115
+ }, z.core.$strict>], "kind">]>>;
116
+ type: z.ZodLiteral<"command">;
117
+ }, z.core.$strict>], "type">;
118
+ type MokaSubmitOptionsInput = z.input<typeof mokaSubmitOptionsSchema>;
119
+ type MokaSubmitOptionsOutput = z.output<typeof mokaSubmitOptionsSchema>;
120
+ type MokaSubmitInput = MokaSubmitOptionsInput & {
121
+ config: PipelineConfig;
122
+ worktreePath?: string;
123
+ };
124
+ type MokaSubmitOptions = MokaSubmitInput;
125
+ type MokaSubmitOutput = z.output<typeof mokaSubmitResultSchema>;
126
+ type MokaSubmitResult = MokaSubmitOutput;
127
+ interface MokaGitContext {
128
+ baseBranch: string;
129
+ project: string;
130
+ sha: string;
131
+ url: string;
132
+ }
133
+ interface SubmitMokaDependencies {
134
+ generateRunId?: () => string;
135
+ generateSchedule?: typeof generateScheduleArtifact;
136
+ readFile?: (path: string) => string;
137
+ resolveGitContext?: (worktreePath: string) => Promise<MokaGitContext>;
138
+ submitWorkflow?: (options: MokaWorkflowSubmitOptions) => Promise<MokaSubmitOutput>;
139
+ }
140
+ interface MokaWorkflowSubmitOptions {
141
+ config: PipelineConfig;
142
+ eventAuthSecretKey?: string;
143
+ eventAuthSecretName?: string;
144
+ generateName?: string;
145
+ githubAuthSecretName?: string;
146
+ image?: string;
147
+ imagePullPolicy?: "Always" | "IfNotPresent" | "Never";
148
+ imagePullSecretName?: string;
149
+ kubeconfigPath?: string;
150
+ name?: string;
151
+ namespace?: string;
152
+ opencodeAuthSecretName?: string;
153
+ payloadJson: string;
154
+ queueName?: string;
155
+ scheduleYaml: string;
156
+ serviceAccountName?: string;
157
+ }
158
+ declare function submitMoka(rawOptions: MokaSubmitInput, dependencies?: SubmitMokaDependencies): Promise<MokaSubmitOutput>;
159
+ //#endregion
160
+ export { MokaSubmitInput, MokaSubmitOptions, MokaSubmitOptionsInput, MokaSubmitOptionsOutput, MokaSubmitOutput, MokaSubmitResult, mokaSubmitOptionsSchema, mokaSubmitResultSchema, submitMoka };
@@ -1,5 +1,6 @@
1
- import { buildRunnerCommandPayload } from "./runner-command-contract.js";
1
+ import { buildRunnerCommandPayload, runnerDeliverySchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskSchema } from "./runner-command-contract.js";
2
2
  import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "./schedule-planner.js";
3
+ import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
3
4
  import { buildCommandScheduleYaml, submitRunnerArgoWorkflow } from "./argo-submit.js";
4
5
  import { z } from "zod";
5
6
  import { readFileSync } from "node:fs";
@@ -21,27 +22,43 @@ const imagePullPolicySchema = z.enum([
21
22
  "IfNotPresent",
22
23
  "Never"
23
24
  ]).default("Always");
25
+ const mokaSubmitTaskInputSchema = z.union([z.string().min(1), runnerTaskSchema]);
26
+ const mokaSubmitEventsSchema = z.object({
27
+ authHeader: z.string().min(1).default("Authorization"),
28
+ authTokenFile: z.string().min(1).optional(),
29
+ url: z.string().url()
30
+ }).strict();
31
+ const mokaSubmitResultSchema = workflowSubmitResultSchema;
24
32
  const mokaSubmitBaseOptionsSchema = z.object({
33
+ delivery: runnerDeliverySchema.default({ pullRequest: false }),
34
+ eventAuthSecretKey: z.string().min(1).default(MOMOKAYA_EVENT_AUTH_SECRET_KEY),
35
+ eventAuthSecretName: z.string().min(1).default(MOMOKAYA_EVENT_AUTH_SECRET_NAME),
25
36
  eventUrl: z.string().url().default(MOMOKAYA_EVENT_URL),
37
+ events: mokaSubmitEventsSchema.optional(),
26
38
  generateName: z.string().min(1).optional(),
39
+ githubAuthSecretName: z.string().min(1).default(MOMOKAYA_GITHUB_AUTH_SECRET_NAME),
27
40
  image: z.string().min(1).optional(),
28
41
  imagePullPolicy: imagePullPolicySchema,
29
42
  imagePullSecretName: z.string().min(1).default(MOMOKAYA_IMAGE_PULL_SECRET_NAME),
30
43
  kubeconfigPath: z.string().min(1).optional(),
31
44
  name: z.string().min(1).optional(),
32
45
  namespace: z.string().min(1).default("momokaya-pipeline"),
46
+ opencodeAuthSecretName: z.string().min(1).default(MOMOKAYA_OPENCODE_AUTH_SECRET_NAME),
33
47
  queueName: z.string().min(1).default(MOMOKAYA_QUEUE_NAME),
48
+ repository: runnerRepositoryContextSchema.optional(),
49
+ run: runnerRunIdentitySchema.optional(),
34
50
  serviceAccountName: z.string().min(1).default(MOMOKAYA_RUNNER_SERVICE_ACCOUNT_NAME)
35
51
  }).strict();
36
52
  const mokaGraphSubmitOptionsSchema = mokaSubmitBaseOptionsSchema.extend({
37
53
  mode: z.enum(["full", "quick"]),
38
54
  schedulePath: z.string().min(1).optional(),
39
- task: z.string().min(1),
55
+ scheduleYaml: z.string().min(1).optional(),
56
+ task: mokaSubmitTaskInputSchema,
40
57
  type: z.literal("graph")
41
58
  }).strict();
42
59
  const mokaCommandSubmitOptionsSchema = mokaSubmitBaseOptionsSchema.extend({
43
60
  commandArgv: z.array(z.string().min(1)).min(1),
44
- task: z.string().min(1).optional(),
61
+ task: mokaSubmitTaskInputSchema.optional(),
45
62
  type: z.literal("command")
46
63
  }).strict();
47
64
  const mokaSubmitOptionsSchema = z.discriminatedUnion("type", [mokaGraphSubmitOptionsSchema, mokaCommandSubmitOptionsSchema]);
@@ -54,47 +71,59 @@ function submitMoka(rawOptions, dependencies = {}) {
54
71
  };
55
72
  return parsedOptions.type === "command" ? submitMokaCommand(parsedOptions, dependencies) : submitMokaGraph(parsedOptions, dependencies);
56
73
  }
74
+ function resolveSubmitWorkflow(dependencies) {
75
+ return dependencies.submitWorkflow ?? submitRunnerArgoWorkflow;
76
+ }
77
+ function submitRunId(options, dependencies) {
78
+ return options.run?.id ?? generateRunId(dependencies);
79
+ }
80
+ function commandTask(options) {
81
+ if (options.task) return normalizeTask(options.task);
82
+ return normalizeTask(options.commandArgv.join(" "));
83
+ }
57
84
  async function submitMokaGraph(options, dependencies) {
58
- const git = await resolveGit(options.worktreePath, dependencies);
59
- const submitWorkflow = dependencies.submitWorkflow ?? submitRunnerArgoWorkflow;
60
- const runId = generateRunId(dependencies);
61
- const scheduleYaml = await graphScheduleYaml(options, dependencies, runId);
85
+ const submitWorkflow = resolveSubmitWorkflow(dependencies);
86
+ const runId = submitRunId(options, dependencies);
87
+ const task = normalizeTask(options.task);
88
+ const context = await resolveSubmissionContext(options, dependencies, runId);
89
+ const scheduleYaml = await graphScheduleYaml(options, dependencies, runId, taskDescription(task));
62
90
  const workflowId = scheduleWorkflowId(options, scheduleYaml);
63
- return submitWorkflow({
91
+ const result = await submitWorkflow({
64
92
  ...workflowSubmitOptions(options),
65
93
  config: options.config,
66
94
  generateName: options.generateName ?? `moka-${options.mode}-`,
67
95
  payloadJson: runnerPayloadJson({
68
- git,
96
+ context,
69
97
  options,
70
98
  submission: {
71
99
  kind: "graph",
72
100
  mode: options.mode
73
101
  },
74
- task: options.task,
102
+ task,
75
103
  runId,
76
104
  workflowId
77
105
  }),
78
106
  scheduleYaml
79
107
  });
108
+ return mokaSubmitResultSchema.parse(result);
80
109
  }
81
110
  async function submitMokaCommand(options, dependencies) {
82
- const git = await resolveGit(options.worktreePath, dependencies);
83
- const task = options.task ?? options.commandArgv.join(" ");
84
- const submitWorkflow = dependencies.submitWorkflow ?? submitRunnerArgoWorkflow;
85
- const runId = generateRunId(dependencies);
111
+ const runId = submitRunId(options, dependencies);
112
+ const task = commandTask(options);
113
+ const context = await resolveSubmissionContext(options, dependencies, runId);
114
+ const submitWorkflow = resolveSubmitWorkflow(dependencies);
86
115
  const scheduleYaml = buildCommandScheduleYaml({
87
116
  command: options.commandArgv,
88
117
  scheduleId: runId,
89
- task
118
+ task: taskDescription(task)
90
119
  });
91
120
  const workflowId = scheduleWorkflowId(options, scheduleYaml);
92
- return submitWorkflow({
121
+ const result = await submitWorkflow({
93
122
  ...workflowSubmitOptions(options),
94
123
  config: options.config,
95
124
  generateName: options.generateName ?? "moka-command-",
96
125
  payloadJson: runnerPayloadJson({
97
- git,
126
+ context,
98
127
  options,
99
128
  submission: {
100
129
  argv: options.commandArgv,
@@ -106,66 +135,129 @@ async function submitMokaCommand(options, dependencies) {
106
135
  }),
107
136
  scheduleYaml
108
137
  });
138
+ return mokaSubmitResultSchema.parse(result);
109
139
  }
110
- async function graphScheduleYaml(options, dependencies, runId) {
111
- const readFile = dependencies.readFile ?? ((path) => readFileSync(path, "utf8"));
112
- if (options.schedulePath) return readFile(options.schedulePath);
113
- const schedule = await (dependencies.generateSchedule ?? generateScheduleArtifact)({
140
+ async function graphScheduleYaml(options, dependencies, runId, task) {
141
+ const explicitScheduleYaml = readExplicitGraphScheduleYaml(options, dependencies);
142
+ if (explicitScheduleYaml) return explicitScheduleYaml;
143
+ const worktreePath = requireScheduleWorktreePath(options);
144
+ return readScheduleFile(dependencies, resolve(worktreePath, (await (dependencies.generateSchedule ?? generateScheduleArtifact)({
114
145
  config: options.config,
115
146
  entrypointId: options.mode === "quick" ? "quick" : "execute",
116
147
  runId,
117
- task: options.task,
118
- worktreePath: options.worktreePath
119
- });
120
- return readFile(resolve(options.worktreePath, schedule.path));
148
+ task,
149
+ worktreePath
150
+ })).path));
151
+ }
152
+ function readExplicitGraphScheduleYaml(options, dependencies) {
153
+ if (options.scheduleYaml) return options.scheduleYaml;
154
+ if (options.schedulePath) return readScheduleFile(dependencies, options.schedulePath);
155
+ return null;
156
+ }
157
+ function readScheduleFile(dependencies, path) {
158
+ return (dependencies.readFile ?? ((filePath) => readFileSync(filePath, "utf8")))(path);
159
+ }
160
+ function requireScheduleWorktreePath(options) {
161
+ if (!options.worktreePath) throw new Error("worktreePath is required when moka submit generates a graph schedule");
162
+ return options.worktreePath;
121
163
  }
122
164
  function scheduleWorkflowId(options, scheduleYaml) {
123
165
  return compileScheduleArtifact(options.config, parseScheduleArtifact(scheduleYaml, "schedule.yaml"), options.worktreePath).workflowId;
124
166
  }
125
167
  function workflowSubmitOptions(options) {
126
168
  return {
127
- eventAuthSecretKey: MOMOKAYA_EVENT_AUTH_SECRET_KEY,
128
- eventAuthSecretName: MOMOKAYA_EVENT_AUTH_SECRET_NAME,
129
- githubAuthSecretName: MOMOKAYA_GITHUB_AUTH_SECRET_NAME,
169
+ eventAuthSecretKey: options.eventAuthSecretKey,
170
+ eventAuthSecretName: options.eventAuthSecretName,
171
+ githubAuthSecretName: options.githubAuthSecretName,
130
172
  image: options.image,
131
173
  imagePullPolicy: options.imagePullPolicy,
132
174
  imagePullSecretName: options.imagePullSecretName,
133
175
  kubeconfigPath: options.kubeconfigPath,
134
176
  name: options.name,
135
177
  namespace: options.namespace,
136
- opencodeAuthSecretName: MOMOKAYA_OPENCODE_AUTH_SECRET_NAME,
178
+ opencodeAuthSecretName: options.opencodeAuthSecretName,
137
179
  queueName: options.queueName,
138
180
  serviceAccountName: options.serviceAccountName
139
181
  };
140
182
  }
141
183
  function runnerPayloadJson(input) {
142
184
  return JSON.stringify(buildRunnerCommandPayload({
143
- delivery: { pullRequest: false },
185
+ delivery: input.options.delivery,
144
186
  events: runnerEvents(input.options),
145
187
  repository: {
146
- baseBranch: input.git.baseBranch,
147
- sha: input.git.sha,
148
- url: input.git.url
188
+ baseBranch: input.context.repository.baseBranch,
189
+ sha: input.context.repository.sha,
190
+ url: input.context.repository.url
149
191
  },
150
192
  run: {
151
193
  id: input.runId,
152
- project: input.git.project
194
+ project: input.context.run.project,
195
+ requestedBy: input.context.run.requestedBy
153
196
  },
154
197
  submission: input.submission,
155
- task: {
156
- kind: "prompt",
157
- prompt: input.task
158
- },
198
+ task: input.task,
159
199
  workflow: { id: input.workflowId }
160
200
  }));
161
201
  }
162
202
  function runnerEvents(options) {
203
+ if (options.events) return {
204
+ authHeader: options.events.authHeader,
205
+ authTokenFile: options.events.authTokenFile ?? eventAuthTokenFile(options),
206
+ url: options.events.url
207
+ };
163
208
  return {
164
209
  authHeader: "Authorization",
165
- authTokenFile: `/etc/pipeline/event-auth/${MOMOKAYA_EVENT_AUTH_SECRET_KEY}`,
210
+ authTokenFile: eventAuthTokenFile(options),
166
211
  url: options.eventUrl
167
212
  };
168
213
  }
214
+ function eventAuthTokenFile(options) {
215
+ return `/etc/pipeline/event-auth/${options.eventAuthSecretKey}`;
216
+ }
217
+ async function resolveSubmissionContext(options, dependencies, runId) {
218
+ const explicitContext = explicitSubmissionContext(options);
219
+ if (explicitContext) return explicitContext;
220
+ const git = await resolveRequiredGit(options, dependencies);
221
+ return {
222
+ repository: repositoryContext(options, git),
223
+ run: runContext(options, git, runId)
224
+ };
225
+ }
226
+ function explicitSubmissionContext(options) {
227
+ if (!(options.repository && options.run)) return null;
228
+ return {
229
+ repository: options.repository,
230
+ run: options.run
231
+ };
232
+ }
233
+ function resolveRequiredGit(options, dependencies) {
234
+ if (!options.worktreePath) throw new Error("worktreePath is required when moka submit must resolve repository or run context");
235
+ return resolveGit(options.worktreePath, dependencies);
236
+ }
237
+ function repositoryContext(options, git) {
238
+ return options.repository ?? {
239
+ baseBranch: git.baseBranch,
240
+ sha: git.sha,
241
+ url: git.url
242
+ };
243
+ }
244
+ function runContext(options, git, runId) {
245
+ return options.run ?? {
246
+ id: runId,
247
+ project: git.project
248
+ };
249
+ }
250
+ function normalizeTask(task) {
251
+ if (typeof task === "string") return {
252
+ kind: "prompt",
253
+ prompt: task
254
+ };
255
+ return task;
256
+ }
257
+ function taskDescription(task) {
258
+ if (task.kind === "prompt") return task.prompt;
259
+ return task.title ? `${task.id} ${task.title}` : task.id;
260
+ }
169
261
  async function resolveGit(worktreePath, dependencies) {
170
262
  if (dependencies.resolveGitContext) return dependencies.resolveGitContext(worktreePath);
171
263
  const git = simpleGit({ baseDir: worktreePath });
@@ -187,4 +279,4 @@ function generateRunId(dependencies) {
187
279
  return dependencies.generateRunId?.() ?? `run-${randomBytes(8).toString("hex")}`;
188
280
  }
189
281
  //#endregion
190
- export { submitMoka };
282
+ export { mokaSubmitOptionsSchema, mokaSubmitResultSchema, submitMoka };
@@ -0,0 +1,84 @@
1
+ import { applyEdits, modify, parse } from "jsonc-parser";
2
+ //#region src/opencode-project-config.ts
3
+ const JSON_FORMAT_OPTIONS = {
4
+ insertSpaces: true,
5
+ tabSize: 2
6
+ };
7
+ function mergeOpenCodeProjectConfig(currentText, projection) {
8
+ if (currentText === void 0) return {
9
+ content: formatJson(projection),
10
+ ok: true
11
+ };
12
+ const parsed = parseOpenCodeProjectConfig(currentText);
13
+ if (!parsed.ok) return parsed;
14
+ return {
15
+ content: ensureTrailingNewline(applyOpenCodeProjection(currentText, parsed.value, projection)),
16
+ ok: true
17
+ };
18
+ }
19
+ function parseOpenCodeProjectConfig(currentText) {
20
+ const errors = [];
21
+ const value = parse(currentText, errors, {
22
+ allowTrailingComma: true,
23
+ disallowComments: false
24
+ });
25
+ if (errors.length > 0 || !isRecord(value)) return {
26
+ errors,
27
+ ok: false
28
+ };
29
+ return {
30
+ ok: true,
31
+ value
32
+ };
33
+ }
34
+ function applyOpenCodeProjection(currentText, parsed, projection) {
35
+ return applyPluginProjection(applyMcpProjection(setIfMissing(setIfMissing(currentText, parsed, ["$schema"], projection.$schema), parsed, ["lsp"], projection.lsp), parsed, projection), parsed, projection);
36
+ }
37
+ function applyMcpProjection(content, parsed, projection) {
38
+ return Object.entries(projection.mcp ?? {}).reduce((nextContent, [name, server]) => setIfMissing(nextContent, parsed, ["mcp", name], server), content);
39
+ }
40
+ function applyPluginProjection(content, parsed, projection) {
41
+ const plugins = mergePluginEntries(parsed.plugin, projection.plugin ?? []);
42
+ return plugins.length > 0 ? applyJsonEdit(content, ["plugin"], plugins) : content;
43
+ }
44
+ function setIfMissing(content, parsed, path, value) {
45
+ if (value === void 0 || hasPath(parsed, path)) return content;
46
+ return applyJsonEdit(content, path, value);
47
+ }
48
+ function hasPath(value, path) {
49
+ let cursor = value;
50
+ for (const segment of path) {
51
+ if (!(isRecord(cursor) && segment in cursor)) return false;
52
+ cursor = cursor[segment];
53
+ }
54
+ return true;
55
+ }
56
+ function mergePluginEntries(existing, projected) {
57
+ const merged = Array.isArray(existing) ? [...existing] : [];
58
+ const seen = new Set(merged.map(pluginKey));
59
+ for (const plugin of projected) {
60
+ const key = pluginKey(plugin);
61
+ if (seen.has(key)) continue;
62
+ merged.push(plugin);
63
+ seen.add(key);
64
+ }
65
+ return merged;
66
+ }
67
+ function pluginKey(value) {
68
+ if (Array.isArray(value)) return typeof value[0] === "string" ? value[0] : JSON.stringify(value);
69
+ return typeof value === "string" ? value : JSON.stringify(value);
70
+ }
71
+ function applyJsonEdit(content, path, value) {
72
+ return applyEdits(content, modify(content, path, value, { formattingOptions: JSON_FORMAT_OPTIONS }));
73
+ }
74
+ function formatJson(value) {
75
+ return `${JSON.stringify(value, null, 2)}\n`;
76
+ }
77
+ function ensureTrailingNewline(value) {
78
+ return value.endsWith("\n") ? value : `${value}\n`;
79
+ }
80
+ function isRecord(value) {
81
+ return typeof value === "object" && value !== null && !Array.isArray(value);
82
+ }
83
+ //#endregion
84
+ export { mergeOpenCodeProjectConfig };
@@ -2,6 +2,7 @@ import { PipelineRuntimeEvent } from "./runtime/contracts/contracts.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/runner-command-contract.d.ts
5
+ declare const gitRemoteUrlSchema: z.ZodString;
5
6
  declare const runnerRunIdentitySchema: z.ZodObject<{
6
7
  id: z.ZodString;
7
8
  project: z.ZodString;
@@ -10,6 +11,17 @@ declare const runnerRunIdentitySchema: z.ZodObject<{
10
11
  declare const runnerWorkflowIdentitySchema: z.ZodObject<{
11
12
  id: z.ZodString;
12
13
  }, z.core.$strict>;
14
+ declare const runnerTaskPromptSchema: z.ZodObject<{
15
+ kind: z.ZodLiteral<"prompt">;
16
+ prompt: z.ZodString;
17
+ title: z.ZodOptional<z.ZodString>;
18
+ }, z.core.$strict>;
19
+ declare const runnerTaskTicketSchema: z.ZodObject<{
20
+ id: z.ZodString;
21
+ kind: z.ZodLiteral<"ticket">;
22
+ path: z.ZodOptional<z.ZodString>;
23
+ title: z.ZodOptional<z.ZodString>;
24
+ }, z.core.$strict>;
13
25
  declare const runnerTaskSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
14
26
  kind: z.ZodLiteral<"prompt">;
15
27
  prompt: z.ZodString;
@@ -31,8 +43,8 @@ declare const runnerDeliverySchema: z.ZodObject<{
31
43
  declare const mokaSubmissionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
32
44
  kind: z.ZodLiteral<"graph">;
33
45
  mode: z.ZodEnum<{
34
- quick: "quick";
35
46
  full: "full";
47
+ quick: "quick";
36
48
  }>;
37
49
  }, z.core.$strict>, z.ZodObject<{
38
50
  argv: z.ZodArray<z.ZodString>;
@@ -76,8 +88,8 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
76
88
  submission: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
77
89
  kind: z.ZodLiteral<"graph">;
78
90
  mode: z.ZodEnum<{
79
- quick: "quick";
80
91
  full: "full";
92
+ quick: "quick";
81
93
  }>;
82
94
  }, z.core.$strict>, z.ZodObject<{
83
95
  argv: z.ZodArray<z.ZodString>;
@@ -259,4 +271,4 @@ declare function buildRunnerCommandPayload(options: BuildRunnerCommandPayloadOpt
259
271
  declare function parseRunnerCommandPayload(rawPayload: string): RunnerCommandPayload;
260
272
  declare function mapRuntimeEventToRunnerEventRecords(event: PipelineRuntimeEvent, context: RunnerEventMappingContext): RunnerEventRecord[];
261
273
  //#endregion
262
- export { BuildRunnerCommandPayloadOptions, MokaSubmission, ResolveRunnerEventSinkAuthTokenOptions, RunnerArtifactDetails, RunnerArtifactStatus, RunnerCommandPayload, RunnerCommandPayloadValidationError, RunnerCommandPayloadValidationIssue, RunnerDelivery, RunnerEventMappingContext, RunnerEventRecord, RunnerEvents, RunnerFinalResultDetails, RunnerGateDetails, RunnerGateStatus, RunnerHookResultDetails, RunnerLogDetails, RunnerLogLevel, RunnerMomokayaContext, RunnerNodeDetails, RunnerNodeStatus, RunnerRepositoryContext, RunnerRunIdentity, RunnerTask, RunnerWorkflowEdgeDetails, RunnerWorkflowEdgeRecordDetails, RunnerWorkflowIdentity, RunnerWorkflowNodeDetails, RunnerWorkflowPlanDetails, buildRunnerCommandPayload, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema };
274
+ export { BuildRunnerCommandPayloadOptions, MokaSubmission, ResolveRunnerEventSinkAuthTokenOptions, RunnerArtifactDetails, RunnerArtifactStatus, RunnerCommandPayload, RunnerCommandPayloadValidationError, RunnerCommandPayloadValidationIssue, RunnerDelivery, RunnerEventMappingContext, RunnerEventRecord, RunnerEvents, RunnerFinalResultDetails, RunnerGateDetails, RunnerGateStatus, RunnerHookResultDetails, RunnerLogDetails, RunnerLogLevel, RunnerMomokayaContext, RunnerNodeDetails, RunnerNodeStatus, RunnerRepositoryContext, RunnerRunIdentity, RunnerTask, RunnerWorkflowEdgeDetails, RunnerWorkflowEdgeRecordDetails, RunnerWorkflowIdentity, RunnerWorkflowNodeDetails, RunnerWorkflowPlanDetails, buildRunnerCommandPayload, gitRemoteUrlSchema, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema, runnerDeliverySchema, runnerEventsSchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskPromptSchema, runnerTaskSchema, runnerTaskTicketSchema, runnerWorkflowIdentitySchema };
@@ -433,4 +433,4 @@ function throwUnhandledRuntimeEvent(value) {
433
433
  throw new Error(`Unhandled runtime event: ${value.type}`);
434
434
  }
435
435
  //#endregion
436
- export { RunnerCommandPayloadValidationError, buildRunnerCommandPayload, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema };
436
+ export { RunnerCommandPayloadValidationError, buildRunnerCommandPayload, gitRemoteUrlSchema, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema, runnerDeliverySchema, runnerEventsSchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskPromptSchema, runnerTaskSchema, runnerTaskTicketSchema, runnerWorkflowIdentitySchema };
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/workflow-submit-contract.d.ts
4
+ declare const workflowSubmitResultSchema: z.ZodObject<{
5
+ namespace: z.ZodString;
6
+ payloadConfigMapName: z.ZodString;
7
+ scheduleConfigMapName: z.ZodString;
8
+ taskDescriptorConfigMapName: z.ZodString;
9
+ workflowName: z.ZodString;
10
+ workflowUid: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$strict>;
12
+ //#endregion
13
+ export { workflowSubmitResultSchema };
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ //#region src/workflow-submit-contract.ts
3
+ const workflowSubmitResultSchema = z.object({
4
+ namespace: z.string().min(1),
5
+ payloadConfigMapName: z.string().min(1),
6
+ scheduleConfigMapName: z.string().min(1),
7
+ taskDescriptorConfigMapName: z.string().min(1),
8
+ workflowName: z.string().min(1),
9
+ workflowUid: z.string().min(1).optional()
10
+ }).strict();
11
+ //#endregion
12
+ export { workflowSubmitResultSchema };
@@ -94,8 +94,14 @@ contains only `pipeline-gateway`.
94
94
  OpenCode receives that gateway through `.opencode/opencode.json` alongside the
95
95
  package-owned runtime projection: `lsp: true`, pinned plugin entries, generated
96
96
  agents, projected skills, explicit permissions, and local TypeScript plugins.
97
- Those resources give OpenCode richer runtime assistance without changing MCP
98
- ownership: upstream servers still live behind the gateway.
97
+ `moka init` and `moka install-commands` merge this OpenCode project config:
98
+ existing repo-local plugin entries are preserved while missing package defaults
99
+ such as `oc-codex-multi-auth` are appended. Existing `mcp.pipeline-gateway`
100
+ settings are also preserved; use `moka mcp gateway configure-host` when the
101
+ host MCP config must be deliberately rewritten. Restart OpenCode after config
102
+ changes because it loads config at startup. These resources give OpenCode richer
103
+ runtime assistance without changing MCP ownership: upstream servers still live
104
+ behind the gateway.
99
105
 
100
106
  ## Candidate Gateway Implementations
101
107
 
@@ -112,7 +118,9 @@ local ToolHive availability for local mode, and legacy direct MCP entries. Use
112
118
  singleton `pipeline-gateway` remote entry. Use `moka install-commands --host all`
113
119
  to refresh generated host files after package upgrades, and use
114
120
  `moka mcp gateway configure-host` as an explicit migration or repair command
115
- when existing host MCP config must be rewritten with a backup.
121
+ when existing host MCP config must be rewritten with a backup. The hosted gateway
122
+ requires `PIPELINE_MCP_GATEWAY_AUTHORIZATION` to be set in the OpenCode
123
+ environment.
116
124
 
117
125
  The package-owned MCP inventory exposed through the ecosystem manifest includes
118
126
  `pipeline-gateway`, Context7, uidotsh, Qdrant, Fallow, Serena, Backlog, GitHub,
@@ -342,10 +342,15 @@ moka mcp gateway local-status
342
342
  moka mcp gateway local-start
343
343
  ```
344
344
 
345
- `moka init` writes generated command surfaces and the singleton gateway server
346
- into project host config. Use `moka mcp gateway configure-host` as an explicit
347
- migration or repair command when direct upstream MCP entries need to be removed
348
- from existing host config with a backup.
345
+ `moka init` writes generated command surfaces and merges the singleton gateway
346
+ server into project host config. For OpenCode, existing repo-local plugin entries
347
+ are preserved while missing package defaults such as `oc-codex-multi-auth` are
348
+ appended, and an existing `mcp.pipeline-gateway` entry is preserved. Use
349
+ `moka mcp gateway configure-host` as an explicit migration or repair command
350
+ when direct upstream MCP entries need to be removed from existing host config
351
+ with a backup. The hosted gateway requires `PIPELINE_MCP_GATEWAY_AUTHORIZATION`
352
+ in the OpenCode environment. Restart OpenCode after config changes because it
353
+ loads config at startup.
349
354
 
350
355
  ## Profile Grant Rules
351
356
 
package/package.json CHANGED
@@ -8,15 +8,16 @@
8
8
  "execa": "^9.5.2",
9
9
  "git-url-parse": "^16.1.0",
10
10
  "gray-matter": "^4.0.3",
11
+ "jsonc-parser": "^3.3.1",
11
12
  "ky": "^2.0.2",
12
13
  "micromatch": "^4.0.8",
13
14
  "p-limit": "^7.3.0",
14
15
  "package-manager-detector": "^1.6.0",
15
16
  "secure-json-parse": "^4.1.0",
16
17
  "simple-git": "^3.36.0",
17
- "zod": "^4.4.3",
18
18
  "xstate": "^5.31.1",
19
- "yaml": "^2.9.0"
19
+ "yaml": "^2.9.0",
20
+ "zod": "^4.4.3"
20
21
  },
21
22
  "devDependencies": {
22
23
  "@biomejs/biome": "2.4.15",
@@ -65,6 +66,10 @@
65
66
  "types": "./dist/hooks.d.ts",
66
67
  "import": "./dist/hooks.js"
67
68
  },
69
+ "./moka-submit": {
70
+ "types": "./dist/moka-submit.d.ts",
71
+ "import": "./dist/moka-submit.js"
72
+ },
68
73
  "./planner": {
69
74
  "types": "./dist/workflow-planner.d.ts",
70
75
  "import": "./dist/workflow-planner.js"
@@ -115,7 +120,7 @@
115
120
  "prepack": "bun run build:cli"
116
121
  },
117
122
  "type": "module",
118
- "version": "1.25.0",
123
+ "version": "1.26.0",
119
124
  "description": "Config-driven multi-agent pipeline runner for repository work",
120
125
  "main": "./dist/index.js",
121
126
  "types": "./dist/index.d.ts",