@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.
@@ -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
@@ -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
  });
@@ -26,6 +26,7 @@ interface DoctorResult {
26
26
  interface DoctorFlags {
27
27
  cluster?: boolean | string;
28
28
  kubeContext?: string;
29
+ kubeconfig?: string;
29
30
  }
30
31
  declare function createCliProgram(): Command;
31
32
  declare function runCli(argv: string[]): Promise<void>;
@@ -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(parseCommandHost)).addOption(new Option("--scope <scope>", "config scope to update").choices(["project", "global"]).default("project").argParser(parseGatewayHostScope)).action((flags) => {
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(["all", "opencode"]).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
+ 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
- namespace: clusterNamespace(options.cluster)
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);
@@ -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, options.kubeContext),
21
- ...secretChecks(namespace, options.kubeContext, resources),
22
- checkExternalSecret(namespace, resources.eventAuthExternalSecretName, resources.externalSecretRemoteRef, options.kubeContext),
23
- checkClusterSecretStore("openbao", options.kubeContext),
24
- checkServiceAccount(namespace, resources.serviceAccountName, options.kubeContext),
25
- checkServiceAccountPermission(namespace, resources.serviceAccountName, {
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, options.kubeContext),
35
+ checkLocalQueue(namespace, resources.queueName, kubectlOptions),
31
36
  checkClusterResource("argo-workflow-crd", [
32
37
  "get",
33
38
  "crd",
34
39
  "workflows.argoproj.io"
35
- ], options.kubeContext),
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
- ], options.kubeContext)
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, kubeContext, resources) {
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, kubeContext));
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, kubeContext) {
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.`, kubeContext);
93
+ ], `Namespace ${namespace} missing or inaccessible.`, kubectlOptions);
89
94
  }
90
- async function checkNamespacedResource(name, args, missingDetail, kubeContext) {
91
- return (await kubectl(args, kubeContext)).ok ? {
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, kubeContext) {
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
- ], kubeContext);
111
- if (!result.ok) return {
112
- detail: `ExternalSecret ${name} missing in ${namespace}; expected it to sync ${remoteRef}.`,
113
- name: `externalsecret/${name}`,
114
- passed: false
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, kubeContext) {
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
- ], kubeContext);
126
- if (!result.ok) return {
127
- detail: `ClusterSecretStore/${name} missing or inaccessible; OpenBao/ESO readiness is an external prerequisite.`,
128
- name: `clustersecretstore/${name}`,
129
- passed: false
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, kubeContext) {
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.`, kubeContext);
152
+ ], `ServiceAccount ${name} missing in ${namespace}; runner pods must use this account for workflow execution.`, kubectlOptions);
141
153
  }
142
- async function checkServiceAccountPermission(namespace, serviceAccountName, options) {
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.kubeContext)).stdout.trim() === "yes" ? {
154
- detail: `${subject} can ${options.verb} ${options.resource}`,
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: `${subject} cannot ${options.verb} ${options.resource}; check runner ServiceAccount RBAC.`,
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, kubeContext) {
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.`, kubeContext);
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, kubeContext) {
173
- const result = await kubectl(args, kubeContext);
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, kubeContext) {
205
+ async function kubectl(args, options) {
197
206
  try {
198
- const result = await execa("kubectl", kubectlArgs(args, kubeContext), { stdin: "ignore" });
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);
@@ -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";