@oisincoveney/pipeline 1.27.17 → 1.27.19
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 +1 -1
- package/dist/argo-submit.d.ts +2 -1
- package/dist/argo-submit.js +3 -1
- package/dist/argo-workflow.d.ts +2 -0
- package/dist/argo-workflow.js +40 -18
- package/dist/codex-auth-sync.js +98 -0
- package/dist/config.js +1 -1
- package/dist/index.js +31 -11
- package/dist/install-commands.js +13 -7
- package/dist/moka-global-config.d.ts +29 -0
- package/dist/moka-global-config.js +56 -0
- package/dist/moka-submit.d.ts +22 -19
- package/dist/moka-submit.js +30 -19
- package/dist/run-state/git-refs.js +97 -22
- package/docs/operator-guide.md +43 -16
- package/docs/pipeline-console-runner-contract.md +16 -5
- package/docs/slash-command-adapter-contract.md +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -241,7 +241,7 @@ runner.
|
|
|
241
241
|
|
|
242
242
|
| Host | Generated files | Invocation |
|
|
243
243
|
| --- | --- | --- |
|
|
244
|
-
| OpenCode | `.opencode/commands
|
|
244
|
+
| OpenCode | `.opencode/commands/moka-<entrypoint>.md`, `.opencode/agents/*.md`, `.opencode/plugins/*.ts`, `.opencode/opencode.json` | `/moka-quick <task>`, `/moka-execute <task>`, `/moka-inspect <task>` |
|
|
245
245
|
|
|
246
246
|
The installer is idempotent, supports `--check` and `--dry-run`, and refuses to
|
|
247
247
|
overwrite manually edited files unless `--force` is supplied.
|
package/dist/argo-submit.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
8
8
|
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
9
9
|
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
10
10
|
generateName: z.ZodOptional<z.ZodString>;
|
|
11
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
11
12
|
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
12
13
|
image: z.ZodOptional<z.ZodString>;
|
|
13
14
|
imagePullPolicy: z.ZodOptional<z.ZodEnum<{
|
|
@@ -18,7 +19,7 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
18
19
|
imagePullSecretName: z.ZodOptional<z.ZodString>;
|
|
19
20
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
20
21
|
name: z.ZodOptional<z.ZodString>;
|
|
21
|
-
namespace: z.
|
|
22
|
+
namespace: z.ZodString;
|
|
22
23
|
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
23
24
|
payloadJson: z.ZodString;
|
|
24
25
|
queueName: z.ZodOptional<z.ZodString>;
|
package/dist/argo-submit.js
CHANGED
|
@@ -24,6 +24,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
24
24
|
eventAuthSecretKey: z.string().min(1).optional(),
|
|
25
25
|
eventAuthSecretName: z.string().min(1).optional(),
|
|
26
26
|
generateName: z.string().min(1).optional(),
|
|
27
|
+
gitCredentialsSecretName: z.string().min(1).optional(),
|
|
27
28
|
githubAuthSecretName: z.string().min(1).optional(),
|
|
28
29
|
image: z.string().min(1).optional(),
|
|
29
30
|
imagePullPolicy: z.enum([
|
|
@@ -34,7 +35,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
34
35
|
imagePullSecretName: z.string().min(1).optional(),
|
|
35
36
|
kubeconfigPath: z.string().min(1).optional(),
|
|
36
37
|
name: z.string().min(1).optional(),
|
|
37
|
-
namespace: z.string().min(1)
|
|
38
|
+
namespace: z.string().min(1),
|
|
38
39
|
opencodeAuthSecretName: z.string().min(1).optional(),
|
|
39
40
|
payloadJson: z.string().min(1),
|
|
40
41
|
queueName: z.string().min(1).optional(),
|
|
@@ -72,6 +73,7 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
|
|
|
72
73
|
eventAuthSecretKey: options.eventAuthSecretKey,
|
|
73
74
|
eventAuthSecretName: options.eventAuthSecretName,
|
|
74
75
|
generateName: options.generateName,
|
|
76
|
+
gitCredentialsSecretName: options.gitCredentialsSecretName,
|
|
75
77
|
githubAuthSecretName: options.githubAuthSecretName,
|
|
76
78
|
image: options.image,
|
|
77
79
|
imagePullPolicy: options.imagePullPolicy,
|
package/dist/argo-workflow.d.ts
CHANGED
|
@@ -102,6 +102,7 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
|
|
|
102
102
|
key: z.ZodString;
|
|
103
103
|
path: z.ZodString;
|
|
104
104
|
}, z.core.$strict>>>;
|
|
105
|
+
optional: z.ZodOptional<z.ZodBoolean>;
|
|
105
106
|
secretName: z.ZodString;
|
|
106
107
|
}, z.core.$strict>>;
|
|
107
108
|
}, z.core.$strict>>;
|
|
@@ -112,6 +113,7 @@ declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
112
113
|
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
113
114
|
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
114
115
|
generateName: z.ZodOptional<z.ZodString>;
|
|
116
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
115
117
|
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
116
118
|
annotations: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
|
|
117
119
|
image: z.ZodDefault<z.ZodString>;
|
package/dist/argo-workflow.js
CHANGED
|
@@ -10,6 +10,11 @@ const RUNNER_WORKFLOW_SERVICE_ACCOUNT = "pipeline-runner";
|
|
|
10
10
|
const RUNNER_WORKFLOW_ENTRYPOINT = "pipeline";
|
|
11
11
|
const RUNNER_WORKFLOW_PAYLOAD_PATH = "/etc/pipeline/payload.json";
|
|
12
12
|
const RUNNER_WORKFLOW_SCHEDULE_PATH = "/etc/pipeline/schedule.yaml";
|
|
13
|
+
const RUNNER_GIT_CREDENTIALS_PATH = "/etc/pipeline/git-credentials";
|
|
14
|
+
const RUNNER_OPENCODE_ENV = [{
|
|
15
|
+
name: "CODEX_AUTH_PER_PROJECT_ACCOUNTS",
|
|
16
|
+
value: "0"
|
|
17
|
+
}];
|
|
13
18
|
const kubernetesNameSchema = z.string().min(1);
|
|
14
19
|
const labelValueSchema = z.string().min(1);
|
|
15
20
|
const stringMapSchema = z.record(z.string().min(1), z.string().min(1));
|
|
@@ -26,6 +31,7 @@ const secretVolumeSchema = z.object({
|
|
|
26
31
|
key: z.string().min(1),
|
|
27
32
|
path: z.string().min(1)
|
|
28
33
|
}).strict()).optional(),
|
|
34
|
+
optional: z.boolean().optional(),
|
|
29
35
|
secretName: kubernetesNameSchema
|
|
30
36
|
}).strict();
|
|
31
37
|
const argoWorkflowVolumeSchema = z.object({
|
|
@@ -110,6 +116,7 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
110
116
|
eventAuthSecretKey: z.string().min(1).optional(),
|
|
111
117
|
eventAuthSecretName: kubernetesNameSchema.optional(),
|
|
112
118
|
generateName: z.string().min(1).optional(),
|
|
119
|
+
gitCredentialsSecretName: kubernetesNameSchema.optional(),
|
|
113
120
|
githubAuthSecretName: kubernetesNameSchema.optional(),
|
|
114
121
|
annotations: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
|
|
115
122
|
image: z.string().min(1).default(RUNNER_WORKFLOW_IMAGE),
|
|
@@ -270,38 +277,51 @@ function runnerWorkflowStorage(options, tasks) {
|
|
|
270
277
|
subPath: "auth.json"
|
|
271
278
|
});
|
|
272
279
|
}
|
|
273
|
-
if (options.
|
|
280
|
+
if (options.gitCredentialsSecretName) {
|
|
274
281
|
volumes.push({
|
|
275
|
-
name: "
|
|
282
|
+
name: "runner-git-credentials",
|
|
276
283
|
secret: {
|
|
284
|
+
defaultMode: 256,
|
|
277
285
|
items: [
|
|
278
286
|
{
|
|
279
|
-
key: "
|
|
280
|
-
path: "
|
|
287
|
+
key: "username",
|
|
288
|
+
path: "username"
|
|
281
289
|
},
|
|
282
290
|
{
|
|
283
|
-
key: "
|
|
284
|
-
path: "
|
|
291
|
+
key: "password",
|
|
292
|
+
path: "password"
|
|
285
293
|
},
|
|
286
294
|
{
|
|
287
|
-
key: "
|
|
288
|
-
path: "
|
|
295
|
+
key: "identity",
|
|
296
|
+
path: "identity"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
key: "known_hosts",
|
|
300
|
+
path: "known_hosts"
|
|
289
301
|
}
|
|
290
302
|
],
|
|
291
|
-
|
|
303
|
+
optional: true,
|
|
304
|
+
secretName: options.gitCredentialsSecretName
|
|
292
305
|
}
|
|
293
306
|
});
|
|
294
307
|
volumeMounts.push({
|
|
295
|
-
mountPath:
|
|
296
|
-
name: "
|
|
297
|
-
readOnly: true
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
308
|
+
mountPath: RUNNER_GIT_CREDENTIALS_PATH,
|
|
309
|
+
name: "runner-git-credentials",
|
|
310
|
+
readOnly: true
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (options.githubAuthSecretName) {
|
|
314
|
+
volumes.push({
|
|
301
315
|
name: "github-auth",
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
316
|
+
secret: {
|
|
317
|
+
items: [{
|
|
318
|
+
key: "hosts.yml",
|
|
319
|
+
path: "hosts.yml"
|
|
320
|
+
}],
|
|
321
|
+
secretName: options.githubAuthSecretName
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
volumeMounts.push({
|
|
305
325
|
mountPath: "/root/.config/gh/hosts.yml",
|
|
306
326
|
name: "github-auth",
|
|
307
327
|
readOnly: true,
|
|
@@ -330,6 +350,7 @@ function runnerCommandTemplate(task, options, volumeMounts) {
|
|
|
330
350
|
RUNNER_WORKFLOW_SCHEDULE_PATH
|
|
331
351
|
],
|
|
332
352
|
command: ["moka"],
|
|
353
|
+
env: [...RUNNER_OPENCODE_ENV],
|
|
333
354
|
image: options.image,
|
|
334
355
|
imagePullPolicy: options.imagePullPolicy,
|
|
335
356
|
name: "runner",
|
|
@@ -352,6 +373,7 @@ function runnerFinalizerTemplate(options, volumeMounts) {
|
|
|
352
373
|
"{{workflow.status}}"
|
|
353
374
|
],
|
|
354
375
|
command: ["moka"],
|
|
376
|
+
env: [...RUNNER_OPENCODE_ENV],
|
|
355
377
|
image: options.image,
|
|
356
378
|
imagePullPolicy: options.imagePullPolicy,
|
|
357
379
|
name: "runner",
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { mergeOpenCodeProjectConfig } from "./opencode-project-config.js";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { parse } from "jsonc-parser";
|
|
6
|
+
//#region src/codex-auth-sync.ts
|
|
7
|
+
const CODEX_MULTI_AUTH_PLUGIN = "oc-codex-multi-auth";
|
|
8
|
+
const GLOBAL_CODEX_AUTH_CONFIG_PATH = join(homedir(), ".opencode/openai-codex-auth-config.json");
|
|
9
|
+
function syncLocalCodexAuth(options) {
|
|
10
|
+
const items = [syncGlobalPluginConfig(options.globalConfigPath ?? GLOBAL_CODEX_AUTH_CONFIG_PATH, options), ...discoverGitRepositories(options.root).map((repo) => syncProjectOpenCodeConfig(repo, options))];
|
|
11
|
+
const hasRequiredChanges = items.some((item) => item.action === "create" || item.action === "update");
|
|
12
|
+
const hasErrors = items.some((item) => item.action === "error");
|
|
13
|
+
const checkFailed = options.check === true && hasRequiredChanges;
|
|
14
|
+
return {
|
|
15
|
+
items,
|
|
16
|
+
ok: !(hasErrors || checkFailed)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function formatCodexAuthSyncResult(result) {
|
|
20
|
+
const lines = result.items.map((item) => {
|
|
21
|
+
const suffix = item.message ? `: ${item.message}` : "";
|
|
22
|
+
return `${item.action} ${item.path}${suffix}`;
|
|
23
|
+
});
|
|
24
|
+
if (!result.ok) lines.push("codex-auth sync-local check failed");
|
|
25
|
+
return lines.join("\n");
|
|
26
|
+
}
|
|
27
|
+
function syncGlobalPluginConfig(path, options) {
|
|
28
|
+
const currentText = existsSync(path) ? readFileSync(path, "utf8") : void 0;
|
|
29
|
+
const parsed = parseJsonObject(currentText ?? "{}");
|
|
30
|
+
if (!parsed.ok) return {
|
|
31
|
+
action: "error",
|
|
32
|
+
message: formatParseErrors(parsed.errors),
|
|
33
|
+
path
|
|
34
|
+
};
|
|
35
|
+
return writeIfChanged(path, currentText, `${JSON.stringify({
|
|
36
|
+
...parsed.value,
|
|
37
|
+
perProjectAccounts: false
|
|
38
|
+
}, null, 2)}\n`, options);
|
|
39
|
+
}
|
|
40
|
+
function syncProjectOpenCodeConfig(repo, options) {
|
|
41
|
+
const path = join(repo, ".opencode/opencode.json");
|
|
42
|
+
const currentText = existsSync(path) ? readFileSync(path, "utf8") : void 0;
|
|
43
|
+
const merged = mergeOpenCodeProjectConfig(currentText, { plugin: [CODEX_MULTI_AUTH_PLUGIN] });
|
|
44
|
+
if (!merged.ok) return {
|
|
45
|
+
action: "error",
|
|
46
|
+
message: formatParseErrors(merged.errors),
|
|
47
|
+
path
|
|
48
|
+
};
|
|
49
|
+
return writeIfChanged(path, currentText, merged.content, options);
|
|
50
|
+
}
|
|
51
|
+
function writeIfChanged(path, currentText, nextText, options) {
|
|
52
|
+
if (currentText === nextText) return {
|
|
53
|
+
action: "unchanged",
|
|
54
|
+
path
|
|
55
|
+
};
|
|
56
|
+
const action = currentText === void 0 ? "create" : "update";
|
|
57
|
+
if (!(options.check || options.dryRun)) {
|
|
58
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
59
|
+
writeFileSync(path, nextText);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
action,
|
|
63
|
+
path
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function discoverGitRepositories(root) {
|
|
67
|
+
return readdirSync(root).map((name) => join(root, name)).filter((path) => isDirectory(path) && existsSync(join(path, ".git"))).sort((a, b) => a.localeCompare(b));
|
|
68
|
+
}
|
|
69
|
+
function isDirectory(path) {
|
|
70
|
+
try {
|
|
71
|
+
return statSync(path).isDirectory();
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function parseJsonObject(content) {
|
|
77
|
+
const errors = [];
|
|
78
|
+
const value = parse(content, errors, {
|
|
79
|
+
allowTrailingComma: true,
|
|
80
|
+
disallowComments: false
|
|
81
|
+
});
|
|
82
|
+
if (errors.length > 0 || !isRecord(value)) return {
|
|
83
|
+
errors,
|
|
84
|
+
ok: false
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
value
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function formatParseErrors(errors) {
|
|
92
|
+
return errors.length > 0 ? `invalid JSONC (${errors.length} parse error${errors.length === 1 ? "" : "s"})` : "expected a JSON object";
|
|
93
|
+
}
|
|
94
|
+
function isRecord(value) {
|
|
95
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { formatCodexAuthSyncResult, syncLocalCodexAuth };
|
package/dist/config.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,12 +4,14 @@ import { PipelineConfigError, loadPipelineConfig } from "./config.js";
|
|
|
4
4
|
import { createOrchestratorLaunchPlan, createRunnerLaunchPlan } from "./runner.js";
|
|
5
5
|
import { compileWorkflowPlan } from "./workflow-planner.js";
|
|
6
6
|
import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "./schedule-planner.js";
|
|
7
|
+
import { formatCodexAuthSyncResult, syncLocalCodexAuth } from "./codex-auth-sync.js";
|
|
7
8
|
import { BUILTIN_PIPE_COMMANDS, registerConfiguredEntrypointCommands } from "./commands/pipeline-command.js";
|
|
8
9
|
import { configureGatewayHosts, localGatewayStatus, reconcileGateway, renderGatewayConfig, runGatewayDoctor, startLocalGateway } from "./mcp/gateway.js";
|
|
9
10
|
import { resolvePackageAssetPath } from "./package-assets.js";
|
|
10
11
|
import { formatConfigError, runPipelineFromConfig } from "./pipeline-runtime.js";
|
|
11
12
|
import { registerRunnerCommandCommand } from "./commands/runner-command-command.js";
|
|
12
13
|
import { formatInstallCommandsResult, installCommands, parseCommandHost } from "./install-commands.js";
|
|
14
|
+
import { loadMokaGlobalConfig } from "./moka-global-config.js";
|
|
13
15
|
import { submitMoka } from "./moka-submit.js";
|
|
14
16
|
import { formatPipelineInitResult, initPipelineProject } from "./pipeline-init.js";
|
|
15
17
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
@@ -288,6 +290,15 @@ function createCliProgram() {
|
|
|
288
290
|
});
|
|
289
291
|
console.log(formatInstallCommandsResult(result));
|
|
290
292
|
});
|
|
293
|
+
program.command("codex-auth").description("Manage local Codex multi-auth integration").command("sync-local").description("Use one local oc-codex account pool and declare the plugin in dev repos").option("--root <path>", "directory containing repositories to sync").option("--dry-run", "show planned changes without writing files").option("--check", "fail if local Codex auth config is not synced").action((flags) => {
|
|
294
|
+
const result = syncLocalCodexAuth({
|
|
295
|
+
check: flags.check,
|
|
296
|
+
dryRun: flags.dryRun,
|
|
297
|
+
root: resolve(flags.root ?? process.env.PIPELINE_TARGET_PATH ?? process.cwd())
|
|
298
|
+
});
|
|
299
|
+
console.log(formatCodexAuthSyncResult(result));
|
|
300
|
+
if (!result.ok) process.exitCode = 1;
|
|
301
|
+
});
|
|
291
302
|
addMokaSubmitOptions(program.command("submit").description("Submit work to Momokaya as an Argo Workflow").argument("[input...]", "task description, or command argv with --command")).action(async (input, flags) => {
|
|
292
303
|
const result = await runMokaSubmitFromCli(input, flags);
|
|
293
304
|
console.log(`Workflow submitted: ${result.workflowName} in ${result.namespace}`);
|
|
@@ -313,31 +324,40 @@ function addMokaSubmitOptions(command) {
|
|
|
313
324
|
}
|
|
314
325
|
function runMokaSubmitFromCli(input, flags) {
|
|
315
326
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
327
|
+
const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
328
|
+
const globalConfig = loadMokaGlobalConfig();
|
|
316
329
|
const commonOptions = mokaCommonSubmitOptions({
|
|
317
|
-
config
|
|
330
|
+
config,
|
|
318
331
|
cwd,
|
|
319
|
-
eventUrl: resolveMokaEventUrl(flags),
|
|
320
|
-
flags
|
|
332
|
+
eventUrl: resolveMokaEventUrl(flags, globalConfig),
|
|
333
|
+
flags,
|
|
334
|
+
globalConfig
|
|
321
335
|
});
|
|
322
336
|
if (flags.command) return submitMokaCommandFromCli(input, flags, commonOptions);
|
|
323
337
|
return submitMokaGraphFromCli(input, flags, commonOptions);
|
|
324
338
|
}
|
|
325
|
-
function resolveMokaEventUrl(flags) {
|
|
326
|
-
return flags.eventUrl ??
|
|
339
|
+
function resolveMokaEventUrl(flags, globalConfig) {
|
|
340
|
+
return flags.eventUrl ?? globalConfig?.momokaya.submit.eventUrl;
|
|
327
341
|
}
|
|
328
342
|
function mokaCommonSubmitOptions(input) {
|
|
343
|
+
const momokaya = input.globalConfig?.momokaya;
|
|
329
344
|
return {
|
|
330
345
|
config: input.config,
|
|
331
346
|
eventUrl: input.eventUrl,
|
|
347
|
+
eventAuthSecretKey: momokaya?.submit.eventAuthSecretKey,
|
|
348
|
+
eventAuthSecretName: momokaya?.submit.eventAuthSecretName,
|
|
332
349
|
generateName: input.flags.generateName,
|
|
350
|
+
gitCredentialsSecretName: momokaya?.submit.gitCredentialsSecretName,
|
|
351
|
+
githubAuthSecretName: momokaya?.submit.githubAuthSecretName,
|
|
333
352
|
image: input.flags.image,
|
|
334
353
|
imagePullPolicy: parseImagePullPolicy(input.flags.imagePullPolicy),
|
|
335
|
-
imagePullSecretName: input.flags.imagePullSecret,
|
|
336
|
-
kubeconfigPath: input.flags.kubeconfig,
|
|
354
|
+
imagePullSecretName: input.flags.imagePullSecret ?? momokaya?.submit.imagePullSecretName,
|
|
355
|
+
kubeconfigPath: input.flags.kubeconfig ?? momokaya?.kubernetes.kubeconfig,
|
|
337
356
|
name: input.flags.name,
|
|
338
|
-
namespace: input.flags.namespace,
|
|
339
|
-
|
|
340
|
-
|
|
357
|
+
namespace: input.flags.namespace ?? momokaya?.kubernetes.namespace,
|
|
358
|
+
opencodeAuthSecretName: momokaya?.submit.opencodeAuthSecretName,
|
|
359
|
+
queueName: input.flags.queueName ?? momokaya?.submit.queueName,
|
|
360
|
+
serviceAccountName: input.flags.serviceAccount ?? momokaya?.submit.serviceAccountName,
|
|
341
361
|
worktreePath: input.cwd
|
|
342
362
|
};
|
|
343
363
|
}
|
|
@@ -377,7 +397,7 @@ function parseGatewayHostScope(value) {
|
|
|
377
397
|
throw new Error("scope must be project or global");
|
|
378
398
|
}
|
|
379
399
|
function addRunnerArgoOptions(command, options = {}) {
|
|
380
|
-
command.option("--name <name>", "Workflow metadata.name").option("--generate-name <prefix>", "Workflow metadata.generateName").option("--namespace <namespace>", "Workflow namespace"
|
|
400
|
+
command.option("--name <name>", "Workflow metadata.name").option("--generate-name <prefix>", "Workflow metadata.generateName").option("--namespace <namespace>", "Workflow namespace");
|
|
381
401
|
if (options.kubeconfig) command.option("--kubeconfig <path>", "kubeconfig path");
|
|
382
402
|
return command.option("--queue-name <name>", "Kueue LocalQueue label for Workflow pods").option("--service-account <name>", "Workflow service account").option("--image <image>", "runner image").addOption(new Option("--image-pull-policy <policy>", "runner image pull policy").choices([
|
|
383
403
|
"Always",
|
package/dist/install-commands.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST, loadPipelineConfig } from "./config.js";
|
|
2
2
|
import { compileWorkflowPlan } from "./workflow-planner.js";
|
|
3
|
+
import { mergeOpenCodeProjectConfig } from "./opencode-project-config.js";
|
|
3
4
|
import { renderOpenCodeGatewayConfig } from "./mcp/gateway.js";
|
|
4
5
|
import { resolvePackageAssetPath } from "./package-assets.js";
|
|
5
|
-
import { mergeOpenCodeProjectConfig } from "./opencode-project-config.js";
|
|
6
6
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
7
7
|
import { basename, dirname, join, relative } from "node:path";
|
|
8
8
|
import matter from "gray-matter";
|
|
@@ -18,7 +18,8 @@ const AGENTS_MD_START = "<!-- @oisincoveney/pipeline:agents:start -->";
|
|
|
18
18
|
const AGENTS_MD_END = "<!-- @oisincoveney/pipeline:agents:end -->";
|
|
19
19
|
const SINGLE_OPENCODE_PLUGIN_ARRAY_RE = /\n {2}"plugin": \[\n {4}("[^"]+")\n {2}\]/;
|
|
20
20
|
const OPENCODE_PROJECT_CONFIG_PATH = ".opencode/opencode.json";
|
|
21
|
-
const
|
|
21
|
+
const OPENCODE_COMMAND_PREFIX = "moka-";
|
|
22
|
+
const ENTRYPOINT_PATH_PATTERNS = { opencode: [/^\.opencode\/commands\/(?:moka-)?([^/]+)\.md$/] };
|
|
22
23
|
const COMMAND_HOSTS = ["opencode"];
|
|
23
24
|
const OPENCODE_ORCHESTRATOR_AGENT_ID = "MoKa Orchestrator";
|
|
24
25
|
const MOKA_PROFILE_PREFIX = "moka-";
|
|
@@ -172,7 +173,7 @@ function entrypointDispatchBlock(host, config, id, entrypoint) {
|
|
|
172
173
|
`The schedule policy is \`${entrypoint.schedule}\`.`,
|
|
173
174
|
id === "quick" ? "Run `moka submit --quick <task description>` to submit the graph as an Argo Workflow." : `Run \`moka submit <task description>\` to submit the \`${id}\` graph as an Argo Workflow.`,
|
|
174
175
|
"The pipeline runtime executes as Argo DAG tasks using the package-owned runner image.",
|
|
175
|
-
"
|
|
176
|
+
"Configure the target in `~/.config/moka/config.yaml`; use `--kubeconfig <path>` and `--namespace <namespace>` only for explicit command overrides.",
|
|
176
177
|
"Use `moka submit --schedule <schedule.yaml> <task description>` only when rerunning an existing schedule artifact."
|
|
177
178
|
].join("\n");
|
|
178
179
|
}
|
|
@@ -334,7 +335,7 @@ function opencodeDefinitions(config, cwd) {
|
|
|
334
335
|
]).join("\n")),
|
|
335
336
|
host: "opencode",
|
|
336
337
|
invocation: invocationForHost("opencode", id),
|
|
337
|
-
path: `.opencode/commands/${id}.md`
|
|
338
|
+
path: `.opencode/commands/${commandIdForHost("opencode", id)}.md`
|
|
338
339
|
})),
|
|
339
340
|
{
|
|
340
341
|
content: renderOpenCodeProjectConfig(config),
|
|
@@ -401,7 +402,7 @@ function projectAgentsMdDefinition(cwd, host) {
|
|
|
401
402
|
"",
|
|
402
403
|
"This repository uses package-owned `@oisincoveney/pipeline` config.",
|
|
403
404
|
"",
|
|
404
|
-
"- Use `/quick`, `/execute`, or `/inspect` for OpenCode slash-command entrypoints when available.",
|
|
405
|
+
"- Use `/moka-quick`, `/moka-execute`, or `/moka-inspect` for OpenCode slash-command entrypoints when available.",
|
|
405
406
|
"- Load and follow the relevant skill from `.agents/skills` before doing specialized work.",
|
|
406
407
|
"- Prefer the package-defined pipeline profiles and generated command surfaces over ad hoc subagent prompts.",
|
|
407
408
|
"",
|
|
@@ -446,7 +447,8 @@ function selectedHosts(host) {
|
|
|
446
447
|
const GENERATED_RESOURCE_ROOTS = { opencode: [
|
|
447
448
|
".opencode/commands",
|
|
448
449
|
".opencode/agents",
|
|
449
|
-
".opencode/plugins"
|
|
450
|
+
".opencode/plugins",
|
|
451
|
+
".opencode/skills"
|
|
450
452
|
] };
|
|
451
453
|
async function listFiles(root) {
|
|
452
454
|
if (!existsSync(root)) return [];
|
|
@@ -484,7 +486,11 @@ function entrypointIdFromGeneratedPath(host, path) {
|
|
|
484
486
|
}
|
|
485
487
|
}
|
|
486
488
|
function invocationForHost(host, entrypointId = "execute") {
|
|
487
|
-
return `${{ opencode: "/" }[host]}${entrypointId} <task description>`;
|
|
489
|
+
return `${{ opencode: "/" }[host]}${commandIdForHost(host, entrypointId)} <task description>`;
|
|
490
|
+
}
|
|
491
|
+
function commandIdForHost(host, entrypointId) {
|
|
492
|
+
if (host === "opencode") return `${OPENCODE_COMMAND_PREFIX}${entrypointId}`;
|
|
493
|
+
return entrypointId;
|
|
488
494
|
}
|
|
489
495
|
function resolveDefinitionContent(definition, target) {
|
|
490
496
|
if (definition.path !== OPENCODE_PROJECT_CONFIG_PATH || !existsSync(target)) return {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/moka-global-config.d.ts
|
|
4
|
+
declare const MOKA_GLOBAL_CONFIG_PATH = ".config/moka/config.yaml";
|
|
5
|
+
declare const mokaGlobalConfigSchema: z.ZodObject<{
|
|
6
|
+
momokaya: z.ZodObject<{
|
|
7
|
+
kubernetes: z.ZodObject<{
|
|
8
|
+
kubeconfig: z.ZodOptional<z.ZodString>;
|
|
9
|
+
namespace: z.ZodString;
|
|
10
|
+
}, z.core.$strict>;
|
|
11
|
+
submit: z.ZodObject<{
|
|
12
|
+
eventAuthSecretKey: z.ZodString;
|
|
13
|
+
eventAuthSecretName: z.ZodString;
|
|
14
|
+
eventUrl: z.ZodString;
|
|
15
|
+
gitCredentialsSecretName: z.ZodString;
|
|
16
|
+
githubAuthSecretName: z.ZodString;
|
|
17
|
+
imagePullSecretName: z.ZodString;
|
|
18
|
+
opencodeAuthSecretName: z.ZodString;
|
|
19
|
+
queueName: z.ZodString;
|
|
20
|
+
serviceAccountName: z.ZodString;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
}, z.core.$strict>;
|
|
24
|
+
type MokaGlobalConfig = z.infer<typeof mokaGlobalConfigSchema>;
|
|
25
|
+
declare function mokaGlobalConfigPath(homeDir?: string): string;
|
|
26
|
+
declare function loadMokaGlobalConfig(): MokaGlobalConfig | null;
|
|
27
|
+
declare function parseMokaGlobalConfig(source: string, sourcePath: string): MokaGlobalConfig;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { MOKA_GLOBAL_CONFIG_PATH, MokaGlobalConfig, loadMokaGlobalConfig, mokaGlobalConfigPath, mokaGlobalConfigSchema, parseMokaGlobalConfig };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PipelineConfigError } from "./config.js";
|
|
2
|
+
import { parseDocument } from "yaml";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
//#region src/moka-global-config.ts
|
|
8
|
+
const MOKA_GLOBAL_CONFIG_PATH = ".config/moka/config.yaml";
|
|
9
|
+
const mokaSubmitGlobalConfigSchema = z.object({
|
|
10
|
+
eventAuthSecretKey: z.string().min(1),
|
|
11
|
+
eventAuthSecretName: z.string().min(1),
|
|
12
|
+
eventUrl: z.string().url(),
|
|
13
|
+
gitCredentialsSecretName: z.string().min(1),
|
|
14
|
+
githubAuthSecretName: z.string().min(1),
|
|
15
|
+
imagePullSecretName: z.string().min(1),
|
|
16
|
+
opencodeAuthSecretName: z.string().min(1),
|
|
17
|
+
queueName: z.string().min(1),
|
|
18
|
+
serviceAccountName: z.string().min(1)
|
|
19
|
+
}).strict();
|
|
20
|
+
const mokaKubernetesGlobalConfigSchema = z.object({
|
|
21
|
+
kubeconfig: z.string().min(1).optional(),
|
|
22
|
+
namespace: z.string().min(1)
|
|
23
|
+
}).strict();
|
|
24
|
+
const mokaGlobalConfigSchema = z.object({ momokaya: z.object({
|
|
25
|
+
kubernetes: mokaKubernetesGlobalConfigSchema,
|
|
26
|
+
submit: mokaSubmitGlobalConfigSchema
|
|
27
|
+
}).strict() }).strict();
|
|
28
|
+
function mokaGlobalConfigPath(homeDir = homedir()) {
|
|
29
|
+
return join(homeDir, MOKA_GLOBAL_CONFIG_PATH);
|
|
30
|
+
}
|
|
31
|
+
function loadMokaGlobalConfig() {
|
|
32
|
+
const configPath = mokaGlobalConfigPath();
|
|
33
|
+
if (!existsSync(configPath)) return null;
|
|
34
|
+
return parseMokaGlobalConfig(readFileSync(configPath, "utf8"), configPath);
|
|
35
|
+
}
|
|
36
|
+
function parseMokaGlobalConfig(source, sourcePath) {
|
|
37
|
+
const document = parseDocument(source, {
|
|
38
|
+
prettyErrors: false,
|
|
39
|
+
uniqueKeys: true
|
|
40
|
+
});
|
|
41
|
+
if (document.errors.length > 0) throw new PipelineConfigError("PIPELINE_CONFIG_PARSE_ERROR", `Failed to parse ${sourcePath}`, document.errors.map((err) => ({
|
|
42
|
+
message: err.message,
|
|
43
|
+
path: sourcePath
|
|
44
|
+
})));
|
|
45
|
+
const parsed = mokaGlobalConfigSchema.safeParse(document.toJS());
|
|
46
|
+
if (!parsed.success) {
|
|
47
|
+
const issues = parsed.error.issues.map((issue) => ({
|
|
48
|
+
path: issue.path.join("."),
|
|
49
|
+
message: issue.message
|
|
50
|
+
}));
|
|
51
|
+
throw new PipelineConfigError("PIPELINE_CONFIG_VALIDATION_ERROR", [`Invalid ${sourcePath}:`, ...issues.map((issue) => issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`)].join("\n"), issues);
|
|
52
|
+
}
|
|
53
|
+
return parsed.data;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { MOKA_GLOBAL_CONFIG_PATH, loadMokaGlobalConfig, mokaGlobalConfigPath, mokaGlobalConfigSchema, parseMokaGlobalConfig };
|
package/dist/moka-submit.d.ts
CHANGED
|
@@ -73,16 +73,17 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
73
73
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
74
74
|
url: z.ZodString;
|
|
75
75
|
}, z.core.$strict>>;
|
|
76
|
-
eventAuthSecretKey: z.
|
|
77
|
-
eventAuthSecretName: z.
|
|
78
|
-
eventUrl: z.
|
|
76
|
+
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
77
|
+
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
78
|
+
eventUrl: z.ZodOptional<z.ZodString>;
|
|
79
79
|
events: z.ZodOptional<z.ZodObject<{
|
|
80
80
|
authHeader: z.ZodDefault<z.ZodString>;
|
|
81
81
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
82
82
|
url: z.ZodString;
|
|
83
83
|
}, z.core.$strict>>;
|
|
84
84
|
generateName: z.ZodOptional<z.ZodString>;
|
|
85
|
-
|
|
85
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
86
|
+
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
86
87
|
hookPolicy: z.ZodOptional<z.ZodObject<{
|
|
87
88
|
allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
88
89
|
allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -142,12 +143,12 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
142
143
|
IfNotPresent: "IfNotPresent";
|
|
143
144
|
Never: "Never";
|
|
144
145
|
}>>;
|
|
145
|
-
imagePullSecretName: z.
|
|
146
|
+
imagePullSecretName: z.ZodOptional<z.ZodString>;
|
|
146
147
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
147
148
|
name: z.ZodOptional<z.ZodString>;
|
|
148
|
-
namespace: z.
|
|
149
|
-
opencodeAuthSecretName: z.
|
|
150
|
-
queueName: z.
|
|
149
|
+
namespace: z.ZodOptional<z.ZodString>;
|
|
150
|
+
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
151
|
+
queueName: z.ZodOptional<z.ZodString>;
|
|
151
152
|
repository: z.ZodOptional<z.ZodObject<{
|
|
152
153
|
baseBranch: z.ZodString;
|
|
153
154
|
sha: z.ZodOptional<z.ZodString>;
|
|
@@ -158,7 +159,7 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
158
159
|
project: z.ZodString;
|
|
159
160
|
requestedBy: z.ZodOptional<z.ZodString>;
|
|
160
161
|
}, z.core.$strict>>;
|
|
161
|
-
serviceAccountName: z.
|
|
162
|
+
serviceAccountName: z.ZodOptional<z.ZodString>;
|
|
162
163
|
mode: z.ZodEnum<{
|
|
163
164
|
quick: "quick";
|
|
164
165
|
full: "full";
|
|
@@ -185,16 +186,17 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
185
186
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
186
187
|
url: z.ZodString;
|
|
187
188
|
}, z.core.$strict>>;
|
|
188
|
-
eventAuthSecretKey: z.
|
|
189
|
-
eventAuthSecretName: z.
|
|
190
|
-
eventUrl: z.
|
|
189
|
+
eventAuthSecretKey: z.ZodOptional<z.ZodString>;
|
|
190
|
+
eventAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
191
|
+
eventUrl: z.ZodOptional<z.ZodString>;
|
|
191
192
|
events: z.ZodOptional<z.ZodObject<{
|
|
192
193
|
authHeader: z.ZodDefault<z.ZodString>;
|
|
193
194
|
authTokenFile: z.ZodOptional<z.ZodString>;
|
|
194
195
|
url: z.ZodString;
|
|
195
196
|
}, z.core.$strict>>;
|
|
196
197
|
generateName: z.ZodOptional<z.ZodString>;
|
|
197
|
-
|
|
198
|
+
gitCredentialsSecretName: z.ZodOptional<z.ZodString>;
|
|
199
|
+
githubAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
198
200
|
hookPolicy: z.ZodOptional<z.ZodObject<{
|
|
199
201
|
allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
200
202
|
allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -254,12 +256,12 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
254
256
|
IfNotPresent: "IfNotPresent";
|
|
255
257
|
Never: "Never";
|
|
256
258
|
}>>;
|
|
257
|
-
imagePullSecretName: z.
|
|
259
|
+
imagePullSecretName: z.ZodOptional<z.ZodString>;
|
|
258
260
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
259
261
|
name: z.ZodOptional<z.ZodString>;
|
|
260
|
-
namespace: z.
|
|
261
|
-
opencodeAuthSecretName: z.
|
|
262
|
-
queueName: z.
|
|
262
|
+
namespace: z.ZodOptional<z.ZodString>;
|
|
263
|
+
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
264
|
+
queueName: z.ZodOptional<z.ZodString>;
|
|
263
265
|
repository: z.ZodOptional<z.ZodObject<{
|
|
264
266
|
baseBranch: z.ZodString;
|
|
265
267
|
sha: z.ZodOptional<z.ZodString>;
|
|
@@ -270,7 +272,7 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
270
272
|
project: z.ZodString;
|
|
271
273
|
requestedBy: z.ZodOptional<z.ZodString>;
|
|
272
274
|
}, z.core.$strict>>;
|
|
273
|
-
serviceAccountName: z.
|
|
275
|
+
serviceAccountName: z.ZodOptional<z.ZodString>;
|
|
274
276
|
commandArgv: z.ZodArray<z.ZodString>;
|
|
275
277
|
task: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
276
278
|
kind: z.ZodLiteral<"prompt">;
|
|
@@ -315,13 +317,14 @@ interface MokaWorkflowSubmitOptions {
|
|
|
315
317
|
eventAuthSecretKey?: string;
|
|
316
318
|
eventAuthSecretName?: string;
|
|
317
319
|
generateName?: string;
|
|
320
|
+
gitCredentialsSecretName?: string;
|
|
318
321
|
githubAuthSecretName?: string;
|
|
319
322
|
image?: string;
|
|
320
323
|
imagePullPolicy?: "Always" | "IfNotPresent" | "Never";
|
|
321
324
|
imagePullSecretName?: string;
|
|
322
325
|
kubeconfigPath?: string;
|
|
323
326
|
name?: string;
|
|
324
|
-
namespace
|
|
327
|
+
namespace: string;
|
|
325
328
|
opencodeAuthSecretName?: string;
|
|
326
329
|
payloadJson: string;
|
|
327
330
|
queueName?: string;
|
package/dist/moka-submit.js
CHANGED
|
@@ -9,14 +9,6 @@ import parseGitUrl from "git-url-parse";
|
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
10
|
import { simpleGit } from "simple-git";
|
|
11
11
|
//#region src/moka-submit.ts
|
|
12
|
-
const MOMOKAYA_EVENT_AUTH_SECRET_KEY = "OISIN_PIPELINE_EVENT_AUTH_TOKEN";
|
|
13
|
-
const MOMOKAYA_EVENT_AUTH_SECRET_NAME = "pipeline-runner-event-auth";
|
|
14
|
-
const MOMOKAYA_EVENT_URL = "https://pipeline-console.momokaya.ee/api/pipeline/runner-events";
|
|
15
|
-
const MOMOKAYA_GITHUB_AUTH_SECRET_NAME = "oisin-bot-github-auth";
|
|
16
|
-
const MOMOKAYA_IMAGE_PULL_SECRET_NAME = "ghcr-pull-secret";
|
|
17
|
-
const MOMOKAYA_OPENCODE_AUTH_SECRET_NAME = "opencode-auth-1";
|
|
18
|
-
const MOMOKAYA_QUEUE_NAME = "momokaya-pipeline";
|
|
19
|
-
const MOMOKAYA_RUNNER_SERVICE_ACCOUNT_NAME = "pipeline-runner";
|
|
20
12
|
const imagePullPolicySchema = z.enum([
|
|
21
13
|
"Always",
|
|
22
14
|
"IfNotPresent",
|
|
@@ -69,25 +61,26 @@ const mokaSubmitResultSchema = workflowSubmitResultSchema;
|
|
|
69
61
|
const mokaSubmitBaseOptionsSchema = z.object({
|
|
70
62
|
delivery: runnerDeliverySchema.default({ pullRequest: false }),
|
|
71
63
|
eventSink: mokaSubmitEventsSchema.optional(),
|
|
72
|
-
eventAuthSecretKey: z.string().min(1).
|
|
73
|
-
eventAuthSecretName: z.string().min(1).
|
|
74
|
-
eventUrl: z.string().url().
|
|
64
|
+
eventAuthSecretKey: z.string().min(1).optional(),
|
|
65
|
+
eventAuthSecretName: z.string().min(1).optional(),
|
|
66
|
+
eventUrl: z.string().url().optional(),
|
|
75
67
|
events: mokaSubmitEventsSchema.optional(),
|
|
76
68
|
generateName: z.string().min(1).optional(),
|
|
77
|
-
|
|
69
|
+
gitCredentialsSecretName: z.string().min(1).optional(),
|
|
70
|
+
githubAuthSecretName: z.string().min(1).optional(),
|
|
78
71
|
hookPolicy: mokaSubmitHookPolicySchema.optional(),
|
|
79
72
|
hooks: mokaSubmitDirectHooksSchema.optional(),
|
|
80
73
|
image: z.string().min(1).optional(),
|
|
81
74
|
imagePullPolicy: imagePullPolicySchema,
|
|
82
|
-
imagePullSecretName: z.string().min(1).
|
|
75
|
+
imagePullSecretName: z.string().min(1).optional(),
|
|
83
76
|
kubeconfigPath: z.string().min(1).optional(),
|
|
84
77
|
name: z.string().min(1).optional(),
|
|
85
|
-
namespace: z.string().min(1).
|
|
86
|
-
opencodeAuthSecretName: z.string().min(1).
|
|
87
|
-
queueName: z.string().min(1).
|
|
78
|
+
namespace: z.string().min(1).optional(),
|
|
79
|
+
opencodeAuthSecretName: z.string().min(1).optional(),
|
|
80
|
+
queueName: z.string().min(1).optional(),
|
|
88
81
|
repository: runnerRepositoryContextSchema.optional(),
|
|
89
82
|
run: runnerRunIdentitySchema.optional(),
|
|
90
|
-
serviceAccountName: z.string().min(1).
|
|
83
|
+
serviceAccountName: z.string().min(1).optional()
|
|
91
84
|
}).strict();
|
|
92
85
|
const mokaGraphSubmitOptionsSchema = mokaSubmitBaseOptionsSchema.extend({
|
|
93
86
|
mode: z.enum(["full", "quick"]),
|
|
@@ -107,6 +100,11 @@ const mokaSubmitOptionsSchema = z.discriminatedUnion("type", [mokaGraphSubmitOpt
|
|
|
107
100
|
message: "Choose either eventSink or events, not both",
|
|
108
101
|
path: ["eventSink"]
|
|
109
102
|
});
|
|
103
|
+
if (data.eventSink === void 0 && data.events === void 0 && data.eventUrl === void 0) ctx.addIssue({
|
|
104
|
+
code: "custom",
|
|
105
|
+
message: "eventUrl is required unless eventSink or events is provided",
|
|
106
|
+
path: ["eventUrl"]
|
|
107
|
+
});
|
|
110
108
|
});
|
|
111
109
|
const submitHookId = (event) => `moka-submit-${event.replaceAll(".", "-")}`;
|
|
112
110
|
function objectWithoutUndefined(value) {
|
|
@@ -284,18 +282,23 @@ function workflowSubmitOptions(options) {
|
|
|
284
282
|
return {
|
|
285
283
|
eventAuthSecretKey: options.eventAuthSecretKey,
|
|
286
284
|
eventAuthSecretName: options.eventAuthSecretName,
|
|
285
|
+
gitCredentialsSecretName: options.gitCredentialsSecretName,
|
|
287
286
|
githubAuthSecretName: options.githubAuthSecretName,
|
|
288
287
|
image: options.image,
|
|
289
288
|
imagePullPolicy: options.imagePullPolicy,
|
|
290
289
|
imagePullSecretName: options.imagePullSecretName,
|
|
291
290
|
kubeconfigPath: options.kubeconfigPath,
|
|
292
291
|
name: options.name,
|
|
293
|
-
namespace: options.namespace,
|
|
292
|
+
namespace: requireSubmitOption(options.namespace, "namespace"),
|
|
294
293
|
opencodeAuthSecretName: options.opencodeAuthSecretName,
|
|
295
294
|
queueName: options.queueName,
|
|
296
295
|
serviceAccountName: options.serviceAccountName
|
|
297
296
|
};
|
|
298
297
|
}
|
|
298
|
+
function requireSubmitOption(value, name) {
|
|
299
|
+
if (!value) throw new Error(`${name} is required for moka submit`);
|
|
300
|
+
return value;
|
|
301
|
+
}
|
|
299
302
|
function runnerPayloadJson(input) {
|
|
300
303
|
return JSON.stringify(buildRunnerCommandPayload({
|
|
301
304
|
delivery: input.options.delivery,
|
|
@@ -323,6 +326,7 @@ function runnerEvents(options) {
|
|
|
323
326
|
authTokenFile: eventSink.authTokenFile ?? eventAuthTokenFile(options),
|
|
324
327
|
url: eventSink.url
|
|
325
328
|
};
|
|
329
|
+
if (!options.eventUrl) throw new Error("eventUrl is required unless eventSink or events is provided");
|
|
326
330
|
return {
|
|
327
331
|
authHeader: "Authorization",
|
|
328
332
|
authTokenFile: eventAuthTokenFile(options),
|
|
@@ -330,19 +334,23 @@ function runnerEvents(options) {
|
|
|
330
334
|
};
|
|
331
335
|
}
|
|
332
336
|
function eventAuthTokenFile(options) {
|
|
337
|
+
if (!options.eventAuthSecretKey) throw new Error("eventAuthSecretKey is required unless eventSink.authTokenFile is provided");
|
|
333
338
|
return `/etc/pipeline/event-auth/${options.eventAuthSecretKey}`;
|
|
334
339
|
}
|
|
335
340
|
async function resolveSubmissionContext(options, dependencies, runId) {
|
|
336
341
|
const explicitContext = explicitSubmissionContext(options);
|
|
337
342
|
if (explicitContext) return explicitContext;
|
|
338
343
|
const git = await resolveRequiredGit(options, dependencies);
|
|
344
|
+
const repository = repositoryContext(options, git);
|
|
345
|
+
assertRepositoryCredentialConfiguration(options);
|
|
339
346
|
return {
|
|
340
|
-
repository
|
|
347
|
+
repository,
|
|
341
348
|
run: runContext(options, git, runId)
|
|
342
349
|
};
|
|
343
350
|
}
|
|
344
351
|
function explicitSubmissionContext(options) {
|
|
345
352
|
if (!(options.repository && options.run)) return null;
|
|
353
|
+
assertRepositoryCredentialConfiguration(options);
|
|
346
354
|
return {
|
|
347
355
|
repository: options.repository,
|
|
348
356
|
run: options.run
|
|
@@ -359,6 +367,9 @@ function repositoryContext(options, git) {
|
|
|
359
367
|
url: git.url
|
|
360
368
|
};
|
|
361
369
|
}
|
|
370
|
+
function assertRepositoryCredentialConfiguration(options) {
|
|
371
|
+
if (!options.gitCredentialsSecretName) throw new Error("gitCredentialsSecretName is required for runner git clone, fetch, and push operations");
|
|
372
|
+
}
|
|
362
373
|
function runContext(options, git, runId) {
|
|
363
374
|
return options.run ?? {
|
|
364
375
|
id: runId,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { chmodSync,
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
//#region src/run-state/git-refs.ts
|
|
7
7
|
const DEFAULT_WORKSPACE_PATH = "/workspace";
|
|
8
|
-
const
|
|
8
|
+
const DEFAULT_GIT_CREDENTIALS_DIR = "/etc/pipeline/git-credentials";
|
|
9
9
|
const WRITABLE_GIT_CREDENTIAL_STORE = resolve(tmpdir(), "pipeline-git-credentials");
|
|
10
|
+
const SCP_LIKE_SSH_REMOTE_RE = /^[^@\s]+@[^:\s]+:.+/u;
|
|
10
11
|
const execGit = promisify(execFile);
|
|
11
|
-
let
|
|
12
|
+
let preparedBasicAuthCredentialStore;
|
|
12
13
|
function runnerGitRefs(payload, nodeId) {
|
|
13
14
|
const prefix = `refs/heads/pipeline/runs/${payload.run.id}/${payload.workflow.id}`;
|
|
14
15
|
return {
|
|
@@ -102,21 +103,19 @@ async function configureGitCommitter(worktreePath, committer) {
|
|
|
102
103
|
]);
|
|
103
104
|
}
|
|
104
105
|
function runnerGitCommandArgs(args) {
|
|
105
|
-
return [...gitCredentialConfigArgs(), ...args];
|
|
106
|
+
return [...gitCredentialConfigArgs(remoteUrlFromGitArgs(args)), ...args];
|
|
106
107
|
}
|
|
107
108
|
async function runGit(cwd, args) {
|
|
109
|
+
const remoteUrl = remoteUrlFromGitArgs(args);
|
|
108
110
|
const { stdout } = await execGit("git", runnerGitCommandArgs(args), {
|
|
109
111
|
cwd,
|
|
110
112
|
encoding: "utf8",
|
|
111
|
-
env:
|
|
112
|
-
...process.env,
|
|
113
|
-
GIT_TERMINAL_PROMPT: "0"
|
|
114
|
-
}
|
|
113
|
+
env: runnerGitEnv(remoteUrl)
|
|
115
114
|
});
|
|
116
115
|
return stdout;
|
|
117
116
|
}
|
|
118
|
-
function gitCredentialConfigArgs() {
|
|
119
|
-
const writablePath = prepareWritableGitCredentialStore();
|
|
117
|
+
function gitCredentialConfigArgs(remoteUrl) {
|
|
118
|
+
const writablePath = prepareWritableGitCredentialStore(remoteUrl);
|
|
120
119
|
if (!writablePath) return [];
|
|
121
120
|
return [
|
|
122
121
|
"-c",
|
|
@@ -125,26 +124,102 @@ function gitCredentialConfigArgs() {
|
|
|
125
124
|
`credential.helper=store --file=${writablePath}`
|
|
126
125
|
];
|
|
127
126
|
}
|
|
128
|
-
function prepareWritableGitCredentialStore() {
|
|
129
|
-
const sourcePath = availableGitCredentialStore();
|
|
130
|
-
if (!sourcePath) return;
|
|
127
|
+
function prepareWritableGitCredentialStore(remoteUrl) {
|
|
131
128
|
const writablePath = writableGitCredentialStore();
|
|
132
|
-
|
|
133
|
-
return writablePath;
|
|
129
|
+
const basicAuth = availableBasicAuthCredentials();
|
|
130
|
+
if (basicAuth) return prepareBasicAuthCredentialStore(basicAuth, writablePath, remoteUrl);
|
|
131
|
+
}
|
|
132
|
+
function availableBasicAuthCredentials() {
|
|
133
|
+
const credentialsDir = gitCredentialsDir();
|
|
134
|
+
const usernamePath = resolve(credentialsDir, "username");
|
|
135
|
+
const passwordPath = resolve(credentialsDir, "password");
|
|
136
|
+
if (!(existsSync(usernamePath) && existsSync(passwordPath))) return;
|
|
137
|
+
return {
|
|
138
|
+
password: readCredentialFile(passwordPath),
|
|
139
|
+
username: readCredentialFile(usernamePath)
|
|
140
|
+
};
|
|
134
141
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
return existsSync(sourcePath) ? sourcePath : void 0;
|
|
142
|
+
function gitCredentialsDir() {
|
|
143
|
+
return process.env.PIPELINE_GIT_CREDENTIALS_DIR ?? DEFAULT_GIT_CREDENTIALS_DIR;
|
|
138
144
|
}
|
|
139
145
|
function writableGitCredentialStore() {
|
|
140
146
|
return process.env.PIPELINE_WRITABLE_GIT_CREDENTIAL_STORE ?? WRITABLE_GIT_CREDENTIAL_STORE;
|
|
141
147
|
}
|
|
142
|
-
function
|
|
143
|
-
if (preparedCredentialStore === writablePath) return;
|
|
148
|
+
function writeGitCredentialStore(credentials, writablePath, host) {
|
|
144
149
|
mkdirSync(dirname(writablePath), { recursive: true });
|
|
145
|
-
|
|
150
|
+
writeFileSync(writablePath, `https://${encodeURIComponent(credentials.username)}:${encodeURIComponent(credentials.password)}@${host}\n`, { mode: 384 });
|
|
146
151
|
chmodSync(writablePath, 384);
|
|
147
|
-
|
|
152
|
+
}
|
|
153
|
+
function prepareBasicAuthCredentialStore(credentials, writablePath, remoteUrl) {
|
|
154
|
+
const host = remoteUrl ? credentialHost(remoteUrl) : void 0;
|
|
155
|
+
if (!host) return existingPreparedBasicAuthCredentialStore(writablePath);
|
|
156
|
+
if (isPreparedBasicAuthCredentialStore(writablePath, host)) return writablePath;
|
|
157
|
+
writeGitCredentialStore(credentials, writablePath, host);
|
|
158
|
+
preparedBasicAuthCredentialStore = {
|
|
159
|
+
host,
|
|
160
|
+
path: writablePath
|
|
161
|
+
};
|
|
162
|
+
return writablePath;
|
|
163
|
+
}
|
|
164
|
+
function existingPreparedBasicAuthCredentialStore(writablePath) {
|
|
165
|
+
return preparedBasicAuthCredentialStore?.path === writablePath ? writablePath : void 0;
|
|
166
|
+
}
|
|
167
|
+
function isPreparedBasicAuthCredentialStore(writablePath, host) {
|
|
168
|
+
return preparedBasicAuthCredentialStore?.path === writablePath && preparedBasicAuthCredentialStore.host === host;
|
|
169
|
+
}
|
|
170
|
+
function runnerGitEnv(remoteUrl) {
|
|
171
|
+
const env = {
|
|
172
|
+
...process.env,
|
|
173
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
174
|
+
};
|
|
175
|
+
if (remoteUrl && isSshRemote(remoteUrl)) {
|
|
176
|
+
const sshCommand = gitSshCommand();
|
|
177
|
+
if (sshCommand) env.GIT_SSH_COMMAND = sshCommand;
|
|
178
|
+
}
|
|
179
|
+
return env;
|
|
180
|
+
}
|
|
181
|
+
function gitSshCommand() {
|
|
182
|
+
const credentialsDir = gitCredentialsDir();
|
|
183
|
+
const identityPath = resolve(credentialsDir, "identity");
|
|
184
|
+
const knownHostsPath = resolve(credentialsDir, "known_hosts");
|
|
185
|
+
if (!(existsSync(identityPath) && existsSync(knownHostsPath))) return;
|
|
186
|
+
chmodSync(identityPath, 256);
|
|
187
|
+
return [
|
|
188
|
+
"ssh",
|
|
189
|
+
"-i",
|
|
190
|
+
shellQuote(identityPath),
|
|
191
|
+
"-o",
|
|
192
|
+
"IdentitiesOnly=yes",
|
|
193
|
+
"-o",
|
|
194
|
+
`UserKnownHostsFile=${shellQuote(knownHostsPath)}`,
|
|
195
|
+
"-o",
|
|
196
|
+
"StrictHostKeyChecking=yes"
|
|
197
|
+
].join(" ");
|
|
198
|
+
}
|
|
199
|
+
function readCredentialFile(path) {
|
|
200
|
+
return readFileSync(path, "utf8").trim();
|
|
201
|
+
}
|
|
202
|
+
function remoteUrlFromGitArgs(args) {
|
|
203
|
+
if (args[0] === "clone") return args.find((arg) => isRemoteUrl(arg));
|
|
204
|
+
}
|
|
205
|
+
function isRemoteUrl(value) {
|
|
206
|
+
return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("ssh://") || isScpLikeSshRemote(value);
|
|
207
|
+
}
|
|
208
|
+
function isSshRemote(value) {
|
|
209
|
+
return value.startsWith("ssh://") || isScpLikeSshRemote(value);
|
|
210
|
+
}
|
|
211
|
+
function isScpLikeSshRemote(value) {
|
|
212
|
+
return SCP_LIKE_SSH_REMOTE_RE.test(value);
|
|
213
|
+
}
|
|
214
|
+
function credentialHost(remoteUrl) {
|
|
215
|
+
try {
|
|
216
|
+
return new URL(remoteUrl).host || void 0;
|
|
217
|
+
} catch {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function shellQuote(value) {
|
|
222
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
148
223
|
}
|
|
149
224
|
//#endregion
|
|
150
225
|
export { commitAndPushNodeRef, mergeDependencyRefs, prepareRunnerGitWorkspace, promoteFinalRef };
|
package/docs/operator-guide.md
CHANGED
|
@@ -129,11 +129,27 @@ description and current git context, creates payload/schedule ConfigMaps, and
|
|
|
129
129
|
submits an Argo Workflow that runs the graph as DAG tasks using the package-owned
|
|
130
130
|
runner image.
|
|
131
131
|
|
|
132
|
-
`moka submit`
|
|
133
|
-
|
|
132
|
+
`moka submit` reads the private Momokaya target from
|
|
133
|
+
`~/.config/moka/config.yaml`.
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
momokaya:
|
|
137
|
+
kubernetes:
|
|
138
|
+
kubeconfig: /path/to/cluster.kubeconfig
|
|
139
|
+
namespace: <workflow-namespace>
|
|
140
|
+
submit:
|
|
141
|
+
eventAuthSecretKey: <event-auth-secret-key>
|
|
142
|
+
eventAuthSecretName: <event-auth-secret-name>
|
|
143
|
+
eventUrl: <runner-event-sink-url>
|
|
144
|
+
gitCredentialsSecretName: <git-credentials-secret-name>
|
|
145
|
+
githubAuthSecretName: <github-auth-secret-name>
|
|
146
|
+
imagePullSecretName: <image-pull-secret-name>
|
|
147
|
+
opencodeAuthSecretName: <opencode-auth-secret-name>
|
|
148
|
+
queueName: <local-queue-name>
|
|
149
|
+
serviceAccountName: <runner-service-account-name>
|
|
150
|
+
```
|
|
134
151
|
|
|
135
152
|
```shell
|
|
136
|
-
export PIPELINE_EVENT_URL="https://console.example.com/api/pipeline/runner-events"
|
|
137
153
|
moka submit "fix the login bug" --quick
|
|
138
154
|
moka submit "Implement PIPE-54"
|
|
139
155
|
```
|
|
@@ -142,7 +158,7 @@ For a local cluster, point the same commands at that cluster with
|
|
|
142
158
|
`--kubeconfig <path>` and `--namespace <namespace>`:
|
|
143
159
|
|
|
144
160
|
```shell
|
|
145
|
-
moka submit "fix the login bug" --quick --kubeconfig ~/.kube/config --namespace
|
|
161
|
+
moka submit "fix the login bug" --quick --kubeconfig ~/.kube/config --namespace <workflow-namespace>
|
|
146
162
|
```
|
|
147
163
|
|
|
148
164
|
There is no separate workstation-local `submit` path; local submission means
|
|
@@ -169,7 +185,7 @@ apiVersion: argoproj.io/v1alpha1
|
|
|
169
185
|
kind: Workflow
|
|
170
186
|
metadata:
|
|
171
187
|
generateName: pipeline-run-alpha-
|
|
172
|
-
namespace:
|
|
188
|
+
namespace: <workflow-namespace>
|
|
173
189
|
spec:
|
|
174
190
|
entrypoint: pipeline
|
|
175
191
|
onExit: pipeline-finalizer
|
|
@@ -211,17 +227,28 @@ Secret volume at the path configured in `events.authTokenFile`.
|
|
|
211
227
|
|
|
212
228
|
Expected namespace resources:
|
|
213
229
|
|
|
214
|
-
- ServiceAccount `
|
|
215
|
-
- Secret `
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
230
|
+
- The ServiceAccount named by `submit.serviceAccountName` with the required RBAC
|
|
231
|
+
- The OpenCode auth Secret named by `submit.opencodeAuthSecretName` with key
|
|
232
|
+
`auth.json`
|
|
233
|
+
- The event auth Secret named by `submit.eventAuthSecretName` with the key named
|
|
234
|
+
by `submit.eventAuthSecretKey`
|
|
235
|
+
- The git credentials Secret named by `submit.gitCredentialsSecretName` using
|
|
236
|
+
the same key conventions as Flux `GitRepository` credentials: `username` and
|
|
237
|
+
`password` for HTTPS remotes, or `identity` and `known_hosts` for SSH remotes
|
|
238
|
+
- The GitHub CLI auth Secret named by `submit.githubAuthSecretName` with key
|
|
239
|
+
`hosts.yml`; this Secret is for `gh` and pull request delivery, not git
|
|
240
|
+
clone/fetch/push authentication
|
|
220
241
|
- A pipeline-console event sink reachable from the pod
|
|
221
242
|
|
|
222
|
-
Credential rotation
|
|
223
|
-
|
|
224
|
-
|
|
243
|
+
Credential issuance and rotation are owned by the cluster/infra layer, not by
|
|
244
|
+
runner payloads; existing infra repository scripts can continue to own the
|
|
245
|
+
operator-facing lifecycle. Recommended production setups use External Secrets
|
|
246
|
+
Operator or Secrets Store CSI Driver. For GitHub HTTPS, prefer a GitHub App
|
|
247
|
+
installation token materialized by External Secrets Operator's
|
|
248
|
+
`GithubAccessToken` generator with a refresh interval below the token lifetime,
|
|
249
|
+
then template it as `username`/`password`. For SSH, materialize `identity` and
|
|
250
|
+
`known_hosts` from the external secret manager. `moka submit` references
|
|
251
|
+
configured Secret names; it does not accept per-run secret values.
|
|
225
252
|
|
|
226
253
|
## Payload Contract
|
|
227
254
|
|
|
@@ -260,13 +287,13 @@ Troubleshooting:
|
|
|
260
287
|
Generated invocations include:
|
|
261
288
|
|
|
262
289
|
```text
|
|
263
|
-
OpenCode: /quick, /execute, /inspect
|
|
290
|
+
OpenCode: /moka-quick, /moka-execute, /moka-inspect
|
|
264
291
|
Codex: $quick, $execute, $inspect
|
|
265
292
|
```
|
|
266
293
|
|
|
267
294
|
`moka init` and `moka install-commands --host opencode` generate:
|
|
268
295
|
|
|
269
|
-
- `.opencode/commands
|
|
296
|
+
- `.opencode/commands/moka-<entrypoint>.md` for `/moka-quick`, `/moka-execute`, and `/moka-inspect`
|
|
270
297
|
- `.opencode/agents/*.md` for primary and subagent profiles with explicit
|
|
271
298
|
`permission` maps, `task` grants, and denied ungranted tools
|
|
272
299
|
- `.opencode/skills/*/SKILL.md` from `npx skills add`, not Moka generation
|
|
@@ -43,7 +43,7 @@ validation, tests, and docs.
|
|
|
43
43
|
"events": {
|
|
44
44
|
"url": "https://console.example/api/pipeline/runner-events",
|
|
45
45
|
"authHeader": "Authorization",
|
|
46
|
-
"authTokenFile": "/etc/pipeline/event-auth
|
|
46
|
+
"authTokenFile": "/etc/pipeline/event-auth/<event-auth-secret-key>"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
```
|
|
@@ -160,11 +160,16 @@ environment and mounted secrets.
|
|
|
160
160
|
|
|
161
161
|
Expected runner Job secrets and mounts include:
|
|
162
162
|
|
|
163
|
-
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
- The configured OpenCode auth Secret mounted at
|
|
164
|
+
`/root/.local/share/opencode/auth.json`
|
|
165
|
+
- The configured event auth Secret mounted at
|
|
166
|
+
`/etc/pipeline/event-auth/<event-auth-secret-key>`
|
|
166
167
|
- MCP gateway auth Secret mounted at a path referenced by the runner
|
|
167
|
-
-
|
|
168
|
+
- The configured git credentials Secret mounted for `git`; HTTPS remotes use
|
|
169
|
+
`username` and `password`, and SSH remotes use `identity` and `known_hosts`
|
|
170
|
+
following Flux `GitRepository` credential conventions
|
|
171
|
+
- The configured GitHub CLI auth Secret mounted for `gh` when pull request
|
|
172
|
+
delivery is enabled
|
|
168
173
|
|
|
169
174
|
The runner image sets `HOME=/root`; Kubernetes must project each numbered auth
|
|
170
175
|
Secret with the key `auth.json` into the target directory. The runner does not
|
|
@@ -172,6 +177,12 @@ read event auth tokens or payloads from environment variables. No
|
|
|
172
177
|
`OISIN_PIPELINE_RUNNER_PAYLOAD_JSON`, `PIPELINE_EVENT_API_TOKEN`, or
|
|
173
178
|
`OPENCODE_AUTH_JSON` env vars are used.
|
|
174
179
|
|
|
180
|
+
Git credential issuance and rotation are cluster concerns. For GitHub HTTPS,
|
|
181
|
+
operators should prefer GitHub App installation tokens generated by External
|
|
182
|
+
Secrets Operator or equivalent control-plane automation. For SSH, operators
|
|
183
|
+
must provide both the private identity and pinned `known_hosts` data. Runner
|
|
184
|
+
payloads never carry credential values or Secret names.
|
|
185
|
+
|
|
175
186
|
## Boundary
|
|
176
187
|
|
|
177
188
|
The `moka` command is its own user-facing command and runtime. Argo
|
|
@@ -15,7 +15,7 @@ moka install-commands --host all --check
|
|
|
15
15
|
|
|
16
16
|
| Host | Generated resources | Invocation | Mechanical path |
|
|
17
17
|
| -------- | -------------------------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
18
|
-
| OpenCode | `.opencode/commands
|
|
18
|
+
| OpenCode | `.opencode/commands/moka-<entrypoint>.md`, `.opencode/agents/*.md`, `.opencode/opencode.json` | `/moka-quick <task>`, `/moka-execute <task>`, `/moka-inspect <task>` | Project commands run a primary orchestrator and OpenCode native subagents with package-owned skill, MCP, permission, and LSP projection. |
|
|
19
19
|
|
|
20
20
|
## Projection Rules
|
|
21
21
|
|
package/package.json
CHANGED
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"types": "./dist/moka-submit.d.ts",
|
|
71
71
|
"import": "./dist/moka-submit.js"
|
|
72
72
|
},
|
|
73
|
+
"./moka-global-config": {
|
|
74
|
+
"types": "./dist/moka-global-config.d.ts",
|
|
75
|
+
"import": "./dist/moka-global-config.js"
|
|
76
|
+
},
|
|
73
77
|
"./planner": {
|
|
74
78
|
"types": "./dist/workflow-planner.d.ts",
|
|
75
79
|
"import": "./dist/workflow-planner.js"
|
|
@@ -118,7 +122,7 @@
|
|
|
118
122
|
"prepack": "bun run build:cli"
|
|
119
123
|
},
|
|
120
124
|
"type": "module",
|
|
121
|
-
"version": "1.27.
|
|
125
|
+
"version": "1.27.19",
|
|
122
126
|
"description": "Config-driven multi-agent pipeline runner for repository work",
|
|
123
127
|
"main": "./dist/index.js",
|
|
124
128
|
"types": "./dist/index.d.ts",
|