Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs-site/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default defineConfig({
{ label: "Model Ordering", translations: { ko: "모델 정렬에 관하여", "zh-CN": "模型排序", ru: "Сортировка моделей", ja: "モデルの並び順" }, slug: "guides/model-ordering" },
{ label: "Claude Code", translations: { ko: "Claude Code", "zh-CN": "Claude Code", ru: "Claude Code", ja: "Claude Code" }, slug: "guides/claude-code" },
{ label: "Grok Build", translations: { ko: "Grok Build", "zh-CN": "Grok Build", ru: "Grok Build", ja: "Grok Build" }, slug: "guides/grok-build" },
{ label: "opencode", translations: { ko: "opencode", "zh-CN": "opencode", ru: "opencode", ja: "opencode" }, slug: "guides/opencode" },
{ label: "Sidecars: Web Search & Vision", translations: { ko: "사이드카: 웹 검색 & 비전", "zh-CN": "边车:网络搜索与视觉", ru: "Сайдкары: веб-поиск и зрение", ja: "サイドカー: ウェブ検索 & ビジョン" }, slug: "guides/sidecars" },
{ label: "Web Dashboard", translations: { ko: "웹 대시보드", "zh-CN": "网页控制台", ru: "Веб-дашборд", ja: "ウェブダッシュボード" }, slug: "guides/web-dashboard" },
{ label: "Sub-agent Surface", translations: { ko: "서브에이전트 서피스", "zh-CN": "子代理界面", ru: "Интерфейс подагентов", ja: "サブエージェントサーフェス" }, slug: "guides/sub-agent-surface" },
Expand Down
106 changes: 106 additions & 0 deletions docs-site/src/content/docs/guides/opencode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
title: opencode
description: Use any routed model from opencode — opencodex injects a runtime provider block and leaves your own opencode config untouched.
---

opencode reads its providers from merged JSON config layers rather than environment
variables, so there is no `ANTHROPIC_BASE_URL`-style slot to inject. `ocx opencode`
bridges that gap: it ensures the proxy is running, builds a provider block from the
visible catalog, and injects it through OpenCode's inline runtime layer
(`OPENCODE_CONFIG_CONTENT`).

## Quickstart

```bash
ocx opencode
```

This ensures the proxy is running and launches opencode with only the generated
`provider.opencodex` block injected for that process. Extra arguments pass through:
`ocx opencode run "hello"`.

Routed models appear in the picker under the `opencodex` provider:

```text
opencodex/kiro/glm-5
opencodex/gpt-5.6-sol # native slugs stay unprefixed
```

## Your own config is never modified

The launcher does not copy or rewrite `~/.config/opencode/opencode.json`,
project `opencode.json` / `opencode.jsonc`, or any other on-disk config layer. It may
read global or project config to detect a `provider.opencodex` override, while your
existing providers, agents, keybinds, MCP entries, and relative `{file:…}` references
keep resolving from their original files.

For this launch only, opencodex adds the generated `provider.opencodex` block through
OpenCode's inline runtime layer. That layer merges after global/custom/project config
and overrides only conflicting keys for the child process.

| Layer | Behavior with `ocx opencode` |
| --- | --- |
| Global / custom / project config | Left on disk exactly as you wrote it |
| Inline runtime (`OPENCODE_CONFIG_CONTENT`) | Receives only the generated `provider.opencodex` block |
| Relative `{file:…}` paths | Still resolve against the config file that originally defined them |

If a global or project config also defines `provider.opencodex`, the launcher prints an
informational note: the runtime layer from `ocx opencode` overrides it for that launch.

## The admission key is not written to disk

When the proxy requires an API key, the inline runtime config carries opencode's
`{env:…}` reference rather than the secret. Loopback binds use that reference as
`apiKey`; non-loopback binds send it only through `x-opencodex-api-key` so proxy
admission stays separate from any upstream `Authorization` header.

Loopback example:

```json
"options": {
"baseURL": "http://127.0.0.1:10100/v1",
"apiKey": "{env:OPENCODEX_OPENCODE_API_KEY}"
}
```

Non-loopback example:

```json
"options": {
"baseURL": "http://192.168.1.10:10100/v1",
"headers": {
"x-opencodex-api-key": "{env:OPENCODEX_OPENCODE_API_KEY}"
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```

The real value is passed only through the child process environment.
`OPENCODEX_API_AUTH_TOKEN` takes precedence, then the hardened service token file, then
a configured API key — which is what a non-loopback bind requires.

## Reverting

Nothing to undo — no generated config file is written under `~/.opencodex`. Run plain
`opencode` and it reads your own config exactly as before.

## Model limits

`limit.context` is written only when the catalog reports an authoritative context window; when it
does not, the whole `limit` block is omitted and opencode keeps its own defaults.

opencode's schema rejects a `limit` block carrying `context` without `output`, and the catalog has
no authoritative per-model output field, so an `output` budget of `32000` is emitted alongside it,
clamped down to the context window so a small-context model is never given `output > context`.
That figure exists to satisfy the schema — it is not a claim about any specific model's true
maximum.

The `opencodex` provider block is regenerated on every launch, so per-model tweaks made inside it
will not survive. Keep custom entries under a provider key of your own instead.

## Requirements

opencode must be installed and on `PATH`:

```bash
npm install -g opencode-ai
```
15 changes: 15 additions & 0 deletions src/cli/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,20 @@ const helpEntries: Record<string, HelpEntry> = {
"Claude Code settings: ocx claude config <status|set> ...",
],
},
opencode: {
usage: "ocx opencode [opencode args...]",
summary: "Launch opencode wired to the proxy (runtime provider config).",
details: [
"Ensures the proxy is running, then execs `opencode` with the generated `provider.opencodex`",
"block injected through OpenCode's inline runtime layer (`OPENCODE_CONFIG_CONTENT`). Any",
"existing inline config in the environment is preserved and only `provider.opencodex` is",
"overwritten for this launch.",
"Global/project opencode.json may be read to warn about an existing provider.opencodex",
"override; on-disk files are never modified.",
"Routed models appear in the model picker as opencodex/<provider>/<model>.",
"Stop using `ocx opencode` and plain `opencode` behaves exactly as before.",
],
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
restart: {
usage: "ocx restart",
summary: "Stop the proxy and restart it (background). Equivalent to stop + ensure.",
Expand Down Expand Up @@ -256,6 +270,7 @@ Usage:
ocx config <sub> Validated configuration show/get/set/import/export
ocx claude [args...] Launch Claude Code wired to the proxy (model discovery on)
ocx claude desktop [sub] Manage and apply Claude Desktop's four-family profile
ocx opencode [args...] Launch opencode wired to the proxy (runtime provider config)
ocx help [command] Show help
ocx --version | -v Print version

Expand Down
4 changes: 4 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,10 @@ switch (command) {
break;
}
process.exit(await cmdClaude(args.slice(1)));
}
case "opencode": {
const { cmdOpencode } = await import("./opencode");
process.exit(await cmdOpencode(args.slice(1)));
}
case "help":
case "--help":
Expand Down
Loading
Loading