diff --git a/devlog/_fin/260806_pi_loopback_models/evidence/pi-config-preview.png b/devlog/_fin/260806_pi_loopback_models/evidence/pi-config-preview.png
new file mode 100644
index 0000000000..26693304e2
Binary files /dev/null and b/devlog/_fin/260806_pi_loopback_models/evidence/pi-config-preview.png differ
diff --git a/docs-site/src/content/docs/guides/integrations.md b/docs-site/src/content/docs/guides/integrations.md
index c917fd273a..5996a13994 100644
--- a/docs-site/src/content/docs/guides/integrations.md
+++ b/docs-site/src/content/docs/guides/integrations.md
@@ -9,12 +9,15 @@ file, and removes it again. Six clients work this way, each with a switch:
| Client | Config file | Format | When the change takes effect | Credential |
|---|---|---|---|---|
| OpenCode | `~/.config/opencode/opencode.json` | JSON | next direct launch | `OPENCODEX_OPENCODE_API_KEY` |
-| Pi | `~/.pi/agent/models.json` | JSON | new sessions | `OPENCODEX_API_KEY` |
+| Pi | `~/.pi/agent/models.json` | JSON | new sessions | non-secret `opencodex-loopback` placeholder |
| Hermes | `~/.hermes/config.yaml` | YAML | new sessions | `OPENCODEX_HERMES_API_KEY` |
| OpenClaw | `~/.openclaw/openclaw.json` | JSON5 | immediately, on a running gateway | `OPENCODEX_OPENCLAW_API_KEY` |
| Kimi Code | `~/.kimi-code/config.toml` | TOML | on restart, or `/reload` | loopback placeholder |
| Gajae Code | `~/.gjc/agent/models.yml` | YAML | new sessions, or when you open `/model` |`OPENCODEX_GAJAE_API_KEY` |
+Pi's loopback connection normally needs no real admission key. Configure credentials
+for upstream providers in opencodex, not in Pi's generated provider block.
+
Paths honor each client's own environment override where it has one, so a relocated
`HERMES_HOME`, `KIMI_CODE_HOME` or `XDG_CONFIG_HOME` is followed rather than guessed
at. The table lists each client's default; an override always wins.
diff --git a/docs-site/src/content/docs/guides/pi.md b/docs-site/src/content/docs/guides/pi.md
index fa44d2754f..37d4635d4d 100644
--- a/docs-site/src/content/docs/guides/pi.md
+++ b/docs-site/src/content/docs/guides/pi.md
@@ -5,8 +5,7 @@ description: Use any routed model from Pi — ocx export writes a custom provide
Pi reads its providers from a single global JSON file rather than environment variables, so
opencodex does not launch it. Instead, `ocx export` serializes the `opencodex` provider block —
-base URL, model list, and the env reference Pi interpolates — and you merge it into your own
-config.
+base URL, model list, and a non-secret literal `apiKey` placeholder — and you merge it into your own config.
## Quickstart
@@ -17,8 +16,11 @@ ocx start
ocx export --client pi
```
-The output leads with the JSON, then prints the destination path, the merge warning, the env
-export line, and how many models carry authoritative context limits.
+The output leads with the JSON, then prints the destination path, the merge warning, Pi-specific
+pre-launch guidance, the total model count, and how many rows omit context limits.
+
+In Pi's schema, `openai-completions` names the Chat Completions-compatible API; the
+corresponding opencodex adapter name is `openai-chat`.
```json
{
@@ -26,7 +28,7 @@ export line, and how many models carry authoritative context limits.
"opencodex": {
"baseUrl": "http://127.0.0.1:10100/v1",
"api": "openai-completions",
- "apiKey": "$OPENCODEX_API_KEY",
+ "apiKey": "opencodex-loopback",
"models": [
{
"id": "anthropic/claude-opus-5",
@@ -68,29 +70,19 @@ ocx export --client pi --json > ~/opencodex-pi-models.json # or redirect the b
The exported block is a static snapshot, not a live view. Re-run `ocx export` after adding a
provider or changing model visibility, and merge the new block over the old one.
-## The admission key
-
-Two different keys are easy to confuse here, and only the first one appears in this file:
-
-| Key | What it is | Where it lives |
-| --- | --- | --- |
-| 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 |
-| Provider key | your Anthropic / OpenAI / OpenRouter key | opencodex's own config, per [Providers](/guides/providers/) |
+## The Pi `apiKey` placeholder
-The exported config carries only the reference, never a secret. Pi interpolates a bare `$NAME`, so
-the variable is:
-
-```bash
-export OPENCODEX_API_KEY=
-```
+Pi normally calls `/chat/completions` and sends its configured `apiKey` as a Bearer authorization
+value. The generated block therefore includes the non-secret literal `opencodex-loopback` in Pi's
+normal `apiKey` field.
-That name is Pi's alone. opencode uses a different variable
-(`OPENCODEX_OPENCODE_API_KEY`, in `{env:…}` form) — see the [opencode guide](/guides/opencode/).
+That literal is neither a proxy admission credential nor an upstream provider key. The loopback
+proxy ignores it and requires no credential at all. It is still load-bearing for discovery: Pi
+resolves `apiKey` while building its model list and hides the whole provider when the value is an
+unset env reference, so a literal keeps every routed model visible.
-**A loopback proxy needs no key at all.** opencodex binds `127.0.0.1` by default and authenticates
-nothing there, so the `$OPENCODEX_API_KEY` reference is inert and you can leave the variable unset.
-It matters only when `hostname` is set beyond loopback, which is also the case where the proxy
-refuses to start without a token — see [Remote access](/reference/configuration/#remote-access).
+Your provider keys are separate — the Anthropic / OpenAI / OpenRouter key lives in opencodex's own
+config, per [Providers](/guides/providers/), and never appears in this file.
## Model metadata
@@ -109,10 +101,10 @@ a guess.
## Schema status
-:::note[Unverified against a real install]
-The shape above follows Pi's published custom-provider documentation. It has **not** been verified
-against a real `~/.pi/agent/models.json` on a machine with Pi installed. If Pi rejects the exported
-block, the mismatch is on our side — please
+:::note[Verified against a real install]
+The shape above has been verified against Pi 0.83.0 on a real `~/.pi/agent/models.json`: the block
+validates, and every exported routed model with a Pi-supported input modality appears in Pi's
+picker. If a newer Pi rejects the exported block, the mismatch is on our side — please
[open an issue](https://github.com/lidge-jun/opencodex/issues) with what Pi reported.
:::
diff --git a/docs-site/src/content/docs/ja/guides/pi.md b/docs-site/src/content/docs/ja/guides/pi.md
index 788fe48c60..d6f5d2f72a 100644
--- a/docs-site/src/content/docs/ja/guides/pi.md
+++ b/docs-site/src/content/docs/ja/guides/pi.md
@@ -3,7 +3,7 @@ title: 円周率
description: Pi からルーティングされたモデルを使用します。ocx エクスポートは、実行中のプロキシに接続された Pi の models.json のカスタム プロバイダー ブロックを書き込みます。
---
-Pi は環境変数ではなく単一のグローバル JSON ファイルからプロバイダーを読み取るため、opencodex はそれを起動しません。代わりに、`ocx export` は `opencodex` プロバイダー ブロック (ベース URL、モデル リスト、Pi が補間する環境参照) をシリアル化し、それを独自の設定にマージします。
+Pi は環境変数ではなく単一のグローバル JSON ファイルからプロバイダーを読み取るため、opencodex はそれを起動しません。代わりに、`ocx export` は `opencodex` プロバイダー ブロック (ベース URL、モデル リスト、秘密ではないリテラル `apiKey` プレースホルダー) をシリアル化し、それを独自の設定にマージします。
## クイックスタート
@@ -14,7 +14,9 @@ ocx start
ocx export --client pi
```
-出力は JSON で始まり、宛先パス、マージ警告、env エクスポート行、および権威コンテキスト制限を持つモデルの数を出力します。
+出力は JSON で始まり、宛先パス、マージ警告、Pi 固有の起動前ガイダンス、モデルの総数、およびコンテキスト制限を省略した行数を出力します。
+
+Pi スキーマの `openai-completions` は Chat Completions 互換 API を指し、対応する opencodex アダプター名は `openai-chat` です。
```json
{
@@ -22,7 +24,7 @@ ocx export --client pi
"opencodex": {
"baseUrl": "http://127.0.0.1:10100/v1",
"api": "openai-completions",
- "apiKey": "$OPENCODEX_API_KEY",
+ "apiKey": "opencodex-loopback",
"models": [
{
"id": "anthropic/claude-opus-5",
@@ -58,24 +60,13 @@ ocx export --client pi --json > ~/opencodex-pi-models.json # or redirect the b
エクスポートされたブロックは静的なスナップショットであり、ライブ ビューではありません。プロバイダーを追加するかモデルの可視性を変更した後、`ocx export` を再実行し、新しいブロックを古いブロックにマージします。
-## アドミッションキー
-
-ここでは 2 つの異なるキーが混同されやすいため、このファイルには最初のキーのみが表示されます。
-
-|キー |それは何ですか |それが住んでいる場所 |
-| --- | --- | --- |
-|プロキシ アドミッション キー | opencodex 自身の認証情報。ダッシュボードの **API** タブで生成されます。 `apiKey` では `$OPENCODEX_API_KEY` として参照されます。値は環境内に残ります。
-|プロバイダーキー | Anthropic / OpenAI / OpenRouter キー | opencodex 独自の設定、[プロバイダー](/guides/providers/) ごと |
+## Pi の `apiKey` プレースホルダー
-エクスポートされた設定には参照のみが含まれ、シークレットは含まれません。 Pi は裸の `$NAME` を補間するため、変数は次のようになります。
-
-```bash
-export OPENCODEX_API_KEY=
-```
+Pi は通常 `/chat/completions` を呼び出し、設定された `apiKey` を Bearer 認証値として送信します。そのため、生成されるブロックでは、Pi の通常の `apiKey` フィールドに非シークレットのリテラル `opencodex-loopback` を入れます。
-その名前はパイだけです。 opencode は別の変数 (`OPENCODEX_OPENCODE_API_KEY`、`{env:…}` 形式) を使用します。[オープンコードガイド](/guides/opencode/) を参照してください。
+このリテラルは、プロキシのアドミッション認証情報でも上流プロバイダーのキーでもありません。ループバック プロキシはこの値を無視し、認証情報を一切要求しません。ただしモデル検出には必須です。Pi はモデル リストを構築する際に `apiKey` を解決し、その値が未設定の環境変数参照である場合はプロバイダー全体を隠すため、リテラルであればルーティングされたすべてのモデルが表示されます。
-**ループバック プロキシにはキーはまったく必要ありません。** opencodex はデフォルトで `127.0.0.1` をバインドし、そこでは何も認証しないため、`$OPENCODEX_API_KEY` 参照は不活性であり、変数を設定しないままにすることができます。これは、`hostname` がループバックを超えて設定されている場合にのみ問題になります。これは、プロキシがトークンなしでの開始を拒否する場合でもあります。[リモートアクセス](/reference/configuration/#remote-access) を参照してください。
+プロバイダー キーは別のものです。Anthropic / OpenAI / OpenRouter のキーは opencodex 自身の設定にあり ([プロバイダー](/guides/providers/) を参照)、このファイルには決して現れません。
## モデルのメタデータ
@@ -87,8 +78,8 @@ export OPENCODEX_API_KEY=
## スキーマのステータス
-:::note[実際のインストールに対して未検証]
-上の形状は、Pi が公開しているカスタム プロバイダーのドキュメントに従っています。 Pi がインストールされたマシン上の実際の `~/.pi/agent/models.json` に対して検証されていません**。 Pi がエクスポートされたブロックを拒否した場合、不一致は私たちの側にあります。Pi が報告した内容を [問題を開く](https://github.com/lidge-jun/opencodex/issues) してください。
+:::note[実際のインストールで検証済み]
+上の形状は、Pi 0.83.0 がインストールされたマシン上の実際の `~/.pi/agent/models.json` に対して検証されています。ブロックは検証を通過し、Pi がサポートする入力モダリティを持つ、エクスポートされたすべてのルーティング モデルが Pi のピッカーに表示されます。より新しい Pi がエクスポートされたブロックを拒否した場合、不一致は私たちの側にあります。Pi が報告した内容を添えて [問題を開く](https://github.com/lidge-jun/opencodex/issues) してください。
:::
## 要件
diff --git a/docs-site/src/content/docs/ja/reference/cli/agents.md b/docs-site/src/content/docs/ja/reference/cli/agents.md
index c970804e7b..1f15d58aa5 100644
--- a/docs-site/src/content/docs/ja/reference/cli/agents.md
+++ b/docs-site/src/content/docs/ja/reference/cli/agents.md
@@ -127,7 +127,7 @@ Grok Build モデル フェンスを管理および適用します。
### `ocx export --client `
-実行中のプロキシに接続されているクライアント設定を出力します。 opencode と [円周率](/guides/pi/) は環境変数ではなく独自の JSON 設定からプロバイダーを読み取るため、このコマンドは `opencodex` プロバイダー ブロック (ベース URL、モデル リスト、クライアントの環境参照) をシリアル化し、そのファイルにマージできるようにします。
+実行中のプロキシに接続されているクライアント設定を出力します。 opencode と [円周率](/guides/pi/) は環境変数ではなく独自の JSON 設定からプロバイダーを読み取るため、このコマンドは `opencodex` プロバイダー ブロック (ベース URL、モデル リスト、クライアント固有の認証情報参照またはループバック プレースホルダー) をシリアル化し、そのファイルにマージできるようにします。
プロキシが実行されている必要があります。このコマンドはライブ ポートを解決し、`/api/models` を読み取り、Codex が現在認識できるモデルのみを出力します。
@@ -144,20 +144,20 @@ ocx export --client pi --json > pi-models.json # byte-exact JSON for a pipe or
ocx export --client opencode --out ~/opencodex-opencode.json
```
-`--json` がない場合、JSON が先頭に続き、正規の宛先パス、マージ警告、環境エクスポート行、およびコンテキスト制限を省略する行数を含むモデル数が続きます (クライアントはこれらに対して独自のデフォルトを適用します)。
+`--json` がない場合、JSON が先頭に続き、正規の宛先パス、マージ警告、クライアント固有の起動前ガイダンス、およびモデル数が続きます。モデルごとのコンテキスト制限を持つ形式では、それを省略した行数を表示します。セレクターのみを保存する形式では、コンテキスト制限が表現されないことを明示します。
|クライアント |正規の宛先 |ダウンロードファイル名 |環境変数 |
| --- | --- | --- | --- |
| `opencode` | `~/.config/opencode/opencode.json` (設定すると `XDG_CONFIG_HOME` が勝ち) | `opencode.json` | `OPENCODEX_OPENCODE_API_KEY` |
-| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | `OPENCODEX_API_KEY` |
+| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | なし - ブロックにリテラル `opencodex-loopback` が入ります |
-2 つの環境変数名は異なり、各クライアントは独自の名前のみを補間します。 opencode は `{env:OPENCODEX_OPENCODE_API_KEY}` を読み取ります。 Pi は `$OPENCODEX_API_KEY` を読み取ります。
+opencode は `{env:OPENCODEX_OPENCODE_API_KEY}` を補間します。opencodex が生成する Pi のエクスポートには環境変数が不要で、リテラルのプレースホルダー `opencodex-loopback` が入ります。この値は必須です。Pi はモデル リストを構築する際に `apiKey` を解決し、既存の設定に未設定の環境変数参照がある場合はプロバイダー全体を隠すためです。ループバックでは、生成されたプレースホルダーをプロキシが検査することはありません。
:::caution[マージし、決して置き換えないでください]
`ocx export` は実際のクライアント設定を書き込むことはありません。宛先は手動でマージできるように出力されます。`--out` は、`--force` なしで既存のファイルを上書きすることを拒否します。これは、設定を置き換えると、その中にすでに含まれている他のプロバイダー、エージェント、および MCP エントリが破壊されるためです。
:::
-キーはシリアル化されません。設定にはクライアントの環境参照のみが含まれるため、シークレットは環境内に残ります。ループバック プロキシ (`127.0.0.1`、デフォルト) にはアドミッション キーはまったく必要ありません。参照は単に使用されないだけです。プロキシがループバックを超えてバインドする場合にのみ変数を設定します。アドミッションキーの発行方法については、[リモートアクセス](/reference/configuration/#remote-access) を参照してください。上流プロバイダー自体のキーは完全に別のものであり、[プロバイダー](/guides/providers/) ごとに構成されます。
+キーはシリアル化されません。opencode の設定には環境参照のみが含まれるためシークレットは環境内に残り、Pi の設定には認証情報ではなくプレースホルダーが入ります。ループバック プロキシ (`127.0.0.1`、デフォルト) にはアドミッション キーはまったく必要ありません。opencode の変数は、プロキシがループバックを超えてバインドする場合にのみ設定します。アドミッションキーの発行方法については、[リモートアクセス](/reference/configuration/#remote-access) を参照してください。上流プロバイダー自体のキーは完全に別のものであり、[プロバイダー](/guides/providers/) ごとに構成されます。
同じペイロードが `GET /api/client-config` によって提供され、ダッシュボードの [API] タブにレンダリングされるため、CLI、API、および GUI は同じバイトを使用します。
diff --git a/docs-site/src/content/docs/ko/guides/pi.md b/docs-site/src/content/docs/ko/guides/pi.md
index 648d71060e..3814576527 100644
--- a/docs-site/src/content/docs/ko/guides/pi.md
+++ b/docs-site/src/content/docs/ko/guides/pi.md
@@ -5,8 +5,8 @@ description: Pi에서 라우팅된 모델을 그대로 쓸 수 있습니다. `oc
Pi는 provider를 환경 변수 대신 하나의 전역 JSON 파일에서 읽기 때문에,
opencodex가 Pi를 직접 실행하지 않습니다. 대신 `ocx export`가 `opencodex` provider 블록,
-즉 base URL, 모델 목록, 그리고 Pi가 치환하는 환경 변수 참조를 직렬화해서 사용자가
-자신의 설정에 병합하도록 합니다.
+즉 base URL, 모델 목록, 그리고 비밀이 아닌 리터럴 `apiKey` placeholder를 직렬화해서
+사용자가 자신의 설정에 병합하도록 합니다.
## 빠른 시작
@@ -17,8 +17,11 @@ ocx start
ocx export --client pi
```
-출력은 JSON으로 시작하고, 이어서 대상 경로, 병합 경고, 환경 변수 export 줄, 그리고
-공식 context limit이 있는 모델 수를 보여줍니다.
+출력은 JSON으로 시작하고, 이어서 대상 경로, 병합 경고, Pi 전용 실행 전 안내, 전체 모델
+수, 그리고 context limit을 생략한 row 수를 보여줍니다.
+
+Pi 스키마의 `openai-completions`는 Chat Completions 호환 API를 뜻하며, 이에 대응하는
+opencodex adapter 이름은 `openai-chat`입니다.
```json
{
@@ -26,7 +29,7 @@ ocx export --client pi
"opencodex": {
"baseUrl": "http://127.0.0.1:10100/v1",
"api": "openai-completions",
- "apiKey": "$OPENCODEX_API_KEY",
+ "apiKey": "opencodex-loopback",
"models": [
{
"id": "anthropic/claude-opus-5",
@@ -68,30 +71,19 @@ ocx export --client pi --json > ~/opencodex-pi-models.json # or redirect the b
내보낸 블록은 실시간 뷰가 아니라 고정 스냅샷입니다. provider를 추가하거나 모델
가시성을 바꾼 뒤에는 `ocx export`를 다시 실행하고, 새 블록을 옛 블록 위에 병합하세요.
-## 인증 키
-
-여기서는 서로 헷갈리기 쉬운 키가 두 개 있고, 이 파일에 등장하는 것은 첫 번째뿐입니다.
-
-| 키 | 무엇인지 | 어디에 있는지 |
-| --- | --- | --- |
-| Proxy admission key | opencodex의 자체 인증 정보이며, 대시보드의 **API** 탭에서 생성됩니다 | `apiKey`로 `$OPENCODEX_API_KEY`를 참조하며, 값은 환경 변수에 둡니다 |
-| Provider key | Anthropic / OpenAI / OpenRouter 키입니다 | opencodex의 자체 config에 있으며, [Providers](/guides/providers/)마다 따로 둡니다 |
+## Pi의 `apiKey` placeholder
-내보낸 config에는 비밀값이 아니라 참조만 들어갑니다. Pi는 `$NAME` 형태를 그대로
-치환하므로 변수는 다음과 같습니다.
-
-```bash
-export OPENCODEX_API_KEY=
-```
+Pi는 일반적으로 `/chat/completions`를 호출하며, 설정된 `apiKey`를 Bearer 인증 값으로
+보냅니다. 따라서 생성된 블록은 Pi의 일반 `apiKey` 필드에 비밀이 아닌 리터럴
+`opencodex-loopback`을 넣습니다.
-이 이름은 Pi 전용입니다. opencode는 다른 변수를 씁니다
-(`OPENCODEX_OPENCODE_API_KEY`, `{env:…}` 형식) - 자세한 내용은 [opencode 가이드](/guides/opencode/)를 보세요.
+이 리터럴은 프록시 admission credential도 upstream provider 키도 아닙니다. 루프백
+프록시는 이 값을 무시하며 credential을 전혀 요구하지 않습니다. 다만 모델 탐색에는
+필수입니다. Pi는 모델 목록을 만들 때 `apiKey`를 해석하고, 값이 설정되지 않은 환경 변수
+참조이면 provider 전체를 숨기므로, 리터럴이어야 라우팅된 모든 모델이 보입니다.
-**루프백 프록시는 키가 전혀 필요 없습니다.** opencodex는 기본적으로 `127.0.0.1`에
-바인드하고 그곳에서는 아무 것도 인증하지 않으므로, `$OPENCODEX_API_KEY` 참조는
-실제로는 비어 있어도 됩니다. 이 값은 `hostname`이 루프백 바깥으로 설정될 때만
-의미가 있으며, 그 경우에는 프록시가 토큰 없이 시작하지 않습니다. 자세한 내용은
-[Remote access](/reference/configuration/#remote-access)를 보세요.
+Provider 키는 별개입니다. Anthropic / OpenAI / OpenRouter 키는 opencodex의 자체
+config에 있으며([Providers](/guides/providers/) 참조), 이 파일에는 절대 나타나지 않습니다.
## 모델 메타데이터
@@ -110,10 +102,10 @@ export OPENCODEX_API_KEY=
## 스키마 상태
-:::note[실제 설치에서 검증하지 않음]
-위의 형태는 Pi가 공개한 custom-provider 문서를 따른 것입니다. Pi가 설치된 머신의
-실제 `~/.pi/agent/models.json`으로는 아직 검증하지 않았습니다. Pi가 내보낸 블록을
-거부하면 문제는 우리 쪽에 있습니다. Pi가 무엇을 보고했는지와 함께
+:::note[실제 설치에서 검증됨]
+위의 형태는 Pi 0.83.0이 설치된 머신의 실제 `~/.pi/agent/models.json`으로 검증했습니다.
+블록이 유효하고 Pi가 지원하는 입력 modality를 가진 내보낸 라우팅 모델이 모두 선택기에
+표시됩니다. 더 새로운 Pi가 이 블록을 거부하면 문제는 우리 쪽에 있습니다. Pi가 무엇을 보고했는지와 함께
[issue를 열어주세요](https://github.com/lidge-jun/opencodex/issues).
:::
diff --git a/docs-site/src/content/docs/ko/reference/cli/agents.md b/docs-site/src/content/docs/ko/reference/cli/agents.md
index 13f95f948f..3bc50e2645 100644
--- a/docs-site/src/content/docs/ko/reference/cli/agents.md
+++ b/docs-site/src/content/docs/ko/reference/cli/agents.md
@@ -133,7 +133,7 @@ Grok Build model fence를 관리하고 적용합니다.
### `ocx export --client `
-실행 중인 프록시에 연결된 client config를 출력합니다. opencode와 [Pi](/guides/pi/)는 environment variable이 아니라 각자의 JSON config에서 provider를 읽으므로, 이 명령은 `opencodex` provider block, 즉 base URL, model list, 그리고 client의 env reference를 직렬화해서 해당 파일에 병합할 수 있게 해줍니다.
+실행 중인 프록시에 연결된 client config를 출력합니다. opencode와 [Pi](/guides/pi/)는 environment variable이 아니라 각자의 JSON config에서 provider를 읽으므로, 이 명령은 `opencodex` provider block, 즉 base URL, model list, 그리고 client별 credential reference 또는 loopback placeholder를 직렬화해서 해당 파일에 병합할 수 있게 해줍니다.
프록시는 실행 중이어야 합니다. 이 명령은 실제 포트를 확인하고, `/api/models`를 읽고, 현재 Codex가 볼 수 있는 model만 내보냅니다.
@@ -150,20 +150,20 @@ ocx export --client pi --json > pi-models.json # byte-exact JSON for a pipe or
ocx export --client opencode --out ~/opencodex-opencode.json
```
-`--json`이 없으면 JSON이 먼저 나오고, 그다음 표준 대상 경로, merge 경고, env export 줄, 그리고 context limit을 생략한 row 수를 포함한 model count가 이어집니다(이 경우 client는 자체 기본값을 적용합니다).
+`--json`이 없으면 JSON이 먼저 나오고, 그다음 표준 대상 경로, merge 경고, 해당 client의 실행 전 안내, model count가 이어집니다. 모델별 context limit을 지원하는 형식은 이를 생략한 row 수를 표시하고, selector만 저장하는 형식은 context limit을 표현하지 않는다고 명시합니다.
| 클라이언트 | 표준 대상 경로 | 다운로드 파일명 | 환경 변수 |
| --- | --- | --- | --- |
| `opencode` | `~/.config/opencode/opencode.json` (`XDG_CONFIG_HOME`이 설정되어 있으면 우선합니다) | `opencode.json` | `OPENCODEX_OPENCODE_API_KEY` |
-| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | `OPENCODEX_API_KEY` |
+| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | 없음 - 블록에 리터럴 `opencodex-loopback`이 들어갑니다 |
-두 환경 변수 이름은 서로 다르며, 각 client는 자기 것만 보간합니다. opencode는 `{env:OPENCODEX_OPENCODE_API_KEY}`를 읽고, Pi는 `$OPENCODEX_API_KEY`를 읽습니다.
+opencode는 `{env:OPENCODEX_OPENCODE_API_KEY}`를 보간합니다. opencodex가 생성한 Pi 블록에는 환경 변수가 필요 없으며, 리터럴 placeholder인 `opencodex-loopback`이 들어갑니다. 이 값은 필수입니다. Pi는 모델 목록을 만들 때 `apiKey`를 해석하고, 기존 config에 설정되지 않은 env 참조가 있으면 provider 전체를 숨기기 때문입니다. 루프백에서 proxy는 생성된 placeholder를 검사하지 않습니다.
:::caution[Merge, never replace]
`ocx export`는 실제 client config를 절대 쓰지 않습니다. 대상 경로는 손으로 병합하라고 출력되며, `--out`은 `--force` 없이 기존 파일을 덮어쓰지 않습니다. config를 바꾸어 덮어쓰면 이미 들어 있던 다른 provider, agent, MCP entry가 사라지기 때문입니다.
:::
-어떤 key도 직렬화되지 않습니다. config에는 client의 env reference만 들어가므로 secret은 환경 변수에 남습니다. loopback proxy(`127.0.0.1`, 기본값)는 admission key가 전혀 필요하지 않습니다. reference는 단지 사용되지 않을 뿐입니다. proxy가 loopback을 넘어 바인딩할 때만 변수를 설정하십시오. admission key가 어떻게 발급되는지는 [Remote access](/reference/configuration/#remote-access)를 보십시오. upstream provider 자체의 key는 완전히 별개의 것으로, 각 [Providers](/guides/providers/)에 맞게 설정합니다.
+어떤 key도 직렬화되지 않습니다. opencode config에는 env reference만 들어가므로 secret은 환경 변수에 남고, Pi config에는 인증 정보가 아니라 placeholder가 들어갑니다. loopback proxy(`127.0.0.1`, 기본값)는 admission key가 전혀 필요하지 않습니다. opencode 변수는 proxy가 loopback을 넘어 바인딩할 때만 설정하십시오. admission key가 어떻게 발급되는지는 [Remote access](/reference/configuration/#remote-access)를 보십시오. upstream provider 자체의 key는 완전히 별개의 것으로, 각 [Providers](/guides/providers/)에 맞게 설정합니다.
같은 payload는 `GET /api/client-config`로 제공되고 dashboard의 API 탭에도 렌더링되므로, CLI, API, GUI가 모두 같은 바이트를 사용합니다.
diff --git a/docs-site/src/content/docs/reference/cli/agents.md b/docs-site/src/content/docs/reference/cli/agents.md
index 4944a483d2..de9ada88bc 100644
--- a/docs-site/src/content/docs/reference/cli/agents.md
+++ b/docs-site/src/content/docs/reference/cli/agents.md
@@ -150,8 +150,8 @@ Manage and apply the Grok Build model fence.
Print a client config wired to the running proxy. opencode and [Pi](/guides/pi/) read providers
from their own JSON config rather than environment variables, so this command serializes the
-`opencodex` provider block — base URL, model list, and the client's env reference — for you to
-merge into that file.
+`opencodex` provider block — base URL, model list, and the client's credential reference or
+loopback placeholder — for you to merge into that file.
The proxy must be running; the command resolves its live port, reads `/api/models`, and emits only
models Codex can currently see.
@@ -169,17 +169,20 @@ ocx export --client pi --json > pi-models.json # byte-exact JSON for a pipe or
ocx export --client opencode --out ~/opencodex-opencode.json
```
-Without `--json` the JSON leads, then the canonical destination path, the merge warning, the env
-export line, and a model count with how many rows omit context limits (the client applies its own
-defaults for those).
+Without `--json` the JSON leads, then the canonical destination path, the merge warning, the
+client-specific pre-launch guidance, and a model count. Formats with per-model context limits report
+how many rows omit them; selector-only formats state that context limits are not represented.
| Client | Canonical destination | Download filename | Env var |
| --- | --- | --- | --- |
| `opencode` | `~/.config/opencode/opencode.json` (`XDG_CONFIG_HOME` wins when set) | `opencode.json` | `OPENCODEX_OPENCODE_API_KEY` |
-| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | `OPENCODEX_API_KEY` |
+| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | none — the block carries the literal `opencodex-loopback` |
-The two env var names are different, and each client only interpolates its own. opencode reads
-`{env:OPENCODEX_OPENCODE_API_KEY}`; Pi reads `$OPENCODEX_API_KEY`.
+opencode interpolates `{env:OPENCODEX_OPENCODE_API_KEY}`. The generated Pi export does not require
+an environment variable: it carries the literal `opencodex-loopback` placeholder. This is
+load-bearing because Pi resolves `apiKey` while building its model list and hides the whole
+provider when an existing config contains an unset env reference. The proxy never checks the
+generated placeholder on loopback.
:::caution[Merge, never replace]
`ocx export` never writes your real client config. The destination is printed for you to merge by
@@ -187,9 +190,10 @@ hand, and `--out` refuses to overwrite an existing file without `--force`, becau
config destroys the other providers, agents, and MCP entries already in it.
:::
-No key is ever serialized. The config carries only the client's env reference, so the secret stays
-in your environment. A loopback proxy (`127.0.0.1`, the default) requires no admission key at all —
-the reference is simply unused. Set the variable only when the proxy binds beyond loopback; see
+No key is ever serialized. An opencode config carries only the env reference, so the secret stays
+in your environment, and a Pi config carries a placeholder rather than any credential. A loopback
+proxy (`127.0.0.1`, the default) requires no admission key at all. Set the opencode variable only
+when the proxy binds beyond loopback; see
[Remote access](/reference/configuration/#remote-access) for how admission keys are issued. Keys for
the upstream providers themselves are a separate thing entirely, configured per
[Providers](/guides/providers/).
diff --git a/docs-site/src/content/docs/ru/guides/pi.md b/docs-site/src/content/docs/ru/guides/pi.md
index 0960ecf49a..2fc1baba30 100644
--- a/docs-site/src/content/docs/ru/guides/pi.md
+++ b/docs-site/src/content/docs/ru/guides/pi.md
@@ -5,8 +5,8 @@ description: Используйте любую маршрутизируемую
Pi читает провайдеров из одного глобального JSON-файла, а не из переменных окружения, поэтому
opencodex не запускает его сам. Вместо этого `ocx export` сериализует блок провайдера
-`opencodex` — base URL, список моделей и env-ссылку, которую интерполирует Pi, — а вы сливаете
-его в свою конфигурацию.
+`opencodex` — base URL, список моделей и несекретный литеральный плейсхолдер `apiKey`, — а вы
+сливаете его в свою конфигурацию.
## Быстрый старт
@@ -17,8 +17,11 @@ ocx start
ocx export --client pi
```
-Сначала выводится JSON, затем путь назначения, предупреждение о merge, строка `export` для
-переменной окружения и число моделей, для которых есть авторитетные контекстные лимиты.
+Сначала выводится JSON, затем путь назначения, предупреждение о merge, специальные инструкции
+для Pi перед запуском, общее число моделей и число строк без контекстных лимитов.
+
+В схеме Pi значение `openai-completions` обозначает API, совместимый с Chat Completions;
+соответствующий адаптер opencodex называется `openai-chat`.
```json
{
@@ -26,7 +29,7 @@ ocx export --client pi
"opencodex": {
"baseUrl": "http://127.0.0.1:10100/v1",
"api": "openai-completions",
- "apiKey": "$OPENCODEX_API_KEY",
+ "apiKey": "opencodex-loopback",
"models": [
{
"id": "anthropic/claude-opus-5",
@@ -69,30 +72,20 @@ ocx export --client pi --json > ~/opencodex-pi-models.json # or redirect the b
провайдера или изменения видимости моделей заново выполняйте `ocx export`, а новый блок вливайте
поверх старого.
-## Admission key
-
-Здесь легко перепутать два разных ключа, и в этом файле появляется только первый:
-
-| Ключ | Что это | Где хранится |
-| --- | --- | --- |
-| Ключ допуска прокси | собственная учётная запись opencodex, генерируемая на вкладке **API** в дашборде | указывается в `apiKey` как `$OPENCODEX_API_KEY`; само значение остаётся в окружении |
-| Ключ провайдера | ваш ключ Anthropic / OpenAI / OpenRouter | хранится в конфигурации самого opencodex, см. [Провайдеры](/guides/providers/) |
+## Заглушка Pi `apiKey`
-Экспортируемая конфигурация несёт только ссылку, а не секрет. Pi интерполирует голый `$NAME`,
-поэтому переменная должна выглядеть так:
-
-```bash
-export OPENCODEX_API_KEY=
-```
+Pi обычно вызывает `/chat/completions` и отправляет настроенный `apiKey` как значение авторизации
+Bearer. Поэтому сгенерированный блок помещает несекретный литерал `opencodex-loopback` в
+обычное поле Pi `apiKey`.
-Это имя переменной относится только к Pi. opencode использует другую переменную
-(`OPENCODEX_OPENCODE_API_KEY` в форме `{env:…}`) — см. [руководство по opencode](/guides/opencode/).
+Этот литерал — не credential допуска прокси и не ключ upstream-провайдера. Loopback-прокси
+игнорирует его и вообще не требует credential. При этом значение необходимо для обнаружения
+моделей: Pi разрешает `apiKey`, когда строит список, и прячет провайдера целиком, если значение —
+ссылка на незаданную переменную окружения. Литерал сохраняет видимость всех маршрутизируемых
+моделей.
-**Прокси на loopback вообще не требует ключа.** По умолчанию opencodex привязывается к
-`127.0.0.1` и ничего там не аутентифицирует, поэтому ссылка `$OPENCODEX_API_KEY` инертна и
-переменную можно не задавать. Она нужна только когда `hostname` выходит за пределы loopback — а
-именно в этом случае прокси и отказывается запускаться без токена; см.
-[Удалённый доступ](/reference/configuration/#remote-access).
+Ключи провайдеров — отдельная история: ключ Anthropic / OpenAI / OpenRouter хранится в
+конфигурации самого opencodex, см. [Провайдеры](/guides/providers/), и в этом файле не появляется.
## Метаданные моделей
@@ -111,10 +104,11 @@ export OPENCODEX_API_KEY=
## Статус схемы
-:::note[Не проверено на реальной установке]
-Форма выше соответствует опубликованной документации Pi по custom-провайдерам. Она **не была
-проверена** на реальном `~/.pi/agent/models.json` на машине с установленным Pi. Если Pi отвергнет
-экспортированный блок, несоответствие на нашей стороне — пожалуйста,
+:::note[Проверено на реальной установке]
+Форма выше проверена на реальном `~/.pi/agent/models.json` с установленным Pi 0.83.0: блок
+проходит валидацию, и все экспортированные маршрутизируемые модели с поддерживаемой Pi входной
+модальностью видны в выборе моделей. Если более новый Pi отвергнет экспортированный блок,
+несоответствие на нашей стороне — пожалуйста,
[создайте issue](https://github.com/lidge-jun/opencodex/issues) и приложите то, что сообщил Pi.
:::
diff --git a/docs-site/src/content/docs/ru/reference/cli/agents.md b/docs-site/src/content/docs/ru/reference/cli/agents.md
index 2d738ab9b4..a497fab3b9 100644
--- a/docs-site/src/content/docs/ru/reference/cli/agents.md
+++ b/docs-site/src/content/docs/ru/reference/cli/agents.md
@@ -156,8 +156,8 @@ override, но файлы на диске никогда не меняются.
Печатает client config, направленный на работающий прокси. opencode и [Pi](/guides/pi/) читают
провайдеров из собственных JSON-конфигов, а не из переменных окружения, поэтому команда
-сериализует для вас блок провайдера `opencodex` — base URL, список моделей и env-reference
-конкретного клиента.
+сериализует для вас блок провайдера `opencodex` — base URL, список моделей и клиентскую
+ссылку на credential либо loopback-заглушку.
Прокси должен быть запущен; команда определяет его живой порт, читает `/api/models` и выводит
только те модели, которые сейчас видит Codex.
@@ -175,17 +175,20 @@ ocx export --client pi --json > pi-models.json # byte-exact JSON for a pipe or
ocx export --client opencode --out ~/opencodex-opencode.json
```
-Без `--json` сначала идёт JSON, затем канонический путь назначения, предупреждение о merge, строка
-экспорта переменной окружения и количество моделей с указанием, сколько строк не имеют context
-limit'а (для них клиент применяет собственные default'ы).
+Без `--json` сначала идёт JSON, затем канонический путь назначения, предупреждение о merge,
+клиентская подсказка перед запуском и количество моделей. Для форматов с context limit'ом на
+модель указывается число строк без него; для selector-only форматов явно сказано, что лимиты не представлены.
| Клиент | Канонический путь | Имя скачиваемого файла | Переменная окружения |
| --- | --- | --- | --- |
| `opencode` | `~/.config/opencode/opencode.json` (`XDG_CONFIG_HOME` имеет приоритет, если задан) | `opencode.json` | `OPENCODEX_OPENCODE_API_KEY` |
-| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | `OPENCODEX_API_KEY` |
+| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | нет — блок несёт литерал `opencodex-loopback` |
-Имена этих двух env-переменных различаются, и каждый клиент интерполирует только свою. opencode
-читает `{env:OPENCODEX_OPENCODE_API_KEY}`; Pi читает `$OPENCODEX_API_KEY`.
+opencode интерполирует `{env:OPENCODEX_OPENCODE_API_KEY}`. Сгенерированный opencodex экспорт для
+Pi не требует переменной окружения и несёт литеральную заглушку `opencodex-loopback`. Это значение
+обязательно: Pi разрешает `apiKey`, когда строит список моделей, и прячет провайдера целиком, если
+существующий конфиг содержит ссылку на незаданную переменную окружения. На loopback прокси не
+проверяет сгенерированную заглушку.
:::caution[Сливать, а не заменять]
`ocx export` никогда не пишет в ваш реальный клиентский конфиг. Путь назначения лишь
@@ -194,10 +197,10 @@ limit'а (для них клиент применяет собственные d
MCP-записи.
:::
-Никакой ключ никогда не сериализуется. Конфиг несёт только env-reference клиента, так что секрет
-остаётся в вашем окружении. Loopback-прокси (`127.0.0.1`, по умолчанию) вообще не требует
-admission key — ссылка просто остаётся неиспользованной. Задавайте переменную только если прокси
-слушает не на loopback; как выдаются admission key, описано в
+Никакой ключ никогда не сериализуется. Конфиг opencode несёт только env-reference, так что секрет
+остаётся в вашем окружении, а конфиг Pi несёт заглушку вместо учётных данных. Loopback-прокси
+(`127.0.0.1`, по умолчанию) вообще не требует admission key. Задавайте переменную opencode только
+если прокси слушает не на loopback; как выдаются admission key, описано в
[Удалённом доступе](/reference/configuration/#remote-access). Ключи upstream-провайдеров — это
совсем отдельная история и настраиваются в [Провайдерах](/guides/providers/).
diff --git a/docs-site/src/content/docs/zh-cn/guides/pi.md b/docs-site/src/content/docs/zh-cn/guides/pi.md
index ad868e3194..2337b4a7d0 100644
--- a/docs-site/src/content/docs/zh-cn/guides/pi.md
+++ b/docs-site/src/content/docs/zh-cn/guides/pi.md
@@ -3,7 +3,7 @@ title: Pi
description: 在 Pi 中使用任意已路由模型 - `ocx export` 会为 Pi 的 `models.json` 写入一个自定义 provider 块,并连接到正在运行的代理。
---
-Pi 从一个全局 JSON 文件而不是环境变量中读取 providers,所以 opencodex 不会启动它。相反,`ocx export` 会序列化 `opencodex` provider 块 - 基础 URL、模型列表,以及 Pi 会插值的 env 引用 - 然后你把它合并到自己的配置中。
+Pi 从一个全局 JSON 文件而不是环境变量中读取 providers,所以 opencodex 不会启动它。相反,`ocx export` 会序列化 `opencodex` provider 块——基础 URL、模型列表,以及一个非秘密的字面量 `apiKey` 占位值——然后你把它合并到自己的配置中。
## 快速开始
@@ -14,7 +14,9 @@ ocx start
ocx export --client pi
```
-输出会先显示 JSON,然后打印目标路径、合并警告、env 导出行,以及有多少模型带有权威上下文窗口限制。
+输出会先显示 JSON,然后打印目标路径、合并警告、Pi 专用的启动前提示、模型总数,以及省略上下文限制的行数。
+
+在 Pi 的 schema 中,`openai-completions` 表示兼容 Chat Completions 的 API;对应的 opencodex adapter 名称是 `openai-chat`。
```json
{
@@ -22,7 +24,7 @@ ocx export --client pi
"opencodex": {
"baseUrl": "http://127.0.0.1:10100/v1",
"api": "openai-completions",
- "apiKey": "$OPENCODEX_API_KEY",
+ "apiKey": "opencodex-loopback",
"models": [
{
"id": "anthropic/claude-opus-5",
@@ -58,24 +60,13 @@ ocx export --client pi --json > ~/opencodex-pi-models.json # or redirect the b
导出的块是静态快照,不是实时视图。新增 provider 或更改模型可见性后,请重新运行 `ocx export`,再用新的块覆盖旧块进行合并。
-## 准入密钥
-
-这里有两个很容易混淆的 key,但这个文件里只会出现第一个:
-
-| Key | 它是什么 | 它存放在哪里 |
-| --- | --- | --- |
-| 代理准入密钥 | opencodex 自己的凭据,在仪表盘的 **API** 选项卡中生成 | 通过 `apiKey` 以 `$OPENCODEX_API_KEY` 形式引用;实际值保存在你的环境中 |
-| Provider key | 你的 Anthropic / OpenAI / OpenRouter key | opencodex 自己的配置中,见 [Providers](/guides/providers/) |
+## Pi 的 `apiKey` 占位值
-导出的配置只包含引用,从不包含 secret。Pi 会插值裸的 `$NAME`,所以变量是:
-
-```bash
-export OPENCODEX_API_KEY=
-```
+Pi 通常调用 `/chat/completions`,并将配置的 `apiKey` 作为 Bearer 认证值发送。因此,生成的块会在 Pi 的常规 `apiKey` 字段中写入非机密字面值 `opencodex-loopback`。
-这个名字只属于 Pi。opencode 使用不同的变量(`OPENCODEX_OPENCODE_API_KEY`,以 `{env:…}` 形式出现) - 见 [opencode 指南](/guides/opencode/)。
+这个字面值既不是代理准入凭据,也不是上游 provider key。回环代理会忽略它,并且完全不需要凭据。不过它对模型发现是必需的:Pi 在构建模型列表时会解析 `apiKey`,如果该值是未设置的环境变量引用,它就会隐藏整个 provider;使用字面值才能让所有已路由模型保持可见。
-**回环代理根本不需要 key。** opencodex 默认绑定 `127.0.0.1`,在那里不做任何认证,所以 `$OPENCODEX_API_KEY` 引用是无效的,你可以不设置这个变量。它只在 `hostname` 超出回环范围时才有意义,而这也是代理会在没有 token 的情况下拒绝启动的时候 - 见 [远程访问](/reference/configuration/#remote-access)。
+Provider key 是另一回事:你的 Anthropic / OpenAI / OpenRouter key 保存在 opencodex 自己的配置中,见 [Providers](/guides/providers/),它绝不会出现在这个文件里。
## 模型元数据
@@ -87,8 +78,8 @@ export OPENCODEX_API_KEY=
## Schema 状态
-:::note[未在真实安装上验证]
-上面的结构遵循了 Pi 已公开的自定义 provider 文档。它**尚未**在一台安装了 Pi 的机器上、针对真实的 `~/.pi/agent/models.json` 进行验证。如果 Pi 拒绝这个导出块,问题在我们这边 - 请带上 Pi 的报错信息[提交 issue](https://github.com/lidge-jun/opencodex/issues)。
+:::note[已在真实安装上验证]
+上面的结构已在安装了 Pi 0.83.0 的机器上、针对真实的 `~/.pi/agent/models.json` 完成验证:该块通过校验,并且所有具有 Pi 所支持输入模态的已导出路由模型都会出现在 Pi 的模型选择器中。如果更新版本的 Pi 拒绝这个导出块,问题在我们这边 - 请带上 Pi 的报错信息[提交 issue](https://github.com/lidge-jun/opencodex/issues)。
:::
## 需求
diff --git a/docs-site/src/content/docs/zh-cn/reference/cli/agents.md b/docs-site/src/content/docs/zh-cn/reference/cli/agents.md
index dc1960adb3..fcb0695f16 100644
--- a/docs-site/src/content/docs/zh-cn/reference/cli/agents.md
+++ b/docs-site/src/content/docs/zh-cn/reference/cli/agents.md
@@ -128,7 +128,7 @@ ocx claude desktop import [--apply] Validate and import JSON
### `ocx export --client `
-输出连接到正在运行代理的客户端配置。opencode 和 [Pi](/guides/pi/) 不是从环境变量,而是从各自的 JSON 配置中读取 providers,因此此命令会序列化 `opencodex` provider 块——基础 URL、模型列表以及客户端的环境引用——供你合并进那个文件。
+输出连接到正在运行代理的客户端配置。opencode 和 [Pi](/guides/pi/) 不是从环境变量,而是从各自的 JSON 配置中读取 providers,因此此命令会序列化 `opencodex` provider 块——基础 URL、模型列表以及客户端对应的凭据引用或回环占位值——供你合并进那个文件。
代理必须正在运行;该命令会解析其当前端口,读取 `/api/models`,并且只输出 Codex 当前可见的模型。
@@ -145,20 +145,20 @@ ocx export --client pi --json > pi-models.json # byte-exact JSON for a pipe or
ocx export --client opencode --out ~/opencodex-opencode.json
```
-不使用 `--json` 时,JSON 会先输出,随后是规范目标路径、合并警告、环境变量导出行,以及一个模型计数,并标明有多少行省略了上下文限制(客户端会对这些项应用自己的默认值)。
+不使用 `--json` 时,JSON 会先输出,随后是规范目标路径、合并警告、客户端专属的启动前提示和模型计数。支持逐模型上下文限制的格式会报告省略该限制的行数;仅保存 selector 的格式则会明确说明未表示上下文限制。
| 客户端 | 规范目标路径 | 下载文件名 | 环境变量 |
| --- | --- | --- | --- |
| `opencode` | `~/.config/opencode/opencode.json`(设置了 `XDG_CONFIG_HOME` 时以其为准) | `opencode.json` | `OPENCODEX_OPENCODE_API_KEY` |
-| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | `OPENCODEX_API_KEY` |
+| `pi` | `~/.pi/agent/models.json` | `pi-models.json` | 无 - 块中携带字面值 `opencodex-loopback` |
-这两个环境变量名称不同,而且每个客户端只会插入自己的那个。opencode 读取 `{env:OPENCODEX_OPENCODE_API_KEY}`;Pi 读取 `$OPENCODEX_API_KEY`。
+opencode 会插值 `{env:OPENCODEX_OPENCODE_API_KEY}`。opencodex 生成的 Pi 导出不需要环境变量,而是携带字面占位值 `opencodex-loopback`。这个值是必需的:Pi 在构建模型列表时会解析 `apiKey`,如果已有配置包含未设置的环境变量引用,它就会隐藏整个 provider。回环上的代理从不校验生成的占位值。
:::caution[合并,不要替换]
`ocx export` 从不写入你的真实客户端配置。该命令只会打印目标路径供你手动合并,而 `--out` 在没有 `--force` 的情况下拒绝覆盖已有文件,因为替换配置会破坏其中已有的其他 providers、agents 和 MCP 条目。
:::
-任何密钥都不会被序列化。配置里只包含客户端的环境引用,因此密钥仍保留在你的环境中。环回代理(`127.0.0.1`,默认值)根本不需要准入密钥——该引用只是不会被使用。只有当代理绑定到环回地址之外时才设置该变量;关于准入密钥如何签发,请参见 [远程访问](/reference/configuration/#remote-access)。上游 providers 自身的密钥则完全是另一回事,需要按 [Providers](/guides/providers/) 单独配置。
+任何密钥都不会被序列化。opencode 配置里只包含环境引用,因此密钥仍保留在你的环境中;Pi 配置里携带的是占位值而不是任何凭据。环回代理(`127.0.0.1`,默认值)根本不需要准入密钥。只有当代理绑定到环回地址之外时才需要设置 opencode 的那个变量;关于准入密钥如何签发,请参见 [远程访问](/reference/configuration/#remote-access)。上游 providers 自身的密钥则完全是另一回事,需要按 [Providers](/guides/providers/) 单独配置。
同一份负载会通过 `GET /api/client-config` 提供,并在仪表盘的 API 选项卡中渲染,因此 CLI、API 和 GUI 使用的是同一字节内容。
diff --git a/gui/src/components/apikeys-workspace/ClientConfigDialog.tsx b/gui/src/components/apikeys-workspace/ClientConfigDialog.tsx
index 0d315825f1..689266c392 100644
--- a/gui/src/components/apikeys-workspace/ClientConfigDialog.tsx
+++ b/gui/src/components/apikeys-workspace/ClientConfigDialog.tsx
@@ -78,9 +78,11 @@ export default function ClientConfigDialog({
{t("api.clientConfig.missingLimits", { count: envelope.modelsWithoutLimits, total: envelope.modelCount })}
)}
- {!hasKeys && (
+ {!hasKeys && envelope.apiKeyEnv !== "" && (
// Informational, never blocking: an agent may legitimately want the shape
- // first, so both actions stay enabled.
+ // first, so both actions stay enabled. A client with no env var at all
+ // (Pi, Kimi) carries its non-secret loopback placeholder in its own file,
+ // so naming a variable here would render an empty name.
@@ -98,7 +100,9 @@ export default function ClientConfigDialog({
{/* The old is gone — this is the one place the answer lives now,
so it is a labelled paragraph rather than a fold. */}