@oisincoveney/pipeline 2.0.0 → 2.1.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/.agents/skills/claude-code-opencode-execute/SKILL.md +116 -0
- package/defaults/opencode-ecosystem.yaml +51 -1
- package/dist/argo-workflow.js +0 -19
- package/dist/cli/program.d.ts +1 -0
- package/dist/cli/program.js +17 -6
- package/dist/cluster-doctor.js +69 -48
- package/dist/codex-auth-sync.js +1 -1
- package/dist/config/defaults.d.ts +37 -0
- package/dist/config/defaults.js +42 -12
- package/dist/config/schemas.d.ts +5 -5
- package/dist/hooks.d.ts +1 -1
- package/dist/install-commands/claude-code.js +24 -0
- package/dist/install-commands/opencode.js +434 -0
- package/dist/install-commands/shared.js +29 -0
- package/dist/install-commands.js +21 -447
- package/dist/moka-submit.d.ts +1 -1
- package/dist/opencode-project-config.js +23 -5
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/schedule/backlog-context.js +114 -0
- package/dist/schedule/baseline.js +267 -0
- package/dist/schedule/planner.js +3 -373
- package/package.json +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-code-opencode-execute
|
|
3
|
+
description: Use in Claude Code when executing Moka work locally through OpenCode; loads execute first, then spawns Claude Task agents that run opencode run with the correct MoKa agent prompts.
|
|
4
|
+
allowed-tools: Bash(opencode run *) Bash(pwd) Bash(git status *) Task
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Claude Code OpenCode Execute
|
|
8
|
+
|
|
9
|
+
Use this skill when Claude Code is the interactive host, but the work should be executed by local OpenCode MoKa agents through `opencode run` subprocesses.
|
|
10
|
+
|
|
11
|
+
This skill is a host adapter. It does not replace [[execute]]. Load and follow [[execute]] first; use this skill only for the Claude Code dispatch mechanics.
|
|
12
|
+
|
|
13
|
+
## Contract
|
|
14
|
+
|
|
15
|
+
1. Load [[execute]] and let it classify the request, required companion skills, acceptance criteria, and verification path.
|
|
16
|
+
2. Keep the execution doctrine from [[execute]]: vertical slices, test-first for behavior, root-cause fixes, fidelity checks, critique, and verification.
|
|
17
|
+
3. Use Claude Code `Task` subagents as wrappers around local OpenCode subprocesses when work can be delegated.
|
|
18
|
+
4. Each delegated Task agent should run exactly one MoKa-flavored OpenCode command and return the command, exit status, parsed evidence, touched files, and blockers.
|
|
19
|
+
5. Batch independent nodes in parallel, wait at barriers, synthesize outputs, then decide the next batch.
|
|
20
|
+
|
|
21
|
+
## OpenCode command shape
|
|
22
|
+
|
|
23
|
+
Task agents should run OpenCode with this shape from the repository root:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
opencode run --agent "<MoKa Agent Name>" --format json --dir "$PWD" '<node prompt>'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Use `--format json` so the parent can inspect structured event output. If the output contains multiple assistant text candidates, prefer the latest candidate that satisfies the requested JSON or evidence contract.
|
|
30
|
+
|
|
31
|
+
Do not use `moka submit` for this local Claude Code adapter unless the user explicitly asks to submit an Argo Workflow. `moka submit` is the durable pipeline path; this skill is for interactive local orchestration through Claude Task agents and `opencode run`.
|
|
32
|
+
|
|
33
|
+
## Agent Selection
|
|
34
|
+
|
|
35
|
+
Select the MoKa agent by the slice's role:
|
|
36
|
+
|
|
37
|
+
| Role | OpenCode agent |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| Intake, repository research, requirements clarification | `MoKa Researcher` |
|
|
40
|
+
| Read-only code inspection | `MoKa Inspector` |
|
|
41
|
+
| Schedule graph generation or schedule review | `MoKa Schedule Planner` |
|
|
42
|
+
| Focused failing tests | `MoKa Test Writer` |
|
|
43
|
+
| Production implementation | `MoKa Code Writer` |
|
|
44
|
+
| Acceptance criteria audit | `MoKa Acceptance Reviewer` |
|
|
45
|
+
| Final quality review | `MoKa Thermo Nuclear Reviewer` |
|
|
46
|
+
| Verification evidence and command checks | `MoKa Verifier` |
|
|
47
|
+
| Durable lessons after a completed run | `MoKa Learner` |
|
|
48
|
+
|
|
49
|
+
Do not delegate normal child work to `MoKa Orchestrator`; the Claude Code parent is the local orchestrator for this adapter.
|
|
50
|
+
|
|
51
|
+
## Prompt Contract
|
|
52
|
+
|
|
53
|
+
Every `opencode run` prompt must include:
|
|
54
|
+
|
|
55
|
+
- The original user task.
|
|
56
|
+
- The current execution contract from [[execute]].
|
|
57
|
+
- The node id and role.
|
|
58
|
+
- The selected MoKa agent name and why it was selected.
|
|
59
|
+
- The exact files or modules in scope, or a read-only discovery scope.
|
|
60
|
+
- Dependency outputs from earlier nodes.
|
|
61
|
+
- The acceptance criteria this node owns.
|
|
62
|
+
- The output shape expected by the parent.
|
|
63
|
+
|
|
64
|
+
Use this template for delegated prompts:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
You are running as <MoKa Agent Name> for a Claude Code local Moka execution.
|
|
68
|
+
|
|
69
|
+
Original task:
|
|
70
|
+
<user task>
|
|
71
|
+
|
|
72
|
+
Execution contract:
|
|
73
|
+
<contract produced by execute>
|
|
74
|
+
|
|
75
|
+
Node:
|
|
76
|
+
- id: <node id>
|
|
77
|
+
- role: <role>
|
|
78
|
+
- selected agent: <MoKa Agent Name>
|
|
79
|
+
- scope: <files/modules/commands>
|
|
80
|
+
|
|
81
|
+
Dependency outputs:
|
|
82
|
+
<summaries or "none">
|
|
83
|
+
|
|
84
|
+
Acceptance criteria owned by this node:
|
|
85
|
+
<criteria>
|
|
86
|
+
|
|
87
|
+
Instructions:
|
|
88
|
+
- Follow the skills and grants configured for this MoKa agent.
|
|
89
|
+
- Stay inside this node's scope.
|
|
90
|
+
- Do not claim completion without fresh evidence.
|
|
91
|
+
- Return only the requested output shape.
|
|
92
|
+
|
|
93
|
+
Output shape:
|
|
94
|
+
<JSON or concise evidence contract>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Batching Rules
|
|
98
|
+
|
|
99
|
+
- Run read-only discovery agents in parallel when their scopes do not require a shared conclusion first.
|
|
100
|
+
- Run test-writing before production implementation for behavior changes.
|
|
101
|
+
- Run multiple `MoKa Code Writer` agents in the same batch only when [[execute]] has produced independent vertical slices with disjoint files or clearly isolated worktrees.
|
|
102
|
+
- Never let two write-capable agents edit the same file in the same batch.
|
|
103
|
+
- Run acceptance review, final quality review, and verifier after implementation outputs have been integrated.
|
|
104
|
+
- If any delegated command fails, stop the batch, read the output, classify the blocker, and return to [[execute]] routing instead of retrying blindly.
|
|
105
|
+
|
|
106
|
+
## Parent Responsibilities
|
|
107
|
+
|
|
108
|
+
The Claude Code parent must:
|
|
109
|
+
|
|
110
|
+
- Inspect outputs before launching the next batch.
|
|
111
|
+
- Integrate or reconcile changes itself when multiple agents wrote code.
|
|
112
|
+
- Run the verification required by [[execute]] in the real repository context.
|
|
113
|
+
- Inspect the final diff for accidental edits, secrets, generated churn, and unrelated changes.
|
|
114
|
+
- Report partial verification honestly if the real command path cannot be exercised.
|
|
115
|
+
|
|
116
|
+
This skill is complete only when [[execute]] would allow the parent to claim completion.
|
|
@@ -64,7 +64,7 @@ ecosystem_code:
|
|
|
64
64
|
package: oc-codex-multi-auth
|
|
65
65
|
plugin:
|
|
66
66
|
kind: npm
|
|
67
|
-
package: oc-codex-multi-auth
|
|
67
|
+
package: oc-codex-multi-auth@6.3.2
|
|
68
68
|
role: ChatGPT Plus/Pro OAuth with multi-account rotation, health checks, and Codex/GPT-5 routing for OpenCode
|
|
69
69
|
default_stack: true
|
|
70
70
|
source: https://github.com/ndycode/oc-codex-multi-auth
|
|
@@ -83,6 +83,56 @@ ecosystem_code:
|
|
|
83
83
|
role: OpenCode policy enforcement through tool execution hooks
|
|
84
84
|
default_stack: true
|
|
85
85
|
source: https://cupcake.eqtylab.io/getting-started/usage/opencode/
|
|
86
|
+
provider_models:
|
|
87
|
+
# Reasoning-effort variant selectors for ChatGPT OAuth Codex routing.
|
|
88
|
+
# Each id must match the oc-codex-multi-auth MODEL_MAP exactly; the plugin
|
|
89
|
+
# normalizes the selector back to the base API model (gpt-5.5) and the
|
|
90
|
+
# reasoning effort is carried by the generated options.reasoningEffort.
|
|
91
|
+
- id: gpt-5.5-none
|
|
92
|
+
provider: openai
|
|
93
|
+
role: zero-reasoning GPT-5.5 selector for mechanical low-stakes work
|
|
94
|
+
options:
|
|
95
|
+
reasoningEffort: none
|
|
96
|
+
reasoningSummary: auto
|
|
97
|
+
textVerbosity: medium
|
|
98
|
+
include: [reasoning.encrypted_content]
|
|
99
|
+
store: false
|
|
100
|
+
- id: gpt-5.5-low
|
|
101
|
+
provider: openai
|
|
102
|
+
role: low-reasoning GPT-5.5 selector for cheap inspection and bookkeeping roles
|
|
103
|
+
options:
|
|
104
|
+
reasoningEffort: low
|
|
105
|
+
reasoningSummary: auto
|
|
106
|
+
textVerbosity: medium
|
|
107
|
+
include: [reasoning.encrypted_content]
|
|
108
|
+
store: false
|
|
109
|
+
- id: gpt-5.5-medium
|
|
110
|
+
provider: openai
|
|
111
|
+
role: default-reasoning GPT-5.5 selector for research and coverage roles
|
|
112
|
+
options:
|
|
113
|
+
reasoningEffort: medium
|
|
114
|
+
reasoningSummary: auto
|
|
115
|
+
textVerbosity: medium
|
|
116
|
+
include: [reasoning.encrypted_content]
|
|
117
|
+
store: false
|
|
118
|
+
- id: gpt-5.5-high
|
|
119
|
+
provider: openai
|
|
120
|
+
role: high-reasoning GPT-5.5 selector for implementation roles
|
|
121
|
+
options:
|
|
122
|
+
reasoningEffort: high
|
|
123
|
+
reasoningSummary: detailed
|
|
124
|
+
textVerbosity: medium
|
|
125
|
+
include: [reasoning.encrypted_content]
|
|
126
|
+
store: false
|
|
127
|
+
- id: gpt-5.5-xhigh
|
|
128
|
+
provider: openai
|
|
129
|
+
role: maximum-reasoning GPT-5.5 selector for schedule planning
|
|
130
|
+
options:
|
|
131
|
+
reasoningEffort: xhigh
|
|
132
|
+
reasoningSummary: detailed
|
|
133
|
+
textVerbosity: medium
|
|
134
|
+
include: [reasoning.encrypted_content]
|
|
135
|
+
store: false
|
|
86
136
|
mcp_backends:
|
|
87
137
|
- id: pipeline-gateway
|
|
88
138
|
locality: project-remote
|
package/dist/argo-workflow.js
CHANGED
|
@@ -303,25 +303,6 @@ function runnerWorkflowStorage(options, tasks) {
|
|
|
303
303
|
name: "runner-git-credentials",
|
|
304
304
|
secret: {
|
|
305
305
|
defaultMode: 256,
|
|
306
|
-
items: [
|
|
307
|
-
{
|
|
308
|
-
key: "username",
|
|
309
|
-
path: "username"
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
key: "password",
|
|
313
|
-
path: "password"
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
key: "identity",
|
|
317
|
-
path: "identity"
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
key: "known_hosts",
|
|
321
|
-
path: "known_hosts"
|
|
322
|
-
}
|
|
323
|
-
],
|
|
324
|
-
optional: true,
|
|
325
306
|
secretName: options.gitCredentialsSecretName
|
|
326
307
|
}
|
|
327
308
|
});
|
package/dist/cli/program.d.ts
CHANGED
package/dist/cli/program.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createOrchestratorLaunchPlan, createRunnerLaunchPlan } from "../runner.
|
|
|
5
5
|
import { compileWorkflowPlan } from "../workflow-planner.js";
|
|
6
6
|
import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "../schedule/planner.js";
|
|
7
7
|
import "../schedule-planner.js";
|
|
8
|
+
import { loadMokaGlobalConfig } from "../moka-global-config.js";
|
|
8
9
|
import { defaultClusterDoctorNamespace, runClusterDoctor } from "../cluster-doctor.js";
|
|
9
10
|
import { formatCodexAuthSyncResult, syncLocalCodexAuth } from "../codex-auth-sync.js";
|
|
10
11
|
import { registerConfiguredEntrypointCommands } from "../commands/pipeline-command.js";
|
|
@@ -133,7 +134,7 @@ function createCliProgram() {
|
|
|
133
134
|
const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
134
135
|
console.log(formatSelectedWorkflowPlan(config, cwd, flags));
|
|
135
136
|
});
|
|
136
|
-
program.command("doctor").description("Check local prerequisites for pipeline init and execution").option("--cluster [namespace]", "also check runner-job Kubernetes prerequisites").option("--kube-context <context>", "kubectl context for cluster checks").action(async (flags) => {
|
|
137
|
+
program.command("doctor").description("Check local prerequisites for pipeline init and execution").option("--cluster [namespace]", "also check runner-job Kubernetes prerequisites").option("--kube-context <context>", "kubectl context for cluster checks").option("--kubeconfig <path>", "kubeconfig path for cluster checks").action(async (flags) => {
|
|
137
138
|
const result = await runDoctor(process.env.PIPELINE_TARGET_PATH ?? process.cwd(), flags);
|
|
138
139
|
console.log(formatDoctorResult(result));
|
|
139
140
|
if (!result.passed) throw new Error("Doctor checks failed.");
|
|
@@ -149,7 +150,7 @@ function createCliProgram() {
|
|
|
149
150
|
const config = loadPipelineConfig(process.env.PIPELINE_TARGET_PATH ?? process.cwd(), { allowMissingLintFileReferences: true });
|
|
150
151
|
console.log(renderGatewayConfig(config));
|
|
151
152
|
});
|
|
152
|
-
gatewayCommand.command("configure-host").description("Rewrite host MCP config to the singleton pipeline gateway").addOption(new Option("--host <host>", "host config to update").choices(["all", "opencode"]).default("all").argParser(
|
|
153
|
+
gatewayCommand.command("configure-host").description("Rewrite host MCP config to the singleton pipeline gateway").addOption(new Option("--host <host>", "host config to update").choices(["all", "opencode"]).default("all").argParser(parseGatewayHost)).addOption(new Option("--scope <scope>", "config scope to update").choices(["project", "global"]).default("project").argParser(parseGatewayHostScope)).action((flags) => {
|
|
153
154
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
154
155
|
const result = configureGatewayHosts(loadPipelineConfig(cwd, { allowMissingLintFileReferences: true }), {
|
|
155
156
|
cwd,
|
|
@@ -181,7 +182,11 @@ function createCliProgram() {
|
|
|
181
182
|
const result = await initPipelineProject({ cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd() });
|
|
182
183
|
console.log(formatPipelineInitResult(result));
|
|
183
184
|
});
|
|
184
|
-
program.command("install-commands").description("Install generated slash-command adapters into this repository").addOption(new Option("--host <host>", "host command set to install").choices([
|
|
185
|
+
program.command("install-commands").description("Install generated slash-command adapters into this repository").addOption(new Option("--host <host>", "host command set to install").choices([
|
|
186
|
+
"all",
|
|
187
|
+
"opencode",
|
|
188
|
+
"claude-code"
|
|
189
|
+
]).default("all").argParser(parseCommandHost)).option("--dry-run", "show planned changes without writing files").option("--check", "fail if generated command files are missing or stale").option("--force", "overwrite manually edited command files").action(async (flags) => {
|
|
185
190
|
const result = await installCommands({
|
|
186
191
|
...flags,
|
|
187
192
|
cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd()
|
|
@@ -231,6 +236,10 @@ function parseGatewayHostScope(value) {
|
|
|
231
236
|
if (value === "project" || value === "global") return value;
|
|
232
237
|
throw new Error("scope must be project or global");
|
|
233
238
|
}
|
|
239
|
+
function parseGatewayHost(value) {
|
|
240
|
+
if (value === "all" || value === "opencode") return value;
|
|
241
|
+
throw new Error(`Unsupported gateway host "${value}"`);
|
|
242
|
+
}
|
|
234
243
|
async function runCli(argv) {
|
|
235
244
|
await createCliProgram().parseAsync(argv, { from: "node" });
|
|
236
245
|
}
|
|
@@ -241,9 +250,11 @@ async function runDoctor(cwd, options = {}) {
|
|
|
241
250
|
checkCommand("fallow", ["--version"], cwd)
|
|
242
251
|
]);
|
|
243
252
|
const configCheck = checkPipelineConfig(cwd);
|
|
253
|
+
const globalConfig = loadMokaGlobalConfig();
|
|
244
254
|
const clusterResult = options.cluster ? await runClusterDoctor({
|
|
245
255
|
kubeContext: options.kubeContext,
|
|
246
|
-
|
|
256
|
+
kubeconfigPath: options.kubeconfig ?? globalConfig?.momokaya.kubernetes.kubeconfig,
|
|
257
|
+
namespace: clusterNamespace(options.cluster, globalConfig?.momokaya.kubernetes.namespace)
|
|
247
258
|
}) : { checks: [] };
|
|
248
259
|
const checks = [
|
|
249
260
|
...commandChecks,
|
|
@@ -255,8 +266,8 @@ async function runDoctor(cwd, options = {}) {
|
|
|
255
266
|
passed: checks.every((check) => check.passed)
|
|
256
267
|
};
|
|
257
268
|
}
|
|
258
|
-
function clusterNamespace(value) {
|
|
259
|
-
return typeof value === "string" && value.length > 0 ? value : defaultClusterDoctorNamespace();
|
|
269
|
+
function clusterNamespace(value, configuredNamespace) {
|
|
270
|
+
return typeof value === "string" && value.length > 0 ? value : configuredNamespace ?? defaultClusterDoctorNamespace();
|
|
260
271
|
}
|
|
261
272
|
function checkCommand(name, args, cwd) {
|
|
262
273
|
return checkCommandWithRunner(name, name, args, cwd);
|
package/dist/cluster-doctor.js
CHANGED
|
@@ -13,33 +13,38 @@ const DEFAULT_RESOURCES = {
|
|
|
13
13
|
queueName: "pipeline-runner",
|
|
14
14
|
serviceAccountName: "pipeline-runner"
|
|
15
15
|
};
|
|
16
|
+
const FORBIDDEN_RE = /forbidden/i;
|
|
16
17
|
async function runClusterDoctor(options = {}) {
|
|
17
18
|
const resources = clusterResources();
|
|
18
19
|
const namespace = options.namespace ?? DEFAULT_NAMESPACE;
|
|
20
|
+
const kubectlOptions = {
|
|
21
|
+
kubeContext: options.kubeContext,
|
|
22
|
+
kubeconfigPath: options.kubeconfigPath
|
|
23
|
+
};
|
|
19
24
|
const checks = await Promise.all([
|
|
20
|
-
checkKubectlNamespace(namespace,
|
|
21
|
-
...secretChecks(namespace,
|
|
22
|
-
checkExternalSecret(namespace, resources.eventAuthExternalSecretName, resources.externalSecretRemoteRef,
|
|
23
|
-
checkClusterSecretStore("openbao",
|
|
24
|
-
checkServiceAccount(namespace, resources.serviceAccountName,
|
|
25
|
-
|
|
26
|
-
kubeContext: options.kubeContext,
|
|
25
|
+
checkKubectlNamespace(namespace, kubectlOptions),
|
|
26
|
+
...secretChecks(namespace, kubectlOptions, resources),
|
|
27
|
+
checkExternalSecret(namespace, resources.eventAuthExternalSecretName, resources.externalSecretRemoteRef, kubectlOptions),
|
|
28
|
+
checkClusterSecretStore("openbao", kubectlOptions),
|
|
29
|
+
checkServiceAccount(namespace, resources.serviceAccountName, kubectlOptions),
|
|
30
|
+
checkWorkflowSubmitPermission(namespace, {
|
|
27
31
|
resource: "workflows.argoproj.io",
|
|
28
|
-
verb: "create"
|
|
32
|
+
verb: "create",
|
|
33
|
+
...kubectlOptions
|
|
29
34
|
}),
|
|
30
|
-
checkLocalQueue(namespace, resources.queueName,
|
|
35
|
+
checkLocalQueue(namespace, resources.queueName, kubectlOptions),
|
|
31
36
|
checkClusterResource("argo-workflow-crd", [
|
|
32
37
|
"get",
|
|
33
38
|
"crd",
|
|
34
39
|
"workflows.argoproj.io"
|
|
35
|
-
],
|
|
40
|
+
], kubectlOptions),
|
|
36
41
|
checkClusterResource("argo-workflow-controller", [
|
|
37
42
|
"get",
|
|
38
43
|
"pods",
|
|
39
44
|
"-A",
|
|
40
45
|
"-l",
|
|
41
46
|
"app=workflow-controller"
|
|
42
|
-
],
|
|
47
|
+
], kubectlOptions)
|
|
43
48
|
]);
|
|
44
49
|
return {
|
|
45
50
|
checks,
|
|
@@ -62,7 +67,7 @@ function clusterResources() {
|
|
|
62
67
|
serviceAccountName: configured.serviceAccountName
|
|
63
68
|
} : DEFAULT_RESOURCES;
|
|
64
69
|
}
|
|
65
|
-
function secretChecks(namespace,
|
|
70
|
+
function secretChecks(namespace, kubectlOptions, resources) {
|
|
66
71
|
return [
|
|
67
72
|
[resources.eventAuthSecretName, eventAuthMissingDetail(namespace)],
|
|
68
73
|
[resources.imagePullSecretName, `Secret ${resources.imagePullSecretName} missing in ${namespace}; expected imagePullSecret for ghcr.io/oisin-ee/pipeline-runner.`],
|
|
@@ -75,30 +80,31 @@ function secretChecks(namespace, kubeContext, resources) {
|
|
|
75
80
|
name,
|
|
76
81
|
"-n",
|
|
77
82
|
namespace
|
|
78
|
-
], missingDetail,
|
|
83
|
+
], missingDetail, kubectlOptions));
|
|
79
84
|
}
|
|
80
85
|
function eventAuthMissingDetail(namespace) {
|
|
81
86
|
return `Secret pipeline-runner-event-auth missing in ${namespace}; expected ExternalSecret pipeline-runner-event-auth to sync it from agent-runtime/pipeline-runner/event-auth.`;
|
|
82
87
|
}
|
|
83
|
-
function checkKubectlNamespace(namespace,
|
|
88
|
+
function checkKubectlNamespace(namespace, kubectlOptions) {
|
|
84
89
|
return checkNamespacedResource(`namespace/${namespace}`, [
|
|
85
90
|
"get",
|
|
86
91
|
"namespace",
|
|
87
92
|
namespace
|
|
88
|
-
], `Namespace ${namespace} missing or inaccessible.`,
|
|
93
|
+
], `Namespace ${namespace} missing or inaccessible.`, kubectlOptions);
|
|
89
94
|
}
|
|
90
|
-
async function checkNamespacedResource(name, args, missingDetail,
|
|
91
|
-
|
|
95
|
+
async function checkNamespacedResource(name, args, missingDetail, kubectlOptions) {
|
|
96
|
+
const result = await kubectl(args, kubectlOptions);
|
|
97
|
+
return result.ok ? {
|
|
92
98
|
detail: "present",
|
|
93
99
|
name,
|
|
94
100
|
passed: true
|
|
95
101
|
} : {
|
|
96
|
-
detail: missingDetail,
|
|
102
|
+
detail: inaccessibleOrMissingDetail(name, missingDetail, result),
|
|
97
103
|
name,
|
|
98
104
|
passed: false
|
|
99
105
|
};
|
|
100
106
|
}
|
|
101
|
-
async function checkExternalSecret(namespace, name, remoteRef,
|
|
107
|
+
async function checkExternalSecret(namespace, name, remoteRef, kubectlOptions) {
|
|
102
108
|
const result = await kubectl([
|
|
103
109
|
"get",
|
|
104
110
|
"externalsecret",
|
|
@@ -107,76 +113,79 @@ async function checkExternalSecret(namespace, name, remoteRef, kubeContext) {
|
|
|
107
113
|
namespace,
|
|
108
114
|
"-o",
|
|
109
115
|
"json"
|
|
110
|
-
],
|
|
111
|
-
if (!result.ok)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
], kubectlOptions);
|
|
117
|
+
if (!result.ok) {
|
|
118
|
+
const missingDetail = `ExternalSecret ${name} missing in ${namespace}; expected it to sync ${remoteRef}.`;
|
|
119
|
+
return {
|
|
120
|
+
detail: inaccessibleOrMissingDetail(`externalsecret/${name}`, missingDetail, result),
|
|
121
|
+
name: `externalsecret/${name}`,
|
|
122
|
+
passed: false
|
|
123
|
+
};
|
|
124
|
+
}
|
|
116
125
|
return readyConditionCheck(`externalsecret/${name}`, result.stdout);
|
|
117
126
|
}
|
|
118
|
-
async function checkClusterSecretStore(name,
|
|
127
|
+
async function checkClusterSecretStore(name, kubectlOptions) {
|
|
119
128
|
const result = await kubectl([
|
|
120
129
|
"get",
|
|
121
130
|
"clustersecretstore",
|
|
122
131
|
name,
|
|
123
132
|
"-o",
|
|
124
133
|
"json"
|
|
125
|
-
],
|
|
126
|
-
if (!result.ok)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
], kubectlOptions);
|
|
135
|
+
if (!result.ok) {
|
|
136
|
+
const missingDetail = `ClusterSecretStore/${name} missing or inaccessible; OpenBao/ESO readiness is an external prerequisite.`;
|
|
137
|
+
return {
|
|
138
|
+
detail: inaccessibleOrMissingDetail(`clustersecretstore/${name}`, missingDetail, result),
|
|
139
|
+
name: `clustersecretstore/${name}`,
|
|
140
|
+
passed: false
|
|
141
|
+
};
|
|
142
|
+
}
|
|
131
143
|
return readyConditionCheck(`clustersecretstore/${name}`, result.stdout);
|
|
132
144
|
}
|
|
133
|
-
function checkServiceAccount(namespace, name,
|
|
145
|
+
function checkServiceAccount(namespace, name, kubectlOptions) {
|
|
134
146
|
return checkNamespacedResource(`serviceaccount/${name}`, [
|
|
135
147
|
"get",
|
|
136
148
|
"serviceaccount",
|
|
137
149
|
name,
|
|
138
150
|
"-n",
|
|
139
151
|
namespace
|
|
140
|
-
], `ServiceAccount ${name} missing in ${namespace}; runner pods must use this account for workflow execution.`,
|
|
152
|
+
], `ServiceAccount ${name} missing in ${namespace}; runner pods must use this account for workflow execution.`, kubectlOptions);
|
|
141
153
|
}
|
|
142
|
-
async function
|
|
143
|
-
const subject = `system:serviceaccount:${namespace}:${serviceAccountName}`;
|
|
154
|
+
async function checkWorkflowSubmitPermission(namespace, options) {
|
|
144
155
|
return (await kubectl([
|
|
145
156
|
"auth",
|
|
146
157
|
"can-i",
|
|
147
158
|
options.verb,
|
|
148
159
|
options.resource,
|
|
149
|
-
"--as",
|
|
150
|
-
subject,
|
|
151
160
|
"-n",
|
|
152
161
|
namespace
|
|
153
|
-
], options
|
|
154
|
-
detail:
|
|
162
|
+
], options)).stdout.trim() === "yes" ? {
|
|
163
|
+
detail: `current kube identity can ${options.verb} ${options.resource}`,
|
|
155
164
|
name: "rbac/workflow-create",
|
|
156
165
|
passed: true
|
|
157
166
|
} : {
|
|
158
|
-
detail:
|
|
167
|
+
detail: `current kube identity cannot ${options.verb} ${options.resource}; check submitter RBAC for Workflow creation.`,
|
|
159
168
|
name: "rbac/workflow-create",
|
|
160
169
|
passed: false
|
|
161
170
|
};
|
|
162
171
|
}
|
|
163
|
-
function checkLocalQueue(namespace, queueName,
|
|
172
|
+
function checkLocalQueue(namespace, queueName, kubectlOptions) {
|
|
164
173
|
return checkNamespacedResource(`localqueue/${queueName}`, [
|
|
165
174
|
"get",
|
|
166
175
|
"localqueue",
|
|
167
176
|
queueName,
|
|
168
177
|
"-n",
|
|
169
178
|
namespace
|
|
170
|
-
], `Kueue LocalQueue ${queueName} missing in ${namespace}; runner Workflow pods cannot be admitted to the expected queue.`,
|
|
179
|
+
], `Kueue LocalQueue ${queueName} missing in ${namespace}; runner Workflow pods cannot be admitted to the expected queue.`, kubectlOptions);
|
|
171
180
|
}
|
|
172
|
-
async function checkClusterResource(name, args,
|
|
173
|
-
const result = await kubectl(args,
|
|
181
|
+
async function checkClusterResource(name, args, kubectlOptions) {
|
|
182
|
+
const result = await kubectl(args, kubectlOptions);
|
|
174
183
|
return result.ok ? {
|
|
175
184
|
detail: "present",
|
|
176
185
|
name,
|
|
177
186
|
passed: true
|
|
178
187
|
} : {
|
|
179
|
-
detail: result.stderr || "missing or inaccessible",
|
|
188
|
+
detail: isForbidden(result) ? inaccessibleDetail(name, result) : result.stderr || "missing or inaccessible",
|
|
180
189
|
name,
|
|
181
190
|
passed: false
|
|
182
191
|
};
|
|
@@ -193,9 +202,12 @@ function readyConditionCheck(name, source) {
|
|
|
193
202
|
passed: false
|
|
194
203
|
};
|
|
195
204
|
}
|
|
196
|
-
async function kubectl(args,
|
|
205
|
+
async function kubectl(args, options) {
|
|
197
206
|
try {
|
|
198
|
-
const result = await execa("kubectl", kubectlArgs(args, kubeContext), {
|
|
207
|
+
const result = await execa("kubectl", kubectlArgs(args, options.kubeContext), {
|
|
208
|
+
env: options.kubeconfigPath ? { KUBECONFIG: options.kubeconfigPath } : void 0,
|
|
209
|
+
stdin: "ignore"
|
|
210
|
+
});
|
|
199
211
|
return {
|
|
200
212
|
ok: true,
|
|
201
213
|
stderr: result.stderr,
|
|
@@ -217,6 +229,15 @@ function kubectlArgs(args, kubeContext) {
|
|
|
217
229
|
...args
|
|
218
230
|
] : args;
|
|
219
231
|
}
|
|
232
|
+
function inaccessibleOrMissingDetail(name, missingDetail, result) {
|
|
233
|
+
return isForbidden(result) ? inaccessibleDetail(name, result) : missingDetail;
|
|
234
|
+
}
|
|
235
|
+
function inaccessibleDetail(name, result) {
|
|
236
|
+
return `${name} inaccessible with the current kube identity: ${result.stderr}`;
|
|
237
|
+
}
|
|
238
|
+
function isForbidden(result) {
|
|
239
|
+
return FORBIDDEN_RE.test(result.stderr);
|
|
240
|
+
}
|
|
220
241
|
function parseJson(source) {
|
|
221
242
|
try {
|
|
222
243
|
return JSON.parse(source);
|
package/dist/codex-auth-sync.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { parse } from "jsonc-parser";
|
|
6
6
|
//#region src/codex-auth-sync.ts
|
|
7
|
-
const CODEX_MULTI_AUTH_PLUGIN = "oc-codex-multi-auth";
|
|
7
|
+
const CODEX_MULTI_AUTH_PLUGIN = "oc-codex-multi-auth@6.3.2";
|
|
8
8
|
const GLOBAL_CODEX_AUTH_CONFIG_PATH = join(homedir(), ".opencode/openai-codex-auth-config.json");
|
|
9
9
|
function syncLocalCodexAuth(options) {
|
|
10
10
|
const items = [syncGlobalPluginConfig(options.globalConfigPath ?? GLOBAL_CODEX_AUTH_CONFIG_PATH, options), ...discoverGitRepositories(options.root).map((repo) => syncProjectOpenCodeConfig(repo, options))];
|
|
@@ -55,6 +55,31 @@ declare const openCodeEcosystemManifestSchema: z.ZodObject<{
|
|
|
55
55
|
source: z.ZodOptional<z.ZodString>;
|
|
56
56
|
used_by: z.ZodArray<z.ZodString>;
|
|
57
57
|
}, z.core.$strict>>;
|
|
58
|
+
provider_models: z.ZodArray<z.ZodObject<{
|
|
59
|
+
id: z.ZodString;
|
|
60
|
+
options: z.ZodObject<{
|
|
61
|
+
include: z.ZodArray<z.ZodString>;
|
|
62
|
+
reasoningEffort: z.ZodEnum<{
|
|
63
|
+
none: "none";
|
|
64
|
+
low: "low";
|
|
65
|
+
medium: "medium";
|
|
66
|
+
high: "high";
|
|
67
|
+
xhigh: "xhigh";
|
|
68
|
+
}>;
|
|
69
|
+
reasoningSummary: z.ZodEnum<{
|
|
70
|
+
auto: "auto";
|
|
71
|
+
detailed: "detailed";
|
|
72
|
+
}>;
|
|
73
|
+
store: z.ZodLiteral<false>;
|
|
74
|
+
textVerbosity: z.ZodEnum<{
|
|
75
|
+
low: "low";
|
|
76
|
+
medium: "medium";
|
|
77
|
+
high: "high";
|
|
78
|
+
}>;
|
|
79
|
+
}, z.core.$strict>;
|
|
80
|
+
provider: z.ZodString;
|
|
81
|
+
role: z.ZodString;
|
|
82
|
+
}, z.core.$strict>>;
|
|
58
83
|
runtime: z.ZodObject<{
|
|
59
84
|
compatibility_runners: z.ZodArray<z.ZodString>;
|
|
60
85
|
default_runner: z.ZodLiteral<"opencode">;
|
|
@@ -125,6 +150,18 @@ declare const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST: {
|
|
|
125
150
|
path?: string | undefined;
|
|
126
151
|
source?: string | undefined;
|
|
127
152
|
}[];
|
|
153
|
+
provider_models: {
|
|
154
|
+
id: string;
|
|
155
|
+
options: {
|
|
156
|
+
include: string[];
|
|
157
|
+
reasoningEffort: "none" | "low" | "medium" | "high" | "xhigh";
|
|
158
|
+
reasoningSummary: "auto" | "detailed";
|
|
159
|
+
store: false;
|
|
160
|
+
textVerbosity: "low" | "medium" | "high";
|
|
161
|
+
};
|
|
162
|
+
provider: string;
|
|
163
|
+
role: string;
|
|
164
|
+
}[];
|
|
128
165
|
runtime: {
|
|
129
166
|
compatibility_runners: string[];
|
|
130
167
|
default_runner: "opencode";
|