@oisincoveney/pipeline 3.12.3 → 3.13.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 +5 -5
- package/dist/agent-assets.js +7 -0
- package/dist/argo-submit.d.ts +5 -0
- package/dist/argo-submit.js +3 -0
- package/dist/argo-workflow.d.ts +15 -2
- package/dist/argo-workflow.js +41 -7
- package/dist/broker-auth.d.ts +16 -0
- package/dist/broker-auth.js +172 -0
- package/dist/cli/program.js +1 -0
- package/dist/cli/submit-options.js +1 -0
- package/dist/cluster-doctor.js +4 -2
- package/dist/codex-auth-sync.js +14 -1
- package/dist/config/lint.js +1 -1
- package/dist/config/schemas.d.ts +4 -4
- package/dist/install-commands/claude-code.js +1 -1
- package/dist/install-commands/opencode.js +1 -1
- package/dist/install-commands.js +2 -2
- package/dist/install-hooks.js +25 -16
- package/dist/install-rules.js +5 -4
- package/dist/loop/loop-command.js +1 -0
- package/dist/moka-global-config.d.ts +6 -1
- package/dist/moka-global-config.js +3 -1
- package/dist/moka-submit.d.ts +18 -6
- package/dist/moka-submit.js +3 -0
- package/dist/pipeline-init.js +3 -2
- package/dist/run-state/opencode-accounts.js +45 -4
- package/dist/runner-command/run.js +1 -0
- package/dist/runner-event-schema.d.ts +6 -6
- package/dist/runtime/opencode-session-executor.js +1 -5
- package/docs/config-architecture.md +7 -8
- package/docs/mcp-gateway.md +42 -0
- package/docs/operator-guide.md +9 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,10 +45,10 @@ moka init
|
|
|
45
45
|
|
|
46
46
|
`moka init` installs or refreshes the whole per-machine harness in one step:
|
|
47
47
|
the package's default skills, generated host command surfaces, the singleton
|
|
48
|
-
`pipeline-gateway` MCP entry, copied hook files from
|
|
49
|
-
`oisin-ee/agent
|
|
50
|
-
package default runtime. The command does not create repo-local
|
|
51
|
-
config files.
|
|
48
|
+
`pipeline-gateway` MCP entry, copied hook files from private
|
|
49
|
+
`oisin-ee/agent/hooks`, and global instruction files from `oisin-ee/agent/rules`.
|
|
50
|
+
OpenCode is the package default runtime. The command does not create repo-local
|
|
51
|
+
`.pipeline` config files.
|
|
52
52
|
|
|
53
53
|
The default MCP gateway can run locally or point at the hosted Momokaya gateway.
|
|
54
54
|
Set `PIPELINE_MCP_GATEWAY_AUTHORIZATION` to the full HTTP `Authorization` header
|
|
@@ -59,7 +59,7 @@ export PIPELINE_MCP_GATEWAY_AUTHORIZATION="Basic $(printf '%s' 'user:password' |
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
Verify the generated harness (commands, hooks, rules) is current after package
|
|
62
|
-
upgrades or edits to `oisin-ee/agent
|
|
62
|
+
upgrades or edits to `oisin-ee/agent`, without writing anything:
|
|
63
63
|
|
|
64
64
|
```shell
|
|
65
65
|
moka init --check
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/agent-assets.ts
|
|
2
|
+
const AGENT_ASSET_SOURCE = "oisin-ee/agent";
|
|
3
|
+
const AGENT_SKILL_SOURCE = "oisin-ee/agent/skills";
|
|
4
|
+
const AGENT_HOOKS_DIR = "hooks";
|
|
5
|
+
const AGENT_RULES_DIR = "rules";
|
|
6
|
+
//#endregion
|
|
7
|
+
export { AGENT_ASSET_SOURCE, AGENT_HOOKS_DIR, AGENT_RULES_DIR, AGENT_SKILL_SOURCE };
|
package/dist/argo-submit.d.ts
CHANGED
|
@@ -20,6 +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
|
+
brokerAuth: z.ZodOptional<z.ZodObject<{
|
|
24
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
25
|
+
secretName: z.ZodString;
|
|
26
|
+
url: z.ZodDefault<z.ZodString>;
|
|
27
|
+
}, z.core.$strict>>;
|
|
23
28
|
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
24
29
|
opencodeOpenaiAccountsSecretName: z.ZodOptional<z.ZodString>;
|
|
25
30
|
payloadJson: 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,6 +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),
|
|
42
|
+
brokerAuth: brokerAuthOptionSchema.optional(),
|
|
41
43
|
opencodeAuthSecretName: z.string().min(1).optional(),
|
|
42
44
|
opencodeOpenaiAccountsSecretName: z.string().min(1).optional(),
|
|
43
45
|
payloadJson: z.string().min(1),
|
|
@@ -89,6 +91,7 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
|
|
|
89
91
|
labels,
|
|
90
92
|
name: options.name,
|
|
91
93
|
namespace: options.namespace,
|
|
94
|
+
brokerAuth: options.brokerAuth,
|
|
92
95
|
opencodeAuthSecretName: options.opencodeAuthSecretName,
|
|
93
96
|
opencodeOpenaiAccountsSecret: options.opencodeOpenaiAccountsSecretName ? { name: options.opencodeOpenaiAccountsSecretName } : void 0,
|
|
94
97
|
payloadConfigMapName,
|
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,6 +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;
|
|
148
|
+
brokerAuth: z.ZodOptional<z.ZodObject<{
|
|
149
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
150
|
+
secretName: z.ZodString;
|
|
151
|
+
url: z.ZodDefault<z.ZodString>;
|
|
152
|
+
}, z.core.$strict>>;
|
|
140
153
|
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
141
154
|
opencodeOpenaiAccountsSecret: z.ZodOptional<z.ZodObject<{
|
|
142
155
|
key: z.ZodOptional<z.ZodString>;
|
package/dist/argo-workflow.js
CHANGED
|
@@ -92,14 +92,21 @@ const argoWorkflowRetryStrategySchema = z.object({
|
|
|
92
92
|
"OnTransientError"
|
|
93
93
|
])
|
|
94
94
|
}).strict();
|
|
95
|
+
const argoWorkflowEnvVarSchema = z.union([z.object({
|
|
96
|
+
name: z.string().min(1),
|
|
97
|
+
value: z.string()
|
|
98
|
+
}).strict(), z.object({
|
|
99
|
+
name: z.string().min(1),
|
|
100
|
+
valueFrom: z.object({ secretKeyRef: z.object({
|
|
101
|
+
key: z.string().min(1),
|
|
102
|
+
name: kubernetesNameSchema
|
|
103
|
+
}).strict() }).strict()
|
|
104
|
+
}).strict()]);
|
|
95
105
|
const argoWorkflowTemplateSchema = z.object({
|
|
96
106
|
container: z.object({
|
|
97
107
|
args: z.array(z.string().min(1)).min(1),
|
|
98
108
|
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(),
|
|
109
|
+
env: z.array(argoWorkflowEnvVarSchema).optional(),
|
|
103
110
|
image: z.string().min(1),
|
|
104
111
|
imagePullPolicy: z.enum([
|
|
105
112
|
"Always",
|
|
@@ -169,6 +176,11 @@ const buildRunnerArgoWorkflowOptionsSchema = z.object({
|
|
|
169
176
|
labels: z.record(z.string().min(1), z.string().min(1).optional()).default({}),
|
|
170
177
|
name: z.string().min(1).optional(),
|
|
171
178
|
namespace: kubernetesNameSchema,
|
|
179
|
+
brokerAuth: z.object({
|
|
180
|
+
secretKey: z.string().min(1).default("api-key"),
|
|
181
|
+
secretName: kubernetesNameSchema,
|
|
182
|
+
url: z.string().min(1).default("https://cliproxy.momokaya.ee")
|
|
183
|
+
}).strict().optional(),
|
|
172
184
|
opencodeAuthSecretName: kubernetesNameSchema.optional(),
|
|
173
185
|
opencodeOpenaiAccountsSecret: z.object({
|
|
174
186
|
key: z.string().min(1).optional(),
|
|
@@ -374,6 +386,28 @@ function runnerWorkflowStorage(options, tasks) {
|
|
|
374
386
|
volumes: z.array(argoWorkflowVolumeSchema).parse(volumes)
|
|
375
387
|
};
|
|
376
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* The runner container env: the static opencode/agent tuning plus, when broker
|
|
391
|
+
* auth is configured, BROKER_URL (literal) and BROKER_API_KEY (sourced from the
|
|
392
|
+
* broker secret key, never inlined into the manifest).
|
|
393
|
+
*/
|
|
394
|
+
function runnerContainerEnv(options) {
|
|
395
|
+
if (!options.brokerAuth) return [...RUNNER_OPENCODE_ENV];
|
|
396
|
+
return [
|
|
397
|
+
...RUNNER_OPENCODE_ENV,
|
|
398
|
+
{
|
|
399
|
+
name: "BROKER_URL",
|
|
400
|
+
value: options.brokerAuth.url
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
name: "BROKER_API_KEY",
|
|
404
|
+
valueFrom: { secretKeyRef: {
|
|
405
|
+
key: options.brokerAuth.secretKey,
|
|
406
|
+
name: options.brokerAuth.secretName
|
|
407
|
+
} }
|
|
408
|
+
}
|
|
409
|
+
];
|
|
410
|
+
}
|
|
377
411
|
function runnerLifecycleTemplate(options, volumeMounts) {
|
|
378
412
|
return {
|
|
379
413
|
container: {
|
|
@@ -387,7 +421,7 @@ function runnerLifecycleTemplate(options, volumeMounts) {
|
|
|
387
421
|
RUNNER_WORKFLOW_SCHEDULE_PATH
|
|
388
422
|
],
|
|
389
423
|
command: ["moka"],
|
|
390
|
-
env:
|
|
424
|
+
env: runnerContainerEnv(options),
|
|
391
425
|
image: options.image,
|
|
392
426
|
imagePullPolicy: options.imagePullPolicy,
|
|
393
427
|
name: "runner",
|
|
@@ -416,7 +450,7 @@ function runnerCommandTemplate(task, options, volumeMounts) {
|
|
|
416
450
|
RUNNER_WORKFLOW_SCHEDULE_PATH
|
|
417
451
|
],
|
|
418
452
|
command: ["moka"],
|
|
419
|
-
env:
|
|
453
|
+
env: runnerContainerEnv(options),
|
|
420
454
|
image: options.image,
|
|
421
455
|
imagePullPolicy: options.imagePullPolicy,
|
|
422
456
|
name: "runner",
|
|
@@ -441,7 +475,7 @@ function runnerFinalizerTemplate(options, volumeMounts) {
|
|
|
441
475
|
"{{workflow.status}}"
|
|
442
476
|
],
|
|
443
477
|
command: ["moka"],
|
|
444
|
-
env:
|
|
478
|
+
env: runnerContainerEnv(options),
|
|
445
479
|
image: options.image,
|
|
446
480
|
imagePullPolicy: options.imagePullPolicy,
|
|
447
481
|
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,172 @@
|
|
|
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 the
|
|
43
|
+
* runner is not broker-authenticated (local dev, non-broker fallback). The
|
|
44
|
+
* `BROKER_URL` env is optional and defaults to the production broker origin.
|
|
45
|
+
*/
|
|
46
|
+
function resolveBrokerCredentials(env = process.env) {
|
|
47
|
+
const apiKey = env[BROKER_API_KEY_ENV];
|
|
48
|
+
if (apiKey === void 0 || apiKey.length === 0) return;
|
|
49
|
+
const rawUrl = env[BROKER_URL_ENV];
|
|
50
|
+
return {
|
|
51
|
+
apiKey,
|
|
52
|
+
baseUrl: rawUrl !== void 0 && rawUrl.length > 0 ? rawUrl.replace(TRAILING_SLASH_RE, "") : DEFAULT_BROKER_URL
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** The broker's OpenAI-compatible endpoint (`<baseUrl>/v1`). */
|
|
56
|
+
function brokerV1Url(credentials) {
|
|
57
|
+
return `${credentials.baseUrl}/v1`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* opencode host auth store contents for broker mode — the `openai` provider
|
|
61
|
+
* authenticates with the broker api-key. Other providers in an existing store
|
|
62
|
+
* are intentionally NOT preserved here: in broker mode the runner owns this
|
|
63
|
+
* file outright (the multi-auth pool that previously populated it is gone).
|
|
64
|
+
*/
|
|
65
|
+
function renderOpencodeBrokerAuthJson(credentials) {
|
|
66
|
+
return formatJson({ [OPENCODE_OPENAI_PROVIDER_ID]: {
|
|
67
|
+
key: credentials.apiKey,
|
|
68
|
+
type: "api"
|
|
69
|
+
} });
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Inject the codex broker model provider into an existing `config.toml`,
|
|
73
|
+
* preserving every other section. Idempotent: re-running replaces the
|
|
74
|
+
* `[model_providers.broker]` block and the top-level `model_provider` key.
|
|
75
|
+
*/
|
|
76
|
+
function applyCodexBrokerProvider(currentText, credentials) {
|
|
77
|
+
const withoutProvider = removeCodexBrokerSections(currentText ?? "");
|
|
78
|
+
const projection = renderCodexBrokerProvider(credentials);
|
|
79
|
+
return ensureTrailingNewline([withoutProvider.trimEnd(), projection.trimEnd()].filter(Boolean).join("\n\n"));
|
|
80
|
+
}
|
|
81
|
+
function renderCodexBrokerProvider(credentials) {
|
|
82
|
+
return [
|
|
83
|
+
`model_provider = "${CODEX_BROKER_PROVIDER_ID}"`,
|
|
84
|
+
"",
|
|
85
|
+
`[model_providers.${CODEX_BROKER_PROVIDER_ID}]`,
|
|
86
|
+
`name = "${CODEX_BROKER_PROVIDER_ID}"`,
|
|
87
|
+
`base_url = "${brokerV1Url(credentials)}"`,
|
|
88
|
+
`env_key = "${BROKER_API_KEY_ENV}"`,
|
|
89
|
+
"wire_api = \"responses\""
|
|
90
|
+
].join("\n");
|
|
91
|
+
}
|
|
92
|
+
const CODEX_BROKER_SECTION_HEADER = `[model_providers.${CODEX_BROKER_PROVIDER_ID}]`;
|
|
93
|
+
const CODEX_MODEL_PROVIDER_KEY_RE = /^\s*model_provider\s*=/;
|
|
94
|
+
function removeCodexBrokerSections(content) {
|
|
95
|
+
const lines = content.split("\n");
|
|
96
|
+
const kept = [];
|
|
97
|
+
let removing = false;
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
if (line.trim() === CODEX_BROKER_SECTION_HEADER) {
|
|
100
|
+
removing = true;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (removing && isTomlSectionHeader(line)) removing = false;
|
|
104
|
+
if (removing) continue;
|
|
105
|
+
if (CODEX_MODEL_PROVIDER_KEY_RE.test(line)) continue;
|
|
106
|
+
kept.push(line);
|
|
107
|
+
}
|
|
108
|
+
return kept.join("\n");
|
|
109
|
+
}
|
|
110
|
+
function isTomlSectionHeader(line) {
|
|
111
|
+
const trimmed = line.trim();
|
|
112
|
+
return trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Point an opencode config (global or project `opencode.json`) at the broker:
|
|
116
|
+
* set `provider.openai.options.{baseURL,store,include}` and drop the
|
|
117
|
+
* `oc-codex-multi-auth` plugin entry. Preserves all other config (models, mcp,
|
|
118
|
+
* other plugins). Creates a minimal config when `currentText` is undefined.
|
|
119
|
+
*/
|
|
120
|
+
function applyOpencodeBrokerProvider(currentText, credentials) {
|
|
121
|
+
const options = {
|
|
122
|
+
baseURL: brokerV1Url(credentials),
|
|
123
|
+
include: ["reasoning.encrypted_content"],
|
|
124
|
+
store: false
|
|
125
|
+
};
|
|
126
|
+
if (currentText === void 0) return { content: formatJson({
|
|
127
|
+
$schema: "https://opencode.ai/config.json",
|
|
128
|
+
provider: { [OPENCODE_OPENAI_PROVIDER_ID]: { options } }
|
|
129
|
+
}) };
|
|
130
|
+
const parsed = parseJsonRecord(currentText);
|
|
131
|
+
if (!parsed.ok) return { error: "invalid opencode config JSON" };
|
|
132
|
+
const withProvider = applyJsonEdit(currentText, [
|
|
133
|
+
"provider",
|
|
134
|
+
OPENCODE_OPENAI_PROVIDER_ID,
|
|
135
|
+
"options"
|
|
136
|
+
], mergeOpenaiOptions(parsed.value, options));
|
|
137
|
+
const nextPlugins = pluginsWithoutMultiAuth(parsed.value.plugin);
|
|
138
|
+
return { content: ensureTrailingNewline(nextPlugins === void 0 ? withProvider : applyJsonEdit(withProvider, ["plugin"], nextPlugins)) };
|
|
139
|
+
}
|
|
140
|
+
function mergeOpenaiOptions(parsed, options) {
|
|
141
|
+
return {
|
|
142
|
+
...currentOpenaiOptions(parsed),
|
|
143
|
+
...options
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function currentOpenaiOptions(parsed) {
|
|
147
|
+
const provider = parsed.provider;
|
|
148
|
+
if (!isRecord(provider)) return {};
|
|
149
|
+
const openai = provider[OPENCODE_OPENAI_PROVIDER_ID];
|
|
150
|
+
if (!isRecord(openai)) return {};
|
|
151
|
+
return isRecord(openai.options) ? openai.options : {};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Return the plugin array with every `oc-codex-multi-auth` entry removed, or
|
|
155
|
+
* `undefined` when there was nothing to remove (so the caller can skip the
|
|
156
|
+
* edit). Handles bare-string and `[name, opts]` tuple plugin specifiers.
|
|
157
|
+
*/
|
|
158
|
+
function pluginsWithoutMultiAuth(plugin) {
|
|
159
|
+
if (!Array.isArray(plugin)) return;
|
|
160
|
+
const filtered = plugin.filter((entry) => !isMultiAuthPlugin(entry));
|
|
161
|
+
return filtered.length === plugin.length ? void 0 : filtered;
|
|
162
|
+
}
|
|
163
|
+
function isMultiAuthPlugin(entry) {
|
|
164
|
+
const specifier = Array.isArray(entry) ? entry[0] : entry;
|
|
165
|
+
if (typeof specifier !== "string") return false;
|
|
166
|
+
return (specifier.indexOf("@", 1) === -1 ? specifier : specifier.slice(0, specifier.indexOf("@", 1))) === OC_CODEX_MULTI_AUTH_PLUGIN_NAME;
|
|
167
|
+
}
|
|
168
|
+
function isRecord(value) {
|
|
169
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
export { applyCodexBrokerProvider, applyOpencodeBrokerProvider, brokerAuthOptionSchema, renderOpencodeBrokerAuthJson, resolveBrokerCredentials };
|
package/dist/cli/program.js
CHANGED
|
@@ -415,6 +415,7 @@ function buildLoopSubmitInput(options) {
|
|
|
415
415
|
githubAuthSecretName: momokaya?.submit.githubAuthSecretName,
|
|
416
416
|
kubeconfigPath: momokaya?.kubernetes.kubeconfig,
|
|
417
417
|
namespace: momokaya?.kubernetes.namespace,
|
|
418
|
+
brokerAuth: momokaya?.submit.brokerAuth,
|
|
418
419
|
opencodeAuthSecretName: momokaya?.submit.opencodeAuthSecretName,
|
|
419
420
|
opencodeOpenaiAccountsSecretName: momokaya?.submit.opencodeOpenaiAccountsSecretName,
|
|
420
421
|
serviceAccountName: momokaya?.submit.serviceAccountName,
|
|
@@ -48,6 +48,7 @@ function mokaCommonSubmitOptions(input) {
|
|
|
48
48
|
kubeconfigPath: input.flags.kubeconfig ?? momokaya?.kubernetes.kubeconfig,
|
|
49
49
|
name: input.flags.name,
|
|
50
50
|
namespace: input.flags.namespace ?? momokaya?.kubernetes.namespace,
|
|
51
|
+
brokerAuth: momokaya?.submit.brokerAuth,
|
|
51
52
|
opencodeAuthSecretName: momokaya?.submit.opencodeAuthSecretName,
|
|
52
53
|
opencodeOpenaiAccountsSecretName: momokaya?.submit.opencodeOpenaiAccountsSecretName,
|
|
53
54
|
serviceAccountName: input.flags.serviceAccount ?? momokaya?.submit.serviceAccountName,
|
package/dist/cluster-doctor.js
CHANGED
|
@@ -62,19 +62,21 @@ function clusterResources() {
|
|
|
62
62
|
const configured = loadMokaGlobalConfig()?.momokaya.submit;
|
|
63
63
|
return configured ? {
|
|
64
64
|
...DEFAULT_RESOURCES,
|
|
65
|
+
brokerAuthSecretName: configured.brokerAuth?.secretName,
|
|
65
66
|
eventAuthSecretName: configured.eventAuthSecretName,
|
|
66
67
|
gitCredentialsSecretName: configured.gitCredentialsSecretName,
|
|
67
68
|
githubAuthSecretName: configured.githubAuthSecretName,
|
|
68
69
|
imagePullSecretName: configured.imagePullSecretName,
|
|
69
|
-
opencodeAuthSecretName: configured.opencodeAuthSecretName,
|
|
70
|
+
opencodeAuthSecretName: configured.opencodeAuthSecretName ?? DEFAULT_RESOURCES.opencodeAuthSecretName,
|
|
70
71
|
serviceAccountName: configured.serviceAccountName
|
|
71
72
|
} : DEFAULT_RESOURCES;
|
|
72
73
|
}
|
|
73
74
|
function secretChecks(namespace, kubectlOptions, resources) {
|
|
75
|
+
const authSecretCheck = resources.brokerAuthSecretName ? [resources.brokerAuthSecretName, `Secret ${resources.brokerAuthSecretName} missing in ${namespace}; expected broker api-key mount by name.`] : [resources.opencodeAuthSecretName, `Secret ${resources.opencodeAuthSecretName} missing in ${namespace}; expected OpenCode auth mount by name.`];
|
|
74
76
|
return [
|
|
75
77
|
[resources.eventAuthSecretName, eventAuthMissingDetail(namespace)],
|
|
76
78
|
[resources.imagePullSecretName, `Secret ${resources.imagePullSecretName} missing in ${namespace}; expected imagePullSecret for ghcr.io/oisin-ee/pipeline-runner.`],
|
|
77
|
-
|
|
79
|
+
authSecretCheck,
|
|
78
80
|
[resources.gitCredentialsSecretName, `Secret ${resources.gitCredentialsSecretName} missing in ${namespace}; expected runner git credentials mount by name.`],
|
|
79
81
|
[resources.githubAuthSecretName, `Secret ${resources.githubAuthSecretName} missing in ${namespace}; expected GitHub auth mount by name.`]
|
|
80
82
|
].map(([name, missingDetail]) => checkNamespacedResource(`secret/${name}`, [
|
package/dist/codex-auth-sync.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyOpencodeBrokerProvider, resolveBrokerCredentials } from "./broker-auth.js";
|
|
1
2
|
import { mergeOpenCodeProjectConfig } from "./opencode-project-config.js";
|
|
2
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { homedir } from "node:os";
|
|
@@ -7,7 +8,8 @@ import { parse } from "jsonc-parser";
|
|
|
7
8
|
const CODEX_MULTI_AUTH_PLUGIN = "oc-codex-multi-auth@6.3.2";
|
|
8
9
|
const GLOBAL_CODEX_AUTH_CONFIG_PATH = join(homedir(), ".opencode/openai-codex-auth-config.json");
|
|
9
10
|
function syncLocalCodexAuth(options) {
|
|
10
|
-
const
|
|
11
|
+
const broker = options.broker === void 0 ? resolveBrokerCredentials() : options.broker;
|
|
12
|
+
const items = broker ? discoverGitRepositories(options.root).map((repo) => syncProjectBrokerConfig(repo, broker, options)) : [syncGlobalPluginConfig(options.globalConfigPath ?? GLOBAL_CODEX_AUTH_CONFIG_PATH, options), ...discoverGitRepositories(options.root).map((repo) => syncProjectOpenCodeConfig(repo, options))];
|
|
11
13
|
const hasRequiredChanges = items.some((item) => item.action === "create" || item.action === "update");
|
|
12
14
|
const hasErrors = items.some((item) => item.action === "error");
|
|
13
15
|
const checkFailed = options.check === true && hasRequiredChanges;
|
|
@@ -48,6 +50,17 @@ function syncProjectOpenCodeConfig(repo, options) {
|
|
|
48
50
|
};
|
|
49
51
|
return writeIfChanged(path, currentText, merged.content, options);
|
|
50
52
|
}
|
|
53
|
+
function syncProjectBrokerConfig(repo, broker, options) {
|
|
54
|
+
const path = join(repo, ".opencode/opencode.json");
|
|
55
|
+
const currentText = existsSync(path) ? readFileSync(path, "utf8") : void 0;
|
|
56
|
+
const result = applyOpencodeBrokerProvider(currentText, broker);
|
|
57
|
+
if ("error" in result) return {
|
|
58
|
+
action: "error",
|
|
59
|
+
message: result.error,
|
|
60
|
+
path
|
|
61
|
+
};
|
|
62
|
+
return writeIfChanged(path, currentText, result.content, options);
|
|
63
|
+
}
|
|
51
64
|
function writeIfChanged(path, currentText, nextText, options) {
|
|
52
65
|
if (currentText === nextText) return {
|
|
53
66
|
action: "unchanged",
|
package/dist/config/lint.js
CHANGED
|
@@ -61,7 +61,7 @@ function missingFileReferenceWarning(path, value) {
|
|
|
61
61
|
}
|
|
62
62
|
function missingFileReferenceMessage(path, value) {
|
|
63
63
|
const base = `${path} references missing file '${value}'`;
|
|
64
|
-
if (path.startsWith("skills.") && value.startsWith(".agents/skills/")) return `${base}. Run \`moka init\` to install project-local skills with \`npx --yes skills add oisin-ee/skills\`.`;
|
|
64
|
+
if (path.startsWith("skills.") && value.startsWith(".agents/skills/")) return `${base}. Run \`moka init\` to install project-local skills with \`npx --yes skills add oisin-ee/agent/skills\`.`;
|
|
65
65
|
return base;
|
|
66
66
|
}
|
|
67
67
|
function resolveLintPathReference(projectRoot, ref) {
|
package/dist/config/schemas.d.ts
CHANGED
|
@@ -233,8 +233,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
233
233
|
policy: z.ZodOptional<z.ZodObject<{
|
|
234
234
|
commands: z.ZodOptional<z.ZodEnum<{
|
|
235
235
|
allow: "allow";
|
|
236
|
-
deny: "deny";
|
|
237
236
|
"trusted-only": "trusted-only";
|
|
237
|
+
deny: "deny";
|
|
238
238
|
}>>;
|
|
239
239
|
modules: z.ZodOptional<z.ZodEnum<{
|
|
240
240
|
allow: "allow";
|
|
@@ -262,8 +262,8 @@ declare const configSchema: z.ZodObject<{
|
|
|
262
262
|
global: "global";
|
|
263
263
|
}>>;
|
|
264
264
|
mode: z.ZodEnum<{
|
|
265
|
-
local: "local";
|
|
266
265
|
hosted: "hosted";
|
|
266
|
+
local: "local";
|
|
267
267
|
}>;
|
|
268
268
|
provider: z.ZodLiteral<"toolhive">;
|
|
269
269
|
authorization_env: z.ZodDefault<z.ZodString>;
|
|
@@ -306,10 +306,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
306
306
|
}, z.core.$strict>>;
|
|
307
307
|
output: z.ZodOptional<z.ZodObject<{
|
|
308
308
|
format: z.ZodEnum<{
|
|
309
|
-
json_schema: "json_schema";
|
|
310
309
|
text: "text";
|
|
311
310
|
json: "json";
|
|
312
311
|
jsonl: "jsonl";
|
|
312
|
+
json_schema: "json_schema";
|
|
313
313
|
}>;
|
|
314
314
|
repair: z.ZodOptional<z.ZodObject<{
|
|
315
315
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -385,10 +385,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
385
385
|
disabled: "disabled";
|
|
386
386
|
}>>>;
|
|
387
387
|
output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
388
|
-
json_schema: "json_schema";
|
|
389
388
|
text: "text";
|
|
390
389
|
json: "json";
|
|
391
390
|
jsonl: "jsonl";
|
|
391
|
+
json_schema: "json_schema";
|
|
392
392
|
}>>>;
|
|
393
393
|
rules: z.ZodOptional<z.ZodBoolean>;
|
|
394
394
|
skills: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { CLAUDE_PROJECT_CONFIG_PATH, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost } from "./shared.js";
|
|
1
2
|
import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
|
|
2
3
|
import { mergeClaudeSettings } from "../claude-settings-config.js";
|
|
3
|
-
import { CLAUDE_PROJECT_CONFIG_PATH, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost } from "./shared.js";
|
|
4
4
|
import { agentDispatchRoutes, entrypointDispatchBlock, grants, header, markdown, projectAgentsMdDefinition, resolvedHostModel } from "./opencode.js";
|
|
5
5
|
//#region src/install-commands/claude-code.ts
|
|
6
6
|
const CLAUDE_CODE_HOST = "claude-code";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AGENTS_MD_END, AGENTS_MD_START, COMMAND_HOSTS, GENERATED_MARKER, GENERATED_TS_MARKER, OPENCODE_PROJECT_CONFIG_PATH, OWNER_MARKER_PREFIX, OWNER_TS_MARKER_PREFIX, SINGLE_OPENCODE_PLUGIN_ARRAY_RE, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost, profileEntries } from "./shared.js";
|
|
1
2
|
import { DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST } from "../config/defaults.js";
|
|
2
3
|
import { resolvePackageAssetPath } from "../package-assets.js";
|
|
3
4
|
import "../config.js";
|
|
@@ -6,7 +7,6 @@ import { renderOpenCodeGatewayConfig } from "../mcp/gateway.js";
|
|
|
6
7
|
import { compileWorkflowPlan } from "../planning/compile.js";
|
|
7
8
|
import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
|
|
8
9
|
import { mergeOpenCodeProjectConfig } from "../opencode-project-config.js";
|
|
9
|
-
import { AGENTS_MD_END, AGENTS_MD_START, COMMAND_HOSTS, GENERATED_MARKER, GENERATED_TS_MARKER, OPENCODE_PROJECT_CONFIG_PATH, OWNER_MARKER_PREFIX, OWNER_TS_MARKER_PREFIX, SINGLE_OPENCODE_PLUGIN_ARRAY_RE, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost, profileEntries } from "./shared.js";
|
|
10
10
|
import { Effect } from "effect";
|
|
11
11
|
import { basename } from "node:path";
|
|
12
12
|
import matter from "gray-matter";
|
package/dist/install-commands.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { isRecord } from "./json-config-merge.js";
|
|
2
|
+
import { CLAUDE_USER_CONFIG_PATH, CODEX_CONFIG_PATH, COMMAND_HOSTS, ENTRYPOINT_PATH_PATTERNS, INSTALL_HOSTS, invocationForHost, resolveHarnessTarget } from "./install-commands/shared.js";
|
|
1
3
|
import { loadPipelineConfig } from "./config/load.js";
|
|
2
4
|
import "./config.js";
|
|
3
|
-
import { isRecord } from "./json-config-merge.js";
|
|
4
5
|
import { mergeClaudeUserConfig } from "./claude-user-config.js";
|
|
5
6
|
import { mergeCodexConfig } from "./codex-config.js";
|
|
6
7
|
import { renderClaudeGatewayUserConfig, renderCodexGatewayConfig } from "./mcp/gateway.js";
|
|
7
|
-
import { CLAUDE_USER_CONFIG_PATH, CODEX_CONFIG_PATH, COMMAND_HOSTS, ENTRYPOINT_PATH_PATTERNS, INSTALL_HOSTS, invocationForHost, resolveHarnessTarget } from "./install-commands/shared.js";
|
|
8
8
|
import { opencodeAdapter } from "./install-commands/opencode.js";
|
|
9
9
|
import { claudeCodeAdapter } from "./install-commands/claude-code.js";
|
|
10
10
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
package/dist/install-hooks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { applyJsonEdit, ensureTrailingNewline, parseJsonRecord } from "./json-config-merge.js";
|
|
2
2
|
import { resolveHarnessTarget } from "./install-commands/shared.js";
|
|
3
|
+
import { AGENT_ASSET_SOURCE, AGENT_HOOKS_DIR } from "./agent-assets.js";
|
|
3
4
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
4
5
|
import { tmpdir } from "node:os";
|
|
5
6
|
import { dirname, join, relative } from "node:path";
|
|
@@ -7,7 +8,7 @@ import { execa } from "execa";
|
|
|
7
8
|
import { createHash } from "node:crypto";
|
|
8
9
|
import { mkdir, mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
|
|
9
10
|
//#region src/install-hooks.ts
|
|
10
|
-
const DEFAULT_HOOK_INSTALL_SOURCE =
|
|
11
|
+
const DEFAULT_HOOK_INSTALL_SOURCE = AGENT_ASSET_SOURCE;
|
|
11
12
|
const HOOK_HOSTS = [
|
|
12
13
|
"claude-code",
|
|
13
14
|
"codex",
|
|
@@ -19,6 +20,7 @@ const HOST_TARGET_ROOT = {
|
|
|
19
20
|
codex: ".codex",
|
|
20
21
|
opencode: ".opencode"
|
|
21
22
|
};
|
|
23
|
+
const NON_HOOK_OWNED_TARGETS = new Set([".opencode/opencode.json"]);
|
|
22
24
|
function hashContent(content) {
|
|
23
25
|
return createHash("sha256").update(content).digest("hex");
|
|
24
26
|
}
|
|
@@ -63,8 +65,8 @@ async function cloneHookRepository(targetDir) {
|
|
|
63
65
|
], { stdio: "inherit" });
|
|
64
66
|
}
|
|
65
67
|
async function withHookSource(useSource) {
|
|
66
|
-
const parent = await mkdtemp(join(tmpdir(), "moka-agent-
|
|
67
|
-
const source = join(parent, "agent
|
|
68
|
+
const parent = await mkdtemp(join(tmpdir(), "moka-agent-"));
|
|
69
|
+
const source = join(parent, "agent");
|
|
68
70
|
try {
|
|
69
71
|
await cloneHookRepository(source);
|
|
70
72
|
return await useSource(source);
|
|
@@ -86,17 +88,17 @@ async function listFiles(root) {
|
|
|
86
88
|
}
|
|
87
89
|
async function sourceHookFiles(source) {
|
|
88
90
|
return (await Promise.all(HOOK_HOSTS.map(async (host) => {
|
|
89
|
-
const hostRoot = join(source, host);
|
|
90
|
-
return (await listFiles(hostRoot)).
|
|
91
|
+
const hostRoot = join(source, AGENT_HOOKS_DIR, host);
|
|
92
|
+
return (await listFiles(hostRoot)).flatMap((file) => {
|
|
91
93
|
const relativePath = relative(hostRoot, file).replaceAll("\\", "/");
|
|
92
94
|
const content = readFileSync(file);
|
|
93
95
|
const path = `${HOST_TARGET_ROOT[host]}/${relativePath}`;
|
|
94
|
-
return {
|
|
96
|
+
return isHookOwnedTarget(path) ? [{
|
|
95
97
|
content,
|
|
96
98
|
hash: targetIdentityHash(path, content),
|
|
97
99
|
host,
|
|
98
100
|
path
|
|
99
|
-
};
|
|
101
|
+
}] : [];
|
|
100
102
|
});
|
|
101
103
|
}))).flat().sort((a, b) => a.path.localeCompare(b.path));
|
|
102
104
|
}
|
|
@@ -110,6 +112,9 @@ function emptyManifest() {
|
|
|
110
112
|
version: 1
|
|
111
113
|
};
|
|
112
114
|
}
|
|
115
|
+
function isHookOwnedTarget(path) {
|
|
116
|
+
return !NON_HOOK_OWNED_TARGETS.has(path);
|
|
117
|
+
}
|
|
113
118
|
function readManifest(host) {
|
|
114
119
|
const path = manifestPath(host);
|
|
115
120
|
if (!existsSync(path)) return emptyManifest();
|
|
@@ -153,18 +158,22 @@ function planFiles(files, force, manifests) {
|
|
|
153
158
|
function planObsoleteFiles(desiredPaths, force, manifests) {
|
|
154
159
|
const obsolete = [];
|
|
155
160
|
for (const [host, manifest] of manifests) for (const [path, entry] of Object.entries(manifest.files)) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (!existsSync(target)) continue;
|
|
159
|
-
const currentHash = hashContent(readFileSync(target));
|
|
160
|
-
obsolete.push({
|
|
161
|
-
action: force || currentHash === entry.hash ? "delete" : "conflict",
|
|
162
|
-
host,
|
|
163
|
-
path
|
|
164
|
-
});
|
|
161
|
+
const planned = planObsoleteFile(host, path, entry, force, desiredPaths);
|
|
162
|
+
if (planned) obsolete.push(planned);
|
|
165
163
|
}
|
|
166
164
|
return obsolete.sort((a, b) => a.path.localeCompare(b.path));
|
|
167
165
|
}
|
|
166
|
+
function planObsoleteFile(host, path, entry, force, desiredPaths) {
|
|
167
|
+
if (desiredPaths.has(path) || !isHookOwnedTarget(path)) return;
|
|
168
|
+
const target = targetPath(path);
|
|
169
|
+
if (!existsSync(target)) return;
|
|
170
|
+
const currentHash = hashContent(readFileSync(target));
|
|
171
|
+
return {
|
|
172
|
+
action: force || currentHash === entry.hash ? "delete" : "conflict",
|
|
173
|
+
host,
|
|
174
|
+
path
|
|
175
|
+
};
|
|
176
|
+
}
|
|
168
177
|
async function writePlannedFile(file) {
|
|
169
178
|
if (file.action === "conflict" || file.action === "unchanged") return;
|
|
170
179
|
const target = targetPath(file.path);
|
package/dist/install-rules.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { AGENT_ASSET_SOURCE, AGENT_RULES_DIR } from "./agent-assets.js";
|
|
1
2
|
import { homedir, tmpdir } from "node:os";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { execa } from "execa";
|
|
4
5
|
import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
6
|
//#region src/install-rules.ts
|
|
6
|
-
const DEFAULT_RULES_INSTALL_SOURCE =
|
|
7
|
+
const DEFAULT_RULES_INSTALL_SOURCE = AGENT_ASSET_SOURCE;
|
|
7
8
|
const RULESYNC_PACKAGE = "rulesync@8.30.1";
|
|
8
9
|
const RULESYNC_TARGETS = [
|
|
9
10
|
"claudecode",
|
|
@@ -23,8 +24,8 @@ async function cloneRulesRepository(targetDir) {
|
|
|
23
24
|
}
|
|
24
25
|
async function withRulesSource(sourceOverride, useSource) {
|
|
25
26
|
if (sourceOverride !== void 0) return useSource(sourceOverride);
|
|
26
|
-
const parent = await mkdtemp(join(tmpdir(), "moka-rules-"));
|
|
27
|
-
const source = join(parent, "
|
|
27
|
+
const parent = await mkdtemp(join(tmpdir(), "moka-agent-rules-"));
|
|
28
|
+
const source = join(parent, "agent");
|
|
28
29
|
try {
|
|
29
30
|
await cloneRulesRepository(source);
|
|
30
31
|
return await useSource(source);
|
|
@@ -52,7 +53,7 @@ async function defaultRulesyncRunner(args, opts) {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
async function buildRootRule(source) {
|
|
55
|
-
const rulesDir = join(source,
|
|
56
|
+
const rulesDir = join(source, AGENT_RULES_DIR);
|
|
56
57
|
let entries = [];
|
|
57
58
|
try {
|
|
58
59
|
entries = (await readdir(rulesDir, { withFileTypes: true })).filter((d) => d.isFile() && d.name.endsWith(".md")).map((d) => d.name).sort((a, b) => a.localeCompare(b));
|
|
@@ -80,6 +80,7 @@ function loopControllerSubmitInput(input) {
|
|
|
80
80
|
image: input.image,
|
|
81
81
|
kubeconfigPath: input.kubeconfigPath,
|
|
82
82
|
namespace: input.namespace,
|
|
83
|
+
brokerAuth: input.brokerAuth,
|
|
83
84
|
opencodeAuthSecretName: input.opencodeAuthSecretName,
|
|
84
85
|
opencodeOpenaiAccountsSecretName: input.opencodeOpenaiAccountsSecretName,
|
|
85
86
|
serviceAccountName: input.serviceAccountName,
|
|
@@ -9,13 +9,18 @@ declare const mokaGlobalConfigSchema: z.ZodObject<{
|
|
|
9
9
|
namespace: z.ZodString;
|
|
10
10
|
}, z.core.$strict>;
|
|
11
11
|
submit: z.ZodObject<{
|
|
12
|
+
brokerAuth: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
14
|
+
secretName: z.ZodString;
|
|
15
|
+
url: z.ZodDefault<z.ZodString>;
|
|
16
|
+
}, z.core.$strict>>;
|
|
12
17
|
eventAuthSecretKey: z.ZodString;
|
|
13
18
|
eventAuthSecretName: z.ZodString;
|
|
14
19
|
eventUrl: z.ZodString;
|
|
15
20
|
gitCredentialsSecretName: z.ZodString;
|
|
16
21
|
githubAuthSecretName: z.ZodString;
|
|
17
22
|
imagePullSecretName: z.ZodString;
|
|
18
|
-
opencodeAuthSecretName: z.ZodString
|
|
23
|
+
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
19
24
|
opencodeOpenaiAccountsSecretName: z.ZodOptional<z.ZodString>;
|
|
20
25
|
serviceAccountName: z.ZodString;
|
|
21
26
|
}, z.core.$strict>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brokerAuthOptionSchema } from "./broker-auth.js";
|
|
1
2
|
import { PipelineConfigError } from "./config/schemas.js";
|
|
2
3
|
import { ConfigIoService, runConfigIoSync } from "./runtime/services/config-io-service.js";
|
|
3
4
|
import "./config.js";
|
|
@@ -8,13 +9,14 @@ import { join } from "node:path";
|
|
|
8
9
|
//#region src/moka-global-config.ts
|
|
9
10
|
const MOKA_GLOBAL_CONFIG_PATH = ".config/moka/config.yaml";
|
|
10
11
|
const mokaSubmitGlobalConfigSchema = z.object({
|
|
12
|
+
brokerAuth: brokerAuthOptionSchema.optional(),
|
|
11
13
|
eventAuthSecretKey: z.string().min(1),
|
|
12
14
|
eventAuthSecretName: z.string().min(1),
|
|
13
15
|
eventUrl: z.string().url(),
|
|
14
16
|
gitCredentialsSecretName: z.string().min(1),
|
|
15
17
|
githubAuthSecretName: z.string().min(1),
|
|
16
18
|
imagePullSecretName: z.string().min(1),
|
|
17
|
-
opencodeAuthSecretName: z.string().min(1),
|
|
19
|
+
opencodeAuthSecretName: z.string().min(1).optional(),
|
|
18
20
|
opencodeOpenaiAccountsSecretName: z.string().min(1).optional(),
|
|
19
21
|
serviceAccountName: z.string().min(1)
|
|
20
22
|
}).strict();
|
package/dist/moka-submit.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { PipelineConfig } from "./config/schemas.js";
|
|
2
|
+
import { BrokerAuthOption } from "./broker-auth.js";
|
|
2
3
|
import { generateScheduleArtifact } from "./planning/generate.js";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
|
|
5
6
|
//#region src/moka-submit.d.ts
|
|
6
7
|
declare const mokaSubmitDirectHooksSchema: z.ZodRecord<z.ZodEnum<{
|
|
7
8
|
"workflow.start": "workflow.start";
|
|
8
|
-
"node.finish": "node.finish";
|
|
9
|
-
"node.start": "node.start";
|
|
10
9
|
"workflow.success": "workflow.success";
|
|
11
10
|
"workflow.failure": "workflow.failure";
|
|
12
11
|
"workflow.complete": "workflow.complete";
|
|
12
|
+
"node.start": "node.start";
|
|
13
13
|
"node.success": "node.success";
|
|
14
14
|
"node.error": "node.error";
|
|
15
|
+
"node.finish": "node.finish";
|
|
15
16
|
"gate.failure": "gate.failure";
|
|
16
17
|
}> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
17
18
|
failure: z.ZodDefault<z.ZodEnum<{
|
|
@@ -98,13 +99,13 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
98
99
|
}, z.core.$strict>>;
|
|
99
100
|
hooks: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
100
101
|
"workflow.start": "workflow.start";
|
|
101
|
-
"node.finish": "node.finish";
|
|
102
|
-
"node.start": "node.start";
|
|
103
102
|
"workflow.success": "workflow.success";
|
|
104
103
|
"workflow.failure": "workflow.failure";
|
|
105
104
|
"workflow.complete": "workflow.complete";
|
|
105
|
+
"node.start": "node.start";
|
|
106
106
|
"node.success": "node.success";
|
|
107
107
|
"node.error": "node.error";
|
|
108
|
+
"node.finish": "node.finish";
|
|
108
109
|
"gate.failure": "gate.failure";
|
|
109
110
|
}> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
110
111
|
failure: z.ZodDefault<z.ZodEnum<{
|
|
@@ -151,6 +152,11 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
151
152
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
152
153
|
name: z.ZodOptional<z.ZodString>;
|
|
153
154
|
namespace: z.ZodOptional<z.ZodString>;
|
|
155
|
+
brokerAuth: z.ZodOptional<z.ZodObject<{
|
|
156
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
157
|
+
secretName: z.ZodString;
|
|
158
|
+
url: z.ZodDefault<z.ZodString>;
|
|
159
|
+
}, z.core.$strict>>;
|
|
154
160
|
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
155
161
|
opencodeOpenaiAccountsSecretName: z.ZodOptional<z.ZodString>;
|
|
156
162
|
repository: z.ZodOptional<z.ZodObject<{
|
|
@@ -216,13 +222,13 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
216
222
|
}, z.core.$strict>>;
|
|
217
223
|
hooks: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
218
224
|
"workflow.start": "workflow.start";
|
|
219
|
-
"node.finish": "node.finish";
|
|
220
|
-
"node.start": "node.start";
|
|
221
225
|
"workflow.success": "workflow.success";
|
|
222
226
|
"workflow.failure": "workflow.failure";
|
|
223
227
|
"workflow.complete": "workflow.complete";
|
|
228
|
+
"node.start": "node.start";
|
|
224
229
|
"node.success": "node.success";
|
|
225
230
|
"node.error": "node.error";
|
|
231
|
+
"node.finish": "node.finish";
|
|
226
232
|
"gate.failure": "gate.failure";
|
|
227
233
|
}> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
228
234
|
failure: z.ZodDefault<z.ZodEnum<{
|
|
@@ -269,6 +275,11 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
269
275
|
kubeconfigPath: z.ZodOptional<z.ZodString>;
|
|
270
276
|
name: z.ZodOptional<z.ZodString>;
|
|
271
277
|
namespace: z.ZodOptional<z.ZodString>;
|
|
278
|
+
brokerAuth: z.ZodOptional<z.ZodObject<{
|
|
279
|
+
secretKey: z.ZodDefault<z.ZodString>;
|
|
280
|
+
secretName: z.ZodString;
|
|
281
|
+
url: z.ZodDefault<z.ZodString>;
|
|
282
|
+
}, z.core.$strict>>;
|
|
272
283
|
opencodeAuthSecretName: z.ZodOptional<z.ZodString>;
|
|
273
284
|
opencodeOpenaiAccountsSecretName: z.ZodOptional<z.ZodString>;
|
|
274
285
|
repository: z.ZodOptional<z.ZodObject<{
|
|
@@ -323,6 +334,7 @@ interface SubmitMokaDependencies {
|
|
|
323
334
|
submitWorkflow?: (options: MokaWorkflowSubmitOptions) => Promise<MokaSubmitOutput>;
|
|
324
335
|
}
|
|
325
336
|
interface MokaWorkflowSubmitOptions {
|
|
337
|
+
brokerAuth?: BrokerAuthOption;
|
|
326
338
|
config: PipelineConfig;
|
|
327
339
|
eventAuthSecretKey?: string;
|
|
328
340
|
eventAuthSecretName?: string;
|
package/dist/moka-submit.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brokerAuthOptionSchema } from "./broker-auth.js";
|
|
1
2
|
import { buildRunnerCommandPayload, runnerDeliverySchema, runnerHookPolicySchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskSchema } from "./runner-command-contract.js";
|
|
2
3
|
import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.js";
|
|
3
4
|
import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
|
|
@@ -80,6 +81,7 @@ const mokaSubmitBaseOptionsSchema = z.object({
|
|
|
80
81
|
kubeconfigPath: z.string().min(1).optional(),
|
|
81
82
|
name: z.string().min(1).optional(),
|
|
82
83
|
namespace: z.string().min(1).optional(),
|
|
84
|
+
brokerAuth: brokerAuthOptionSchema.optional(),
|
|
83
85
|
opencodeAuthSecretName: z.string().min(1).optional(),
|
|
84
86
|
opencodeOpenaiAccountsSecretName: z.string().min(1).optional(),
|
|
85
87
|
repository: runnerRepositoryContextSchema.optional(),
|
|
@@ -305,6 +307,7 @@ function workflowSubmitOptions(options) {
|
|
|
305
307
|
kubeconfigPath: options.kubeconfigPath,
|
|
306
308
|
name: options.name,
|
|
307
309
|
namespace: requireSubmitOption(options.namespace, "namespace"),
|
|
310
|
+
brokerAuth: options.brokerAuth,
|
|
308
311
|
opencodeAuthSecretName: options.opencodeAuthSecretName,
|
|
309
312
|
opencodeOpenaiAccountsSecretName: options.opencodeOpenaiAccountsSecretName,
|
|
310
313
|
serviceAccountName: options.serviceAccountName
|
package/dist/pipeline-init.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { AGENT_SKILL_SOURCE } from "./agent-assets.js";
|
|
1
2
|
import { installCommands } from "./install-commands.js";
|
|
2
3
|
import { installHooks } from "./install-hooks.js";
|
|
3
4
|
import { installRules } from "./install-rules.js";
|
|
4
5
|
import { execa } from "execa";
|
|
5
6
|
//#region src/pipeline-init.ts
|
|
6
|
-
const DEFAULT_SKILL_INSTALL_SOURCE =
|
|
7
|
+
const DEFAULT_SKILL_INSTALL_SOURCE = AGENT_SKILL_SOURCE;
|
|
7
8
|
const SKILL_INSTALL_AGENT_ARGS = [
|
|
8
9
|
"--agent",
|
|
9
10
|
"opencode",
|
|
@@ -84,7 +85,7 @@ function formatPipelineInitResult(result, mode = {}) {
|
|
|
84
85
|
const copy = INIT_RESULT_COPY[initResultMode(mode)];
|
|
85
86
|
return [
|
|
86
87
|
copy.headline,
|
|
87
|
-
"per-machine harness globally (user/global skills + ~/.claude, ~/.config/opencode, ~/.codex); global instruction files generated via rulesync from oisin-ee/rules; inherited by every repo with no per-repo copy",
|
|
88
|
+
"per-machine harness globally (user/global skills + ~/.claude, ~/.config/opencode, ~/.codex); global instruction files generated via rulesync from oisin-ee/agent/rules; inherited by every repo with no per-repo copy",
|
|
88
89
|
...result.files.map((path) => `${copy.fileVerb} ${path}`),
|
|
89
90
|
copy.footer
|
|
90
91
|
].join("\n");
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { applyCodexBrokerProvider, applyOpencodeBrokerProvider, renderOpencodeBrokerAuthJson, resolveBrokerCredentials } from "../broker-auth.js";
|
|
2
|
+
import { resolveHarnessTarget } from "../install-commands/shared.js";
|
|
1
3
|
import { isRecord } from "../safe-json.js";
|
|
2
4
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
5
|
import { homedir } from "node:os";
|
|
@@ -21,12 +23,51 @@ const WRITABLE_OPENCODE_CREDENTIAL_FILES = [{
|
|
|
21
23
|
stagedPath: join(OPENCODE_AUTH_STAGING_DIR, AUTH_FILE_NAME)
|
|
22
24
|
}];
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* Prepare codex + opencode runner credentials. In broker mode, writes the
|
|
27
|
+
* broker provider config and api-key and skips the legacy pool staging. In
|
|
28
|
+
* legacy mode, copies each staged secret to its writable live path and syncs
|
|
29
|
+
* the pool's active openai token into auth.json.
|
|
28
30
|
*/
|
|
29
31
|
function prepareOpencodeCredentials(options = {}) {
|
|
32
|
+
const broker = options.broker === void 0 ? resolveBrokerCredentials() : options.broker;
|
|
33
|
+
if (broker) return {
|
|
34
|
+
brokerConfigured: configureBrokerCredentials(broker, options.brokerPaths),
|
|
35
|
+
copied: [],
|
|
36
|
+
hostOpenaiTokenSynced: false
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
brokerConfigured: [],
|
|
40
|
+
...prepareLegacyPoolCredentials(options)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function defaultBrokerConfigPaths() {
|
|
44
|
+
return {
|
|
45
|
+
codexConfigPath: resolveHarnessTarget(".codex/config.toml"),
|
|
46
|
+
opencodeAuthPath: join(homedir(), ".local", "share", "opencode", AUTH_FILE_NAME),
|
|
47
|
+
opencodeConfigPath: resolveHarnessTarget(".opencode/opencode.json")
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function configureBrokerCredentials(broker, pathsOverride) {
|
|
51
|
+
const paths = pathsOverride ?? defaultBrokerConfigPaths();
|
|
52
|
+
const configured = [];
|
|
53
|
+
writeFileEnsured(paths.opencodeAuthPath, renderOpencodeBrokerAuthJson(broker), 384);
|
|
54
|
+
configured.push(basename(paths.opencodeAuthPath));
|
|
55
|
+
writeFileEnsured(paths.codexConfigPath, applyCodexBrokerProvider(readIfExists(paths.codexConfigPath), broker));
|
|
56
|
+
configured.push(basename(paths.codexConfigPath));
|
|
57
|
+
const opencodeConfig = applyOpencodeBrokerProvider(readIfExists(paths.opencodeConfigPath), broker);
|
|
58
|
+
if ("error" in opencodeConfig) throw new Error(`Cannot configure opencode broker provider: ${opencodeConfig.error}`);
|
|
59
|
+
writeFileEnsured(paths.opencodeConfigPath, opencodeConfig.content);
|
|
60
|
+
configured.push(basename(paths.opencodeConfigPath));
|
|
61
|
+
return configured;
|
|
62
|
+
}
|
|
63
|
+
function readIfExists(path) {
|
|
64
|
+
return existsSync(path) ? readFileSync(path, "utf8") : void 0;
|
|
65
|
+
}
|
|
66
|
+
function writeFileEnsured(path, content, mode) {
|
|
67
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
68
|
+
writeFileSync(path, content, mode === void 0 ? void 0 : { mode });
|
|
69
|
+
}
|
|
70
|
+
function prepareLegacyPoolCredentials(options) {
|
|
30
71
|
const home = homedir();
|
|
31
72
|
const files = options.files ?? WRITABLE_OPENCODE_CREDENTIAL_FILES.map((file) => ({
|
|
32
73
|
destPath: join(home, ...file.destFromHome),
|
|
@@ -106,6 +106,7 @@ function runRunnerCommandEffect(options, runtime) {
|
|
|
106
106
|
}, "opencode.credentials.prepare start");
|
|
107
107
|
const credentialsPrep = yield* io.prepareOpencodeCredentials();
|
|
108
108
|
logger.info({
|
|
109
|
+
brokerConfigured: credentialsPrep.brokerConfigured,
|
|
109
110
|
copied: credentialsPrep.copied,
|
|
110
111
|
hostOpenaiTokenSynced: credentialsPrep.hostOpenaiTokenSynced,
|
|
111
112
|
phase: "opencode.credentials.prepare",
|
|
@@ -11,8 +11,8 @@ declare const runnerEventRecordSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
11
11
|
runId: z.ZodString;
|
|
12
12
|
sequence: z.ZodNumber;
|
|
13
13
|
type: z.ZodEnum<{
|
|
14
|
-
"workflow.planned": "workflow.planned";
|
|
15
14
|
"workflow.start": "workflow.start";
|
|
15
|
+
"workflow.planned": "workflow.planned";
|
|
16
16
|
}>;
|
|
17
17
|
workflowPlan: z.ZodObject<{
|
|
18
18
|
edges: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -58,10 +58,10 @@ declare const runnerEventRecordSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
58
58
|
}>;
|
|
59
59
|
}, z.core.$strip>;
|
|
60
60
|
type: z.ZodEnum<{
|
|
61
|
+
"node.start": "node.start";
|
|
62
|
+
"node.finish": "node.finish";
|
|
61
63
|
"agent.finish": "agent.finish";
|
|
62
64
|
"agent.start": "agent.start";
|
|
63
|
-
"node.finish": "node.finish";
|
|
64
|
-
"node.start": "node.start";
|
|
65
65
|
}>;
|
|
66
66
|
}, z.core.$strip>, z.ZodObject<{
|
|
67
67
|
at: z.ZodOptional<z.ZodString>;
|
|
@@ -256,8 +256,8 @@ declare const runnerEventBatchSchema: z.ZodObject<{
|
|
|
256
256
|
runId: z.ZodString;
|
|
257
257
|
sequence: z.ZodNumber;
|
|
258
258
|
type: z.ZodEnum<{
|
|
259
|
-
"workflow.planned": "workflow.planned";
|
|
260
259
|
"workflow.start": "workflow.start";
|
|
260
|
+
"workflow.planned": "workflow.planned";
|
|
261
261
|
}>;
|
|
262
262
|
workflowPlan: z.ZodObject<{
|
|
263
263
|
edges: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -303,10 +303,10 @@ declare const runnerEventBatchSchema: z.ZodObject<{
|
|
|
303
303
|
}>;
|
|
304
304
|
}, z.core.$strip>;
|
|
305
305
|
type: z.ZodEnum<{
|
|
306
|
+
"node.start": "node.start";
|
|
307
|
+
"node.finish": "node.finish";
|
|
306
308
|
"agent.finish": "agent.finish";
|
|
307
309
|
"agent.start": "agent.start";
|
|
308
|
-
"node.finish": "node.finish";
|
|
309
|
-
"node.start": "node.start";
|
|
310
310
|
}>;
|
|
311
311
|
}, z.core.$strip>, z.ZodObject<{
|
|
312
312
|
at: z.ZodOptional<z.ZodString>;
|
|
@@ -29,14 +29,10 @@ function executeOpencodeSession(deps, plan, options) {
|
|
|
29
29
|
function boundByAgentTimeout(plan) {
|
|
30
30
|
return (effect) => {
|
|
31
31
|
const timeoutMs = plan.timeoutMs;
|
|
32
|
-
process.stderr.write(`[agent-timeout] wired nodeId=${plan.nodeId} timeoutMs=${String(timeoutMs)}\n`);
|
|
33
32
|
if (!timeoutMs || timeoutMs <= 0) return effect;
|
|
34
33
|
return Effect.timeoutFail(Effect.disconnect(effect), {
|
|
35
34
|
duration: Duration.millis(timeoutMs),
|
|
36
|
-
onTimeout: () => {
|
|
37
|
-
process.stderr.write(`[agent-timeout] FIRED nodeId=${plan.nodeId} after ${timeoutMs}ms\n`);
|
|
38
|
-
return /* @__PURE__ */ new Error(`agent session timed out after ${timeoutMs}ms`);
|
|
39
|
-
}
|
|
35
|
+
onTimeout: () => /* @__PURE__ */ new Error(`agent session timed out after ${timeoutMs}ms`)
|
|
40
36
|
});
|
|
41
37
|
};
|
|
42
38
|
}
|
|
@@ -152,7 +152,7 @@ receive explicit grants:
|
|
|
152
152
|
- `output`: text, JSON, JSONL, or JSON Schema output.
|
|
153
153
|
|
|
154
154
|
Default skills resolve from project-installed skill files created by
|
|
155
|
-
`moka init` via `npx --yes skills add oisin-ee/skills`:
|
|
155
|
+
`moka init` via `npx --yes skills add oisin-ee/agent/skills`:
|
|
156
156
|
|
|
157
157
|
```yaml
|
|
158
158
|
skills:
|
|
@@ -164,14 +164,13 @@ Project-authored skill and rule paths resolve from the project root and must
|
|
|
164
164
|
exist for runtime use. If default skill files are missing, run `moka init` to
|
|
165
165
|
install them before executing workflows.
|
|
166
166
|
|
|
167
|
-
Default agent hooks are copied by `moka init` from
|
|
168
|
-
|
|
169
|
-
host-level layout:
|
|
167
|
+
Default agent hooks are copied by `moka init` from private `oisin-ee/agent`.
|
|
168
|
+
That source repository has one canonical hook layout:
|
|
170
169
|
|
|
171
170
|
```text
|
|
172
|
-
claude-code/
|
|
173
|
-
codex/
|
|
174
|
-
opencode/
|
|
171
|
+
hooks/claude-code/
|
|
172
|
+
hooks/codex/
|
|
173
|
+
hooks/opencode/
|
|
175
174
|
```
|
|
176
175
|
|
|
177
176
|
Moka overlays those folders onto `.claude`, `.codex`, and `.opencode` for
|
|
@@ -215,7 +214,7 @@ OpenCode host resources are generated from the same profile registry:
|
|
|
215
214
|
- `.opencode/skills/*/SKILL.md` is installed by `skills add`; Moka only
|
|
216
215
|
generates agents, commands, plugins, and project config.
|
|
217
216
|
- Additional manually authored OpenCode hook plugins can be copied from
|
|
218
|
-
`oisin-ee/agent
|
|
217
|
+
`oisin-ee/agent/hooks/opencode/` by `moka init`.
|
|
219
218
|
- `.opencode/plugins/pipeline-goal-context.ts` projects package-owned
|
|
220
219
|
continuation context into OpenCode compaction.
|
|
221
220
|
- `.opencode/opencode.json` contains the gateway MCP config, enables LSP, and
|
package/docs/mcp-gateway.md
CHANGED
|
@@ -128,3 +128,45 @@ The package-owned MCP inventory exposed through the ecosystem manifest includes
|
|
|
128
128
|
Backlog, GitHub, and Neon. Repo-scoped backends must bind to
|
|
129
129
|
`PIPELINE_TARGET_PATH` or the current workspace path supplied by the gateway
|
|
130
130
|
configuration.
|
|
131
|
+
|
|
132
|
+
## Browser automation backend (Steel)
|
|
133
|
+
|
|
134
|
+
The `Playwright` backend's tools (`playwright_browser_*`) are served by a
|
|
135
|
+
self-hosted **Steel Browser** (Chromium) pool, not a browser launched inside the
|
|
136
|
+
MCP pod. Microsoft's `@playwright/mcp` connects to Steel over CDP, so the tool
|
|
137
|
+
surface is unchanged — agents keep calling the same `playwright_browser_*` tools
|
|
138
|
+
through `pipeline-gateway`.
|
|
139
|
+
|
|
140
|
+
Topology (infra repo, `k8s/charts/pipeline-mcp-gateway`):
|
|
141
|
+
|
|
142
|
+
- A StatefulSet of N backend pods (`playwright.backendReplicas`, default 3). Each
|
|
143
|
+
pod is `mcp` (`@playwright/mcp`) + a private `steel` sidecar (its own Chrome on
|
|
144
|
+
`localhost:3000`) + an `auth-seed` native sidecar. One pod = one isolated,
|
|
145
|
+
verify-bot-authenticated browser.
|
|
146
|
+
- Auth: the seed runs a real headless Zitadel login and POSTs the session into
|
|
147
|
+
the pod's Steel (`POST /v1/sessions`); the pod stays NotReady until the first
|
|
148
|
+
seed lands (fail-closed — an unauthenticated browser is never served) and
|
|
149
|
+
re-seeds every ~3 days, inside the oauth2-proxy 7-day cookie window.
|
|
150
|
+
|
|
151
|
+
Usage:
|
|
152
|
+
|
|
153
|
+
- **One authenticated browser (default).** Call `playwright_browser_*` through
|
|
154
|
+
`pipeline-gateway` (`https://pipeline-mcp.momokaya.ee/mcp/`). You get a single,
|
|
155
|
+
pre-authenticated browser. After a gateway backend restart the vMCP client
|
|
156
|
+
session can drop — reconnect the MCP client (do not bounce pods).
|
|
157
|
+
- **N concurrent isolated browsers.** The single gateway/proxy endpoint does
|
|
158
|
+
**not** auto-distribute sessions across the pool — toolhive pins every session
|
|
159
|
+
to one backend pod (Redis session storage and scaling proxy replicas do not
|
|
160
|
+
change this). To use the pool concurrently, address the backend pods directly:
|
|
161
|
+
each pod's `@playwright/mcp` listens on port `8931` and is a full
|
|
162
|
+
`playwright_browser_*` endpoint (`http://<pod-ip-or-headless-dns>:8931/mcp`).
|
|
163
|
+
Proven: 3 concurrent per-pod sessions, each on a distinct authenticated
|
|
164
|
+
browser.
|
|
165
|
+
- Scale the pool with `playwright.backendReplicas`.
|
|
166
|
+
|
|
167
|
+
Operational notes: Steel runs as root in-pod (its bundled nginx requires it,
|
|
168
|
+
otherwise `nginx [emerg] chown(/var/lib/nginx/body) Operation not permitted`);
|
|
169
|
+
health is `GET /v1/health`; on ARM nodes set `SKIP_FINGERPRINT_INJECTION=true`;
|
|
170
|
+
CDP over a service-DNS host needs `--cdp-header "Host: localhost"` (Chrome's
|
|
171
|
+
anti-DNS-rebinding check), but the in-pod `localhost:3000` path needs no header.
|
|
172
|
+
See infra `INFRA-074`.
|
package/docs/operator-guide.md
CHANGED
|
@@ -177,8 +177,8 @@ OpenBao, publish Secret values, or mutate ESO resources from this package.
|
|
|
177
177
|
|
|
178
178
|
Installs or refreshes the whole per-machine harness in one command: the
|
|
179
179
|
package's default skills, generated host-native command surfaces and MCP
|
|
180
|
-
entries, copied agent hooks from
|
|
181
|
-
|
|
180
|
+
entries, copied agent hooks from private `oisin-ee/agent/hooks`, and global
|
|
181
|
+
instruction files generated via rulesync from `oisin-ee/agent/rules`.
|
|
182
182
|
OpenCode is the package default runtime. The harness is always installed
|
|
183
183
|
globally (`~/.claude`, `~/.config/opencode`, `~/.codex`); there is no `--scope`.
|
|
184
184
|
`moka init` does not create repo-local `.pipeline` config files.
|
|
@@ -192,12 +192,10 @@ moka init --force # overwrite manually edited harness files
|
|
|
192
192
|
|
|
193
193
|
`--check` and `--dry-run` write nothing and skip the network skill install.
|
|
194
194
|
By default `moka init` refuses to overwrite manually edited hook or command
|
|
195
|
-
files; `--force` overwrites them. For agent hooks, Moka clones
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
(`claude-code/`, `codex/`, `opencode/`); there is no source override flag and no
|
|
200
|
-
symlink mode.
|
|
195
|
+
files; `--force` overwrites them. For agent hooks, Moka clones `oisin-ee/agent`,
|
|
196
|
+
copies files from `hooks/<host>`, and tracks installed hashes so later runs update
|
|
197
|
+
unchanged owned files, delete removed owned files, and (without `--force`) refuse
|
|
198
|
+
to clobber manual edits. There is no source override flag and no symlink mode.
|
|
201
199
|
|
|
202
200
|
Use `PIPELINE_TARGET_PATH=/path/to/repo` when invoking `moka` from outside the
|
|
203
201
|
target worktree.
|
|
@@ -384,9 +382,9 @@ Claude Code: /moka-quick, /moka-execute, /moka-inspect
|
|
|
384
382
|
- `.opencode/opencode.json` with LSP, the singleton `pipeline-gateway` MCP
|
|
385
383
|
server, and pinned package-selected plugins
|
|
386
384
|
|
|
387
|
-
`moka init` also copies hook files from `oisin-ee/agent
|
|
388
|
-
`opencode/`, `claude-code/`, and `codex/` onto the host config
|
|
389
|
-
are authored in the
|
|
385
|
+
`moka init` also copies hook files from `oisin-ee/agent/hooks` by overlaying
|
|
386
|
+
`hooks/opencode/`, `hooks/claude-code/`, and `hooks/codex/` onto the host config
|
|
387
|
+
roots. Hook files are authored in the agent asset repo, not generated by Moka.
|
|
390
388
|
|
|
391
389
|
For Claude Code, `moka init` generates `.claude/commands/moka-<entrypoint>.md`
|
|
392
390
|
slash commands.
|
package/package.json
CHANGED
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"prepack": "bun run build:cli"
|
|
129
129
|
},
|
|
130
130
|
"type": "module",
|
|
131
|
-
"version": "3.
|
|
131
|
+
"version": "3.13.0",
|
|
132
132
|
"description": "Config-driven multi-agent pipeline runner for repository work",
|
|
133
133
|
"main": "./dist/index.js",
|
|
134
134
|
"types": "./dist/index.d.ts",
|