@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 +7 -0
- package/dist/argo-submit.d.ts +2 -9
- package/dist/argo-submit.js +2 -9
- package/dist/config.d.ts +3 -3
- package/dist/hooks.d.ts +1 -1
- package/dist/install-commands.js +96 -25
- package/dist/moka-submit.d.ts +160 -0
- package/dist/moka-submit.js +132 -40
- package/dist/opencode-project-config.js +84 -0
- package/dist/runner-command-contract.d.ts +15 -3
- package/dist/runner-command-contract.js +1 -1
- package/dist/workflow-submit-contract.d.ts +13 -0
- package/dist/workflow-submit-contract.js +12 -0
- package/docs/mcp-gateway.md +11 -3
- package/docs/operator-guide.md +9 -4
- package/package.json +8 -3
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
|
package/dist/argo-submit.d.ts
CHANGED
|
@@ -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
|
|
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">;
|
package/dist/argo-submit.js
CHANGED
|
@@ -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
|
|
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>;
|
package/dist/install-commands.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
items.
|
|
567
|
-
if (
|
|
568
|
-
|
|
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
|
-
...
|
|
627
|
+
...conflicts.map((item) => `- ${item.path}`),
|
|
571
628
|
"Re-run with --force to overwrite them."
|
|
572
629
|
].join("\n"));
|
|
573
|
-
|
|
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 };
|
package/dist/moka-submit.js
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
96
|
+
context,
|
|
69
97
|
options,
|
|
70
98
|
submission: {
|
|
71
99
|
kind: "graph",
|
|
72
100
|
mode: options.mode
|
|
73
101
|
},
|
|
74
|
-
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
|
|
83
|
-
const task = options
|
|
84
|
-
const
|
|
85
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
112
|
-
if (
|
|
113
|
-
const
|
|
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
|
|
118
|
-
worktreePath
|
|
119
|
-
});
|
|
120
|
-
|
|
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:
|
|
128
|
-
eventAuthSecretName:
|
|
129
|
-
githubAuthSecretName:
|
|
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:
|
|
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:
|
|
185
|
+
delivery: input.options.delivery,
|
|
144
186
|
events: runnerEvents(input.options),
|
|
145
187
|
repository: {
|
|
146
|
-
baseBranch: input.
|
|
147
|
-
sha: input.
|
|
148
|
-
url: input.
|
|
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.
|
|
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:
|
|
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 };
|
package/docs/mcp-gateway.md
CHANGED
|
@@ -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
|
-
|
|
98
|
-
|
|
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,
|
package/docs/operator-guide.md
CHANGED
|
@@ -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
|
|
346
|
-
into project host config.
|
|
347
|
-
|
|
348
|
-
|
|
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.
|
|
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",
|