Skip to content

Commit b04f2ba

Browse files
committed
docs: document ocx export and the Pi client
Phase 050 of devlog/_plan/260731_client_config_export. The export command shipped in the previous four commits with nothing in docs-site pointing at it, and Pi had no page at all. Adds the ocx export section to the CLI reference (English plus the four translated copies), a section in the opencode guide on getting the provider block into your own config, and a Pi guide. Three things the docs have to get right, each verified against the code while writing: the two clients use DIFFERENT env vars (OPENCODEX_OPENCODE_API_KEY vs OPENCODEX_API_KEY), a loopback bind needs no admission key at all since shouldInjectApiAuthHeader is just !isLoopbackHostname, and an exported config must be merged rather than replace an existing file. BYOK is split rather than restated: the proxy admission key is what the exported config references, provider keys stay in providers.<name>.apiKey and keep their existing home in the providers guide. The Pi schema is still unverified against a real install and the guide says so.
1 parent f7ac037 commit b04f2ba

9 files changed

Lines changed: 510 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# 050 — Phase 5: user-facing docs + hardening
2+
3+
Second work-phase of this unit, appended after the four implementation phases
4+
closed (LOOP-UNIT-CHAIN-01). The feature ships; nothing tells a user it exists.
5+
6+
## Why this phase exists
7+
8+
`ocx export` and the GUI panel landed in `f7ac037e0`. A grep of `docs-site/`
9+
finds:
10+
11+
- `reference/cli.md` — the command table lists `ocx access`, `ocx config`,
12+
`ocx observe`. There is no `ocx export` row and no subcommand section.
13+
- `guides/opencode.md` — 106 lines about `ocx opencode`. It states "your own
14+
config is never modified", which is still true, but there is now a supported
15+
way to GET the block into your own config and the guide does not mention it.
16+
- Pi — no guide, no mention anywhere in the repo.
17+
- BYOK — the term appears nowhere in `src/`, `gui/`, or `docs-site/`.
18+
19+
A feature a user cannot discover is not shipped.
20+
21+
## The BYOK question
22+
23+
The user asked for "byok 같은 곳 설정하는 법". Read against the tree, BYOK here
24+
is not a new subsystem — it is the **credential half** of the export story, and
25+
it spans two different keys that are easy to confuse:
26+
27+
| Key | What it is | Where it goes |
28+
|-----|-----------|---------------|
29+
| Proxy admission (`ocx_…`) | opencodex's own key, generated on the API tab | the exported config's env reference |
30+
| Provider key (`sk-…`, etc.) | your Anthropic/OpenAI/OpenRouter key | `providers.<name>.apiKey` in opencodex config |
31+
32+
The export flow only ever references the FIRST. The second is what "bring your
33+
own key" normally means, is documented in `guides/providers.md` §Auth modes and
34+
`reference/configuration.md` (`apiKey`, `apiKeyPool`, `${ENV_VAR}`), and is NOT
35+
re-documented here — this phase links to it instead of forking a second
36+
explanation that would drift.
37+
38+
What is genuinely missing is the seam: a reader who exports a config has no
39+
page telling them which key the `{env:...}` reference wants, how to generate
40+
it, or that a loopback proxy does not require one at all.
41+
42+
## Scope
43+
44+
IN
45+
- MODIFY `docs-site/src/content/docs/reference/cli.md` — command table row +
46+
an `ocx export` subcommand section.
47+
- MODIFY `docs-site/src/content/docs/guides/opencode.md` — a section on taking
48+
the provider block into your own config, pointing at `ocx export`.
49+
- NEW `docs-site/src/content/docs/guides/pi.md` — the Pi guide, including the
50+
credential seam.
51+
- Locale sync for any translated page whose English source changed, per
52+
AGENTS.md ("keep translated locales from contradicting the English source").
53+
54+
OUT
55+
- No `src/` or `gui/` behavior change. This phase is docs + whatever hardening
56+
the docs review exposes, and a code change discovered here becomes its own
57+
amendment rather than a silent edit.
58+
- No re-documentation of provider auth modes; link to `guides/providers.md`.
59+
- No new BYOK subsystem. BYOK is an existing capability, not a feature to add.
60+
61+
## Hardening review (the docs pass doubles as an adversarial read)
62+
63+
Writing the docs forces every claim to be checked against the shipped code.
64+
Four claims to verify while writing, each a real failure if wrong:
65+
66+
1. **The destination path we print is the one Pi/OpenCode actually reads.**
67+
`EXPORT_CLIENTS.*.destination` honors `XDG_CONFIG_HOME` for OpenCode.
68+
Pi's path is still UNVERIFIED against a real install (`001` §2) — the guide
69+
must say so rather than assert it.
70+
2. **The env var name in the docs matches the one in the emitted config.**
71+
`OPENCODEX_OPENCODE_API_KEY` for OpenCode, `OPENCODEX_API_KEY` for Pi. Two
72+
different names; a doc that mixes them sends the reader in a circle.
73+
3. **A loopback-only user needs no key at all.** `shouldInjectApiAuthHeader`
74+
decides this. If the docs tell every reader to generate a key, most readers
75+
do unnecessary work; if they tell nobody, non-loopback binds break.
76+
4. **The merge warning is in the docs, not just the CLI.** The download and the
77+
`--out` refusal both exist because replacing an existing config destroys
78+
other providers. The docs must carry the same warning.
79+
80+
## Accept criteria
81+
82+
1. `ocx export` appears in the `reference/cli.md` command table AND has a
83+
subcommand section documenting `--client`, `--json`, `--out`, `--force`.
84+
**Activation:** grep the built page for `ocx export`.
85+
2. The Pi guide states the exact destination path, the exact env var, and marks
86+
the Pi schema as unverified against a real install.
87+
3. Every env var name in the new docs matches the string the code emits.
88+
**Activation:** grep `OPENCODEX_OPENCODE_API_KEY` / `OPENCODEX_API_KEY` in
89+
both `src/clients/config-export.ts` and the new docs; the sets must agree.
90+
4. The docs state that loopback binds need no admission key, and that a config
91+
must be merged rather than used to replace an existing file.
92+
5. `bun run privacy:scan` green — the new docs use `~/…`, never a real home path.
93+
6. Docs build passes.

docs-site/astro.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export default defineConfig({
8989
{ label: "Claude Code", translations: { ko: "Claude Code", "zh-CN": "Claude Code", ru: "Claude Code", ja: "Claude Code" }, slug: "guides/claude-code" },
9090
{ label: "Grok Build", translations: { ko: "Grok Build", "zh-CN": "Grok Build", ru: "Grok Build", ja: "Grok Build" }, slug: "guides/grok-build" },
9191
{ label: "opencode", translations: { ko: "opencode", "zh-CN": "opencode", ru: "opencode", ja: "opencode" }, slug: "guides/opencode" },
92+
{ label: "Pi", translations: { ko: "Pi", "zh-CN": "Pi", ru: "Pi", ja: "Pi" }, slug: "guides/pi" },
9293
{ label: "Sidecars: Web Search & Vision", translations: { ko: "사이드카: 웹 검색 & 비전", "zh-CN": "边车:网络搜索与视觉", ru: "Сайдкары: веб-поиск и зрение", ja: "サイドカー: ウェブ検索 & ビジョン" }, slug: "guides/sidecars" },
9394
{ label: "Web Dashboard", translations: { ko: "웹 대시보드", "zh-CN": "网页控制台", ru: "Веб-дашборд", ja: "ウェブダッシュボード" }, slug: "guides/web-dashboard" },
9495
{ label: "Sub-agent Surface", translations: { ko: "서브에이전트 서피스", "zh-CN": "子代理界面", ru: "Интерфейс подагентов", ja: "サブエージェントサーフェス" }, slug: "guides/sub-agent-surface" },

docs-site/src/content/docs/guides/opencode.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,43 @@ and overrides only conflicting keys for the child process.
4747
If a global or project config also defines `provider.opencodex`, the launcher prints an
4848
informational note: the runtime layer from `ocx opencode` overrides it for that launch.
4949

50+
## Putting the block into your own config
51+
52+
`ocx opencode` injects the provider block for one launch only, which means plain `opencode` still
53+
knows nothing about the proxy. When you want routed models available from plain `opencode` — or
54+
from an editor extension that never goes through the launcher — `ocx export` prints the same
55+
provider block for you to merge into your own config:
56+
57+
```bash
58+
ocx export --client opencode
59+
```
60+
61+
The proxy must be running. The command prints the config, the canonical destination
62+
(`~/.config/opencode/opencode.json`, or under `XDG_CONFIG_HOME` when that is set), the merge
63+
warning, and the env export line. It never touches that file — the section above stays true, and
64+
moving the block into your config is your explicit act.
65+
66+
:::caution[Merge, never replace]
67+
Merge the `provider.opencodex` block into your existing config. Replacing the whole file with the
68+
exported one destroys your other providers, agents, keybinds, and MCP entries. `ocx export --out`
69+
refuses to overwrite an existing file for exactly this reason, so point `--out` at a scratch path
70+
and copy the block across:
71+
72+
```bash
73+
ocx export --client opencode --out ~/opencodex-opencode.json
74+
```
75+
:::
76+
77+
Unlike the launcher's runtime block, a merged block is a static snapshot: it does not follow your
78+
catalog. Re-run `ocx export` after you add a provider or change model visibility.
79+
80+
Once merged, export the admission key before launching opencode — unless the proxy is on loopback,
81+
where none is needed:
82+
83+
```bash
84+
export OPENCODEX_OPENCODE_API_KEY=<your key>
85+
```
86+
5087
## The admission key is not written to disk
5188

5289
When the proxy requires an API key, the inline runtime config carries opencode's
@@ -78,6 +115,12 @@ The real value is passed only through the child process environment.
78115
`OPENCODEX_API_AUTH_TOKEN` takes precedence, then the hardened service token file, then
79116
a configured API key — which is what a non-loopback bind requires.
80117

118+
A loopback bind (`127.0.0.1`, the default) authenticates nothing, so the `{env:…}` reference is
119+
inert and you can leave the variable unset. It matters only when `hostname` is set beyond loopback;
120+
see [Remote access](/reference/configuration/#remote-access). This admission key is opencodex's
121+
own, and is unrelated to the upstream provider keys configured under
122+
[Providers](/guides/providers/).
123+
81124
## Reverting
82125

83126
Nothing to undo — no generated config file is written under `~/.opencodex`. Run plain
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
---
2+
title: Pi
3+
description: Use any routed model from Pi — ocx export writes a custom provider block for Pi's models.json, wired to the running proxy.
4+
---
5+
6+
Pi reads its providers from a single global JSON file rather than environment variables, so
7+
opencodex does not launch it. Instead, `ocx export` serializes the `opencodex` provider block —
8+
base URL, model list, and the env reference Pi interpolates — and you merge it into your own
9+
config.
10+
11+
## Quickstart
12+
13+
Start the proxy, then print the config:
14+
15+
```bash
16+
ocx start
17+
ocx export --client pi
18+
```
19+
20+
The output leads with the JSON, then prints the destination path, the merge warning, the env
21+
export line, and how many models carry authoritative context limits.
22+
23+
```json
24+
{
25+
"providers": {
26+
"opencodex": {
27+
"baseUrl": "http://127.0.0.1:10100/v1",
28+
"api": "openai-completions",
29+
"apiKey": "$OPENCODEX_API_KEY",
30+
"models": [
31+
{
32+
"id": "anthropic/claude-opus-5",
33+
"name": "Claude Opus 5 (anthropic)",
34+
"input": ["text"],
35+
"contextWindow": 200000,
36+
"maxTokens": 32000
37+
}
38+
]
39+
}
40+
}
41+
}
42+
```
43+
44+
Model ids are the proxy's canonical selectors, so routed models appear as `provider/model`
45+
(`anthropic/claude-opus-5`) and native OpenAI slugs stay unprefixed (`gpt-5.6-sol`). The `name`
46+
suffix — `(anthropic)`, `(native)`, `(routed)` — is what makes two same-named models from
47+
different upstreams distinguishable in Pi's picker.
48+
49+
## Where it goes
50+
51+
Pi's global model config is:
52+
53+
```text
54+
~/.pi/agent/models.json
55+
```
56+
57+
:::caution[Merge, never replace]
58+
`ocx export` never writes that file. Merge the `providers.opencodex` block into it — replacing the
59+
file destroys every other provider you have configured there. `--out` exists for a scratch path
60+
and refuses to overwrite an existing file without `--force`:
61+
62+
```bash
63+
ocx export --client pi --out ~/opencodex-pi-models.json
64+
ocx export --client pi --json > ~/opencodex-pi-models.json # or redirect the byte-exact JSON
65+
```
66+
:::
67+
68+
The exported block is a static snapshot, not a live view. Re-run `ocx export` after adding a
69+
provider or changing model visibility, and merge the new block over the old one.
70+
71+
## The admission key
72+
73+
Two different keys are easy to confuse here, and only the first one appears in this file:
74+
75+
| Key | What it is | Where it lives |
76+
| --- | --- | --- |
77+
| Proxy admission key | opencodex's own credential, generated on the dashboard's **API** tab | referenced by `apiKey` as `$OPENCODEX_API_KEY`; the value stays in your environment |
78+
| Provider key | your Anthropic / OpenAI / OpenRouter key | opencodex's own config, per [Providers](/guides/providers/) |
79+
80+
The exported config carries only the reference, never a secret. Pi interpolates a bare `$NAME`, so
81+
the variable is:
82+
83+
```bash
84+
export OPENCODEX_API_KEY=<your key>
85+
```
86+
87+
That name is Pi's alone. opencode uses a different variable
88+
(`OPENCODEX_OPENCODE_API_KEY`, in `{env:…}` form) — see the [opencode guide](/guides/opencode/).
89+
90+
**A loopback proxy needs no key at all.** opencodex binds `127.0.0.1` by default and authenticates
91+
nothing there, so the `$OPENCODEX_API_KEY` reference is inert and you can leave the variable unset.
92+
It matters only when `hostname` is set beyond loopback, which is also the case where the proxy
93+
refuses to start without a token — see [Remote access](/reference/configuration/#remote-access).
94+
95+
## Model metadata
96+
97+
`contextWindow` and `maxTokens` are emitted only when the catalog reports an authoritative context
98+
window. When it does not, both fields are omitted for that model and Pi applies its own defaults;
99+
`ocx export` prints how many rows fell into that case.
100+
101+
`maxTokens` is a schema-satisfying budget of `32000`, clamped down to the context window so a
102+
small-context model is never given more output than context. It is not a claim about any specific
103+
model's true maximum.
104+
105+
Two fields are deliberately absent. `cost` requires all four price fields and opencodex has no
106+
price data for routed models — emitting zeros would assert that every model is free. `reasoning` is
107+
a boolean in Pi while the catalog carries an effort ladder, and mapping one onto the other would be
108+
a guess.
109+
110+
## Schema status
111+
112+
:::note[Unverified against a real install]
113+
The shape above follows Pi's published custom-provider documentation. It has **not** been verified
114+
against a real `~/.pi/agent/models.json` on a machine with Pi installed. If Pi rejects the exported
115+
block, the mismatch is on our side — please
116+
[open an issue](https://github.com/lidge-jun/opencodex/issues) with what Pi reported.
117+
:::
118+
119+
## Requirements
120+
121+
A running opencodex proxy (`ocx start`) and Pi installed. `ocx export` reads the live catalog
122+
through the proxy's management API, so a config can never be emitted with an empty model list.

docs-site/src/content/docs/ja/reference/cli.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,56 @@ security find-generic-password -w openrouter | ocx account add-key openrouter --
321321

322322
`--json``{ ok: true, id: string | null, label?: string }` を返し key を含みません。
323323

324+
### `ocx export --client <opencode|pi>`
325+
326+
実行中のプロキシに接続されたクライアント設定を出力します。opencode と Pi は環境変数ではなく自分の
327+
JSON 設定ファイルからプロバイダーを読むため、このコマンドが `opencodex` プロバイダーブロック
328+
(base URL、モデル一覧、クライアントが解釈する環境変数参照) を直列化します。自分のファイルへの
329+
マージはユーザーが行います。
330+
331+
プロキシが動いている必要があります。実行中のポートを解決して `/api/models` を読み、今 Codex から
332+
見えるモデルだけを出力します。
333+
334+
| フラグ | 動作 |
335+
| --- | --- |
336+
| `--client <opencode\|pi>` | 必須。クライアント方言を選びます。opencode は key 付き `provider` オブジェクト、Pi は `providers` 配列です。 |
337+
| `--json` | stdout に設定 JSON だけを出力するので、リダイレクトしてもバイト単位で正確です。`--out` の書き込み通知を含むすべての診断は stderr に出ます。 |
338+
| `--out <path>` | 設定を `<path>` に書きます。既存ファイルの置き換えは拒否します。 |
339+
| `--force` | `--out` が既存ファイルを置き換えることを許可します。 |
340+
341+
```bash
342+
ocx export --client opencode # 設定に加えて宛先パス、マージ警告、件数
343+
ocx export --client pi --json > pi-models.json # パイプや diff 用のバイト正確な JSON
344+
ocx export --client opencode --out ~/opencodex-opencode.json
345+
```
346+
347+
`--json` なしでは JSON が先に出て、続いて正規の宛先パス、マージ警告、環境変数の export 行、モデル
348+
件数とコンテキスト上限を持たない行数 (それらはクライアント側の既定値が使われます) が出力されます。
349+
350+
| クライアント | 正規の宛先 | ダウンロードファイル名 | 環境変数 |
351+
| --- | --- | --- | --- |
352+
| `opencode` | `~/.config/opencode/opencode.json` (`XDG_CONFIG_HOME` が設定されていればそちら) | `opencode.json` | `OPENCODEX_OPENCODE_API_KEY` |
353+
| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | `OPENCODEX_API_KEY` |
354+
355+
2 つの環境変数名は異なり、各クライアントは自分のものだけを解釈します。opencode は
356+
`{env:OPENCODEX_OPENCODE_API_KEY}`、Pi は `$OPENCODEX_API_KEY` を読みます。
357+
358+
:::caution[置き換えではなくマージ]
359+
`ocx export` が実際のクライアント設定ファイルを書くことはありません。宛先パスは手動でマージする
360+
ために表示され、`--out``--force` なしでは既存ファイルを上書きしません。設定ファイルを丸ごと
361+
置き換えると、そこにあった他のプロバイダー、エージェント、MCP エントリが失われるからです。
362+
:::
363+
364+
key が直列化されることはありません。設定にはクライアントの環境変数参照だけが入り、secret は環境に
365+
残ります。ループバックのプロキシ (既定の `127.0.0.1`) では admission key 自体が不要で、参照は使わ
366+
れません。プロキシをループバック外にバインドするときだけ変数を設定してください。admission key の
367+
発行方法は [リモートアクセス](/ja/reference/configuration/#リモートアクセス) を参照してください。
368+
上流プロバイダーの key はまったく別物で、[プロバイダー](/ja/guides/providers/) で設定します。Pi
369+
ガイドは英語のみです: [Pi](/guides/pi/)
370+
371+
同じペイロードを `GET /api/client-config` が返し、ダッシュボードの API タブが描画するので、CLI と
372+
API と GUI が異なるバイトを見せることはありません。
373+
324374
## 認証
325375

326376
### `ocx login <provider>`

0 commit comments

Comments
 (0)