@oisincoveney/pipeline 3.12.4 → 3.14.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/defaults/opencode-ecosystem.yaml +2 -12
- package/defaults/pipeline.yaml +0 -4
- package/dist/argo-submit.d.ts +5 -2
- package/dist/argo-submit.js +3 -4
- package/dist/argo-workflow.d.ts +15 -7
- package/dist/argo-workflow.js +52 -50
- package/dist/broker-auth.d.ts +16 -0
- package/dist/broker-auth.js +173 -0
- package/dist/cli/doctor.js +2 -2
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.js +58 -54
- package/dist/cli/submit-options.js +7 -3
- package/dist/cluster-doctor.js +5 -4
- package/dist/codex-auth-sync.js +23 -45
- package/dist/commands/ticket/complete.js +61 -0
- package/dist/commands/ticket/create.js +115 -0
- package/dist/commands/ticket/graph-check.js +16 -0
- package/dist/commands/ticket/next.js +43 -0
- package/dist/commands/ticket/sequence.js +18 -0
- package/dist/commands/ticket/shared.d.ts +14 -0
- package/dist/commands/ticket/shared.js +77 -0
- package/dist/commands/ticket/start.js +94 -0
- package/dist/commands/ticket-command.d.ts +2 -15
- package/dist/commands/ticket-command.js +14 -319
- package/dist/config/load.js +28 -7
- package/dist/config/schemas.d.ts +18 -21
- package/dist/config/schemas.js +2 -7
- package/dist/gates.js +1 -1
- package/dist/install-commands/claude-code.js +1 -1
- package/dist/install-commands/opencode.js +19 -1
- package/dist/install-hooks.js +2 -2
- package/dist/install-rules.js +1 -1
- package/dist/loop/controller-deps.js +1 -2
- package/dist/loop/loop-command.js +1 -2
- package/dist/loop/loop-controller-entrypoint.js +7 -4
- package/dist/moka-global-config.d.ts +22 -3
- package/dist/moka-global-config.js +43 -4
- package/dist/moka-submit.d.ts +19 -13
- package/dist/moka-submit.js +3 -4
- package/dist/pipeline-runtime.d.ts +22 -1
- package/dist/pipeline-runtime.js +38 -20
- package/dist/planning/generate.d.ts +20 -20
- package/dist/run-control/commands.js +78 -40
- package/dist/run-control/next-node.js +112 -0
- package/dist/run-control/postgres/postgres-run-control-store.js +243 -0
- package/dist/run-control/postgres/schema.js +52 -0
- package/dist/run-control/run-control-store.js +62 -0
- package/dist/run-control/runtime-reporter.js +10 -12
- package/dist/run-control/store.js +19 -8
- package/dist/run-control/submit-result.js +59 -0
- package/dist/run-control/supervisor.js +20 -16
- package/dist/run-state/opencode-accounts.js +33 -78
- package/dist/runner-command/run.js +29 -12
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/runner-event-schema.d.ts +10 -10
- package/dist/runner.d.ts +7 -0
- package/dist/runner.js +53 -19
- package/dist/runtime/contracts/contracts.d.ts +20 -1
- package/dist/runtime/contracts/index.d.ts +1 -1
- package/dist/runtime/durable-store/durable-store.js +53 -0
- package/dist/runtime/durable-store/postgres/postgres-store.js +133 -0
- package/dist/runtime/durable-store/postgres/schema.js +31 -0
- package/dist/runtime/events/events.js +1 -1
- package/dist/runtime/gates/adjudication/llm-judge.js +56 -0
- package/dist/runtime/gates/adjudication/structured-claim.js +52 -0
- package/dist/runtime/gates/adjudicator/adjudicator.js +69 -0
- package/dist/runtime/gates/adjudicator/index.js +39 -0
- package/dist/runtime/gates/contract.js +25 -0
- package/dist/runtime/gates/gates.js +15 -368
- package/dist/runtime/gates/index.js +3 -0
- package/dist/runtime/gates/kinds/acceptance/acceptance.js +109 -0
- package/dist/runtime/gates/kinds/acceptance/index.js +9 -0
- package/dist/runtime/gates/kinds/artifact/artifact.js +20 -0
- package/dist/runtime/gates/kinds/artifact/index.js +9 -0
- package/dist/runtime/gates/kinds/builtin/builtin.js +21 -0
- package/dist/runtime/gates/kinds/builtin/index.js +9 -0
- package/dist/runtime/gates/kinds/changed-files/changed-files.js +61 -0
- package/dist/runtime/gates/kinds/changed-files/index.js +9 -0
- package/dist/runtime/gates/kinds/command/command.js +21 -0
- package/dist/runtime/gates/kinds/command/index.js +9 -0
- package/dist/runtime/gates/kinds/json-schema/index.js +9 -0
- package/dist/runtime/gates/kinds/json-schema/json-schema.js +32 -0
- package/dist/runtime/gates/kinds/verdict/index.js +9 -0
- package/dist/runtime/gates/kinds/verdict/verdict.js +32 -0
- package/dist/runtime/gates/orchestrator.js +161 -0
- package/dist/runtime/gates/registry.js +47 -0
- package/dist/runtime/json-validation/json-validation.js +1 -1
- package/dist/runtime/node-protocol/node-protocol.js +83 -0
- package/dist/runtime/opencode-session-executor.js +48 -8
- package/dist/runtime/protected-paths/protected-paths.js +97 -0
- package/dist/runtime/run-journal.d.ts +21 -0
- package/dist/runtime/scheduler.js +13 -8
- package/dist/runtime/services/opencode-sdk-service.js +4 -0
- package/dist/runtime/services/repo-io-service.d.ts +2 -0
- package/dist/runtime/services/runner-command-io-service.js +1 -1
- package/dist/tickets/backlog-task-store.d.ts +2 -0
- package/dist/tickets/completion/complete-ticket.js +96 -0
- package/dist/tickets/ticket-graph.d.ts +2 -0
- package/dist/tickets/ticket-selection.d.ts +1 -0
- package/docs/mcp-gateway.md +6 -6
- package/docs/moka-orchestrator-design.md +79 -0
- package/docs/okteto-runner.md +1 -1
- package/docs/operator-guide.md +8 -5
- package/docs/pipeline-console-runner-contract.md +8 -7
- package/package.json +5 -2
- package/dist/runtime/run-journal.js +0 -28
- package/dist/runtime/services/run-journal-file-service.js +0 -20
|
@@ -59,15 +59,6 @@ ecosystem_code:
|
|
|
59
59
|
role: long-running goal mode with /goal slash command, persistent state, evidence-gated completion, and TUI sidebar
|
|
60
60
|
default_stack: true
|
|
61
61
|
source: https://www.npmjs.com/package/@prevalentware/opencode-goal-plugin
|
|
62
|
-
- id: oc-codex-multi-auth
|
|
63
|
-
name: oc-codex-multi-auth
|
|
64
|
-
package: oc-codex-multi-auth
|
|
65
|
-
plugin:
|
|
66
|
-
kind: npm
|
|
67
|
-
package: oc-codex-multi-auth@6.3.2
|
|
68
|
-
role: ChatGPT Plus/Pro OAuth with multi-account rotation, health checks, and Codex/GPT-5 routing for OpenCode
|
|
69
|
-
default_stack: true
|
|
70
|
-
source: https://github.com/ndycode/oc-codex-multi-auth
|
|
71
62
|
- id: opencode-snip
|
|
72
63
|
name: opencode-snip
|
|
73
64
|
role: prompt snippet and command-discipline helper patterns
|
|
@@ -86,9 +77,8 @@ ecosystem_code:
|
|
|
86
77
|
# Reasoning effort is no longer expressed as synthetic per-effort model ids.
|
|
87
78
|
# Pipeline config carries `reasoning_effort` as data on each node/profile and the
|
|
88
79
|
# runner applies it as the opencode model variant (`--variant` / prompt body
|
|
89
|
-
# `variant`) for the selected GPT-5 model.
|
|
90
|
-
#
|
|
91
|
-
# reasoning.encrypted_content), so moka registers no provider models here.
|
|
80
|
+
# `variant`) for the selected GPT-5 model. Broker auth owns Codex request
|
|
81
|
+
# options, so moka registers no provider models here.
|
|
92
82
|
mcp_backends:
|
|
93
83
|
- id: pipeline-gateway
|
|
94
84
|
locality: project-remote
|
package/defaults/pipeline.yaml
CHANGED
|
@@ -11,10 +11,6 @@ context_handoff:
|
|
|
11
11
|
# seeded by the node's task + handoff artifacts, within token_budget.
|
|
12
12
|
repo_map:
|
|
13
13
|
enabled: true
|
|
14
|
-
# durability: journal each terminal node result so a killed run resumes from the
|
|
15
|
-
# last passed node without re-running (or re-spending tokens on) finished work.
|
|
16
|
-
durability:
|
|
17
|
-
enabled: true
|
|
18
14
|
# parallel_worktrees: opt-in git-worktree isolation for parallel child nodes —
|
|
19
15
|
# each parallel child runs on its own branch with its own per-worktree opencode
|
|
20
16
|
# server so concurrent edits and sessions can't collide. OFF by default; enable
|
package/dist/argo-submit.d.ts
CHANGED
|
@@ -20,8 +20,11 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
20
20
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
21
21
|
name: z.ZodOptional<z.ZodString>;
|
|
22
22
|
namespace: z.ZodString;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
brokerAuth: z.ZodObject<{
|
|
24
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
25
|
+
secretName: z.ZodString;
|
|
26
|
+
url: z.ZodDefault<z.ZodString>;
|
|
27
|
+
}, z.core.$strict>;
|
|
25
28
|
payloadJson: z.ZodString;
|
|
26
29
|
scheduleYaml: z.ZodString;
|
|
27
30
|
serviceAccountName: z.ZodOptional<z.ZodString>;
|
package/dist/argo-submit.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
|
|
2
|
+
import { brokerAuthOptionSchema } from "./broker-auth.js";
|
|
2
3
|
import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
|
|
3
4
|
import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
|
|
4
5
|
import { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
|
|
@@ -38,8 +39,7 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
38
39
|
kubeconfigPath: z.string().min(1).optional(),
|
|
39
40
|
name: z.string().min(1).optional(),
|
|
40
41
|
namespace: z.string().min(1),
|
|
41
|
-
|
|
42
|
-
opencodeOpenaiAccountsSecretName: z.string().min(1).optional(),
|
|
42
|
+
brokerAuth: brokerAuthOptionSchema,
|
|
43
43
|
payloadJson: z.string().min(1),
|
|
44
44
|
scheduleYaml: z.string().min(1),
|
|
45
45
|
serviceAccountName: z.string().min(1).optional()
|
|
@@ -89,8 +89,7 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
89
89
|
labels,
|
|
90
90
|
name: options.name,
|
|
91
91
|
namespace: options.namespace,
|
|
92
|
-
|
|
93
|
-
opencodeOpenaiAccountsSecret: options.opencodeOpenaiAccountsSecretName ? { name: options.opencodeOpenaiAccountsSecretName } : void 0,
|
|
92
|
+
brokerAuth: options.brokerAuth,
|
|
94
93
|
payloadConfigMapName,
|
|
95
94
|
plan: compiled.plan,
|
|
96
95
|
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
package/dist/argo-workflow.d.ts
CHANGED
|
@@ -27,10 +27,18 @@ declare const runnerArgoWorkflowManifestSchema: z.ZodObject<{
|
|
|
27
27
|
container: z.ZodOptional<z.ZodObject<{
|
|
28
28
|
args: z.ZodArray<z.ZodString>;
|
|
29
29
|
command: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
30
|
-
env: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
30
|
+
env: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
31
31
|
name: z.ZodString;
|
|
32
32
|
value: z.ZodString;
|
|
33
|
-
}, z.core.$strict
|
|
33
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
34
|
+
name: z.ZodString;
|
|
35
|
+
valueFrom: z.ZodObject<{
|
|
36
|
+
secretKeyRef: z.ZodObject<{
|
|
37
|
+
key: z.ZodString;
|
|
38
|
+
name: z.ZodString;
|
|
39
|
+
}, z.core.$strict>;
|
|
40
|
+
}, z.core.$strict>;
|
|
41
|
+
}, z.core.$strict>]>>>;
|
|
34
42
|
image: z.ZodString;
|
|
35
43
|
imagePullPolicy: z.ZodEnum<{
|
|
36
44
|
Always: "Always";
|
|
@@ -137,11 +145,11 @@ declare const buildRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
|
|
|
137
145
|
labels: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodOptional<z.ZodString>>>;
|
|
138
146
|
name: z.ZodOptional<z.ZodString>;
|
|
139
147
|
namespace: z.ZodString;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}, z.core.$strict
|
|
148
|
+
brokerAuth: z.ZodObject<{
|
|
149
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
150
|
+
secretName: z.ZodString;
|
|
151
|
+
url: z.ZodDefault<z.ZodString>;
|
|
152
|
+
}, z.core.$strict>;
|
|
145
153
|
payloadConfigMapKey: z.ZodDefault<z.ZodString>;
|
|
146
154
|
payloadConfigMapName: z.ZodString;
|
|
147
155
|
resources: z.ZodOptional<z.ZodObject<{
|
package/dist/argo-workflow.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { compileArgoExecutionGraph } from "./argo-graph.js";
|
|
2
|
-
import { OPENCODE_AUTH_STAGING_DIR, OPENCODE_OPENAI_ACCOUNTS_STAGING_DIR } from "./run-state/opencode-accounts.js";
|
|
3
2
|
import { DEFAULT_RUNNER_TASK_DESCRIPTOR_PATH } from "./runner-command/task-descriptor.js";
|
|
4
3
|
import { stringify } from "yaml";
|
|
5
4
|
import { z } from "zod";
|
|
@@ -27,6 +26,10 @@ const RUNNER_OPENCODE_ENV = [
|
|
|
27
26
|
name: "PIPELINE_AGENT_TIMEOUT_MS",
|
|
28
27
|
value: "600000"
|
|
29
28
|
},
|
|
29
|
+
{
|
|
30
|
+
name: "PIPELINE_AGENT_IDLE_TIMEOUT_MS",
|
|
31
|
+
value: "180000"
|
|
32
|
+
},
|
|
30
33
|
{
|
|
31
34
|
name: "PIPELINE_DISABLED_MODELS",
|
|
32
35
|
value: "opencode-go/qwen3.7-max"
|
|
@@ -92,14 +95,21 @@ const argoWorkflowRetryStrategySchema = z.object({
|
|
|
92
95
|
"OnTransientError"
|
|
93
96
|
])
|
|
94
97
|
}).strict();
|
|
98
|
+
const argoWorkflowEnvVarSchema = z.union([z.object({
|
|
99
|
+
name: z.string().min(1),
|
|
100
|
+
value: z.string()
|
|
101
|
+
}).strict(), z.object({
|
|
102
|
+
name: z.string().min(1),
|
|
103
|
+
valueFrom: z.object({ secretKeyRef: z.object({
|
|
104
|
+
key: z.string().min(1),
|
|
105
|
+
name: kubernetesNameSchema
|
|
106
|
+
}).strict() }).strict()
|
|
107
|
+
}).strict()]);
|
|
95
108
|
const argoWorkflowTemplateSchema = z.object({
|
|
96
109
|
container: z.object({
|
|
97
110
|
args: z.array(z.string().min(1)).min(1),
|
|
98
111
|
command: z.array(z.string().min(1)).min(1).optional(),
|
|
99
|
-
env: z.array(
|
|
100
|
-
name: z.string().min(1),
|
|
101
|
-
value: z.string()
|
|
102
|
-
}).strict()).optional(),
|
|
112
|
+
env: z.array(argoWorkflowEnvVarSchema).optional(),
|
|
103
113
|
image: z.string().min(1),
|
|
104
114
|
imagePullPolicy: z.enum([
|
|
105
115
|
"Always",
|
|
@@ -169,11 +179,11 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
169
179
|
labels: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
|
|
170
180
|
name: z.string().min(1).optional(),
|
|
171
181
|
namespace: kubernetesNameSchema,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}).strict()
|
|
182
|
+
brokerAuth: z.object({
|
|
183
|
+
secretKey: z.string().min(1).default("api-key"),
|
|
184
|
+
secretName: kubernetesNameSchema,
|
|
185
|
+
url: z.string().min(1).default("https://cliproxy.momokaya.ee")
|
|
186
|
+
}).strict(),
|
|
177
187
|
payloadConfigMapKey: z.string().min(1).default("payload.json"),
|
|
178
188
|
payloadConfigMapName: kubernetesNameSchema,
|
|
179
189
|
resources: argoWorkflowResourceRequirementsSchema.optional(),
|
|
@@ -300,43 +310,6 @@ function runnerWorkflowStorage(options, tasks) {
|
|
|
300
310
|
readOnly: true
|
|
301
311
|
});
|
|
302
312
|
}
|
|
303
|
-
if (options.opencodeAuthSecretName) {
|
|
304
|
-
volumes.push({
|
|
305
|
-
name: "opencode-auth",
|
|
306
|
-
secret: {
|
|
307
|
-
defaultMode: 256,
|
|
308
|
-
items: [{
|
|
309
|
-
key: "auth.json",
|
|
310
|
-
path: "auth.json"
|
|
311
|
-
}],
|
|
312
|
-
secretName: options.opencodeAuthSecretName
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
volumeMounts.push({
|
|
316
|
-
mountPath: OPENCODE_AUTH_STAGING_DIR,
|
|
317
|
-
name: "opencode-auth",
|
|
318
|
-
readOnly: true
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
if (options.opencodeOpenaiAccountsSecret) {
|
|
322
|
-
const accountsKey = options.opencodeOpenaiAccountsSecret.key ?? "accounts.json";
|
|
323
|
-
volumes.push({
|
|
324
|
-
name: "opencode-openai-accounts",
|
|
325
|
-
secret: {
|
|
326
|
-
defaultMode: 256,
|
|
327
|
-
items: [{
|
|
328
|
-
key: accountsKey,
|
|
329
|
-
path: "accounts.json"
|
|
330
|
-
}],
|
|
331
|
-
secretName: options.opencodeOpenaiAccountsSecret.name
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
volumeMounts.push({
|
|
335
|
-
mountPath: OPENCODE_OPENAI_ACCOUNTS_STAGING_DIR,
|
|
336
|
-
name: "opencode-openai-accounts",
|
|
337
|
-
readOnly: true
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
313
|
if (options.gitCredentialsSecretName) {
|
|
341
314
|
volumes.push({
|
|
342
315
|
name: "runner-git-credentials",
|
|
@@ -374,6 +347,35 @@ function runnerWorkflowStorage(options, tasks) {
|
|
|
374
347
|
volumes: z.array(argoWorkflowVolumeSchema).parse(volumes)
|
|
375
348
|
};
|
|
376
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* The runner container env: static opencode/agent tuning plus BROKER_URL
|
|
352
|
+
* (literal) and BROKER_API_KEY (sourced from the broker secret key, never
|
|
353
|
+
* inlined into the manifest).
|
|
354
|
+
*/
|
|
355
|
+
function runnerContainerEnv(options) {
|
|
356
|
+
return [
|
|
357
|
+
...RUNNER_OPENCODE_ENV,
|
|
358
|
+
{
|
|
359
|
+
name: "BROKER_URL",
|
|
360
|
+
value: options.brokerAuth.url
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: "PIPELINE_BROKER_SECRET_NAME",
|
|
364
|
+
value: options.brokerAuth.secretName
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "PIPELINE_BROKER_SECRET_KEY",
|
|
368
|
+
value: options.brokerAuth.secretKey
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
name: "BROKER_API_KEY",
|
|
372
|
+
valueFrom: { secretKeyRef: {
|
|
373
|
+
key: options.brokerAuth.secretKey,
|
|
374
|
+
name: options.brokerAuth.secretName
|
|
375
|
+
} }
|
|
376
|
+
}
|
|
377
|
+
];
|
|
378
|
+
}
|
|
377
379
|
function runnerLifecycleTemplate(options, volumeMounts) {
|
|
378
380
|
return {
|
|
379
381
|
container: {
|
|
@@ -387,7 +389,7 @@ function runnerLifecycleTemplate(options, volumeMounts) {
|
|
|
387
389
|
RUNNER_WORKFLOW_SCHEDULE_PATH
|
|
388
390
|
],
|
|
389
391
|
command: ["moka"],
|
|
390
|
-
env:
|
|
392
|
+
env: runnerContainerEnv(options),
|
|
391
393
|
image: options.image,
|
|
392
394
|
imagePullPolicy: options.imagePullPolicy,
|
|
393
395
|
name: "runner",
|
|
@@ -416,7 +418,7 @@ function runnerCommandTemplate(task, options, volumeMounts) {
|
|
|
416
418
|
RUNNER_WORKFLOW_SCHEDULE_PATH
|
|
417
419
|
],
|
|
418
420
|
command: ["moka"],
|
|
419
|
-
env:
|
|
421
|
+
env: runnerContainerEnv(options),
|
|
420
422
|
image: options.image,
|
|
421
423
|
imagePullPolicy: options.imagePullPolicy,
|
|
422
424
|
name: "runner",
|
|
@@ -441,7 +443,7 @@ function runnerFinalizerTemplate(options, volumeMounts) {
|
|
|
441
443
|
"{{workflow.status}}"
|
|
442
444
|
],
|
|
443
445
|
command: ["moka"],
|
|
444
|
-
env:
|
|
446
|
+
env: runnerContainerEnv(options),
|
|
445
447
|
image: options.image,
|
|
446
448
|
imagePullPolicy: options.imagePullPolicy,
|
|
447
449
|
name: "runner",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/broker-auth.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Submit-time broker auth options: the runner sources BROKER_API_KEY from
|
|
6
|
+
* `secretName[secretKey]` and BROKER_URL from `url`. Shared by every submit
|
|
7
|
+
* entrypoint so the broker wiring is declared in exactly one place.
|
|
8
|
+
*/
|
|
9
|
+
declare const brokerAuthOptionSchema: z.ZodObject<{
|
|
10
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
11
|
+
secretName: z.ZodString;
|
|
12
|
+
url: z.ZodDefault<z.ZodString>;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
type BrokerAuthOption = z.input<typeof brokerAuthOptionSchema>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { BrokerAuthOption };
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { applyJsonEdit, ensureTrailingNewline, formatJson, parseJsonRecord } from "./json-config-merge.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/broker-auth.ts
|
|
4
|
+
/**
|
|
5
|
+
* Central CLIProxyAPI broker auth for codex + opencode.
|
|
6
|
+
*
|
|
7
|
+
* When `BROKER_API_KEY` is present, codex and opencode authenticate through the
|
|
8
|
+
* central broker (an OpenAI-compatible `/v1` endpoint) instead of materializing
|
|
9
|
+
* the bespoke multi-auth account pool. The broker owns OAuth refresh / rotation
|
|
10
|
+
* / failover, so the runner no longer stages `oc-codex-multi-auth` accounts, the
|
|
11
|
+
* mounted `~/.codex/auth.json`, or the multi-auth opencode plugin.
|
|
12
|
+
*
|
|
13
|
+
* This mirrors the proven coder dev-workspace template
|
|
14
|
+
* (infra: coder-templates/dev-workspace/main.tf):
|
|
15
|
+
* - codex ~/.codex/config.toml: `model_provider = "broker"` +
|
|
16
|
+
* `[model_providers.broker]` base_url=<broker>/v1 env_key=BROKER_API_KEY
|
|
17
|
+
* wire_api="responses".
|
|
18
|
+
* - opencode global config: `provider.openai.options.baseURL=<broker>/v1`
|
|
19
|
+
* plus `store=false` and `include=["reasoning.encrypted_content"]` (required
|
|
20
|
+
* by the Codex/Responses backend the broker fronts).
|
|
21
|
+
* - opencode auth store: `{"openai":{"type":"api","key":<BROKER_API_KEY>}}`.
|
|
22
|
+
*/
|
|
23
|
+
const BROKER_API_KEY_ENV = "BROKER_API_KEY";
|
|
24
|
+
const BROKER_URL_ENV = "BROKER_URL";
|
|
25
|
+
const DEFAULT_BROKER_URL = "https://cliproxy.momokaya.ee";
|
|
26
|
+
const TRAILING_SLASH_RE = /\/+$/;
|
|
27
|
+
const CODEX_BROKER_PROVIDER_ID = "broker";
|
|
28
|
+
const OPENCODE_OPENAI_PROVIDER_ID = "openai";
|
|
29
|
+
/** Plugin removed from opencode config in broker mode (broker fronts auth). */
|
|
30
|
+
const OC_CODEX_MULTI_AUTH_PLUGIN_NAME = "oc-codex-multi-auth";
|
|
31
|
+
/**
|
|
32
|
+
* Submit-time broker auth options: the runner sources BROKER_API_KEY from
|
|
33
|
+
* `secretName[secretKey]` and BROKER_URL from `url`. Shared by every submit
|
|
34
|
+
* entrypoint so the broker wiring is declared in exactly one place.
|
|
35
|
+
*/
|
|
36
|
+
const brokerAuthOptionSchema = z.object({
|
|
37
|
+
secretKey: z.string().min(1).default("api-key"),
|
|
38
|
+
secretName: z.string().min(1),
|
|
39
|
+
url: z.string().min(1).default(DEFAULT_BROKER_URL)
|
|
40
|
+
}).strict();
|
|
41
|
+
/**
|
|
42
|
+
* Resolve broker credentials from the environment, or `undefined` when a
|
|
43
|
+
* non-runner caller has not supplied broker auth. Remote runner manifests always
|
|
44
|
+
* inject BROKER_API_KEY. The `BROKER_URL` env is optional and defaults to the
|
|
45
|
+
* production broker origin.
|
|
46
|
+
*/
|
|
47
|
+
function resolveBrokerCredentials(env = process.env) {
|
|
48
|
+
const apiKey = env[BROKER_API_KEY_ENV];
|
|
49
|
+
if (apiKey === void 0 || apiKey.length === 0) return;
|
|
50
|
+
const rawUrl = env[BROKER_URL_ENV];
|
|
51
|
+
return {
|
|
52
|
+
apiKey,
|
|
53
|
+
baseUrl: rawUrl !== void 0 && rawUrl.length > 0 ? rawUrl.replace(TRAILING_SLASH_RE, "") : DEFAULT_BROKER_URL
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** The broker's OpenAI-compatible endpoint (`<baseUrl>/v1`). */
|
|
57
|
+
function brokerV1Url(credentials) {
|
|
58
|
+
return `${credentials.baseUrl}/v1`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* opencode host auth store contents for broker mode — the `openai` provider
|
|
62
|
+
* authenticates with the broker api-key. Other providers in an existing store
|
|
63
|
+
* are intentionally NOT preserved here: in broker mode the runner owns this
|
|
64
|
+
* file outright (the multi-auth pool that previously populated it is gone).
|
|
65
|
+
*/
|
|
66
|
+
function renderOpencodeBrokerAuthJson(credentials) {
|
|
67
|
+
return formatJson({ [OPENCODE_OPENAI_PROVIDER_ID]: {
|
|
68
|
+
key: credentials.apiKey,
|
|
69
|
+
type: "api"
|
|
70
|
+
} });
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Inject the codex broker model provider into an existing `config.toml`,
|
|
74
|
+
* preserving every other section. Idempotent: re-running replaces the
|
|
75
|
+
* `[model_providers.broker]` block and the top-level `model_provider` key.
|
|
76
|
+
*/
|
|
77
|
+
function applyCodexBrokerProvider(currentText, credentials) {
|
|
78
|
+
const withoutProvider = removeCodexBrokerSections(currentText ?? "");
|
|
79
|
+
const projection = renderCodexBrokerProvider(credentials);
|
|
80
|
+
return ensureTrailingNewline([withoutProvider.trimEnd(), projection.trimEnd()].filter(Boolean).join("\n\n"));
|
|
81
|
+
}
|
|
82
|
+
function renderCodexBrokerProvider(credentials) {
|
|
83
|
+
return [
|
|
84
|
+
`model_provider = "${CODEX_BROKER_PROVIDER_ID}"`,
|
|
85
|
+
"",
|
|
86
|
+
`[model_providers.${CODEX_BROKER_PROVIDER_ID}]`,
|
|
87
|
+
`name = "${CODEX_BROKER_PROVIDER_ID}"`,
|
|
88
|
+
`base_url = "${brokerV1Url(credentials)}"`,
|
|
89
|
+
`env_key = "${BROKER_API_KEY_ENV}"`,
|
|
90
|
+
"wire_api = \"responses\""
|
|
91
|
+
].join("\n");
|
|
92
|
+
}
|
|
93
|
+
const CODEX_BROKER_SECTION_HEADER = `[model_providers.${CODEX_BROKER_PROVIDER_ID}]`;
|
|
94
|
+
const CODEX_MODEL_PROVIDER_KEY_RE = /^\s*model_provider\s*=/;
|
|
95
|
+
function removeCodexBrokerSections(content) {
|
|
96
|
+
const lines = content.split("\n");
|
|
97
|
+
const kept = [];
|
|
98
|
+
let removing = false;
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
if (line.trim() === CODEX_BROKER_SECTION_HEADER) {
|
|
101
|
+
removing = true;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (removing && isTomlSectionHeader(line)) removing = false;
|
|
105
|
+
if (removing) continue;
|
|
106
|
+
if (CODEX_MODEL_PROVIDER_KEY_RE.test(line)) continue;
|
|
107
|
+
kept.push(line);
|
|
108
|
+
}
|
|
109
|
+
return kept.join("\n");
|
|
110
|
+
}
|
|
111
|
+
function isTomlSectionHeader(line) {
|
|
112
|
+
const trimmed = line.trim();
|
|
113
|
+
return trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Point an opencode config (global or project `opencode.json`) at the broker:
|
|
117
|
+
* set `provider.openai.options.{baseURL,store,include}` and drop the
|
|
118
|
+
* `oc-codex-multi-auth` plugin entry. Preserves all other config (models, mcp,
|
|
119
|
+
* other plugins). Creates a minimal config when `currentText` is undefined.
|
|
120
|
+
*/
|
|
121
|
+
function applyOpencodeBrokerProvider(currentText, credentials) {
|
|
122
|
+
const options = {
|
|
123
|
+
baseURL: brokerV1Url(credentials),
|
|
124
|
+
include: ["reasoning.encrypted_content"],
|
|
125
|
+
store: false
|
|
126
|
+
};
|
|
127
|
+
if (currentText === void 0) return { content: formatJson({
|
|
128
|
+
$schema: "https://opencode.ai/config.json",
|
|
129
|
+
provider: { [OPENCODE_OPENAI_PROVIDER_ID]: { options } }
|
|
130
|
+
}) };
|
|
131
|
+
const parsed = parseJsonRecord(currentText);
|
|
132
|
+
if (!parsed.ok) return { error: "invalid opencode config JSON" };
|
|
133
|
+
const withProvider = applyJsonEdit(currentText, [
|
|
134
|
+
"provider",
|
|
135
|
+
OPENCODE_OPENAI_PROVIDER_ID,
|
|
136
|
+
"options"
|
|
137
|
+
], mergeOpenaiOptions(parsed.value, options));
|
|
138
|
+
const nextPlugins = pluginsWithoutMultiAuth(parsed.value.plugin);
|
|
139
|
+
return { content: ensureTrailingNewline(nextPlugins === void 0 ? withProvider : applyJsonEdit(withProvider, ["plugin"], nextPlugins)) };
|
|
140
|
+
}
|
|
141
|
+
function mergeOpenaiOptions(parsed, options) {
|
|
142
|
+
return {
|
|
143
|
+
...currentOpenaiOptions(parsed),
|
|
144
|
+
...options
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function currentOpenaiOptions(parsed) {
|
|
148
|
+
const provider = parsed.provider;
|
|
149
|
+
if (!isRecord(provider)) return {};
|
|
150
|
+
const openai = provider[OPENCODE_OPENAI_PROVIDER_ID];
|
|
151
|
+
if (!isRecord(openai)) return {};
|
|
152
|
+
return isRecord(openai.options) ? openai.options : {};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Return the plugin array with every `oc-codex-multi-auth` entry removed, or
|
|
156
|
+
* `undefined` when there was nothing to remove (so the caller can skip the
|
|
157
|
+
* edit). Handles bare-string and `[name, opts]` tuple plugin specifiers.
|
|
158
|
+
*/
|
|
159
|
+
function pluginsWithoutMultiAuth(plugin) {
|
|
160
|
+
if (!Array.isArray(plugin)) return;
|
|
161
|
+
const filtered = plugin.filter((entry) => !isMultiAuthPlugin(entry));
|
|
162
|
+
return filtered.length === plugin.length ? void 0 : filtered;
|
|
163
|
+
}
|
|
164
|
+
function isMultiAuthPlugin(entry) {
|
|
165
|
+
const specifier = Array.isArray(entry) ? entry[0] : entry;
|
|
166
|
+
if (typeof specifier !== "string") return false;
|
|
167
|
+
return (specifier.indexOf("@", 1) === -1 ? specifier : specifier.slice(0, specifier.indexOf("@", 1))) === OC_CODEX_MULTI_AUTH_PLUGIN_NAME;
|
|
168
|
+
}
|
|
169
|
+
function isRecord(value) {
|
|
170
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { applyCodexBrokerProvider, applyOpencodeBrokerProvider, brokerAuthOptionSchema, renderOpencodeBrokerAuthJson, resolveBrokerCredentials };
|
package/dist/cli/doctor.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { PipelineConfigError } from "../config/schemas.js";
|
|
2
2
|
import { loadPipelineConfig } from "../config/load.js";
|
|
3
3
|
import "../config.js";
|
|
4
|
-
import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
|
|
5
4
|
import { loadMokaGlobalConfig } from "../moka-global-config.js";
|
|
5
|
+
import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
|
|
6
6
|
import { defaultClusterDoctorNamespace, runClusterDoctor } from "../cluster-doctor.js";
|
|
7
7
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
9
8
|
import { execa } from "execa";
|
|
9
|
+
import { join } from "node:path";
|
|
10
10
|
import matter from "gray-matter";
|
|
11
11
|
//#region src/cli/doctor.ts
|
|
12
12
|
const HEADLESS_AGENT_PERMISSION_VALUES = new Set(["ask"]);
|
package/dist/cli/program.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RunEffort, RunMode, RunTarget } from "../run-control/contracts.js";
|
|
2
2
|
import { RunCommand } from "./run-command.js";
|
|
3
|
-
import { TicketCommandOptions } from "../commands/ticket
|
|
3
|
+
import { TicketCommandOptions } from "../commands/ticket/shared.js";
|
|
4
4
|
import { runPipelineFromConfig } from "../pipeline-runtime.js";
|
|
5
5
|
import { Effect } from "effect";
|
|
6
6
|
import { Command } from "commander";
|