Skip to content
Closed
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 @@ -121,6 +121,7 @@ export default defineConfig({
{ label: "Codex App Model Picker", translations: { ko: "Codex App 모델 선택기", "zh-CN": "Codex App 模型选择器", ru: "Выбор модели в Codex App", ja: "Codex App モデルピッカー" }, slug: "guides/codex-app-models" },
{ 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: "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
95 changes: 95 additions & 0 deletions docs-site/src/content/docs/guides/opencode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: opencode
description: Use any routed model from opencode — opencodex generates a provider config and points OPENCODE_CONFIG at it, leaving your own opencode.json untouched.
---

opencode reads its providers from a JSON config rather than environment variables, so there is
no `ANTHROPIC_BASE_URL`-style slot to inject. `ocx opencode` bridges that gap: it generates a
provider block from the proxy's visible catalog and points `OPENCODE_CONFIG` at the result.

## Quickstart

```bash
ocx opencode
```

This ensures the proxy is running, writes `~/.opencodex/opencode-config.json`, and launches
opencode with `OPENCODE_CONFIG` set to that file. Extra arguments pass through:
`ocx opencode run "hello"`.

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

```
opencodex/kiro/glm-5
opencodex/gpt-5.6-sol # native slugs stay unprefixed
```
Comment on lines +22 to +25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language identifier to this fenced block.

Markdownlint reports MD040 for the unlabeled fence at Line 22. Use text or console after the opening backticks.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 22-22: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs-site/src/content/docs/guides/opencode.md` around lines 22 - 25, Add a
text or console language identifier to the fenced code block containing the
opencodex model slugs in the opencode documentation, while preserving its
contents unchanged.

Sources: Path instructions, Linters/SAST tools


## Your own config is never modified

The launcher does not write to `~/.config/opencode/opencode.json`. Instead it reads your
effective config, merges it forward into the generated copy, and overwrites only the
`opencodex` provider key. Every other provider, plus unrelated top-level fields (`model`,
`agents`, `keybinds`, `mcp`, …), is preserved verbatim.

| Source | Behavior |
| --- | --- |
| `OPENCODE_CONFIG` already exported | Used as the base config, then superseded by the generated path |
| `~/.config/opencode/opencode.json` (or `$XDG_CONFIG_HOME`) | Used as the base config when no explicit export exists |
| Neither exists | A standalone config containing only the `opencodex` provider |
| Base config is malformed | The launch is refused rather than silently dropping your settings |

Comments and trailing commas are fine — opencode documents `opencode.json` as JSONC, and the
launcher parses the same syntax the client accepts.
Comment on lines +29 to +42

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not promise byte-for-byte preservation of the base config.

src/cli/opencode.ts parses the base JSONC and rewrites the merged object with JSON.stringify, so settings are preserved semantically, but comments and formatting are not carried into the generated file. Please replace “preserved verbatim” and clarify that JSONC comments/trailing commas are accepted on input only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs-site/src/content/docs/guides/opencode.md` around lines 29 - 42, The
documentation incorrectly promises byte-for-byte preservation of the base
configuration. In the launcher behavior section, replace “preserved verbatim”
with wording that promises semantic preservation of providers and top-level
fields, and clarify that JSONC comments and trailing commas are accepted when
reading input but are not retained in the generated file.

Source: Path instructions


Carrying your config forward means the command behaves identically whether opencode merges the
`OPENCODE_CONFIG` layer or replaces it.

### Project configs still win

opencode loads a project-level `opencode.json` *after* the `OPENCODE_CONFIG` layer. If your
project config defines `provider.opencodex`, it overrides the generated block and the child may
talk to a stale base URL. The launcher cannot outrank that layer without writing to one of your
files, so it prints a warning instead. Rename the project-level key to resolve it.

## The admission key is not written to disk

When the proxy requires an API key, the generated config carries opencode's `{env:…}` reference
rather than the secret:

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

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

## Reverting

Nothing to undo — the generated file lives in the opencodex config dir and is only consulted
when the launcher sets `OPENCODE_CONFIG` for the child process. 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
```
13 changes: 13 additions & 0 deletions src/cli/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ const helpEntries: Record<string, HelpEntry> = {
"User-exported ANTHROPIC_* variables always take precedence.",
],
},
opencode: {
usage: "ocx opencode [opencode args...]",
summary: "Launch opencode wired to the proxy (generated provider config).",
details: [
"Ensures the proxy is running, then execs `opencode` with OPENCODE_CONFIG pointed at a",
"generated config in the opencodex config dir. Your own opencode.json is never modified —",
"its settings are merged forward into the generated copy, and only the `opencodex`",
"provider key is overwritten.",
"Routed models appear in the model picker as opencodex/<provider>/<model>.",
"Stop using `ocx opencode` and plain `opencode` behaves exactly as before.",
],
},
restart: {
usage: "ocx restart",
summary: "Stop the proxy and restart it (background). Equivalent to stop + ensure.",
Expand Down Expand Up @@ -192,6 +204,7 @@ Usage:
ocx account <sub> Accounts/keys (list|current|use|refresh|auto-switch|remove|add-key)
ocx models <sub> List models; manage custom models (add|remove|list-custom)
ocx claude [args...] Launch Claude Code wired to the proxy (model discovery on)
ocx opencode [args...] Launch opencode wired to the proxy (generated 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 @@ -810,6 +810,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