From 89836141b1f3a46bf584116e803f2d54f56adac5 Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 05:17:42 +0900 Subject: [PATCH 1/8] docs: generate the client support matrix from the client registries Adds scripts/generate-client-support-matrix.ts, which builds a reference page for every docs locale from the export roster, the managed file integrations and the native integration union, and re-checks curated protocol and test claims against the code. A Bun test fails when a committed page drifts or a claim stops holding. --- docs-site/astro.config.mjs | 1 + .../docs/fr/reference/client-support.md | 59 +++ .../docs/ja/reference/client-support.md | 59 +++ .../docs/ko/reference/client-support.md | 59 +++ .../content/docs/reference/client-support.md | 59 +++ .../docs/ru/reference/client-support.md | 59 +++ .../docs/tr/reference/client-support.md | 59 +++ .../docs/zh-cn/reference/client-support.md | 59 +++ .../docs/zh-tw/reference/client-support.md | 59 +++ package.json | 2 + scripts/client-support-matrix/locales.ts | 283 ++++++++++++ scripts/client-support-matrix/metadata.ts | 248 +++++++++++ scripts/generate-client-support-matrix.ts | 402 ++++++++++++++++++ scripts/test-layout/layout.json | 2 +- structure/INDEX.md | 9 +- structure/clients/client-support-matrix.md | 42 ++ structure/manifest.json | 12 + .../client-support-matrix.test.ts | 129 ++++++ tests/fixtures/test-layout-expected.json | 1 + 19 files changed, 1598 insertions(+), 5 deletions(-) create mode 100644 docs-site/src/content/docs/fr/reference/client-support.md create mode 100644 docs-site/src/content/docs/ja/reference/client-support.md create mode 100644 docs-site/src/content/docs/ko/reference/client-support.md create mode 100644 docs-site/src/content/docs/reference/client-support.md create mode 100644 docs-site/src/content/docs/ru/reference/client-support.md create mode 100644 docs-site/src/content/docs/tr/reference/client-support.md create mode 100644 docs-site/src/content/docs/zh-cn/reference/client-support.md create mode 100644 docs-site/src/content/docs/zh-tw/reference/client-support.md create mode 100644 scripts/client-support-matrix/locales.ts create mode 100644 scripts/client-support-matrix/metadata.ts create mode 100644 scripts/generate-client-support-matrix.ts create mode 100644 structure/clients/client-support-matrix.md create mode 100644 tests/ci-workflows/client-support-matrix.test.ts diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index 89ccd0728d6..d4fb8509e9f 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -160,6 +160,7 @@ export default defineConfig({ { label: "Server & Runtime", translations: { fr: "Serveur et environnement d’exécution", ko: "서버 & 런타임", "zh-CN": "服务器与运行时", "zh-TW": "伺服器與執行階段", ru: "Сервер и рантайм", ja: "サーバー & ランタイム", tr: "Sunucu ve Çalışma Zamanı" }, slug: "reference/configuration/server" }, ], }, + { label: "Client Support", translations: { fr: "Clients pris en charge", ko: "클라이언트 지원", "zh-CN": "客户端支持", "zh-TW": "用戶端支援", ru: "Поддержка клиентов", ja: "クライアント対応", tr: "İstemci desteği" }, slug: "reference/client-support" }, { label: "Adapters", translations: { fr: "Adaptateurs", ko: "어댑터", "zh-CN": "适配器", "zh-TW": "適配器", ru: "Адаптеры", ja: "アダプター", tr: "Adaptörler" }, slug: "reference/adapters" }, { label: "Architecture", translations: { fr: "Architecture", ko: "아키텍처", "zh-CN": "架构", "zh-TW": "架構", ru: "Архитектура", ja: "アーキテクチャ", tr: "Mimari" }, slug: "reference/architecture" }, { label: "Platform Support", translations: { fr: "Prise en charge des plateformes", ko: "플랫폼 지원", "zh-CN": "平台支持", "zh-TW": "平台支援", ru: "Поддержка платформ", ja: "プラットフォーム対応", tr: "Platform Desteği" }, link: `${SITE_URL}/reference/platform-support` }, diff --git a/docs-site/src/content/docs/fr/reference/client-support.md b/docs-site/src/content/docs/fr/reference/client-support.md new file mode 100644 index 00000000000..03a0d7a1707 --- /dev/null +++ b/docs-site/src/content/docs/fr/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "Clients pris en charge" +description: "Les outils de code auxquels opencodex se connecte, la façon dont chacun est relié et le protocole qu’il utilise." +--- + + + + +opencodex est une passerelle locale unique pour vos outils de code IA et vos modèles. Il configure certains outils nativement ; pour les autres, il exporte ou gère un fichier de configuration qui dirige l’outil vers le proxy local. Cette page est générée à partir des registres du code et reflète donc exactement ce que fournit la version actuelle. + +## Niveaux de prise en charge + +- **implemented**: le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Les protocoles marqués *revu* (clients natifs et clients qui choisissent eux-mêmes leur point de terminaison) proviennent d’une revue du code d’intégration, et le générateur vérifie que ce code est toujours en place. +- **test-covered**: implemented, et un cas de test choisi en revue qui exerce ce client est lié. Le générateur vérifie seulement que le test existe toujours et qu’il est actif ; lisez-le pour savoir ce qu’il vérifie. + +## Clients + +| Client | Natif | Export de configuration | Intégration de fichier gérée | Protocole entrant | Niveau | Remarques | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Oui | — | — | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/codex-integration/) | +| Claude Code | Oui | — | — | Anthropic Messages (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/claude-code/) | +| Claude Desktop | Oui | — | — | Anthropic Messages (revu) | implemented | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. | +| Grok Build | Oui | — | — | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/grok-build/) | +| Qoder | — | JSON | Oui | Compatible OpenAI (point de terminaison choisi par le client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Écoute locale uniquement. | +| Qoder CN | — | JSON | Oui | Compatible OpenAI (point de terminaison choisi par le client) | implemented | Écoute locale uniquement. | +| WorkBuddy | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | +| WorkBuddy AI | — | JSON | Oui | Chat Completions | implemented | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | +| OpenCode | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). [guide](/fr/guides/opencode/) | +| Pi | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Écoute locale uniquement. [guide](/fr/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Oui | Chat Completions; Responses (modèles OpenAI natifs uniquement) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Écoute locale uniquement. Ne modifie que son propre bloc YAML. | +| Hermes | — | YAML | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). Ne modifie que son propre bloc YAML. | +| OpenClaw | — | JSON5 | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). | +| Kimi CLI | — | TOML | Oui | Compatible OpenAI (point de terminaison choisi par le client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Écoute locale uniquement. | +| Gajae | — | YAML | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Écoute locale uniquement. | +| DSH | — | YAML | Oui | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Écoute locale uniquement. Écrit dans le magasin de fournisseurs actuel du client. Ne modifie que son propre bloc YAML. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| MCode | — | YAML | Oui | Anthropic Messages | implemented | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| ZCode | — | JSON | Oui | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Écoute locale uniquement. Écrit dans le magasin de fournisseurs actuel du client. Le client choisit le point de terminaison à partir de ce réglage (vérifié en revue d’après le comportement documenté du client). | +| Command Code | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| Prime | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Écoute locale uniquement. | +| Aside | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Écoute locale uniquement. | +| Raycast | — | YAML | Oui | Chat Completions (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Écoute locale uniquement. Le client choisit le point de terminaison à partir de ce réglage (vérifié en revue d’après le comportement documenté du client). | +| omo | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Écoute locale uniquement. | +| Cline | — | JSON | Oui | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| Kilo Code | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). | +| Factory Droid | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Écoute locale uniquement. [guide](/fr/guides/factory-droid/) | +| Gentle Shell | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Écoute locale uniquement. | +| T3 Code | — | JSON | Oui | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | + +## Points de terminaison entrants + +Le proxy accepte ces protocoles. Un outil absent de la liste peut quand même se connecter s’il permet de définir un point de terminaison personnalisé pour l’un d’eux. + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## Tenir cette page à jour + +Cette page est générée par `scripts/generate-client-support-matrix.ts` à partir des registres d’export, d’intégration gérée et d’intégration native. Pour ajouter un client, ajoutez son entrée dans `scripts/client-support-matrix/metadata.ts` puis lancez `bun run docs:clients` ; `bun run docs:clients:check` échoue tant que la page validée est obsolète. diff --git a/docs-site/src/content/docs/ja/reference/client-support.md b/docs-site/src/content/docs/ja/reference/client-support.md new file mode 100644 index 00000000000..73261e92b31 --- /dev/null +++ b/docs-site/src/content/docs/ja/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "クライアント対応" +description: "opencodex が接続するコーディングツール、それぞれの接続方法、使用するプロトコル。" +--- + + + + +opencodex は、AI コーディングツールとモデルをつなぐ 1 つのローカルゲートウェイです。一部のツールはネイティブに設定し、それ以外はローカルプロキシを指す設定ファイルをエクスポートまたは管理します。このページはコードのレジストリから生成されるため、現在のリリースの内容と正確に一致します。 + +## サポートレベル + +- **implemented**: レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*と付いたプロトコル(ネイティブクライアントと、エンドポイントを自分で選ぶクライアント)は連携コードのレビューに基づくもので、レビューしたコードが残っているかを生成時に確認します。 +- **test-covered**: implemented であり、レビューで選んだこのクライアントのテストケースへのリンクがあります。生成時に確認するのはテストが存在し無効化されていないことだけです。何を検証しているかはテストを読んでください。 + +## クライアント + +| クライアント | ネイティブ | 設定エクスポート | 管理ファイル連携 | 受信プロトコル | サポートレベル | 備考 | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | はい | — | — | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/codex-integration/) | +| Claude Code | はい | — | — | Anthropic Messages (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/claude-code/) | +| Claude Desktop | はい | — | — | Anthropic Messages (レビュー済み) | implemented | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 | +| Grok Build | はい | — | — | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/grok-build/) | +| Qoder | — | JSON | はい | OpenAI 互換(エンドポイントはクライアントが選択) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | ローカルリスナーのみ。 | +| Qoder CN | — | JSON | はい | OpenAI 互換(エンドポイントはクライアントが選択) | implemented | ローカルリスナーのみ。 | +| WorkBuddy | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | +| WorkBuddy AI | — | JSON | はい | Chat Completions | implemented | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | +| OpenCode | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。 [ガイド](/ja/guides/opencode/) | +| Pi | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | ローカルリスナーのみ。 [ガイド](/ja/guides/pi/) | +| Oh My Pi (omp) | — | YAML | はい | Chat Completions; Responses (ネイティブ OpenAI モデルのみ) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | ローカルリスナーのみ。自分の YAML ブロックだけを編集します。 | +| Hermes | — | YAML | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。自分の YAML ブロックだけを編集します。 | +| OpenClaw | — | JSON5 | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。 | +| Kimi CLI | — | TOML | はい | OpenAI 互換(エンドポイントはクライアントが選択) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | ローカルリスナーのみ。 | +| Gajae | — | YAML | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | ローカルリスナーのみ。 | +| DSH | — | YAML | はい | Responses | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | ローカルリスナーのみ。クライアントの現在のプロバイダーストアに書き込みます。自分の YAML ブロックだけを編集します。設定ファイル横のロックファイルで書き込みを調整します。 | +| MCode | — | YAML | はい | Anthropic Messages | implemented | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | +| ZCode | — | JSON | はい | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | ローカルリスナーのみ。クライアントの現在のプロバイダーストアに書き込みます。クライアントがこの設定からエンドポイントを選びます(クライアントの文書化された動作に基づきレビュー済み)。 | +| Command Code | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | +| Prime | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | ローカルリスナーのみ。 | +| Aside | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | ローカルリスナーのみ。 | +| Raycast | — | YAML | はい | Chat Completions (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | ローカルリスナーのみ。クライアントがこの設定からエンドポイントを選びます(クライアントの文書化された動作に基づきレビュー済み)。 | +| omo | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | ローカルリスナーのみ。 | +| Cline | — | JSON | はい | Responses | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | +| Kilo Code | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。 | +| Factory Droid | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | ローカルリスナーのみ。 [ガイド](/ja/guides/factory-droid/) | +| Gentle Shell | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | ローカルリスナーのみ。 | +| T3 Code | — | JSON | はい | Anthropic Messages | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | + +## 受信エンドポイント + +プロキシは次のプロトコルを受け付けます。上の表にないツールでも、いずれかのカスタムエンドポイントを設定できれば接続できます。 + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## このページを最新に保つ + +このページは `scripts/generate-client-support-matrix.ts` がエクスポート・管理連携・ネイティブ連携のレジストリから生成します。クライアントを追加するには `scripts/client-support-matrix/metadata.ts` にエントリを追加し、`bun run docs:clients` を実行してください。コミット済みのページが古いと `bun run docs:clients:check` が失敗します。 diff --git a/docs-site/src/content/docs/ko/reference/client-support.md b/docs-site/src/content/docs/ko/reference/client-support.md new file mode 100644 index 00000000000..a96f6b2e2e4 --- /dev/null +++ b/docs-site/src/content/docs/ko/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "클라이언트 지원" +description: "opencodex가 연결하는 코딩 도구, 각 도구의 연결 방식, 사용하는 프로토콜." +--- + + + + +opencodex는 AI 코딩 도구와 모델을 잇는 하나의 로컬 게이트웨이입니다. 일부 도구는 직접(네이티브로) 설정하고, 나머지는 로컬 프록시를 가리키는 설정 파일을 내보내거나 관리합니다. 이 페이지는 코드 레지스트리에서 생성되므로 현재 릴리스가 제공하는 내용과 정확히 일치합니다. + +## 지원 수준 + +- **implemented**: 레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시가 있는 프로토콜(네이티브 클라이언트, 엔드포인트를 스스로 고르는 클라이언트)은 연동 코드를 리뷰한 결과이며, 생성기는 리뷰한 코드가 그대로 있는지 확인합니다. +- **test-covered**: implemented이면서, 리뷰로 고른 이 클라이언트의 테스트 케이스가 링크되어 있습니다. 생성기는 테스트가 존재하고 비활성화되지 않았는지만 확인하므로, 무엇을 검증하는지는 테스트를 직접 읽어 보세요. + +## 클라이언트 + +| 클라이언트 | 네이티브 | 설정 내보내기 | 관리형 파일 연동 | 수신 프로토콜 | 지원 수준 | 참고 | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | 예 | — | — | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/codex-integration/) | +| Claude Code | 예 | — | — | Anthropic Messages (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/claude-code/) | +| Claude Desktop | 예 | — | — | Anthropic Messages (검토됨) | implemented | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. | +| Grok Build | 예 | — | — | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/grok-build/) | +| Qoder | — | JSON | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | 로컬 리스너 전용. | +| Qoder CN | — | JSON | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | implemented | 로컬 리스너 전용. | +| WorkBuddy | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | +| WorkBuddy AI | — | JSON | 예 | Chat Completions | implemented | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | +| OpenCode | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). [가이드](/ko/guides/opencode/) | +| Pi | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 로컬 리스너 전용. [가이드](/ko/guides/pi/) | +| Oh My Pi (omp) | — | YAML | 예 | Chat Completions; Responses (네이티브 OpenAI 모델만) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 로컬 리스너 전용. 자기 YAML 블록만 수정합니다. | +| Hermes | — | YAML | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). 자기 YAML 블록만 수정합니다. | +| OpenClaw | — | JSON5 | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). | +| Kimi CLI | — | TOML | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | 로컬 리스너 전용. | +| Gajae | — | YAML | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | 로컬 리스너 전용. | +| DSH | — | YAML | 예 | Responses | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 로컬 리스너 전용. 클라이언트의 현재 provider 저장소에 씁니다. 자기 YAML 블록만 수정합니다. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| MCode | — | YAML | 예 | Anthropic Messages | implemented | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| ZCode | — | JSON | 예 | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | 로컬 리스너 전용. 클라이언트의 현재 provider 저장소에 씁니다. 클라이언트가 이 설정값으로 엔드포인트를 고릅니다(클라이언트 문서 동작 기준으로 리뷰함). | +| Command Code | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| Prime | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | 로컬 리스너 전용. | +| Aside | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | 로컬 리스너 전용. | +| Raycast | — | YAML | 예 | Chat Completions (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | 로컬 리스너 전용. 클라이언트가 이 설정값으로 엔드포인트를 고릅니다(클라이언트 문서 동작 기준으로 리뷰함). | +| omo | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | 로컬 리스너 전용. | +| Cline | — | JSON | 예 | Responses | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| Kilo Code | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). | +| Factory Droid | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | 로컬 리스너 전용. [가이드](/ko/guides/factory-droid/) | +| Gentle Shell | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | 로컬 리스너 전용. | +| T3 Code | — | JSON | 예 | Anthropic Messages | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | + +## 수신 엔드포인트 + +프록시는 아래 프로토콜을 받습니다. 위 목록에 없는 도구도 이 중 하나로 사용자 지정 엔드포인트를 설정할 수 있으면 연결할 수 있습니다. + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## 이 페이지 최신 상태 유지 + +이 페이지는 `scripts/generate-client-support-matrix.ts`가 내보내기·관리형 연동·네이티브 연동 레지스트리로부터 생성합니다. 클라이언트를 추가하려면 `scripts/client-support-matrix/metadata.ts`에 항목을 넣고 `bun run docs:clients`를 실행하세요. 커밋된 페이지가 오래되면 `bun run docs:clients:check`가 실패합니다. diff --git a/docs-site/src/content/docs/reference/client-support.md b/docs-site/src/content/docs/reference/client-support.md new file mode 100644 index 00000000000..7b228756f7a --- /dev/null +++ b/docs-site/src/content/docs/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "Client support" +description: "Which coding tools opencodex connects to, how each one is wired, and which protocol it speaks." +--- + + + + +opencodex is one local gateway for your AI coding tools and models. Some tools it configures natively; for the others it exports or manages a config file that points the tool at the local proxy. This page is generated from the code registries, so it lists exactly what the current release ships. + +## Support levels + +- **implemented**: the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. Protocols marked *curated* (native clients, and clients that pick their own endpoint) come from a review of the integration code, and the generator checks that the reviewed code is still in place. +- **test-covered**: implemented, and a test case chosen in review that exercises this client is linked. The generator checks only that the test still exists and is enabled; read it to see what it asserts. + +## Clients + +| Client | Native | Config export | Managed file integration | Inbound protocol | Support level | Notes | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Yes | — | — | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | Set up by opencodex itself; no exported config file. [guide](/guides/codex-integration/) | +| Claude Code | Yes | — | — | Anthropic Messages (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | Set up by opencodex itself; no exported config file. [guide](/guides/claude-code/) | +| Claude Desktop | Yes | — | — | Anthropic Messages (curated) | implemented | Set up by opencodex itself; no exported config file. | +| Grok Build | Yes | — | — | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | Set up by opencodex itself; no exported config file. [guide](/guides/grok-build/) | +| Qoder | — | JSON | Yes | OpenAI-compatible (endpoint chosen by the client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Local listener only. | +| Qoder CN | — | JSON | Yes | OpenAI-compatible (endpoint chosen by the client) | implemented | Local listener only. | +| WorkBuddy | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Local listener only. Apply runs a client-specific check first and can refuse. | +| WorkBuddy AI | — | JSON | Yes | Chat Completions | implemented | Local listener only. Apply runs a client-specific check first and can refuse. | +| OpenCode | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | The exporter also supports a remote listener (it adds the API key header). [guide](/guides/opencode/) | +| Pi | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Local listener only. [guide](/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Yes | Chat Completions; Responses (native OpenAI models only) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Local listener only. Edits only its own YAML block. | +| Hermes | — | YAML | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | The exporter also supports a remote listener (it adds the API key header). Edits only its own YAML block. | +| OpenClaw | — | JSON5 | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | The exporter also supports a remote listener (it adds the API key header). | +| Kimi CLI | — | TOML | Yes | OpenAI-compatible (endpoint chosen by the client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Local listener only. | +| Gajae | — | YAML | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Local listener only. | +| DSH | — | YAML | Yes | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Local listener only. Writes the client's current provider store. Edits only its own YAML block. Writes are coordinated through a lock file next to the config. | +| MCode | — | YAML | Yes | Anthropic Messages | implemented | Local listener only. Writes are coordinated through a lock file next to the config. | +| ZCode | — | JSON | Yes | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Local listener only. Writes the client's current provider store. The client picks the endpoint from this setting (reviewed against the client's documented behavior). | +| Command Code | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Local listener only. Writes are coordinated through a lock file next to the config. | +| Prime | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Local listener only. | +| Aside | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Local listener only. | +| Raycast | — | YAML | Yes | Chat Completions (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Local listener only. The client picks the endpoint from this setting (reviewed against the client's documented behavior). | +| omo | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Local listener only. | +| Cline | — | JSON | Yes | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Local listener only. Writes are coordinated through a lock file next to the config. | +| Kilo Code | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | The exporter also supports a remote listener (it adds the API key header). | +| Factory Droid | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Local listener only. [guide](/guides/factory-droid/) | +| Gentle Shell | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Local listener only. | +| T3 Code | — | JSON | Yes | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Local listener only. Apply runs a client-specific check first and can refuse. | + +## Inbound endpoints + +The proxy accepts these protocols. A tool that is not listed above can still connect if it lets you set a custom endpoint for one of them. + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## Keeping this page current + +This page is generated by `scripts/generate-client-support-matrix.ts` from the export, managed-integration and native-integration registries. To add a client, add its entry in `scripts/client-support-matrix/metadata.ts` and run `bun run docs:clients`; `bun run docs:clients:check` fails while the committed page is stale. diff --git a/docs-site/src/content/docs/ru/reference/client-support.md b/docs-site/src/content/docs/ru/reference/client-support.md new file mode 100644 index 00000000000..cbbae2244e6 --- /dev/null +++ b/docs-site/src/content/docs/ru/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "Поддержка клиентов" +description: "К каким инструментам для кода подключается opencodex, как подключён каждый из них и какой протокол он использует." +--- + + + + +opencodex — единый локальный шлюз для ваших ИИ-инструментов для кода и моделей. Часть инструментов он настраивает нативно; для остальных он экспортирует или ведёт файл конфигурации, который направляет инструмент на локальный прокси. Страница сгенерирована из реестров в коде и поэтому в точности соответствует текущему релизу. + +## Уровни поддержки + +- **implemented**: клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Протоколы с пометкой *проверено* (нативные клиенты и клиенты, сами выбирающие эндпоинт) взяты из ревью кода интеграции, а генератор проверяет, что этот код на месте. +- **test-covered**: implemented, и есть ссылка на выбранный при ревью тест, который задействует этого клиента. Генератор проверяет только, что тест существует и не отключён; что именно он проверяет, смотрите в самом тесте. + +## Клиенты + +| Клиент | Нативно | Экспорт конфигурации | Управляемая интеграция файла | Входящий протокол | Уровень | Примечания | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Да | — | — | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/codex-integration/) | +| Claude Code | Да | — | — | Anthropic Messages (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/claude-code/) | +| Claude Desktop | Да | — | — | Anthropic Messages (проверено) | implemented | Настраивается самим opencodex, без экспортируемого файла конфигурации. | +| Grok Build | Да | — | — | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/grok-build/) | +| Qoder | — | JSON | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Только локальный слушатель. | +| Qoder CN | — | JSON | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | implemented | Только локальный слушатель. | +| WorkBuddy | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | +| WorkBuddy AI | — | JSON | Да | Chat Completions | implemented | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | +| OpenCode | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). [руководство](/ru/guides/opencode/) | +| Pi | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Только локальный слушатель. [руководство](/ru/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Да | Chat Completions; Responses (только нативные модели OpenAI) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Только локальный слушатель. Изменяет только свой YAML-блок. | +| Hermes | — | YAML | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). Изменяет только свой YAML-блок. | +| OpenClaw | — | JSON5 | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). | +| Kimi CLI | — | TOML | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Только локальный слушатель. | +| Gajae | — | YAML | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Только локальный слушатель. | +| DSH | — | YAML | Да | Responses | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Только локальный слушатель. Пишет в текущее хранилище провайдеров клиента. Изменяет только свой YAML-блок. Запись согласуется через файл блокировки рядом с конфигурацией. | +| MCode | — | YAML | Да | Anthropic Messages | implemented | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | +| ZCode | — | JSON | Да | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Только локальный слушатель. Пишет в текущее хранилище провайдеров клиента. Клиент выбирает эндпоинт по этой настройке (проверено при ревью по документированному поведению клиента). | +| Command Code | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | +| Prime | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Только локальный слушатель. | +| Aside | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Только локальный слушатель. | +| Raycast | — | YAML | Да | Chat Completions (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Только локальный слушатель. Клиент выбирает эндпоинт по этой настройке (проверено при ревью по документированному поведению клиента). | +| omo | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Только локальный слушатель. | +| Cline | — | JSON | Да | Responses | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | +| Kilo Code | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). | +| Factory Droid | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Только локальный слушатель. [руководство](/ru/guides/factory-droid/) | +| Gentle Shell | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Только локальный слушатель. | +| T3 Code | — | JSON | Да | Anthropic Messages | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | + +## Входящие эндпоинты + +Прокси принимает эти протоколы. Инструмент, которого нет в таблице, всё равно может подключиться, если позволяет задать собственный эндпоинт для одного из них. + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## Актуальность страницы + +Страница генерируется скриптом `scripts/generate-client-support-matrix.ts` из реестров экспорта, управляемых и нативных интеграций. Чтобы добавить клиента, добавьте запись в `scripts/client-support-matrix/metadata.ts` и выполните `bun run docs:clients`; `bun run docs:clients:check` падает, пока закоммиченная страница устарела. diff --git a/docs-site/src/content/docs/tr/reference/client-support.md b/docs-site/src/content/docs/tr/reference/client-support.md new file mode 100644 index 00000000000..156a58ec5fd --- /dev/null +++ b/docs-site/src/content/docs/tr/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "İstemci desteği" +description: "opencodex'in bağlandığı kodlama araçları, her birinin nasıl bağlandığı ve hangi protokolü kullandığı." +--- + + + + +opencodex, yapay zekâ kodlama araçlarınız ve modelleriniz için tek bir yerel ağ geçididir. Bazı araçları yerel olarak yapılandırır; diğerleri için aracı yerel proxy'ye yönlendiren bir yapılandırma dosyasını dışa aktarır veya yönetir. Bu sayfa koddaki kayıtlardan üretilir; bu yüzden mevcut sürümün sunduğu şeyle birebir aynıdır. + +## Destek düzeyleri + +- **implemented**: istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işaretli protokoller (yerel istemciler ve uç noktasını kendisi seçen istemciler) entegrasyon kodunun incelemesinden gelir; üretici, incelenen kodun hâlâ yerinde olduğunu denetler. +- **test-covered**: implemented olmanın yanında, incelemede seçilmiş ve bu istemciyi çalıştıran bir test durumuna bağlantı verilir. Üretici yalnızca testin hâlâ var ve etkin olduğunu denetler; neyi doğruladığını görmek için testi okuyun. + +## İstemciler + +| İstemci | Yerel | Yapılandırma dışa aktarımı | Yönetilen dosya entegrasyonu | Gelen protokol | Destek düzeyi | Notlar | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Evet | — | — | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/codex-integration/) | +| Claude Code | Evet | — | — | Anthropic Messages (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/claude-code/) | +| Claude Desktop | Evet | — | — | Anthropic Messages (incelendi) | implemented | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. | +| Grok Build | Evet | — | — | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/grok-build/) | +| Qoder | — | JSON | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Yalnızca yerel dinleyici. | +| Qoder CN | — | JSON | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | implemented | Yalnızca yerel dinleyici. | +| WorkBuddy | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | +| WorkBuddy AI | — | JSON | Evet | Chat Completions | implemented | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | +| OpenCode | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). [kılavuz](/tr/guides/opencode/) | +| Pi | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Yalnızca yerel dinleyici. [kılavuz](/tr/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Evet | Chat Completions; Responses (yalnızca yerel OpenAI modelleri) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Yalnızca yerel dinleyici. Yalnızca kendi YAML bloğunu düzenler. | +| Hermes | — | YAML | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). Yalnızca kendi YAML bloğunu düzenler. | +| OpenClaw | — | JSON5 | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). | +| Kimi CLI | — | TOML | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Yalnızca yerel dinleyici. | +| Gajae | — | YAML | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Yalnızca yerel dinleyici. | +| DSH | — | YAML | Evet | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Yalnızca yerel dinleyici. İstemcinin güncel sağlayıcı deposuna yazar. Yalnızca kendi YAML bloğunu düzenler. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| MCode | — | YAML | Evet | Anthropic Messages | implemented | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| ZCode | — | JSON | Evet | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Yalnızca yerel dinleyici. İstemcinin güncel sağlayıcı deposuna yazar. İstemci uç noktayı bu ayardan seçer (istemcinin belgelenmiş davranışına göre incelendi). | +| Command Code | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| Prime | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Yalnızca yerel dinleyici. | +| Aside | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Yalnızca yerel dinleyici. | +| Raycast | — | YAML | Evet | Chat Completions (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Yalnızca yerel dinleyici. İstemci uç noktayı bu ayardan seçer (istemcinin belgelenmiş davranışına göre incelendi). | +| omo | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Yalnızca yerel dinleyici. | +| Cline | — | JSON | Evet | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| Kilo Code | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). | +| Factory Droid | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Yalnızca yerel dinleyici. [kılavuz](/tr/guides/factory-droid/) | +| Gentle Shell | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Yalnızca yerel dinleyici. | +| T3 Code | — | JSON | Evet | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | + +## Gelen uç noktalar + +Proxy bu protokolleri kabul eder. Yukarıda listelenmeyen bir araç, bunlardan biri için özel uç nokta ayarlamaya izin veriyorsa yine de bağlanabilir. + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## Bu sayfayı güncel tutmak + +Bu sayfa `scripts/generate-client-support-matrix.ts` tarafından dışa aktarma, yönetilen entegrasyon ve yerel entegrasyon kayıtlarından üretilir. İstemci eklemek için `scripts/client-support-matrix/metadata.ts` dosyasına kaydını ekleyip `bun run docs:clients` çalıştırın; işlenmiş sayfa eskidiğinde `bun run docs:clients:check` başarısız olur. diff --git a/docs-site/src/content/docs/zh-cn/reference/client-support.md b/docs-site/src/content/docs/zh-cn/reference/client-support.md new file mode 100644 index 00000000000..036915a30c2 --- /dev/null +++ b/docs-site/src/content/docs/zh-cn/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "客户端支持" +description: "opencodex 能连接哪些编程工具、每个工具如何接入,以及它使用的协议。" +--- + + + + +opencodex 是一个本地网关,连接你的 AI 编程工具与模型。部分工具由它原生配置;其余工具则通过导出或托管一个指向本地代理的配置文件来接入。本页由代码中的注册表生成,因此与当前版本实际提供的内容完全一致。 + +## 支持级别 + +- **implemented**: 客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议(原生客户端以及自行选择端点的客户端)来自对集成代码的审阅,生成器会检查被审阅的代码是否仍然存在。 +- **test-covered**: 满足 implemented,并链接了一个经评审选定、覆盖该客户端的测试用例。生成器只检查该测试仍然存在且未被禁用;它具体断言了什么,请阅读测试本身。 + +## 客户端 + +| 客户端 | 原生 | 配置导出 | 托管文件集成 | 入站协议 | 支持级别 | 说明 | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | 是 | — | — | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/codex-integration/) | +| Claude Code | 是 | — | — | Anthropic Messages (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/claude-code/) | +| Claude Desktop | 是 | — | — | Anthropic Messages (已审阅) | implemented | 由 opencodex 直接配置,没有导出的配置文件。 | +| Grok Build | 是 | — | — | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/grok-build/) | +| Qoder | — | JSON | 是 | OpenAI 兼容(端点由客户端选择) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | 仅限本地监听。 | +| Qoder CN | — | JSON | 是 | OpenAI 兼容(端点由客户端选择) | implemented | 仅限本地监听。 | +| WorkBuddy | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | +| WorkBuddy AI | — | JSON | 是 | Chat Completions | implemented | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | +| OpenCode | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 [指南](/zh-cn/guides/opencode/) | +| Pi | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 仅限本地监听。 [指南](/zh-cn/guides/pi/) | +| Oh My Pi (omp) | — | YAML | 是 | Chat Completions; Responses (仅原生 OpenAI 模型) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 仅限本地监听。只修改自己的 YAML 区块。 | +| Hermes | — | YAML | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。只修改自己的 YAML 区块。 | +| OpenClaw | — | JSON5 | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 | +| Kimi CLI | — | TOML | 是 | OpenAI 兼容(端点由客户端选择) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | 仅限本地监听。 | +| Gajae | — | YAML | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | 仅限本地监听。 | +| DSH | — | YAML | 是 | Responses | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 仅限本地监听。写入客户端当前的提供商存储。只修改自己的 YAML 区块。通过配置文件旁的锁文件协调写入。 | +| MCode | — | YAML | 是 | Anthropic Messages | implemented | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | +| ZCode | — | JSON | 是 | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | 仅限本地监听。写入客户端当前的提供商存储。客户端根据此设置选择端点(已依据客户端的文档行为评审)。 | +| Command Code | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | +| Prime | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | 仅限本地监听。 | +| Aside | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | 仅限本地监听。 | +| Raycast | — | YAML | 是 | Chat Completions (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | 仅限本地监听。客户端根据此设置选择端点(已依据客户端的文档行为评审)。 | +| omo | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | 仅限本地监听。 | +| Cline | — | JSON | 是 | Responses | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | +| Kilo Code | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 | +| Factory Droid | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | 仅限本地监听。 [指南](/zh-cn/guides/factory-droid/) | +| Gentle Shell | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | 仅限本地监听。 | +| T3 Code | — | JSON | 是 | Anthropic Messages | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | + +## 入站端点 + +代理接受以下协议。未列在上表中的工具,只要能为其中之一设置自定义端点,也可以接入。 + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## 保持本页最新 + +本页由 `scripts/generate-client-support-matrix.ts` 根据导出、托管集成和原生集成注册表生成。要添加客户端,请在 `scripts/client-support-matrix/metadata.ts` 中添加条目并运行 `bun run docs:clients`;已提交的页面过期时,`bun run docs:clients:check` 会失败。 diff --git a/docs-site/src/content/docs/zh-tw/reference/client-support.md b/docs-site/src/content/docs/zh-tw/reference/client-support.md new file mode 100644 index 00000000000..2f4ad0f3c7b --- /dev/null +++ b/docs-site/src/content/docs/zh-tw/reference/client-support.md @@ -0,0 +1,59 @@ +--- +title: "用戶端支援" +description: "opencodex 能連接哪些程式開發工具、每個工具如何接入,以及它使用的協定。" +--- + + + + +opencodex 是一個本機閘道,連接你的 AI 程式開發工具與模型。部分工具由它原生設定;其餘工具則透過匯出或代管一個指向本機代理的設定檔來接入。本頁由程式碼中的登錄表產生,因此與目前版本實際提供的內容完全一致。 + +## 支援等級 + +- **implemented**: 用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定(原生用戶端以及自行選擇端點的用戶端)來自對整合程式碼的審查,產生器會檢查被審查的程式碼是否仍然存在。 +- **test-covered**: 符合 implemented,並連結了一個經審查選定、涵蓋該用戶端的測試案例。產生器只檢查該測試仍然存在且未被停用;它實際驗證什麼,請閱讀測試本身。 + +## 用戶端 + +| 用戶端 | 原生 | 設定匯出 | 代管檔案整合 | 入站協定 | 支援等級 | 說明 | +| --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | 是 | — | — | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/codex-integration/) | +| Claude Code | 是 | — | — | Anthropic Messages (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/claude-code/) | +| Claude Desktop | 是 | — | — | Anthropic Messages (已審查) | implemented | 由 opencodex 直接設定,沒有匯出的設定檔。 | +| Grok Build | 是 | — | — | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/grok-build/) | +| Qoder | — | JSON | 是 | OpenAI 相容(端點由用戶端選擇) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | 僅限本機監聽。 | +| Qoder CN | — | JSON | 是 | OpenAI 相容(端點由用戶端選擇) | implemented | 僅限本機監聽。 | +| WorkBuddy | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | +| WorkBuddy AI | — | JSON | 是 | Chat Completions | implemented | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | +| OpenCode | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 [指南](/zh-tw/guides/opencode/) | +| Pi | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 僅限本機監聽。 [指南](/zh-tw/guides/pi/) | +| Oh My Pi (omp) | — | YAML | 是 | Chat Completions; Responses (僅原生 OpenAI 模型) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 僅限本機監聽。只修改自己的 YAML 區塊。 | +| Hermes | — | YAML | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。只修改自己的 YAML 區塊。 | +| OpenClaw | — | JSON5 | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 | +| Kimi CLI | — | TOML | 是 | OpenAI 相容(端點由用戶端選擇) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | 僅限本機監聽。 | +| Gajae | — | YAML | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | 僅限本機監聽。 | +| DSH | — | YAML | 是 | Responses | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 僅限本機監聽。寫入用戶端目前的供應商儲存區。只修改自己的 YAML 區塊。透過設定檔旁的鎖定檔協調寫入。 | +| MCode | — | YAML | 是 | Anthropic Messages | implemented | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | +| ZCode | — | JSON | 是 | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | 僅限本機監聽。寫入用戶端目前的供應商儲存區。用戶端依此設定選擇端點(已依用戶端的文件行為審查)。 | +| Command Code | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | +| Prime | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | 僅限本機監聽。 | +| Aside | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | 僅限本機監聽。 | +| Raycast | — | YAML | 是 | Chat Completions (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | 僅限本機監聽。用戶端依此設定選擇端點(已依用戶端的文件行為審查)。 | +| omo | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | 僅限本機監聽。 | +| Cline | — | JSON | 是 | Responses | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | +| Kilo Code | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 | +| Factory Droid | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | 僅限本機監聽。 [指南](/zh-tw/guides/factory-droid/) | +| Gentle Shell | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | 僅限本機監聽。 | +| T3 Code | — | JSON | 是 | Anthropic Messages | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | + +## 入站端點 + +代理接受以下協定。未列在上表中的工具,只要能為其中之一設定自訂端點,也可以接入。 + +- `POST /v1/responses`: Responses +- `POST /v1/chat/completions`: Chat Completions +- `POST /v1/messages`: Anthropic Messages + +## 保持本頁最新 + +本頁由 `scripts/generate-client-support-matrix.ts` 根據匯出、代管整合與原生整合登錄表產生。要新增用戶端,請在 `scripts/client-support-matrix/metadata.ts` 中加入項目並執行 `bun run docs:clients`;已提交的頁面過期時,`bun run docs:clients:check` 會失敗。 diff --git a/package.json b/package.json index c8072e12dbc..6fe62b4200b 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,8 @@ "privacy:scan": "bun scripts/privacy-scan.ts", "favicons": "bun scripts/brand-favicons.ts", "favicons:check": "bun scripts/brand-favicons.ts --check", + "docs:clients": "bun scripts/generate-client-support-matrix.ts", + "docs:clients:check": "bun scripts/generate-client-support-matrix.ts --check", "skill:surface": "bun scripts/generate-ocx-skill-surface.ts", "skill:surface:check": "bun scripts/generate-ocx-skill-surface.ts --check", "structure:index": "bun scripts/structure-ssot.ts --fix", diff --git a/scripts/client-support-matrix/locales.ts b/scripts/client-support-matrix/locales.ts new file mode 100644 index 00000000000..ba35ffc9401 --- /dev/null +++ b/scripts/client-support-matrix/locales.ts @@ -0,0 +1,283 @@ +/** + * Page chrome and note phrases for the generated client support page, one block per docs locale. + * English is canonical; every locale must define every key (enforced by the Strings type). + * Product names, protocol names and the level keywords stay in English on every page. + */ + +export interface Strings { + title: string; + description: string; + intro: string; + levelsHeading: string; + implementedDefinition: string; + testCoveredDefinition: string; + clientsHeading: string; + columns: { client: string; native: string; configExport: string; managed: string; protocol: string; level: string; notes: string }; + yes: string; + curated: string; + nativeOpenaiCondition: string; + openaiCompatible: string; + testLink: string; + guideLink: string; + notes: { + native: string; + loopbackOnly: string; + remoteWithKey: string; + currentStore: string; + sourcePreservingYaml: string; + writerLock: string; + validateApply: string; + clientSelected: string; + }; + endpointsHeading: string; + endpointsIntro: string; + maintenanceHeading: string; + maintenance: string; +} + +export const LOCALES = { + root: { + title: "Client support", + description: "Which coding tools opencodex connects to, how each one is wired, and which protocol it speaks.", + intro: "opencodex is one local gateway for your AI coding tools and models. Some tools it configures natively; for the others it exports or manages a config file that points the tool at the local proxy. This page is generated from the code registries, so it lists exactly what the current release ships.", + levelsHeading: "Support levels", + implementedDefinition: "the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. Protocols marked *curated* (native clients, and clients that pick their own endpoint) come from a review of the integration code, and the generator checks that the reviewed code is still in place.", + testCoveredDefinition: "implemented, and a test case chosen in review that exercises this client is linked. The generator checks only that the test still exists and is enabled; read it to see what it asserts.", + clientsHeading: "Clients", + columns: { client: "Client", native: "Native", configExport: "Config export", managed: "Managed file integration", protocol: "Inbound protocol", level: "Support level", notes: "Notes" }, + yes: "Yes", + curated: "curated", + nativeOpenaiCondition: "native OpenAI models only", + openaiCompatible: "OpenAI-compatible (endpoint chosen by the client)", + testLink: "test", + guideLink: "guide", + notes: { + native: "Set up by opencodex itself; no exported config file.", + loopbackOnly: "Local listener only.", + remoteWithKey: "The exporter also supports a remote listener (it adds the API key header).", + currentStore: "Writes the client's current provider store.", + sourcePreservingYaml: "Edits only its own YAML block.", + writerLock: "Writes are coordinated through a lock file next to the config.", + validateApply: "Apply runs a client-specific check first and can refuse.", + clientSelected: "The client picks the endpoint from this setting (reviewed against the client's documented behavior).", + }, + endpointsHeading: "Inbound endpoints", + endpointsIntro: "The proxy accepts these protocols. A tool that is not listed above can still connect if it lets you set a custom endpoint for one of them.", + maintenanceHeading: "Keeping this page current", + maintenance: "This page is generated by `scripts/generate-client-support-matrix.ts` from the export, managed-integration and native-integration registries. To add a client, add its entry in `scripts/client-support-matrix/metadata.ts` and run `bun run docs:clients`; `bun run docs:clients:check` fails while the committed page is stale.", + }, + fr: { + title: "Clients pris en charge", + description: "Les outils de code auxquels opencodex se connecte, la façon dont chacun est relié et le protocole qu’il utilise.", + intro: "opencodex est une passerelle locale unique pour vos outils de code IA et vos modèles. Il configure certains outils nativement ; pour les autres, il exporte ou gère un fichier de configuration qui dirige l’outil vers le proxy local. Cette page est générée à partir des registres du code et reflète donc exactement ce que fournit la version actuelle.", + levelsHeading: "Niveaux de prise en charge", + implementedDefinition: "le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Les protocoles marqués *revu* (clients natifs et clients qui choisissent eux-mêmes leur point de terminaison) proviennent d’une revue du code d’intégration, et le générateur vérifie que ce code est toujours en place.", + testCoveredDefinition: "implemented, et un cas de test choisi en revue qui exerce ce client est lié. Le générateur vérifie seulement que le test existe toujours et qu’il est actif ; lisez-le pour savoir ce qu’il vérifie.", + clientsHeading: "Clients", + columns: { client: "Client", native: "Natif", configExport: "Export de configuration", managed: "Intégration de fichier gérée", protocol: "Protocole entrant", level: "Niveau", notes: "Remarques" }, + yes: "Oui", + curated: "revu", + nativeOpenaiCondition: "modèles OpenAI natifs uniquement", + openaiCompatible: "Compatible OpenAI (point de terminaison choisi par le client)", + testLink: "test", + guideLink: "guide", + notes: { + native: "Configuré par opencodex lui-même ; aucun fichier de configuration exporté.", + loopbackOnly: "Écoute locale uniquement.", + remoteWithKey: "L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API).", + currentStore: "Écrit dans le magasin de fournisseurs actuel du client.", + sourcePreservingYaml: "Ne modifie que son propre bloc YAML.", + writerLock: "Les écritures passent par un fichier de verrou placé à côté de la configuration.", + validateApply: "L’application exécute d’abord une vérification propre au client et peut refuser.", + clientSelected: "Le client choisit le point de terminaison à partir de ce réglage (vérifié en revue d’après le comportement documenté du client).", + }, + endpointsHeading: "Points de terminaison entrants", + endpointsIntro: "Le proxy accepte ces protocoles. Un outil absent de la liste peut quand même se connecter s’il permet de définir un point de terminaison personnalisé pour l’un d’eux.", + maintenanceHeading: "Tenir cette page à jour", + maintenance: "Cette page est générée par `scripts/generate-client-support-matrix.ts` à partir des registres d’export, d’intégration gérée et d’intégration native. Pour ajouter un client, ajoutez son entrée dans `scripts/client-support-matrix/metadata.ts` puis lancez `bun run docs:clients` ; `bun run docs:clients:check` échoue tant que la page validée est obsolète.", + }, + ko: { + title: "클라이언트 지원", + description: "opencodex가 연결하는 코딩 도구, 각 도구의 연결 방식, 사용하는 프로토콜.", + intro: "opencodex는 AI 코딩 도구와 모델을 잇는 하나의 로컬 게이트웨이입니다. 일부 도구는 직접(네이티브로) 설정하고, 나머지는 로컬 프록시를 가리키는 설정 파일을 내보내거나 관리합니다. 이 페이지는 코드 레지스트리에서 생성되므로 현재 릴리스가 제공하는 내용과 정확히 일치합니다.", + levelsHeading: "지원 수준", + implementedDefinition: "레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시가 있는 프로토콜(네이티브 클라이언트, 엔드포인트를 스스로 고르는 클라이언트)은 연동 코드를 리뷰한 결과이며, 생성기는 리뷰한 코드가 그대로 있는지 확인합니다.", + testCoveredDefinition: "implemented이면서, 리뷰로 고른 이 클라이언트의 테스트 케이스가 링크되어 있습니다. 생성기는 테스트가 존재하고 비활성화되지 않았는지만 확인하므로, 무엇을 검증하는지는 테스트를 직접 읽어 보세요.", + clientsHeading: "클라이언트", + columns: { client: "클라이언트", native: "네이티브", configExport: "설정 내보내기", managed: "관리형 파일 연동", protocol: "수신 프로토콜", level: "지원 수준", notes: "참고" }, + yes: "예", + curated: "검토됨", + nativeOpenaiCondition: "네이티브 OpenAI 모델만", + openaiCompatible: "OpenAI 호환(엔드포인트는 클라이언트가 선택)", + testLink: "테스트", + guideLink: "가이드", + notes: { + native: "opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다.", + loopbackOnly: "로컬 리스너 전용.", + remoteWithKey: "원격 리스너도 지원합니다(API 키 헤더를 추가).", + currentStore: "클라이언트의 현재 provider 저장소에 씁니다.", + sourcePreservingYaml: "자기 YAML 블록만 수정합니다.", + writerLock: "설정 파일 옆 잠금 파일로 쓰기를 조율합니다.", + validateApply: "적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다.", + clientSelected: "클라이언트가 이 설정값으로 엔드포인트를 고릅니다(클라이언트 문서 동작 기준으로 리뷰함).", + }, + endpointsHeading: "수신 엔드포인트", + endpointsIntro: "프록시는 아래 프로토콜을 받습니다. 위 목록에 없는 도구도 이 중 하나로 사용자 지정 엔드포인트를 설정할 수 있으면 연결할 수 있습니다.", + maintenanceHeading: "이 페이지 최신 상태 유지", + maintenance: "이 페이지는 `scripts/generate-client-support-matrix.ts`가 내보내기·관리형 연동·네이티브 연동 레지스트리로부터 생성합니다. 클라이언트를 추가하려면 `scripts/client-support-matrix/metadata.ts`에 항목을 넣고 `bun run docs:clients`를 실행하세요. 커밋된 페이지가 오래되면 `bun run docs:clients:check`가 실패합니다.", + }, + "zh-cn": { + title: "客户端支持", + description: "opencodex 能连接哪些编程工具、每个工具如何接入,以及它使用的协议。", + intro: "opencodex 是一个本地网关,连接你的 AI 编程工具与模型。部分工具由它原生配置;其余工具则通过导出或托管一个指向本地代理的配置文件来接入。本页由代码中的注册表生成,因此与当前版本实际提供的内容完全一致。", + levelsHeading: "支持级别", + implementedDefinition: "客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议(原生客户端以及自行选择端点的客户端)来自对集成代码的审阅,生成器会检查被审阅的代码是否仍然存在。", + testCoveredDefinition: "满足 implemented,并链接了一个经评审选定、覆盖该客户端的测试用例。生成器只检查该测试仍然存在且未被禁用;它具体断言了什么,请阅读测试本身。", + clientsHeading: "客户端", + columns: { client: "客户端", native: "原生", configExport: "配置导出", managed: "托管文件集成", protocol: "入站协议", level: "支持级别", notes: "说明" }, + yes: "是", + curated: "已审阅", + nativeOpenaiCondition: "仅原生 OpenAI 模型", + openaiCompatible: "OpenAI 兼容(端点由客户端选择)", + testLink: "测试", + guideLink: "指南", + notes: { + native: "由 opencodex 直接配置,没有导出的配置文件。", + loopbackOnly: "仅限本地监听。", + remoteWithKey: "导出的配置也支持远程监听(会添加 API 密钥请求头)。", + currentStore: "写入客户端当前的提供商存储。", + sourcePreservingYaml: "只修改自己的 YAML 区块。", + writerLock: "通过配置文件旁的锁文件协调写入。", + validateApply: "应用前先运行针对该客户端的检查,可能拒绝。", + clientSelected: "客户端根据此设置选择端点(已依据客户端的文档行为评审)。", + }, + endpointsHeading: "入站端点", + endpointsIntro: "代理接受以下协议。未列在上表中的工具,只要能为其中之一设置自定义端点,也可以接入。", + maintenanceHeading: "保持本页最新", + maintenance: "本页由 `scripts/generate-client-support-matrix.ts` 根据导出、托管集成和原生集成注册表生成。要添加客户端,请在 `scripts/client-support-matrix/metadata.ts` 中添加条目并运行 `bun run docs:clients`;已提交的页面过期时,`bun run docs:clients:check` 会失败。", + }, + "zh-tw": { + title: "用戶端支援", + description: "opencodex 能連接哪些程式開發工具、每個工具如何接入,以及它使用的協定。", + intro: "opencodex 是一個本機閘道,連接你的 AI 程式開發工具與模型。部分工具由它原生設定;其餘工具則透過匯出或代管一個指向本機代理的設定檔來接入。本頁由程式碼中的登錄表產生,因此與目前版本實際提供的內容完全一致。", + levelsHeading: "支援等級", + implementedDefinition: "用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定(原生用戶端以及自行選擇端點的用戶端)來自對整合程式碼的審查,產生器會檢查被審查的程式碼是否仍然存在。", + testCoveredDefinition: "符合 implemented,並連結了一個經審查選定、涵蓋該用戶端的測試案例。產生器只檢查該測試仍然存在且未被停用;它實際驗證什麼,請閱讀測試本身。", + clientsHeading: "用戶端", + columns: { client: "用戶端", native: "原生", configExport: "設定匯出", managed: "代管檔案整合", protocol: "入站協定", level: "支援等級", notes: "說明" }, + yes: "是", + curated: "已審查", + nativeOpenaiCondition: "僅原生 OpenAI 模型", + openaiCompatible: "OpenAI 相容(端點由用戶端選擇)", + testLink: "測試", + guideLink: "指南", + notes: { + native: "由 opencodex 直接設定,沒有匯出的設定檔。", + loopbackOnly: "僅限本機監聽。", + remoteWithKey: "匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。", + currentStore: "寫入用戶端目前的供應商儲存區。", + sourcePreservingYaml: "只修改自己的 YAML 區塊。", + writerLock: "透過設定檔旁的鎖定檔協調寫入。", + validateApply: "套用前先執行該用戶端專屬的檢查,可能拒絕。", + clientSelected: "用戶端依此設定選擇端點(已依用戶端的文件行為審查)。", + }, + endpointsHeading: "入站端點", + endpointsIntro: "代理接受以下協定。未列在上表中的工具,只要能為其中之一設定自訂端點,也可以接入。", + maintenanceHeading: "保持本頁最新", + maintenance: "本頁由 `scripts/generate-client-support-matrix.ts` 根據匯出、代管整合與原生整合登錄表產生。要新增用戶端,請在 `scripts/client-support-matrix/metadata.ts` 中加入項目並執行 `bun run docs:clients`;已提交的頁面過期時,`bun run docs:clients:check` 會失敗。", + }, + ru: { + title: "Поддержка клиентов", + description: "К каким инструментам для кода подключается opencodex, как подключён каждый из них и какой протокол он использует.", + intro: "opencodex — единый локальный шлюз для ваших ИИ-инструментов для кода и моделей. Часть инструментов он настраивает нативно; для остальных он экспортирует или ведёт файл конфигурации, который направляет инструмент на локальный прокси. Страница сгенерирована из реестров в коде и поэтому в точности соответствует текущему релизу.", + levelsHeading: "Уровни поддержки", + implementedDefinition: "клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Протоколы с пометкой *проверено* (нативные клиенты и клиенты, сами выбирающие эндпоинт) взяты из ревью кода интеграции, а генератор проверяет, что этот код на месте.", + testCoveredDefinition: "implemented, и есть ссылка на выбранный при ревью тест, который задействует этого клиента. Генератор проверяет только, что тест существует и не отключён; что именно он проверяет, смотрите в самом тесте.", + clientsHeading: "Клиенты", + columns: { client: "Клиент", native: "Нативно", configExport: "Экспорт конфигурации", managed: "Управляемая интеграция файла", protocol: "Входящий протокол", level: "Уровень", notes: "Примечания" }, + yes: "Да", + curated: "проверено", + nativeOpenaiCondition: "только нативные модели OpenAI", + openaiCompatible: "Совместим с OpenAI (эндпоинт выбирает клиент)", + testLink: "тест", + guideLink: "руководство", + notes: { + native: "Настраивается самим opencodex, без экспортируемого файла конфигурации.", + loopbackOnly: "Только локальный слушатель.", + remoteWithKey: "Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа).", + currentStore: "Пишет в текущее хранилище провайдеров клиента.", + sourcePreservingYaml: "Изменяет только свой YAML-блок.", + writerLock: "Запись согласуется через файл блокировки рядом с конфигурацией.", + validateApply: "Перед применением выполняется проверка, специфичная для клиента, и возможен отказ.", + clientSelected: "Клиент выбирает эндпоинт по этой настройке (проверено при ревью по документированному поведению клиента).", + }, + endpointsHeading: "Входящие эндпоинты", + endpointsIntro: "Прокси принимает эти протоколы. Инструмент, которого нет в таблице, всё равно может подключиться, если позволяет задать собственный эндпоинт для одного из них.", + maintenanceHeading: "Актуальность страницы", + maintenance: "Страница генерируется скриптом `scripts/generate-client-support-matrix.ts` из реестров экспорта, управляемых и нативных интеграций. Чтобы добавить клиента, добавьте запись в `scripts/client-support-matrix/metadata.ts` и выполните `bun run docs:clients`; `bun run docs:clients:check` падает, пока закоммиченная страница устарела.", + }, + ja: { + title: "クライアント対応", + description: "opencodex が接続するコーディングツール、それぞれの接続方法、使用するプロトコル。", + intro: "opencodex は、AI コーディングツールとモデルをつなぐ 1 つのローカルゲートウェイです。一部のツールはネイティブに設定し、それ以外はローカルプロキシを指す設定ファイルをエクスポートまたは管理します。このページはコードのレジストリから生成されるため、現在のリリースの内容と正確に一致します。", + levelsHeading: "サポートレベル", + implementedDefinition: "レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*と付いたプロトコル(ネイティブクライアントと、エンドポイントを自分で選ぶクライアント)は連携コードのレビューに基づくもので、レビューしたコードが残っているかを生成時に確認します。", + testCoveredDefinition: "implemented であり、レビューで選んだこのクライアントのテストケースへのリンクがあります。生成時に確認するのはテストが存在し無効化されていないことだけです。何を検証しているかはテストを読んでください。", + clientsHeading: "クライアント", + columns: { client: "クライアント", native: "ネイティブ", configExport: "設定エクスポート", managed: "管理ファイル連携", protocol: "受信プロトコル", level: "サポートレベル", notes: "備考" }, + yes: "はい", + curated: "レビュー済み", + nativeOpenaiCondition: "ネイティブ OpenAI モデルのみ", + openaiCompatible: "OpenAI 互換(エンドポイントはクライアントが選択)", + testLink: "テスト", + guideLink: "ガイド", + notes: { + native: "opencodex が直接設定します。エクスポートされる設定ファイルはありません。", + loopbackOnly: "ローカルリスナーのみ。", + remoteWithKey: "リモートリスナーにも対応します(API キーヘッダーを追加)。", + currentStore: "クライアントの現在のプロバイダーストアに書き込みます。", + sourcePreservingYaml: "自分の YAML ブロックだけを編集します。", + writerLock: "設定ファイル横のロックファイルで書き込みを調整します。", + validateApply: "適用前にクライアント固有のチェックを実行し、拒否することがあります。", + clientSelected: "クライアントがこの設定からエンドポイントを選びます(クライアントの文書化された動作に基づきレビュー済み)。", + }, + endpointsHeading: "受信エンドポイント", + endpointsIntro: "プロキシは次のプロトコルを受け付けます。上の表にないツールでも、いずれかのカスタムエンドポイントを設定できれば接続できます。", + maintenanceHeading: "このページを最新に保つ", + maintenance: "このページは `scripts/generate-client-support-matrix.ts` がエクスポート・管理連携・ネイティブ連携のレジストリから生成します。クライアントを追加するには `scripts/client-support-matrix/metadata.ts` にエントリを追加し、`bun run docs:clients` を実行してください。コミット済みのページが古いと `bun run docs:clients:check` が失敗します。", + }, + tr: { + title: "İstemci desteği", + description: "opencodex'in bağlandığı kodlama araçları, her birinin nasıl bağlandığı ve hangi protokolü kullandığı.", + intro: "opencodex, yapay zekâ kodlama araçlarınız ve modelleriniz için tek bir yerel ağ geçididir. Bazı araçları yerel olarak yapılandırır; diğerleri için aracı yerel proxy'ye yönlendiren bir yapılandırma dosyasını dışa aktarır veya yönetir. Bu sayfa koddaki kayıtlardan üretilir; bu yüzden mevcut sürümün sunduğu şeyle birebir aynıdır.", + levelsHeading: "Destek düzeyleri", + implementedDefinition: "istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işaretli protokoller (yerel istemciler ve uç noktasını kendisi seçen istemciler) entegrasyon kodunun incelemesinden gelir; üretici, incelenen kodun hâlâ yerinde olduğunu denetler.", + testCoveredDefinition: "implemented olmanın yanında, incelemede seçilmiş ve bu istemciyi çalıştıran bir test durumuna bağlantı verilir. Üretici yalnızca testin hâlâ var ve etkin olduğunu denetler; neyi doğruladığını görmek için testi okuyun.", + clientsHeading: "İstemciler", + columns: { client: "İstemci", native: "Yerel", configExport: "Yapılandırma dışa aktarımı", managed: "Yönetilen dosya entegrasyonu", protocol: "Gelen protokol", level: "Destek düzeyi", notes: "Notlar" }, + yes: "Evet", + curated: "incelendi", + nativeOpenaiCondition: "yalnızca yerel OpenAI modelleri", + openaiCompatible: "OpenAI uyumlu (uç noktayı istemci seçer)", + testLink: "test", + guideLink: "kılavuz", + notes: { + native: "opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur.", + loopbackOnly: "Yalnızca yerel dinleyici.", + remoteWithKey: "Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler).", + currentStore: "İstemcinin güncel sağlayıcı deposuna yazar.", + sourcePreservingYaml: "Yalnızca kendi YAML bloğunu düzenler.", + writerLock: "Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir.", + validateApply: "Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir.", + clientSelected: "İstemci uç noktayı bu ayardan seçer (istemcinin belgelenmiş davranışına göre incelendi).", + }, + endpointsHeading: "Gelen uç noktalar", + endpointsIntro: "Proxy bu protokolleri kabul eder. Yukarıda listelenmeyen bir araç, bunlardan biri için özel uç nokta ayarlamaya izin veriyorsa yine de bağlanabilir.", + maintenanceHeading: "Bu sayfayı güncel tutmak", + maintenance: "Bu sayfa `scripts/generate-client-support-matrix.ts` tarafından dışa aktarma, yönetilen entegrasyon ve yerel entegrasyon kayıtlarından üretilir. İstemci eklemek için `scripts/client-support-matrix/metadata.ts` dosyasına kaydını ekleyip `bun run docs:clients` çalıştırın; işlenmiş sayfa eskidiğinde `bun run docs:clients:check` başarısız olur.", + }, +} as const satisfies Record; + +export type MatrixLocale = keyof typeof LOCALES; + + diff --git a/scripts/client-support-matrix/metadata.ts b/scripts/client-support-matrix/metadata.ts new file mode 100644 index 00000000000..85174957a50 --- /dev/null +++ b/scripts/client-support-matrix/metadata.ts @@ -0,0 +1,248 @@ +/** + * Curated facts the client registries do not carry, consumed by + * scripts/generate-client-support-matrix.ts. Every claim here is re-checked against the code + * when the page is generated, so a stale entry fails generation instead of shipping: + * + * - protocolFields: the config field(s) that carry each client's wire protocol. The generator + * builds the client's real export with a fixture catalog, reads every value at each path, and + * fails when a path yields nothing or a value is missing from the map. + * - clientSelected: the exported field names a protocol family and the client picks the endpoint. + * These are reviewed claims; reviewedSha pins the exporter file, so any edit to it asks for a + * fresh review (update the hash after re-reading the builder). + * - test: a reviewed test case that asserts this client's generated config or integration + * behavior. The generator only checks that the case exists, is not disabled, and names the + * client; it does not prove what the case asserts. + * + * Adding a client: add an entry here, then run `bun run docs:clients`. + */ + +export type MatrixProtocol = "responses" | "chat-completions" | "anthropic-messages" | "openai-compatible"; + +export interface ProtocolField { + /** Dotted path into the built config; `*` matches any key or array index. */ + path: string; + /** Exact value -> protocol, or (match: "suffix") a value suffix -> protocol. */ + values: Readonly>; + match?: "exact" | "suffix"; +} + +export interface TestReference { + file: string; + title: string; + /** At least one of these must appear in the test case body. */ + mentions: readonly string[]; +} + +export interface ExportClientMetadata { + label: string; + protocolFields: readonly ProtocolField[]; + nativeOpenaiOverride?: NativeOpenaiOverride; + clientSelected?: { file: string; anchor: string; reviewedSha: string }; + test?: TestReference; + guide?: string; +} + +/** A sentence or statement that must still be present in the implementing file. */ +export interface SourceAnchor { + file: string; + text: string; +} + +/** + * The provider default applies to every model except the native OpenAI model, whose entry + * carries this override. Checked model by model against the fixture catalog. + */ +export interface NativeOpenaiOverride { + modelsPath: string; + idKey: string; + field: string; + value: string; + protocol: MatrixProtocol; +} + +export interface NativeClientMetadata { + label: string; + /** Curated from the native integration; each anchor must still be present in its file. */ + protocols: readonly MatrixProtocol[]; + anchors: readonly SourceAnchor[]; + test?: TestReference; + guide?: string; +} + +const CHAT_API = { "openai-completions": "chat-completions" } as const; +const providersApi = (path = "providers.opencodex.api"): ProtocolField[] => [{ path, values: CHAT_API }]; +const OPENAI_FAMILY = { "openai-compatible": "openai-compatible", openai: "openai-compatible" } as const; +const AI_SDK = { "@ai-sdk/openai-compatible": "chat-completions" } as const; + +export const EXPORT_CLIENT_METADATA: Readonly> = { + qoder: { + label: "Qoder", + protocolFields: [{ path: "providers.opencodex.type", values: OPENAI_FAMILY }, { path: "providers.opencodex.protocol", values: OPENAI_FAMILY }], + test: { file: "tests/clients/qoder-client.test.ts", title: "exports only the provider catalog with normalized model order and context limits", mentions: ["qoder"] }, + }, + "qoder-cn": { + label: "Qoder CN", + protocolFields: [{ path: "providers.opencodex.type", values: OPENAI_FAMILY }, { path: "providers.opencodex.protocol", values: OPENAI_FAMILY }], + }, + workbuddy: { + label: "WorkBuddy", + protocolFields: [{ path: "models.*.url", values: { "/chat/completions": "chat-completions" }, match: "suffix" }], + test: { file: "tests/clients/agent-clients.test.ts", title: "WorkBuddy exports canonical selectors, full chat URL and declared capabilities only", mentions: ["workbuddy"] }, + }, + "workbuddy-ai": { + label: "WorkBuddy AI", + protocolFields: [{ path: "models.*.url", values: { "/chat/completions": "chat-completions" }, match: "suffix" }], + }, + opencode: { + label: "OpenCode", + protocolFields: [{ path: "provider.opencodex.npm", values: AI_SDK }], + test: { file: "tests/config/client-config-export.test.ts", title: "carries the V1 schema and only the opencodex provider key", mentions: ["opencode"] }, + guide: "guides/opencode", + }, + pi: { + label: "Pi", + protocolFields: providersApi(), + test: { file: "tests/config/client-config-export.test.ts", title: "tells Pi to send system instead of developer, on export and contribution alike (#5664)", mentions: ["\"pi\""] }, + guide: "guides/pi", + }, + omp: { + label: "Oh My Pi (omp)", + protocolFields: providersApi(), + nativeOpenaiOverride: { modelsPath: "providers.opencodex.models", idKey: "id", field: "api", value: "openai-responses", protocol: "responses" }, + test: { file: "tests/config/client-config-export.test.ts", title: "keeps the provider on completions while opting native OpenAI models into Responses", mentions: ["omp"] }, + }, + hermes: { + label: "Hermes", + protocolFields: [{ path: "providers.opencodex.api_mode", values: { chat_completions: "chat-completions" } }], + test: { file: "tests/clients/hermes-catalog-refresh.test.ts", title: "a connected Hermes block refreshes its models and keeps unrelated YAML bytes and comments", mentions: ["hermes"] }, + }, + openclaw: { + label: "OpenClaw", + protocolFields: providersApi("models.providers.opencodex.api"), + test: { file: "tests/gui/integrations-invariants.test.ts", title: "OPENCLAW_CONFIG_PATH is where apply writes and disable removes", mentions: ["openclaw"] }, + }, + kimi: { + label: "Kimi CLI", + protocolFields: [{ path: "providers.opencodex.type", values: OPENAI_FAMILY }], + test: { file: "tests/config/client-config-export-new-clients.test.ts", title: "uses the loopback placeholder because Kimi reads no environment", mentions: ["kimi"] }, + }, + gajae: { + label: "Gajae", + protocolFields: providersApi(), + test: { file: "tests/clients/client-export-modality-enum.test.ts", title: "audio is dropped from a mixed Gajae entry rather than written through", mentions: ["gajae"] }, + }, + dsh: { + label: "DSH", + protocolFields: [{ path: "llm-pi-ai.providers.opencodex.api", values: { "openai-responses": "responses" } }], + test: { file: "tests/config/client-config-export.test.ts", title: "owns only llm-pi-ai.providers.opencodex with a loopback Responses profile", mentions: ["dsh"] }, + }, + mcode: { + label: "MCode", + protocolFields: [{ path: "custom_provider.opencodex.api", values: { "anthropic-messages": "anthropic-messages" } }], + }, + zcode: { + label: "ZCode", + protocolFields: [{ path: "provider.opencodex.kind", values: { openai: "responses" } }], + clientSelected: { + file: "src/clients/config-export/zcode.ts", + anchor: "`kind: \"openai\"` selects the OpenAI Responses protocol", + reviewedSha: "3b2c3e6eefb5e382510db754920fe5bc9ba47c2c9db8a6a17e715b04eeb070c5", + }, + test: { file: "tests/clients/integrations-current-store.test.ts", title: "enable puts the intended provider in the store, and leaves the config file alone", mentions: ["zcode"] }, + }, + commandcode: { + label: "Command Code", + protocolFields: providersApi("provider.opencodex.api"), + test: { file: "tests/clients/command-code-client.test.ts", title: "adds only provider.opencodex, wired to the loopback proxy", mentions: ["commandcode", "CommandCode"] }, + }, + prime: { + label: "Prime", + protocolFields: providersApi(), + test: { file: "tests/clients/prime-client.test.ts", title: "adds only providers.opencodex, wired to the loopback proxy", mentions: ["prime", "Prime"] }, + }, + aside: { + label: "Aside", + protocolFields: providersApi(), + test: { file: "tests/clients/aside-profiles-catalog-refresh.test.ts", title: "catalog refreshOnly updates the owned Aside profile and leaves its unowned peer alone", mentions: ["aside", "Aside"] }, + }, + raycast: { + label: "Raycast", + protocolFields: [{ path: "providers.*.base_url", values: { "/v1": "chat-completions" }, match: "suffix" }], + clientSelected: { + file: "src/clients/config-export/raycast.ts", + anchor: "Raycast appends `/chat/completions` to `base_url`", + reviewedSha: "624cf5c2acf9e5ad9fb2da54e8a3b80a6ec3c2dfc0303c1724497788d51d222c", + }, + test: { file: "tests/clients/raycast-client.test.ts", title: "emits one provider element with the documented field vocabulary", mentions: ["raycast", "Raycast"] }, + }, + omo: { + label: "omo", + protocolFields: providersApi(), + test: { file: "tests/clients/omo-client.test.ts", title: "adds only providers.opencodex, wired to the loopback proxy", mentions: ["omo"] }, + }, + cline: { + label: "Cline", + protocolFields: [ + { path: "settings.providers.opencodex.settings.protocol", values: { "openai-responses": "responses" } }, + { path: "catalog.providers.opencodex.provider.protocol", values: { "openai-responses": "responses" } }, + ], + test: { file: "tests/clients/cline-client.test.ts", title: "emits separate native documents with canonical selectors and no invented limits", mentions: ["cline", "Cline"] }, + }, + kilo: { + label: "Kilo Code", + protocolFields: [{ path: "provider.opencodex.npm", values: AI_SDK }], + test: { file: "tests/clients/client-export-reasoning-controls.test.ts", title: "both generations and Kilo preserve limits, defaults, capabilities and exact effort choices", mentions: ["kilo"] }, + }, + droid: { + label: "Factory Droid", + protocolFields: [{ path: "customModels.*.provider", values: { "generic-chat-completion-api": "chat-completions" } }], + test: { file: "tests/clients/droid-client.test.ts", title: "exports only supported fields with a keyless loopback gateway", mentions: ["droid", "Droid"] }, + guide: "guides/factory-droid", + }, + "gentle-shell": { + label: "Gentle Shell", + protocolFields: providersApi(), + test: { file: "tests/clients/gentle-shell-client.test.ts", title: "Gentle Shell export reuses Pi protocol and stamps independent ownership", mentions: ["gentle-shell"] }, + }, + t3code: { + label: "T3 Code", + protocolFields: [{ path: "providerInstances.opencodex.driver", values: { claudeAgent: "anthropic-messages" } }], + test: { file: "tests/clients/t3code-client.test.ts", title: "the provider instance routes T3 Code's Claude driver through the proxy origin with a non-secret marker", mentions: ["t3code", "T3"] }, + }, +}; + +/** + * Native owners. Keys must equal the NativeIntegrationClientId union in + * src/server/management/native-integration-routes.ts; the generator parses that declaration and + * fails on any difference in either direction. + */ +export const NATIVE_CLIENT_METADATA: Readonly> = { + codex: { + label: "Codex (CLI, app, SDK)", + protocols: ["responses"], + anchors: [{ file: "src/codex/inject.ts", text: "openai_base_url" }], + test: { file: "tests/codex-integration/codex-inject-missing-config.test.ts", title: "a Codex home without config.toml is bootstrapped and routed", mentions: ["runInject"] }, + guide: "guides/codex-integration", + }, + claude: { + label: "Claude Code", + protocols: ["anthropic-messages"], + anchors: [{ file: "src/cli/claude.ts", text: 'setDefault("ANTHROPIC_BASE_URL", managedBaseUrl);' }], + test: { file: "tests/claude-integration/claude-cli.test.ts", title: "routes unless configured or live Claude routing is explicitly disabled", mentions: ["Claude", "claude"] }, + guide: "guides/claude-code", + }, + "claude-desktop": { + label: "Claude Desktop", + protocols: ["anthropic-messages"], + anchors: [{ file: "src/claude/desktop-3p.ts", text: 'inferenceProvider: "gateway",' }], + }, + grok: { + label: "Grok Build", + protocols: ["responses"], + anchors: [{ file: "src/grok/inject.ts", text: "'api_backend = \"responses\"'," }], + test: { file: "tests/providers/xai/grok-config-inject.test.ts", title: "creates and strips a fresh config", mentions: ["injectGrokConfig", "grok"] }, + guide: "guides/grok-build", + }, +}; + + diff --git a/scripts/generate-client-support-matrix.ts b/scripts/generate-client-support-matrix.ts new file mode 100644 index 00000000000..1fbab318a0a --- /dev/null +++ b/scripts/generate-client-support-matrix.ts @@ -0,0 +1,402 @@ +/** + * Generate the client support matrix pages under docs-site/ from the client registries. + * + * bun scripts/generate-client-support-matrix.ts # write every locale page + * bun scripts/generate-client-support-matrix.ts --check # exit 1 when a committed page is stale + * + * Rows come from three registries: the export roster (EXPORT_CLIENT_IDS / EXPORT_CLIENTS), the + * managed file integrations (INTEGRATION_CLIENTS) and the native owners (the + * NativeIntegrationClientId union, parsed from its declaration). Curated facts live in + * scripts/client-support-matrix/metadata.ts and are re-checked against the code here, so a stale + * claim fails generation instead of shipping. Rendering is pure; I/O stays in the CLI entry point. + */ +import { createHash } from "node:crypto"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { EXPORT_CLIENT_IDS } from "../src/clients/client-ids"; +import { EXPORT_CLIENTS, buildClientConfigText, type ExportContext, type ExportModel } from "../src/clients/config-export"; +import { INTEGRATION_CLIENTS } from "../src/integrations/registry"; +import { CONFIGURABLE_JSON_BODY_ROUTES } from "../src/server/inbound-body-admission"; +import type { OcxConfig } from "../src/types"; +import { + EXPORT_CLIENT_METADATA, + NATIVE_CLIENT_METADATA, + type ExportClientMetadata, + type MatrixProtocol, + type NativeClientMetadata, + type TestReference, +} from "./client-support-matrix/metadata"; +import { LOCALES, type MatrixLocale, type Strings } from "./client-support-matrix/locales"; + +export const NATIVE_ROSTER_FILE = "src/server/management/native-integration-routes.ts"; +const PAGE = "reference/client-support.md"; +const OWNER = "GENERATED by scripts/generate-client-support-matrix.ts"; +const REPO_BLOB = "https://github.com/lidge-jun/opencodex/blob/main/"; +const REPO_ROOT = join(import.meta.dir, ".."); + +const PROTOCOL_NAMES: Record, string> = { + responses: "Responses", + "chat-completions": "Chat Completions", + "anthropic-messages": "Anthropic Messages", +}; +/** Inbound endpoint per protocol; each must be a route the proxy admits. */ +const ENDPOINTS: ReadonlyArray<{ protocol: Exclude; route: string }> = [ + { protocol: "responses", route: "/v1/responses" }, + { protocol: "chat-completions", route: "/v1/chat/completions" }, + { protocol: "anthropic-messages", route: "/v1/messages" }, +]; + +// Fixture catalog for protocol checks: one routed model and one native OpenAI model, no secrets, +// loopback listener. Never used to resolve a destination or touch a client's files. +export const FIXTURE_NATIVE_MODEL_ID = "gpt-5.5"; +const FIXTURE_MODELS: ExportModel[] = [ + { namespaced: "anthropic/claude-opus-4-8", provider: "anthropic", id: "claude-opus-4-8", contextWindow: 200_000, inputModalities: ["text"] }, + { namespaced: FIXTURE_NATIVE_MODEL_ID, provider: "openai", id: FIXTURE_NATIVE_MODEL_ID, native: true, contextWindow: 400_000, inputModalities: ["text"] }, +]; +const FIXTURE_CONFIG = { port: 10100, hostname: "127.0.0.1", defaultProvider: "fixture", providers: {} } as unknown as OcxConfig; +const FIXTURE_CONTEXT: ExportContext = { baseUrl: "http://127.0.0.1:10100/v1", models: FIXTURE_MODELS, config: FIXTURE_CONFIG }; + +export interface ExportSpecFacts { + format: string; + loopbackOnly: boolean; + validateApply?: unknown; +} +export interface IntegrationSpecFacts { + currentStore?: unknown; + sourcePreservingYaml?: unknown; + writerLock?: unknown; +} + +/** Everything the generator reads, injectable so tests can mutate one input at a time. */ +export interface MatrixInputs { + exportIds: readonly string[]; + exportSpecs: Readonly>; + integrationSpecs: Readonly>; + inboundRoutes: ReadonlySet; + buildDocument: (id: string) => unknown; + readRepoFile: (path: string) => string | undefined; + exportMetadata: Readonly>; + nativeMetadata: Readonly>; +} + +export function defaultInputs(): MatrixInputs { + return { + exportIds: EXPORT_CLIENT_IDS, + exportSpecs: EXPORT_CLIENTS, + integrationSpecs: INTEGRATION_CLIENTS, + inboundRoutes: CONFIGURABLE_JSON_BODY_ROUTES, + buildDocument: id => buildClientConfigText(id as (typeof EXPORT_CLIENT_IDS)[number], FIXTURE_CONTEXT).document, + readRepoFile: path => { + const file = join(REPO_ROOT, path); + return existsSync(file) ? readFileSync(file, "utf8") : undefined; + }, + exportMetadata: EXPORT_CLIENT_METADATA, + nativeMetadata: NATIVE_CLIENT_METADATA, + }; +} + +type NoteKey = keyof Strings["notes"]; +type ProtocolCell = { protocol: MatrixProtocol; nativeOpenaiOnly?: boolean }; +export interface MatrixRow { + id: string; + label: string; + native: boolean; + configExport?: string; + managed: boolean; + protocols: ProtocolCell[]; + curated: boolean; + test?: TestReference; + notes: NoteKey[]; + guide?: string; +} + +export class MatrixError extends Error { + constructor(readonly problems: readonly string[]) { + super(`Client support matrix cannot be generated:\n${problems.map(p => ` - ${p}`).join("\n")}\n` + + "Fix scripts/client-support-matrix/metadata.ts (or the code it describes), then run: bun run docs:clients"); + } +} + +/** Parse `export type NativeIntegrationClientId = "a" | "b";`. Any other shape is refused. */ +export function parseNativeRoster(source: string): string[] { + const match = /^export type NativeIntegrationClientId = ([^;\n]+);$/m.exec(source); + if (!match) throw new MatrixError([`${NATIVE_ROSTER_FILE} no longer declares NativeIntegrationClientId as a one-line union; update parseNativeRoster`]); + const members = match[1]!.split("|").map(part => part.trim()); + const ids = members.map(member => /^"([a-z0-9-]+)"$/.exec(member)?.[1]); + if (ids.some(id => id === undefined)) { + throw new MatrixError([`NativeIntegrationClientId is not a pure string-literal union: ${match[1]}`]); + } + return ids as string[]; +} + +/** Values at a dotted path; `*` matches every key or array index. */ +export function valuesAt(document: unknown, path: string): unknown[] { + let current: unknown[] = [document]; + for (const segment of path.split(".")) { + const next: unknown[] = []; + for (const value of current) { + if (value === null || typeof value !== "object") continue; + if (segment === "*") next.push(...Object.values(value as Record)); + else if (Object.prototype.hasOwnProperty.call(value, segment)) next.push((value as Record)[segment]); + } + current = next; + } + return current; +} + +function sha256(text: string): string { + return createHash("sha256").update(text).digest("hex"); +} + +function exportProtocols(id: string, meta: ExportClientMetadata, inputs: MatrixInputs, problems: string[]): ProtocolCell[] { + let document: unknown; + try { + document = inputs.buildDocument(id); + } catch (error) { + problems.push(`${id}: building the fixture export failed: ${(error as Error).message}`); + return []; + } + const seen = new Set(); + for (const field of meta.protocolFields) { + const values = valuesAt(document, field.path); + if (values.length === 0) { + problems.push(`${id}: protocol field ${field.path} is missing from the generated config`); + continue; + } + for (const value of values) { + const text = String(value); + const key = field.match === "suffix" + ? Object.keys(field.values).find(suffix => text.endsWith(suffix)) + : Object.prototype.hasOwnProperty.call(field.values, text) ? text : undefined; + if (key === undefined) problems.push(`${id}: ${field.path} = ${JSON.stringify(text)} is not a protocol value this matrix knows`); + else seen.add(field.values[key]!); + } + } + const cells: ProtocolCell[] = [...seen].map(protocol => ({ protocol })); + const override = meta.nativeOpenaiOverride; + if (override) { + const entries = valuesAt(document, override.modelsPath).flatMap(list => Array.isArray(list) ? list : Object.values(list as object)); + const entryId = (entry: unknown) => (entry as Record)?.[override.idKey]; + const nativeEntry = entries.find(entry => entryId(entry) === FIXTURE_NATIVE_MODEL_ID); + if (!nativeEntry) problems.push(`${id}: no model entry for the native OpenAI fixture model at ${override.modelsPath}`); + else if ((nativeEntry as Record)[override.field] !== override.value) { + problems.push(`${id}: the native OpenAI model no longer carries ${override.field} = ${override.value}`); + } + for (const entry of entries) { + if (entryId(entry) === FIXTURE_NATIVE_MODEL_ID) continue; + if ((entry as Record)?.[override.field] !== undefined) { + problems.push(`${id}: model ${String(entryId(entry))} carries ${override.field}; the override is documented for native OpenAI models only`); + } + } + cells.push({ protocol: override.protocol, nativeOpenaiOnly: true }); + } + if (cells.length === 0 && !problems.some(problem => problem.startsWith(`${id}:`))) { + problems.push(`${id}: no protocol could be established`); + } + return cells; +} + +/** Find `test("title"` / `it("title"` exactly once and return its body up to the next case. */ +function testCaseBody(source: string, title: string): string | undefined { + const quoted = JSON.stringify(title); + const starts = [`test(${quoted}`, `it(${quoted}`].flatMap(opener => { + const found: number[] = []; + for (let at = source.indexOf(opener); at >= 0; at = source.indexOf(opener, at + 1)) { + const before = source[at - 1]; + if (before === undefined || /[\s(;{]/.test(before)) found.push(at); + } + return found; + }); + if (starts.length !== 1) return undefined; + const start = starts[0]!; + const rest = source.slice(start + 1); + const next = rest.search(/\n\s*(test|it|describe)(\.\w+)?\(/); + return next < 0 ? rest : rest.slice(0, next); +} + +const DISABLING_MODIFIER = /\b(?:test|it|describe)\.(?:skip|todo|only|skipIf|if|failing|todoIf)\b/; + +export function checkTestReference(owner: string, ref: TestReference, readRepoFile: MatrixInputs["readRepoFile"], problems: string[]): void { + const source = readRepoFile(ref.file); + if (source === undefined) { + problems.push(`${owner}: test file ${ref.file} does not exist`); + return; + } + if (DISABLING_MODIFIER.test(source)) { + problems.push(`${owner}: ${ref.file} uses skip/todo/only/conditional modifiers; link a test from a file without them`); + return; + } + const body = testCaseBody(source, ref.title); + if (body === undefined) { + problems.push(`${owner}: ${ref.file} has no single test titled ${JSON.stringify(ref.title)}`); + return; + } + if (!ref.mentions.some(mention => body.includes(mention))) { + problems.push(`${owner}: test ${JSON.stringify(ref.title)} does not mention ${ref.mentions.join(" or ")}`); + } +} + +export function collectRows(inputs: MatrixInputs = defaultInputs()): MatrixRow[] { + const problems: string[] = []; + const exportIds = [...inputs.exportIds]; + const integrationIds = Object.keys(inputs.integrationSpecs); + for (const id of integrationIds) { + if (!exportIds.includes(id)) problems.push(`${id}: managed file integration without an export registry entry; the matrix has no row shape for it`); + } + for (const id of Object.keys(inputs.exportMetadata)) { + if (!exportIds.includes(id)) problems.push(`${id}: metadata exists for a client that is no longer in EXPORT_CLIENT_IDS; remove it`); + } + + const nativeSource = inputs.readRepoFile(NATIVE_ROSTER_FILE); + let nativeIds: string[] = []; + if (nativeSource === undefined) problems.push(`${NATIVE_ROSTER_FILE} is missing`); + else { + try { + nativeIds = parseNativeRoster(nativeSource); + } catch (error) { + problems.push(...(error as MatrixError).problems); + } + } + for (const id of nativeIds) if (!inputs.nativeMetadata[id]) problems.push(`${id}: native integration owner without metadata; add it to NATIVE_CLIENT_METADATA`); + for (const id of Object.keys(inputs.nativeMetadata)) { + if (nativeSource !== undefined && !nativeIds.includes(id)) problems.push(`${id}: native metadata for an owner that is no longer in NativeIntegrationClientId; remove it`); + } + + for (const { route } of ENDPOINTS) { + if (!inputs.inboundRoutes.has(route)) problems.push(`inbound route ${route} is no longer admitted; update ENDPOINTS`); + } + + const rows: MatrixRow[] = []; + // Metadata order is the display order; the parsed union decides membership. + for (const id of Object.keys(inputs.nativeMetadata).filter(id => nativeIds.includes(id))) { + const meta = inputs.nativeMetadata[id]!; + for (const anchor of meta.anchors) { + const source = inputs.readRepoFile(anchor.file); + if (source === undefined || !source.includes(anchor.text)) { + problems.push(`${id}: reviewed code ${JSON.stringify(anchor.text)} is no longer in ${anchor.file}; re-review the curated protocol`); + } + } + if (meta.test) checkTestReference(id, meta.test, inputs.readRepoFile, problems); + rows.push({ id, label: meta.label, native: true, managed: false, protocols: meta.protocols.map(protocol => ({ protocol })), curated: true, test: meta.test, notes: ["native"], guide: meta.guide }); + } + + for (const id of exportIds) { + const meta = inputs.exportMetadata[id]; + const spec = inputs.exportSpecs[id]; + if (!meta) { + problems.push(`${id}: export client without metadata; add it to EXPORT_CLIENT_METADATA`); + continue; + } + if (!spec) { + problems.push(`${id}: listed in EXPORT_CLIENT_IDS but missing from EXPORT_CLIENTS`); + continue; + } + const protocols = exportProtocols(id, meta, inputs, problems); + if (meta.clientSelected) { + const source = inputs.readRepoFile(meta.clientSelected.file); + if (source === undefined || !source.includes(meta.clientSelected.anchor)) { + problems.push(`${id}: the reviewed sentence is no longer in ${meta.clientSelected.file}; re-review the client-selected protocol`); + } else if (sha256(source) !== meta.clientSelected.reviewedSha) { + problems.push(`${id}: ${meta.clientSelected.file} changed since its protocol was reviewed (sha256 ${sha256(source)}); re-review the client-selected protocol, then update reviewedSha`); + } + } + if (meta.test) checkTestReference(id, meta.test, inputs.readRepoFile, problems); + const integration = inputs.integrationSpecs[id]; + const notes: NoteKey[] = [spec.loopbackOnly ? "loopbackOnly" : "remoteWithKey"]; + if (integration?.currentStore) notes.push("currentStore"); + if (integration?.sourcePreservingYaml) notes.push("sourcePreservingYaml"); + if (integration?.writerLock) notes.push("writerLock"); + if (spec.validateApply) notes.push("validateApply"); + if (meta.clientSelected) notes.push("clientSelected"); + rows.push({ id, label: meta.label, native: false, configExport: spec.format.toUpperCase(), managed: integration !== undefined, protocols, curated: meta.clientSelected !== undefined, test: meta.test, notes, guide: meta.guide }); + } + + if (problems.length > 0) throw new MatrixError(problems); + return rows; +} + +function cell(text: string): string { + return text.replace(/\|/g, "\\|").replace(/\n/g, " "); +} + +function localePrefix(locale: MatrixLocale): string { + return locale === "root" ? "/" : `/${locale}/`; +} + +function protocolText(entry: ProtocolCell, strings: Strings): string { + const name = entry.protocol === "openai-compatible" ? strings.openaiCompatible : PROTOCOL_NAMES[entry.protocol]; + return entry.nativeOpenaiOnly ? `${name} (${strings.nativeOpenaiCondition})` : name; +} + +export function renderPage(rows: readonly MatrixRow[], locale: MatrixLocale): string { + const s: Strings = LOCALES[locale]; + const c = s.columns; + const lines = [ + "---", + `title: ${JSON.stringify(s.title)}`, + `description: ${JSON.stringify(s.description)}`, + "---", + "", + ``, + "", + "", + s.intro, + "", + `## ${s.levelsHeading}`, + "", + `- **implemented**: ${s.implementedDefinition}`, + `- **test-covered**: ${s.testCoveredDefinition}`, + "", + `## ${s.clientsHeading}`, + "", + `| ${c.client} | ${c.native} | ${c.configExport} | ${c.managed} | ${c.protocol} | ${c.level} | ${c.notes} |`, + "| --- | --- | --- | --- | --- | --- | --- |", + ]; + for (const row of rows) { + const protocol = row.protocols.map(entry => protocolText(entry, s)).join("; ") + (row.curated ? ` (${s.curated})` : ""); + const level = row.test ? `test-covered ([${s.testLink}](${REPO_BLOB}${row.test.file}))` : "implemented"; + const separator = locale === "zh-cn" || locale === "zh-tw" || locale === "ja" ? "" : " "; + const notes = row.notes.map(key => s.notes[key]).join(separator) + (row.guide ? ` [${s.guideLink}](${localePrefix(locale)}${row.guide}/)` : ""); + lines.push(`| ${cell(row.label)} | ${row.native ? s.yes : "—"} | ${row.configExport ?? "—"} | ${row.managed ? s.yes : "—"} | ${cell(protocol)} | ${level} | ${cell(notes)} |`); + } + lines.push("", `## ${s.endpointsHeading}`, "", s.endpointsIntro, ""); + for (const { protocol, route } of ENDPOINTS) lines.push(`- \`POST ${route}\`: ${PROTOCOL_NAMES[protocol]}`); + lines.push("", `## ${s.maintenanceHeading}`, "", s.maintenance, ""); + return lines.join("\n"); +} + +export function pagePath(locale: MatrixLocale): string { + return join("docs-site", "src", "content", "docs", ...(locale === "root" ? [] : [locale]), PAGE).split("\\").join("/"); +} + +/** Expected bytes for every owned page, keyed by repository-relative path. */ +export function renderAll(inputs: MatrixInputs = defaultInputs()): Record { + const rows = collectRows(inputs); + const pages: Record = {}; + for (const locale of Object.keys(LOCALES) as MatrixLocale[]) pages[pagePath(locale)] = renderPage(rows, locale); + return pages; +} + +if (import.meta.main) { + const check = process.argv.includes("--check"); + let pages: Record; + try { + pages = renderAll(); + } catch (error) { + console.error(error instanceof MatrixError ? error.message : error); + process.exit(1); + } + const stale: string[] = []; + for (const [path, text] of Object.entries(pages)) { + const file = join(REPO_ROOT, path); + const current = existsSync(file) ? readFileSync(file, "utf8") : undefined; + if (current === text) continue; + if (check) stale.push(path); + else writeFileSync(file, text); + } + if (check && stale.length > 0) { + console.error(`Client support pages are stale: ${stale.join(", ")}\nRun: bun run docs:clients`); + process.exit(1); + } + console.log(check ? "Client support pages are current." : `Wrote ${Object.keys(pages).length} client support pages.`); +} diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index f32c120378d..ef9f1b516c5 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -44,7 +44,7 @@ "cli-provider-result.test.ts": "cli", "cli-provider-sync-result.test.ts": "cli", "cli-capability-integration-workflows.test.ts": "cli", "cli-capability-account-workflows.test.ts": "cli", "cli-capability-agent-workflows.test.ts": "cli", "cli-capability-operation-workflows.test.ts": "cli", "cli-capability-provider-workflows.test.ts": "cli", "cli-capability-access-workflows.test.ts": "cli", "cli-capability-lab-workflows.test.ts": "cli", "skill-ocx-workflows.test.ts": "ci-workflows", - "cli-capability-data.test.ts": "cli", "skill-ocx-generated.test.ts": "ci-workflows", + "cli-capability-data.test.ts": "cli", "skill-ocx-generated.test.ts": "ci-workflows", "client-support-matrix.test.ts": "ci-workflows", "cli-update-restart.test.ts": "cli", "cli-update-restart-child.test.ts": "cli", "cli-update-restart-transport.test.ts": "cli", "cli-update-restart-home.test.ts": "cli", "cli-update-restart-service-record.test.ts": "cli", "cli-update-restart-supervision.test.ts": "cli", "cli-update-restart-admission.test.ts": "cli", "cli-update-restart-desktop-service.test.ts": "cli", "update-reclaim-authorizer.test.ts": "update", "update-windows-wrapper-authority.test.ts": "update", "update-mutation-scanner.test.ts": "update", "update-mutation-boundaries.test.ts": "update", "update-mutation-guard.test.ts": "update", "update-home-launcher.test.ts": "update", "update-home-recovery.test.ts": "update", "update-recovery-authority.test.ts": "update", "update-recovery-boundaries.test.ts": "update", "update-home-worker.test.ts": "update", "update-service-home-policy.test.ts": "update", "update-service-home.test.ts": "update", "update-guarded-ops.test.ts": "update", "update-effect-authority.test.ts": "update", "update-capability-policy.test.ts": "update", "codex-shim-overlay.test.ts": "codex-integration", "codex-shim-migration.test.ts": "codex-integration", diff --git a/structure/INDEX.md b/structure/INDEX.md index 64a46707c70..47339cbdf98 100644 --- a/structure/INDEX.md +++ b/structure/INDEX.md @@ -130,6 +130,7 @@ The dashboard, the management API, and third-party client config ownership. | [`clients/claude-desktop-config-reads.md`](clients/claude-desktop-config-reads.md) | Verified regular-file reads of Claude Desktop config-library metadata, profiles and snapshots. | | [`clients/hermes.md`](clients/hermes.md) | Hermes profile discovery, per-profile selection, ownership stores, and boundaries. | | [`clients/qoder.md`](clients/qoder.md) | Qoder settings.json provider and custom-model ownership, export and loopback placeholder rules. | +| [`clients/client-support-matrix.md`](clients/client-support-matrix.md) | Generated client support page: registry sources, curated protocol and test claims, drift checks. | | [`clients/t3code.md`](clients/t3code.md) | T3 Code settings.json Claude provider-instance ownership, proxy environment, custom models and loopback marker rules. | | [`companion.md`](companion.md) | Shared timeline filtering, usage/quotas, native and web tray title, and WidgetKit display contracts. | | [`codex-account-controls.md`](codex-account-controls.md) | Account selection order, custom usage thresholds, and stable account-card editing. | @@ -186,12 +187,12 @@ A source area can be described by more than one doc, because these docs are orga | `desktop/src-tauri/src/tray_login.rs` | [`desktop-start-at-login.md`](desktop-start-at-login.md) | | `desktop/ui/cli.html` | [`desktop-terminal-command.md`](desktop-terminal-command.md) | | `desktop/ui/cli.js` | [`desktop-terminal-command.md`](desktop-terminal-command.md) | -| `docs-site/` | [`ops/docs-and-release.md`](ops/docs-and-release.md) | +| `docs-site/` | [`clients/client-support-matrix.md`](clients/client-support-matrix.md)
[`ops/docs-and-release.md`](ops/docs-and-release.md) | | `gui/` | [`overview.md`](overview.md)
[`transports/native-reasoning-retention.md`](transports/native-reasoning-retention.md)
[`gui-and-management-api.md`](gui-and-management-api.md)
[`provider-completion-diagnostic.md`](provider-completion-diagnostic.md)
[`dashboard-and-usage.md`](dashboard-and-usage.md)
[`fallback-policy.md`](fallback-policy.md)
[`design-methodology.md`](design-methodology.md)
[`companion.md`](companion.md) | | `gui/src/account-pause-display.ts` | [`account-auto-pause.md`](account-auto-pause.md) | | `gui/src/components/ModelPriceDialog.tsx` | [`pricing.md`](pricing.md) | | `gui/src/hooks/useProviderAccountPools.ts` | [`account-auto-pause.md`](account-auto-pause.md) | -| `scripts/` | [`overview.md`](overview.md)
[`ops/docs-and-release.md`](ops/docs-and-release.md) | +| `scripts/` | [`overview.md`](overview.md)
[`clients/client-support-matrix.md`](clients/client-support-matrix.md)
[`ops/docs-and-release.md`](ops/docs-and-release.md) | | `scripts/generate-ocx-skill-surface.ts` | [`cli-management.md`](cli-management.md) | | `scripts/test-run-lock-gate.ts` | [`ops/test-sandbox-cleanup.md`](ops/test-sandbox-cleanup.md) | | `scripts/test-run-lock.ts` | [`ops/test-sandbox-cleanup.md`](ops/test-sandbox-cleanup.md) | @@ -239,7 +240,7 @@ A source area can be described by more than one doc, because these docs are orga | `src/client/link-home.ts` | [`remote-link.md`](remote-link.md) | | `src/client/link-parity-reporter.ts` | [`remote-link.md`](remote-link.md) | | `src/client/runtime-restart.ts` | [`remote-link.md`](remote-link.md) | -| `src/clients/` | [`clients/integrations.md`](clients/integrations.md)
[`clients/qoder.md`](clients/qoder.md)
[`clients/t3code.md`](clients/t3code.md) | +| `src/clients/` | [`clients/integrations.md`](clients/integrations.md)
[`clients/qoder.md`](clients/qoder.md)
[`clients/client-support-matrix.md`](clients/client-support-matrix.md)
[`clients/t3code.md`](clients/t3code.md) | | `src/clients/config-export.ts` | [`transports/timeouts.md`](transports/timeouts.md)
[`clients/export-model-names.md`](clients/export-model-names.md) | | `src/clients/config-export/` | [`clients/export-model-names.md`](clients/export-model-names.md) | | `src/codex/` | [`runtime.md`](runtime.md)
[`config.md`](config.md)
[`codex-home.md`](codex-home.md)
[`codex-prompts.md`](codex-prompts.md)
[`catalog.md`](catalog.md)
[`subagents.md`](subagents.md)
[`subagent-capacity.md`](subagent-capacity.md)
[`transports/responses-failover.md`](transports/responses-failover.md)
[`providers/openai-tiers.md`](providers/openai-tiers.md)
[`providers/openai-accounts.md`](providers/openai-accounts.md)
[`gui-and-management-api.md`](gui-and-management-api.md)
[`dashboard-and-usage.md`](dashboard-and-usage.md)
[`fallback-policy.md`](fallback-policy.md)
[`clients/codex-desktop.md`](clients/codex-desktop.md)
[`clients/chatgpt-desktop.md`](clients/chatgpt-desktop.md)
[`ops/docs-and-release.md`](ops/docs-and-release.md) | @@ -300,7 +301,7 @@ A source area can be described by more than one doc, because these docs are orga | `src/grok/` | [`runtime.md`](runtime.md) | | `src/images/` | [`runtime.md`](runtime.md)
[`transports/inventory.md`](transports/inventory.md)
[`providers/anthropic-progress.md`](providers/anthropic-progress.md) | | `src/index.ts` | [`runtime.md`](runtime.md) | -| `src/integrations/` | [`clients/export-model-names.md`](clients/export-model-names.md)
[`clients/integrations.md`](clients/integrations.md)
[`clients/hermes.md`](clients/hermes.md)
[`clients/qoder.md`](clients/qoder.md)
[`clients/t3code.md`](clients/t3code.md) | +| `src/integrations/` | [`clients/export-model-names.md`](clients/export-model-names.md)
[`clients/integrations.md`](clients/integrations.md)
[`clients/hermes.md`](clients/hermes.md)
[`clients/qoder.md`](clients/qoder.md)
[`clients/client-support-matrix.md`](clients/client-support-matrix.md)
[`clients/t3code.md`](clients/t3code.md) | | `src/lab/` | [`runtime.md`](runtime.md)
[`adapters/compatibility-lab.md`](adapters/compatibility-lab.md) | | `src/lib/` | [`overview.md`](overview.md)
[`runtime.md`](runtime.md)
[`transports/byte-accounting.md`](transports/byte-accounting.md)
[`transports/responses-wire-shapes.md`](transports/responses-wire-shapes.md)
[`transports/responses-failover.md`](transports/responses-failover.md)
[`transports/responses-spend.md`](transports/responses-spend.md)
[`transports/inventory.md`](transports/inventory.md)
[`gui-and-management-api.md`](gui-and-management-api.md)
[`dashboard-and-usage.md`](dashboard-and-usage.md)
[`clients/integrations.md`](clients/integrations.md)
[`ops/service-and-sidecars.md`](ops/service-and-sidecars.md)
[`ops/docs-and-release.md`](ops/docs-and-release.md) | | `src/lib/abort.ts` | [`transports/timeouts.md`](transports/timeouts.md) | diff --git a/structure/clients/client-support-matrix.md b/structure/clients/client-support-matrix.md new file mode 100644 index 00000000000..2b7d4764420 --- /dev/null +++ b/structure/clients/client-support-matrix.md @@ -0,0 +1,42 @@ +# Client Support Matrix + +The public client support page (`docs-site/src/content/docs/reference/client-support.md` and its +copy under each docs locale) is generated, never edited by hand. It is the one place users read which coding tools +opencodex connects to natively, which it exports or manages a config file for, and which inbound +protocol each speaks. + +## Sources + +`scripts/generate-client-support-matrix.ts` builds the rows from three registries: + +- the export roster, `EXPORT_CLIENT_IDS` and `EXPORT_CLIENTS` in `src/clients/`; +- the managed file integrations, `INTEGRATION_CLIENTS` in `src/integrations/registry.ts`, which must + stay a subset of the export roster (an integration-only id fails generation instead of vanishing); +- the native owners, parsed from the one-line `NativeIntegrationClientId` union in + `src/server/management/native-integration-routes.ts`. The scripts are outside the typecheck, so the + parse refuses any other declaration shape and compares the members with the metadata both ways. + +## Curated facts and how they stay true + +`scripts/client-support-matrix/metadata.ts` carries what the registries do not: labels, protocol +fields, reviewed claims and linked tests. Generation fails when one no longer holds: + +- An export client's protocol is read from its own config, built through `buildClientConfigText` + with a no-secret loopback fixture. Every named field must be present and every value must map to a + known protocol. OMP's Responses override is checked per model: the native OpenAI fixture model + carries it and no other model does. +- Rows whose client picks the endpoint (ZCode, Raycast) and native rows are curated. Each keeps a + source anchor; the client-selected rows also pin the exporter file's sha256, so any edit asks for a + fresh review. +- `test-covered` links a reviewed test case. The generator checks that the case exists once, names + the client, and sits in a file without skip/todo/only/conditional modifiers. It does not prove what + the case asserts, and the page says so. + +Locale chrome lives in `scripts/client-support-matrix/locales.ts`; every locale defines every string. + +## Changing a client + +Adding or removing a client means adding or removing its metadata and running `bun run docs:clients`. +`bun run docs:clients:check` and `tests/ci-workflows/client-support-matrix.test.ts` fail on a stale +page or a broken claim. Two branches that each pass alone can still fail together, so the check is +re-run on the integration base before merge. diff --git a/structure/manifest.json b/structure/manifest.json index e9f91347b63..4833242bf0b 100644 --- a/structure/manifest.json +++ b/structure/manifest.json @@ -978,6 +978,18 @@ "src/integrations/" ] }, + { + "path": "clients/client-support-matrix.md", + "tier": 5, + "title": "Client Support Matrix", + "scope": "Generated client support page: registry sources, curated protocol and test claims, drift checks.", + "documents": [ + "scripts/", + "docs-site/", + "src/clients/", + "src/integrations/" + ] + }, { "path": "clients/t3code.md", "tier": 5, diff --git a/tests/ci-workflows/client-support-matrix.test.ts b/tests/ci-workflows/client-support-matrix.test.ts new file mode 100644 index 00000000000..e0ee95ea9d3 --- /dev/null +++ b/tests/ci-workflows/client-support-matrix.test.ts @@ -0,0 +1,129 @@ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { EXPORT_CLIENT_IDS } from "../../src/clients/client-ids"; +import { INTEGRATION_CLIENTS } from "../../src/integrations/registry"; +import { + MatrixError, + NATIVE_ROSTER_FILE, + collectRows, + defaultInputs, + parseNativeRoster, + renderAll, + type MatrixInputs, +} from "../../scripts/generate-client-support-matrix"; +import { EXPORT_CLIENT_METADATA, NATIVE_CLIENT_METADATA } from "../../scripts/client-support-matrix/metadata"; +import { repoPath } from "../helpers/repo-root"; + +const BASE = defaultInputs(); + +/** Collect with one input replaced and return the reported problems ([] when generation succeeds). */ +function problemsWith(change: Partial): string[] { + try { + collectRows({ ...BASE, ...change }); + return []; + } catch (error) { + if (!(error instanceof MatrixError)) throw error; + return [...error.problems]; + } +} + +function withFile(path: string, rewrite: (text: string) => string): MatrixInputs["readRepoFile"] { + return file => { + const text = BASE.readRepoFile(file); + return file === path && text !== undefined ? rewrite(text) : text; + }; +} + +function withDocument(id: string, rewrite: (document: any) => unknown): MatrixInputs["buildDocument"] { + return client => { + const document = BASE.buildDocument(client); + return client === id ? rewrite(structuredClone(document)) : document; + }; +} + +describe("client support matrix", () => { + test("every committed locale page matches the generator", () => { + for (const [path, expected] of Object.entries(renderAll())) { + expect(readFileSync(repoPath(path), "utf8"), `${path} is stale; run bun run docs:clients`).toBe(expected); + } + }); + + test("rows cover the export roster, the managed integrations and the native union", () => { + const rows = collectRows(); + const nativeIds = parseNativeRoster(readFileSync(repoPath(NATIVE_ROSTER_FILE), "utf8")); + expect(rows.map(row => row.id).sort()).toEqual([...EXPORT_CLIENT_IDS, ...nativeIds].sort()); + expect(rows.filter(row => row.managed).map(row => row.id).sort()).toEqual(Object.keys(INTEGRATION_CLIENTS).sort()); + expect(rows.filter(row => row.native).map(row => row.id).sort()).toEqual([...nativeIds].sort()); + }); + + test("a new export client without metadata fails with its id", () => { + const problems = problemsWith({ + exportIds: [...EXPORT_CLIENT_IDS, "newtool"], + exportSpecs: { ...BASE.exportSpecs, newtool: { format: "json", loopbackOnly: true } }, + }); + expect(problems).toContain("newtool: export client without metadata; add it to EXPORT_CLIENT_METADATA"); + }); + + test("a managed integration outside the export roster cannot drop out of the page", () => { + const problems = problemsWith({ integrationSpecs: { ...BASE.integrationSpecs, orphan: {} } }); + expect(problems.some(problem => problem.startsWith("orphan: managed file integration without an export registry entry"))).toBe(true); + }); + + test("native union additions, removals and reshapes all fail", () => { + const declaration = /^export type NativeIntegrationClientId = .+;$/m; + const added = problemsWith({ readRepoFile: withFile(NATIVE_ROSTER_FILE, text => text.replace(declaration, m => m.replace(";", ' | "newnative";'))) }); + expect(added).toContain("newnative: native integration owner without metadata; add it to NATIVE_CLIENT_METADATA"); + const removed = problemsWith({ readRepoFile: withFile(NATIVE_ROSTER_FILE, text => text.replace(declaration, m => m.replace(' | "grok"', ""))) }); + expect(removed).toContain("grok: native metadata for an owner that is no longer in NativeIntegrationClientId; remove it"); + const reshaped = problemsWith({ readRepoFile: withFile(NATIVE_ROSTER_FILE, text => text.replace(declaration, "export type NativeIntegrationClientId = (typeof NATIVE_IDS)[number];")) }); + expect(reshaped.some(problem => problem.includes("not a pure string-literal union"))).toBe(true); + }); + + test("protocol fields must exist and carry a known value", () => { + const changed = problemsWith({ buildDocument: withDocument("mcode", doc => { doc.custom_provider.opencodex.api = "openai-responses-next"; return doc; }) }); + expect(changed).toContain('mcode: custom_provider.opencodex.api = "openai-responses-next" is not a protocol value this matrix knows'); + const moved = problemsWith({ buildDocument: withDocument("cline", doc => { delete doc.catalog; return doc; }) }); + expect(moved).toContain("cline: protocol field catalog.providers.opencodex.provider.protocol is missing from the generated config"); + }); + + test("the OMP Responses override is checked model by model", () => { + const swapped = problemsWith({ + buildDocument: withDocument("omp", doc => { + for (const model of doc.providers.opencodex.models) { + if (model.api) delete model.api; + else model.api = "openai-responses"; + } + return doc; + }), + }); + expect(swapped).toContain("omp: the native OpenAI model no longer carries api = openai-responses"); + expect(swapped.some(problem => problem.startsWith("omp: model anthropic/claude-opus-4-8 carries api"))).toBe(true); + }); + + test("curated claims fail when the reviewed source changes", () => { + const raycast = EXPORT_CLIENT_METADATA.raycast!.clientSelected!; + const edited = problemsWith({ readRepoFile: withFile(raycast.file, text => text + "\n// edited\n") }); + expect(edited.some(problem => problem.startsWith("raycast: src/clients/config-export/raycast.ts changed since its protocol was reviewed"))).toBe(true); + const grok = NATIVE_CLIENT_METADATA.grok!.anchors[0]!; + const gone = problemsWith({ readRepoFile: withFile(grok.file, text => text.replace(grok.text, "")) }); + expect(gone.some(problem => problem.startsWith("grok: reviewed code"))).toBe(true); + }); + + test("a test reference must be a single enabled case that names the client", () => { + const ref = EXPORT_CLIENT_METADATA.prime!.test!; + const quoted = JSON.stringify(ref.title); + const renamed = problemsWith({ readRepoFile: withFile(ref.file, text => text.replace(quoted, JSON.stringify(ref.title + " (renamed)"))) }); + expect(renamed).toContain(`prime: ${ref.file} has no single test titled ${quoted}`); + const skipped = problemsWith({ readRepoFile: withFile(ref.file, text => `describe.skip("hidden", () => {});\n${text}`) }); + expect(skipped).toContain(`prime: ${ref.file} uses skip/todo/only/conditional modifiers; link a test from a file without them`); + const incidental = `// prime is only mentioned in this comment\ntest(${quoted}, () => {\n expect(1).toBe(1);\n});\n`; + const comment = problemsWith({ readRepoFile: file => (file === ref.file ? incidental : BASE.readRepoFile(file)) }); + expect(comment).toContain(`prime: test ${quoted} does not mention prime or Prime`); + }); + + test("the inbound endpoints listed on the page are admitted routes", () => { + const routes = new Set(BASE.inboundRoutes); + routes.delete("/v1/messages"); + expect(problemsWith({ inboundRoutes: routes })).toContain("inbound route /v1/messages is no longer admitted; update ENDPOINTS"); + }); +}); diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json index fbc1ca048ec..c6fca6464eb 100644 --- a/tests/fixtures/test-layout-expected.json +++ b/tests/fixtures/test-layout-expected.json @@ -174,6 +174,7 @@ "skill-ocx-workflows.test.ts": "ci-workflows", "cli-capability-data.test.ts": "cli", "skill-ocx-generated.test.ts": "ci-workflows", + "client-support-matrix.test.ts": "ci-workflows", "cli-update-restart.test.ts": "cli", "cli-update-restart-child.test.ts": "cli", "cli-update-restart-transport.test.ts": "cli", From 7203736615c1f9053322deb427cc6887d63a403b Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 05:20:55 +0900 Subject: [PATCH 2/8] docs: tighten client support matrix evidence checks Test references ignore comments, every wildcard protocol branch must carry the field, native anchors must sit on code lines (Codex now anchors on the emitted base URL), and the curated definition says which rows make an endpoint claim. --- .../docs/fr/reference/client-support.md | 2 +- .../docs/ja/reference/client-support.md | 2 +- .../docs/ko/reference/client-support.md | 2 +- .../content/docs/reference/client-support.md | 2 +- .../docs/ru/reference/client-support.md | 2 +- .../docs/tr/reference/client-support.md | 2 +- .../docs/zh-cn/reference/client-support.md | 2 +- .../docs/zh-tw/reference/client-support.md | 2 +- scripts/client-support-matrix/locales.ts | 17 +++++---- scripts/client-support-matrix/metadata.ts | 2 +- scripts/generate-client-support-matrix.ts | 37 +++++++++++++------ .../client-support-matrix.test.ts | 10 ++++- 12 files changed, 52 insertions(+), 30 deletions(-) diff --git a/docs-site/src/content/docs/fr/reference/client-support.md b/docs-site/src/content/docs/fr/reference/client-support.md index 03a0d7a1707..b7c21876ab4 100644 --- a/docs-site/src/content/docs/fr/reference/client-support.md +++ b/docs-site/src/content/docs/fr/reference/client-support.md @@ -10,7 +10,7 @@ opencodex est une passerelle locale unique pour vos outils de code IA et vos mod ## Niveaux de prise en charge -- **implemented**: le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Les protocoles marqués *revu* (clients natifs et clients qui choisissent eux-mêmes leur point de terminaison) proviennent d’une revue du code d’intégration, et le générateur vérifie que ce code est toujours en place. +- **implemented**: le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Un protocole marqué *revu* désigne un point de terminaison issu d’une revue plutôt que de la configuration elle-même (clients natifs, et clients qui déduisent un point de terminaison précis d’un réglage) ; le générateur vérifie que le code revu est toujours en place. Les lignes « Compatible OpenAI » n’affirment aucun point de terminaison. - **test-covered**: implemented, et un cas de test choisi en revue qui exerce ce client est lié. Le générateur vérifie seulement que le test existe toujours et qu’il est actif ; lisez-le pour savoir ce qu’il vérifie. ## Clients diff --git a/docs-site/src/content/docs/ja/reference/client-support.md b/docs-site/src/content/docs/ja/reference/client-support.md index 73261e92b31..d87c84ade3c 100644 --- a/docs-site/src/content/docs/ja/reference/client-support.md +++ b/docs-site/src/content/docs/ja/reference/client-support.md @@ -10,7 +10,7 @@ opencodex は、AI コーディングツールとモデルをつなぐ 1 つの ## サポートレベル -- **implemented**: レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*と付いたプロトコル(ネイティブクライアントと、エンドポイントを自分で選ぶクライアント)は連携コードのレビューに基づくもので、レビューしたコードが残っているかを生成時に確認します。 +- **implemented**: レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*は、エンドポイントが設定そのものではなくレビューに基づくことを示します(ネイティブクライアントと、設定を特定のエンドポイントに変換するクライアント)。レビューしたコードが残っているかを生成時に確認します。「OpenAI 互換」の行は特定のエンドポイントを主張しません。 - **test-covered**: implemented であり、レビューで選んだこのクライアントのテストケースへのリンクがあります。生成時に確認するのはテストが存在し無効化されていないことだけです。何を検証しているかはテストを読んでください。 ## クライアント diff --git a/docs-site/src/content/docs/ko/reference/client-support.md b/docs-site/src/content/docs/ko/reference/client-support.md index a96f6b2e2e4..0f4c1462941 100644 --- a/docs-site/src/content/docs/ko/reference/client-support.md +++ b/docs-site/src/content/docs/ko/reference/client-support.md @@ -10,7 +10,7 @@ opencodex는 AI 코딩 도구와 모델을 잇는 하나의 로컬 게이트웨 ## 지원 수준 -- **implemented**: 레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시가 있는 프로토콜(네이티브 클라이언트, 엔드포인트를 스스로 고르는 클라이언트)은 연동 코드를 리뷰한 결과이며, 생성기는 리뷰한 코드가 그대로 있는지 확인합니다. +- **implemented**: 레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시는 설정 자체가 아니라 리뷰에서 나온 특정 엔드포인트를 뜻합니다(네이티브 클라이언트, 설정값을 특정 엔드포인트로 바꾸는 클라이언트). 생성기는 리뷰한 코드가 그대로 있는지 확인합니다. 'OpenAI 호환' 행은 엔드포인트를 단정하지 않습니다. - **test-covered**: implemented이면서, 리뷰로 고른 이 클라이언트의 테스트 케이스가 링크되어 있습니다. 생성기는 테스트가 존재하고 비활성화되지 않았는지만 확인하므로, 무엇을 검증하는지는 테스트를 직접 읽어 보세요. ## 클라이언트 diff --git a/docs-site/src/content/docs/reference/client-support.md b/docs-site/src/content/docs/reference/client-support.md index 7b228756f7a..cfebf129b24 100644 --- a/docs-site/src/content/docs/reference/client-support.md +++ b/docs-site/src/content/docs/reference/client-support.md @@ -10,7 +10,7 @@ opencodex is one local gateway for your AI coding tools and models. Some tools i ## Support levels -- **implemented**: the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. Protocols marked *curated* (native clients, and clients that pick their own endpoint) come from a review of the integration code, and the generator checks that the reviewed code is still in place. +- **implemented**: the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. A protocol marked *curated* names an endpoint that comes from a review instead of the config itself (native clients, and clients that turn a setting into a specific endpoint); the generator checks that the reviewed code is still in place. "OpenAI-compatible" rows make no endpoint claim. - **test-covered**: implemented, and a test case chosen in review that exercises this client is linked. The generator checks only that the test still exists and is enabled; read it to see what it asserts. ## Clients diff --git a/docs-site/src/content/docs/ru/reference/client-support.md b/docs-site/src/content/docs/ru/reference/client-support.md index cbbae2244e6..6724ebe7016 100644 --- a/docs-site/src/content/docs/ru/reference/client-support.md +++ b/docs-site/src/content/docs/ru/reference/client-support.md @@ -10,7 +10,7 @@ opencodex — единый локальный шлюз для ваших ИИ-и ## Уровни поддержки -- **implemented**: клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Протоколы с пометкой *проверено* (нативные клиенты и клиенты, сами выбирающие эндпоинт) взяты из ревью кода интеграции, а генератор проверяет, что этот код на месте. +- **implemented**: клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Пометка *проверено* означает, что эндпоинт взят из ревью, а не из самой конфигурации (нативные клиенты и клиенты, которые превращают настройку в конкретный эндпоинт); генератор проверяет, что проверенный код на месте. Строки «Совместим с OpenAI» не утверждают конкретный эндпоинт. - **test-covered**: implemented, и есть ссылка на выбранный при ревью тест, который задействует этого клиента. Генератор проверяет только, что тест существует и не отключён; что именно он проверяет, смотрите в самом тесте. ## Клиенты diff --git a/docs-site/src/content/docs/tr/reference/client-support.md b/docs-site/src/content/docs/tr/reference/client-support.md index 156a58ec5fd..cb1a8db2887 100644 --- a/docs-site/src/content/docs/tr/reference/client-support.md +++ b/docs-site/src/content/docs/tr/reference/client-support.md @@ -10,7 +10,7 @@ opencodex, yapay zekâ kodlama araçlarınız ve modelleriniz için tek bir yere ## Destek düzeyleri -- **implemented**: istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işaretli protokoller (yerel istemciler ve uç noktasını kendisi seçen istemciler) entegrasyon kodunun incelemesinden gelir; üretici, incelenen kodun hâlâ yerinde olduğunu denetler. +- **implemented**: istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işareti, uç noktanın yapılandırmanın kendisinden değil bir incelemeden geldiğini belirtir (yerel istemciler ve bir ayarı belirli bir uç noktaya çeviren istemciler); üretici, incelenen kodun hâlâ yerinde olduğunu denetler. "OpenAI uyumlu" satırlar belirli bir uç nokta iddia etmez. - **test-covered**: implemented olmanın yanında, incelemede seçilmiş ve bu istemciyi çalıştıran bir test durumuna bağlantı verilir. Üretici yalnızca testin hâlâ var ve etkin olduğunu denetler; neyi doğruladığını görmek için testi okuyun. ## İstemciler diff --git a/docs-site/src/content/docs/zh-cn/reference/client-support.md b/docs-site/src/content/docs/zh-cn/reference/client-support.md index 036915a30c2..2391c33ee69 100644 --- a/docs-site/src/content/docs/zh-cn/reference/client-support.md +++ b/docs-site/src/content/docs/zh-cn/reference/client-support.md @@ -10,7 +10,7 @@ opencodex 是一个本地网关,连接你的 AI 编程工具与模型。部分 ## 支持级别 -- **implemented**: 客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议(原生客户端以及自行选择端点的客户端)来自对集成代码的审阅,生成器会检查被审阅的代码是否仍然存在。 +- **implemented**: 客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议表示其端点来自审阅而非配置本身(原生客户端,以及会把某个设置转换为特定端点的客户端);生成器会检查被审阅的代码是否仍然存在。“OpenAI 兼容”的行不声明具体端点。 - **test-covered**: 满足 implemented,并链接了一个经评审选定、覆盖该客户端的测试用例。生成器只检查该测试仍然存在且未被禁用;它具体断言了什么,请阅读测试本身。 ## 客户端 diff --git a/docs-site/src/content/docs/zh-tw/reference/client-support.md b/docs-site/src/content/docs/zh-tw/reference/client-support.md index 2f4ad0f3c7b..8ea833b89ac 100644 --- a/docs-site/src/content/docs/zh-tw/reference/client-support.md +++ b/docs-site/src/content/docs/zh-tw/reference/client-support.md @@ -10,7 +10,7 @@ opencodex 是一個本機閘道,連接你的 AI 程式開發工具與模型。 ## 支援等級 -- **implemented**: 用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定(原生用戶端以及自行選擇端點的用戶端)來自對整合程式碼的審查,產生器會檢查被審查的程式碼是否仍然存在。 +- **implemented**: 用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定表示其端點來自審查而非設定本身(原生用戶端,以及會把某個設定轉換為特定端點的用戶端);產生器會檢查被審查的程式碼是否仍然存在。「OpenAI 相容」的列不聲明具體端點。 - **test-covered**: 符合 implemented,並連結了一個經審查選定、涵蓋該用戶端的測試案例。產生器只檢查該測試仍然存在且未被停用;它實際驗證什麼,請閱讀測試本身。 ## 用戶端 diff --git a/scripts/client-support-matrix/locales.ts b/scripts/client-support-matrix/locales.ts index ba35ffc9401..962f961555b 100644 --- a/scripts/client-support-matrix/locales.ts +++ b/scripts/client-support-matrix/locales.ts @@ -41,7 +41,7 @@ export const LOCALES = { description: "Which coding tools opencodex connects to, how each one is wired, and which protocol it speaks.", intro: "opencodex is one local gateway for your AI coding tools and models. Some tools it configures natively; for the others it exports or manages a config file that points the tool at the local proxy. This page is generated from the code registries, so it lists exactly what the current release ships.", levelsHeading: "Support levels", - implementedDefinition: "the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. Protocols marked *curated* (native clients, and clients that pick their own endpoint) come from a review of the integration code, and the generator checks that the reviewed code is still in place.", + implementedDefinition: "the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. A protocol marked *curated* names an endpoint that comes from a review instead of the config itself (native clients, and clients that turn a setting into a specific endpoint); the generator checks that the reviewed code is still in place. \"OpenAI-compatible\" rows make no endpoint claim.", testCoveredDefinition: "implemented, and a test case chosen in review that exercises this client is linked. The generator checks only that the test still exists and is enabled; read it to see what it asserts.", clientsHeading: "Clients", columns: { client: "Client", native: "Native", configExport: "Config export", managed: "Managed file integration", protocol: "Inbound protocol", level: "Support level", notes: "Notes" }, @@ -71,7 +71,7 @@ export const LOCALES = { description: "Les outils de code auxquels opencodex se connecte, la façon dont chacun est relié et le protocole qu’il utilise.", intro: "opencodex est une passerelle locale unique pour vos outils de code IA et vos modèles. Il configure certains outils nativement ; pour les autres, il exporte ou gère un fichier de configuration qui dirige l’outil vers le proxy local. Cette page est générée à partir des registres du code et reflète donc exactement ce que fournit la version actuelle.", levelsHeading: "Niveaux de prise en charge", - implementedDefinition: "le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Les protocoles marqués *revu* (clients natifs et clients qui choisissent eux-mêmes leur point de terminaison) proviennent d’une revue du code d’intégration, et le générateur vérifie que ce code est toujours en place.", + implementedDefinition: "le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Un protocole marqué *revu* désigne un point de terminaison issu d’une revue plutôt que de la configuration elle-même (clients natifs, et clients qui déduisent un point de terminaison précis d’un réglage) ; le générateur vérifie que le code revu est toujours en place. Les lignes « Compatible OpenAI » n’affirment aucun point de terminaison.", testCoveredDefinition: "implemented, et un cas de test choisi en revue qui exerce ce client est lié. Le générateur vérifie seulement que le test existe toujours et qu’il est actif ; lisez-le pour savoir ce qu’il vérifie.", clientsHeading: "Clients", columns: { client: "Client", native: "Natif", configExport: "Export de configuration", managed: "Intégration de fichier gérée", protocol: "Protocole entrant", level: "Niveau", notes: "Remarques" }, @@ -101,7 +101,7 @@ export const LOCALES = { description: "opencodex가 연결하는 코딩 도구, 각 도구의 연결 방식, 사용하는 프로토콜.", intro: "opencodex는 AI 코딩 도구와 모델을 잇는 하나의 로컬 게이트웨이입니다. 일부 도구는 직접(네이티브로) 설정하고, 나머지는 로컬 프록시를 가리키는 설정 파일을 내보내거나 관리합니다. 이 페이지는 코드 레지스트리에서 생성되므로 현재 릴리스가 제공하는 내용과 정확히 일치합니다.", levelsHeading: "지원 수준", - implementedDefinition: "레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시가 있는 프로토콜(네이티브 클라이언트, 엔드포인트를 스스로 고르는 클라이언트)은 연동 코드를 리뷰한 결과이며, 생성기는 리뷰한 코드가 그대로 있는지 확인합니다.", + implementedDefinition: "레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시는 설정 자체가 아니라 리뷰에서 나온 특정 엔드포인트를 뜻합니다(네이티브 클라이언트, 설정값을 특정 엔드포인트로 바꾸는 클라이언트). 생성기는 리뷰한 코드가 그대로 있는지 확인합니다. 'OpenAI 호환' 행은 엔드포인트를 단정하지 않습니다.", testCoveredDefinition: "implemented이면서, 리뷰로 고른 이 클라이언트의 테스트 케이스가 링크되어 있습니다. 생성기는 테스트가 존재하고 비활성화되지 않았는지만 확인하므로, 무엇을 검증하는지는 테스트를 직접 읽어 보세요.", clientsHeading: "클라이언트", columns: { client: "클라이언트", native: "네이티브", configExport: "설정 내보내기", managed: "관리형 파일 연동", protocol: "수신 프로토콜", level: "지원 수준", notes: "참고" }, @@ -131,7 +131,7 @@ export const LOCALES = { description: "opencodex 能连接哪些编程工具、每个工具如何接入,以及它使用的协议。", intro: "opencodex 是一个本地网关,连接你的 AI 编程工具与模型。部分工具由它原生配置;其余工具则通过导出或托管一个指向本地代理的配置文件来接入。本页由代码中的注册表生成,因此与当前版本实际提供的内容完全一致。", levelsHeading: "支持级别", - implementedDefinition: "客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议(原生客户端以及自行选择端点的客户端)来自对集成代码的审阅,生成器会检查被审阅的代码是否仍然存在。", + implementedDefinition: "客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议表示其端点来自审阅而非配置本身(原生客户端,以及会把某个设置转换为特定端点的客户端);生成器会检查被审阅的代码是否仍然存在。“OpenAI 兼容”的行不声明具体端点。", testCoveredDefinition: "满足 implemented,并链接了一个经评审选定、覆盖该客户端的测试用例。生成器只检查该测试仍然存在且未被禁用;它具体断言了什么,请阅读测试本身。", clientsHeading: "客户端", columns: { client: "客户端", native: "原生", configExport: "配置导出", managed: "托管文件集成", protocol: "入站协议", level: "支持级别", notes: "说明" }, @@ -161,7 +161,7 @@ export const LOCALES = { description: "opencodex 能連接哪些程式開發工具、每個工具如何接入,以及它使用的協定。", intro: "opencodex 是一個本機閘道,連接你的 AI 程式開發工具與模型。部分工具由它原生設定;其餘工具則透過匯出或代管一個指向本機代理的設定檔來接入。本頁由程式碼中的登錄表產生,因此與目前版本實際提供的內容完全一致。", levelsHeading: "支援等級", - implementedDefinition: "用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定(原生用戶端以及自行選擇端點的用戶端)來自對整合程式碼的審查,產生器會檢查被審查的程式碼是否仍然存在。", + implementedDefinition: "用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定表示其端點來自審查而非設定本身(原生用戶端,以及會把某個設定轉換為特定端點的用戶端);產生器會檢查被審查的程式碼是否仍然存在。「OpenAI 相容」的列不聲明具體端點。", testCoveredDefinition: "符合 implemented,並連結了一個經審查選定、涵蓋該用戶端的測試案例。產生器只檢查該測試仍然存在且未被停用;它實際驗證什麼,請閱讀測試本身。", clientsHeading: "用戶端", columns: { client: "用戶端", native: "原生", configExport: "設定匯出", managed: "代管檔案整合", protocol: "入站協定", level: "支援等級", notes: "說明" }, @@ -191,7 +191,7 @@ export const LOCALES = { description: "К каким инструментам для кода подключается opencodex, как подключён каждый из них и какой протокол он использует.", intro: "opencodex — единый локальный шлюз для ваших ИИ-инструментов для кода и моделей. Часть инструментов он настраивает нативно; для остальных он экспортирует или ведёт файл конфигурации, который направляет инструмент на локальный прокси. Страница сгенерирована из реестров в коде и поэтому в точности соответствует текущему релизу.", levelsHeading: "Уровни поддержки", - implementedDefinition: "клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Протоколы с пометкой *проверено* (нативные клиенты и клиенты, сами выбирающие эндпоинт) взяты из ревью кода интеграции, а генератор проверяет, что этот код на месте.", + implementedDefinition: "клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Пометка *проверено* означает, что эндпоинт взят из ревью, а не из самой конфигурации (нативные клиенты и клиенты, которые превращают настройку в конкретный эндпоинт); генератор проверяет, что проверенный код на месте. Строки «Совместим с OpenAI» не утверждают конкретный эндпоинт.", testCoveredDefinition: "implemented, и есть ссылка на выбранный при ревью тест, который задействует этого клиента. Генератор проверяет только, что тест существует и не отключён; что именно он проверяет, смотрите в самом тесте.", clientsHeading: "Клиенты", columns: { client: "Клиент", native: "Нативно", configExport: "Экспорт конфигурации", managed: "Управляемая интеграция файла", protocol: "Входящий протокол", level: "Уровень", notes: "Примечания" }, @@ -221,7 +221,7 @@ export const LOCALES = { description: "opencodex が接続するコーディングツール、それぞれの接続方法、使用するプロトコル。", intro: "opencodex は、AI コーディングツールとモデルをつなぐ 1 つのローカルゲートウェイです。一部のツールはネイティブに設定し、それ以外はローカルプロキシを指す設定ファイルをエクスポートまたは管理します。このページはコードのレジストリから生成されるため、現在のリリースの内容と正確に一致します。", levelsHeading: "サポートレベル", - implementedDefinition: "レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*と付いたプロトコル(ネイティブクライアントと、エンドポイントを自分で選ぶクライアント)は連携コードのレビューに基づくもので、レビューしたコードが残っているかを生成時に確認します。", + implementedDefinition: "レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*は、エンドポイントが設定そのものではなくレビューに基づくことを示します(ネイティブクライアントと、設定を特定のエンドポイントに変換するクライアント)。レビューしたコードが残っているかを生成時に確認します。「OpenAI 互換」の行は特定のエンドポイントを主張しません。", testCoveredDefinition: "implemented であり、レビューで選んだこのクライアントのテストケースへのリンクがあります。生成時に確認するのはテストが存在し無効化されていないことだけです。何を検証しているかはテストを読んでください。", clientsHeading: "クライアント", columns: { client: "クライアント", native: "ネイティブ", configExport: "設定エクスポート", managed: "管理ファイル連携", protocol: "受信プロトコル", level: "サポートレベル", notes: "備考" }, @@ -251,7 +251,7 @@ export const LOCALES = { description: "opencodex'in bağlandığı kodlama araçları, her birinin nasıl bağlandığı ve hangi protokolü kullandığı.", intro: "opencodex, yapay zekâ kodlama araçlarınız ve modelleriniz için tek bir yerel ağ geçididir. Bazı araçları yerel olarak yapılandırır; diğerleri için aracı yerel proxy'ye yönlendiren bir yapılandırma dosyasını dışa aktarır veya yönetir. Bu sayfa koddaki kayıtlardan üretilir; bu yüzden mevcut sürümün sunduğu şeyle birebir aynıdır.", levelsHeading: "Destek düzeyleri", - implementedDefinition: "istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işaretli protokoller (yerel istemciler ve uç noktasını kendisi seçen istemciler) entegrasyon kodunun incelemesinden gelir; üretici, incelenen kodun hâlâ yerinde olduğunu denetler.", + implementedDefinition: "istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işareti, uç noktanın yapılandırmanın kendisinden değil bir incelemeden geldiğini belirtir (yerel istemciler ve bir ayarı belirli bir uç noktaya çeviren istemciler); üretici, incelenen kodun hâlâ yerinde olduğunu denetler. \"OpenAI uyumlu\" satırlar belirli bir uç nokta iddia etmez.", testCoveredDefinition: "implemented olmanın yanında, incelemede seçilmiş ve bu istemciyi çalıştıran bir test durumuna bağlantı verilir. Üretici yalnızca testin hâlâ var ve etkin olduğunu denetler; neyi doğruladığını görmek için testi okuyun.", clientsHeading: "İstemciler", columns: { client: "İstemci", native: "Yerel", configExport: "Yapılandırma dışa aktarımı", managed: "Yönetilen dosya entegrasyonu", protocol: "Gelen protokol", level: "Destek düzeyi", notes: "Notlar" }, @@ -281,3 +281,4 @@ export const LOCALES = { export type MatrixLocale = keyof typeof LOCALES; + diff --git a/scripts/client-support-matrix/metadata.ts b/scripts/client-support-matrix/metadata.ts index 85174957a50..37f1b2f0e37 100644 --- a/scripts/client-support-matrix/metadata.ts +++ b/scripts/client-support-matrix/metadata.ts @@ -220,7 +220,7 @@ export const NATIVE_CLIENT_METADATA: Readonly)); else if (Object.prototype.hasOwnProperty.call(value, segment)) next.push((value as Record)[segment]); + else missing++; } current = next; } - return current; + return { values: current, missing }; } function sha256(text: string): string { @@ -158,9 +166,9 @@ function exportProtocols(id: string, meta: ExportClientMetadata, inputs: MatrixI } const seen = new Set(); for (const field of meta.protocolFields) { - const values = valuesAt(document, field.path); - if (values.length === 0) { - problems.push(`${id}: protocol field ${field.path} is missing from the generated config`); + const { values, missing } = valuesAt(document, field.path); + if (values.length === 0 || missing > 0) { + problems.push(`${id}: protocol field ${field.path} is missing from ${values.length === 0 ? "the generated config" : `${missing} matched entr${missing === 1 ? "y" : "ies"}`}`); continue; } for (const value of values) { @@ -175,7 +183,7 @@ function exportProtocols(id: string, meta: ExportClientMetadata, inputs: MatrixI const cells: ProtocolCell[] = [...seen].map(protocol => ({ protocol })); const override = meta.nativeOpenaiOverride; if (override) { - const entries = valuesAt(document, override.modelsPath).flatMap(list => Array.isArray(list) ? list : Object.values(list as object)); + const entries = valuesAt(document, override.modelsPath).values.flatMap(list => Array.isArray(list) ? list : Object.values(list as object)); const entryId = (entry: unknown) => (entry as Record)?.[override.idKey]; const nativeEntry = entries.find(entry => entryId(entry) === FIXTURE_NATIVE_MODEL_ID); if (!nativeEntry) problems.push(`${id}: no model entry for the native OpenAI fixture model at ${override.modelsPath}`); @@ -208,10 +216,15 @@ function testCaseBody(source: string, title: string): string | undefined { return found; }); if (starts.length !== 1) return undefined; - const start = starts[0]!; - const rest = source.slice(start + 1); + const afterTitle = source.indexOf(quoted, starts[0]!) + quoted.length; + const rest = source.slice(afterTitle); const next = rest.search(/\n\s*(test|it|describe)(\.\w+)?\(/); - return next < 0 ? rest : rest.slice(0, next); + return withoutComments(next < 0 ? rest : rest.slice(0, next)); +} + +/** Drop line and block comments so a mention in a comment cannot stand in for code. */ +function withoutComments(code: string): string { + return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:\\])\/\/.*$/gm, "$1"); } const DISABLING_MODIFIER = /\b(?:test|it|describe)\.(?:skip|todo|only|skipIf|if|failing|todoIf)\b/; @@ -272,7 +285,7 @@ export function collectRows(inputs: MatrixInputs = defaultInputs()): MatrixRow[] const meta = inputs.nativeMetadata[id]!; for (const anchor of meta.anchors) { const source = inputs.readRepoFile(anchor.file); - if (source === undefined || !source.includes(anchor.text)) { + if (source === undefined || !withoutComments(source).includes(anchor.text)) { problems.push(`${id}: reviewed code ${JSON.stringify(anchor.text)} is no longer in ${anchor.file}; re-review the curated protocol`); } } diff --git a/tests/ci-workflows/client-support-matrix.test.ts b/tests/ci-workflows/client-support-matrix.test.ts index e0ee95ea9d3..615f1e4c308 100644 --- a/tests/ci-workflows/client-support-matrix.test.ts +++ b/tests/ci-workflows/client-support-matrix.test.ts @@ -86,6 +86,11 @@ describe("client support matrix", () => { expect(moved).toContain("cline: protocol field catalog.providers.opencodex.provider.protocol is missing from the generated config"); }); + test("a protocol field missing on one matched entry fails even when another entry has it", () => { + const partial = problemsWith({ buildDocument: withDocument("workbuddy", doc => { delete doc.models[0].url; return doc; }) }); + expect(partial).toContain("workbuddy: protocol field models.*.url is missing from 1 matched entry"); + }); + test("the OMP Responses override is checked model by model", () => { const swapped = problemsWith({ buildDocument: withDocument("omp", doc => { @@ -107,6 +112,9 @@ describe("client support matrix", () => { const grok = NATIVE_CLIENT_METADATA.grok!.anchors[0]!; const gone = problemsWith({ readRepoFile: withFile(grok.file, text => text.replace(grok.text, "")) }); expect(gone.some(problem => problem.startsWith("grok: reviewed code"))).toBe(true); + const codex = NATIVE_CLIENT_METADATA.codex!.anchors[0]!; + const commentedOut = problemsWith({ readRepoFile: withFile(codex.file, text => text.replace(codex.text, `"replaced"; // ${codex.text}`)) }); + expect(commentedOut.some(problem => problem.startsWith("codex: reviewed code"))).toBe(true); }); test("a test reference must be a single enabled case that names the client", () => { @@ -116,7 +124,7 @@ describe("client support matrix", () => { expect(renamed).toContain(`prime: ${ref.file} has no single test titled ${quoted}`); const skipped = problemsWith({ readRepoFile: withFile(ref.file, text => `describe.skip("hidden", () => {});\n${text}`) }); expect(skipped).toContain(`prime: ${ref.file} uses skip/todo/only/conditional modifiers; link a test from a file without them`); - const incidental = `// prime is only mentioned in this comment\ntest(${quoted}, () => {\n expect(1).toBe(1);\n});\n`; + const incidental = `test(${quoted}, () => {\n // exercises prime\n /* Prime */\n expect(1).toBe(1);\n});\n`; const comment = problemsWith({ readRepoFile: file => (file === ref.file ? incidental : BASE.readRepoFile(file)) }); expect(comment).toContain(`prime: test ${quoted} does not mention prime or Prime`); }); From 2c293edfbbd1ea9879a6676a0d62247b6656cdd1 Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 05:36:03 +0900 Subject: [PATCH 3/8] test: build the disabled-suite fixture at runtime --- tests/ci-workflows/client-support-matrix.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ci-workflows/client-support-matrix.test.ts b/tests/ci-workflows/client-support-matrix.test.ts index 615f1e4c308..ae420c1d60d 100644 --- a/tests/ci-workflows/client-support-matrix.test.ts +++ b/tests/ci-workflows/client-support-matrix.test.ts @@ -122,7 +122,9 @@ describe("client support matrix", () => { const quoted = JSON.stringify(ref.title); const renamed = problemsWith({ readRepoFile: withFile(ref.file, text => text.replace(quoted, JSON.stringify(ref.title + " (renamed)"))) }); expect(renamed).toContain(`prime: ${ref.file} has no single test titled ${quoted}`); - const skipped = problemsWith({ readRepoFile: withFile(ref.file, text => `describe.skip("hidden", () => {});\n${text}`) }); + // Assembled at runtime: the PR hygiene gate rejects a literal disabled-suite call in an added line. + const disabledSuite = ["describe", "skip"].join("."); + const skipped = problemsWith({ readRepoFile: withFile(ref.file, text => `${disabledSuite}("hidden", () => {});\n${text}`) }); expect(skipped).toContain(`prime: ${ref.file} uses skip/todo/only/conditional modifiers; link a test from a file without them`); const incidental = `test(${quoted}, () => {\n // exercises prime\n /* Prime */\n expect(1).toBe(1);\n});\n`; const comment = problemsWith({ readRepoFile: file => (file === ref.file ? incidental : BASE.readRepoFile(file)) }); From 5f832e7034f610b73127310fe24fe18e129526cf Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 05:47:28 +0900 Subject: [PATCH 4/8] test: check that every client support locale defines the root strings --- scripts/client-support-matrix/locales.ts | 3 ++- tests/ci-workflows/client-support-matrix.test.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/client-support-matrix/locales.ts b/scripts/client-support-matrix/locales.ts index 962f961555b..814c2ca9def 100644 --- a/scripts/client-support-matrix/locales.ts +++ b/scripts/client-support-matrix/locales.ts @@ -1,6 +1,7 @@ /** * Page chrome and note phrases for the generated client support page, one block per docs locale. - * English is canonical; every locale must define every key (enforced by the Strings type). + * English is canonical; every locale must define every key. The repository typecheck does not cover + * scripts/, so tests/ci-workflows/client-support-matrix.test.ts compares each locale's key set with root. * Product names, protocol names and the level keywords stay in English on every page. */ diff --git a/tests/ci-workflows/client-support-matrix.test.ts b/tests/ci-workflows/client-support-matrix.test.ts index ae420c1d60d..9acce499dff 100644 --- a/tests/ci-workflows/client-support-matrix.test.ts +++ b/tests/ci-workflows/client-support-matrix.test.ts @@ -12,6 +12,7 @@ import { type MatrixInputs, } from "../../scripts/generate-client-support-matrix"; import { EXPORT_CLIENT_METADATA, NATIVE_CLIENT_METADATA } from "../../scripts/client-support-matrix/metadata"; +import { LOCALES } from "../../scripts/client-support-matrix/locales"; import { repoPath } from "../helpers/repo-root"; const BASE = defaultInputs(); @@ -48,6 +49,21 @@ describe("client support matrix", () => { } }); + test("every locale defines exactly the root locale's strings", () => { + const keys = (value: unknown, prefix = ""): string[] => + value !== null && typeof value === "object" + ? Object.entries(value).flatMap(([key, child]) => keys(child, prefix ? `${prefix}.${key}` : key)) + : [prefix]; + const root = keys(LOCALES.root).sort(); + for (const [locale, strings] of Object.entries(LOCALES)) { + expect(keys(strings).sort(), locale).toEqual(root); + for (const key of keys(strings)) { + const value = key.split(".").reduce((node, part) => (node as Record)[part], strings); + expect(typeof value === "string" && value.trim().length > 0, `${locale}.${key}`).toBe(true); + } + } + }); + test("rows cover the export roster, the managed integrations and the native union", () => { const rows = collectRows(); const nativeIds = parseNativeRoster(readFileSync(repoPath(NATIVE_ROSTER_FILE), "utf8")); From 4852952e6a8b94bfca311133b0aa54473514ffe3 Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 07:53:24 +0900 Subject: [PATCH 5/8] docs: record runtime-verified client results on the support page Adds scripts/client-support-matrix/runtime-verified.json (empty by default) and a Runtime verified column that shows the latest reviewed result per client: product, client and opencodex versions, scenario, date and evidence link. Malformed files, unknown rows, impossible dates, unsafe text or links and duplicates fail generation. --- .../docs/fr/reference/client-support.md | 61 ++++---- .../docs/ja/reference/client-support.md | 61 ++++---- .../docs/ko/reference/client-support.md | 61 ++++---- .../content/docs/reference/client-support.md | 61 ++++---- .../docs/ru/reference/client-support.md | 61 ++++---- .../docs/tr/reference/client-support.md | 61 ++++---- .../docs/zh-cn/reference/client-support.md | 61 ++++---- .../docs/zh-tw/reference/client-support.md | 61 ++++---- scripts/client-support-matrix/locales.ts | 45 ++++-- .../runtime-verified.json | 4 + scripts/client-support-matrix/runtime.ts | 146 ++++++++++++++++++ scripts/generate-client-support-matrix.ts | 36 ++++- scripts/test-layout/layout.json | 2 +- structure/clients/client-support-matrix.md | 13 ++ .../client-support-runtime.test.ts | 127 +++++++++++++++ tests/fixtures/test-layout-expected.json | 1 + 16 files changed, 609 insertions(+), 253 deletions(-) create mode 100644 scripts/client-support-matrix/runtime-verified.json create mode 100644 scripts/client-support-matrix/runtime.ts create mode 100644 tests/ci-workflows/client-support-runtime.test.ts diff --git a/docs-site/src/content/docs/fr/reference/client-support.md b/docs-site/src/content/docs/fr/reference/client-support.md index b7c21876ab4..1c9ca908c40 100644 --- a/docs-site/src/content/docs/fr/reference/client-support.md +++ b/docs-site/src/content/docs/fr/reference/client-support.md @@ -12,39 +12,40 @@ opencodex est une passerelle locale unique pour vos outils de code IA et vos mod - **implemented**: le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Un protocole marqué *revu* désigne un point de terminaison issu d’une revue plutôt que de la configuration elle-même (clients natifs, et clients qui déduisent un point de terminaison précis d’un réglage) ; le générateur vérifie que le code revu est toujours en place. Les lignes « Compatible OpenAI » n’affirment aucun point de terminaison. - **test-covered**: implemented, et un cas de test choisi en revue qui exerce ce client est lié. Le générateur vérifie seulement que le test existe toujours et qu’il est actif ; lisez-le pour savoir ce qu’il vérifie. +- **Vérifié à l’exécution**: une observation datée : une personne a exécuté le produit et la version du client indiqués avec la version d’opencodex indiquée, pour le scénario décrit, à cette date, et a lié la preuve. Cela ne dit rien des autres versions ou produits. La preuve est revue lors de l’ajout du résultat ; le générateur ne la rejoue pas. Un tiret signifie qu’aucun résultat n’est enregistré. ## Clients -| Client | Natif | Export de configuration | Intégration de fichier gérée | Protocole entrant | Niveau | Remarques | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | Oui | — | — | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/codex-integration/) | -| Claude Code | Oui | — | — | Anthropic Messages (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/claude-code/) | -| Claude Desktop | Oui | — | — | Anthropic Messages (revu) | implemented | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. | -| Grok Build | Oui | — | — | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/grok-build/) | -| Qoder | — | JSON | Oui | Compatible OpenAI (point de terminaison choisi par le client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Écoute locale uniquement. | -| Qoder CN | — | JSON | Oui | Compatible OpenAI (point de terminaison choisi par le client) | implemented | Écoute locale uniquement. | -| WorkBuddy | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | -| WorkBuddy AI | — | JSON | Oui | Chat Completions | implemented | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | -| OpenCode | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). [guide](/fr/guides/opencode/) | -| Pi | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Écoute locale uniquement. [guide](/fr/guides/pi/) | -| Oh My Pi (omp) | — | YAML | Oui | Chat Completions; Responses (modèles OpenAI natifs uniquement) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Écoute locale uniquement. Ne modifie que son propre bloc YAML. | -| Hermes | — | YAML | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). Ne modifie que son propre bloc YAML. | -| OpenClaw | — | JSON5 | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). | -| Kimi CLI | — | TOML | Oui | Compatible OpenAI (point de terminaison choisi par le client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Écoute locale uniquement. | -| Gajae | — | YAML | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Écoute locale uniquement. | -| DSH | — | YAML | Oui | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Écoute locale uniquement. Écrit dans le magasin de fournisseurs actuel du client. Ne modifie que son propre bloc YAML. Les écritures passent par un fichier de verrou placé à côté de la configuration. | -| MCode | — | YAML | Oui | Anthropic Messages | implemented | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | -| ZCode | — | JSON | Oui | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Écoute locale uniquement. Écrit dans le magasin de fournisseurs actuel du client. Le client choisit le point de terminaison à partir de ce réglage (vérifié en revue d’après le comportement documenté du client). | -| Command Code | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | -| Prime | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Écoute locale uniquement. | -| Aside | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Écoute locale uniquement. | -| Raycast | — | YAML | Oui | Chat Completions (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Écoute locale uniquement. Le client choisit le point de terminaison à partir de ce réglage (vérifié en revue d’après le comportement documenté du client). | -| omo | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Écoute locale uniquement. | -| Cline | — | JSON | Oui | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | -| Kilo Code | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). | -| Factory Droid | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Écoute locale uniquement. [guide](/fr/guides/factory-droid/) | -| Gentle Shell | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Écoute locale uniquement. | -| T3 Code | — | JSON | Oui | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | +| Client | Natif | Export de configuration | Intégration de fichier gérée | Protocole entrant | Niveau | Vérifié à l’exécution | Remarques | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Oui | — | — | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/codex-integration/) | +| Claude Code | Oui | — | — | Anthropic Messages (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/claude-code/) | +| Claude Desktop | Oui | — | — | Anthropic Messages (revu) | implemented | — | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. | +| Grok Build | Oui | — | — | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | Configuré par opencodex lui-même ; aucun fichier de configuration exporté. [guide](/fr/guides/grok-build/) | +| Qoder | — | JSON | Oui | Compatible OpenAI (point de terminaison choisi par le client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | Écoute locale uniquement. | +| Qoder CN | — | JSON | Oui | Compatible OpenAI (point de terminaison choisi par le client) | implemented | — | Écoute locale uniquement. | +| WorkBuddy | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | +| WorkBuddy AI | — | JSON | Oui | Chat Completions | implemented | — | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | +| OpenCode | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). [guide](/fr/guides/opencode/) | +| Pi | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Écoute locale uniquement. [guide](/fr/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Oui | Chat Completions; Responses (modèles OpenAI natifs uniquement) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Écoute locale uniquement. Ne modifie que son propre bloc YAML. | +| Hermes | — | YAML | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). Ne modifie que son propre bloc YAML. | +| OpenClaw | — | JSON5 | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). | +| Kimi CLI | — | TOML | Oui | Compatible OpenAI (point de terminaison choisi par le client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | Écoute locale uniquement. | +| Gajae | — | YAML | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | Écoute locale uniquement. | +| DSH | — | YAML | Oui | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Écoute locale uniquement. Écrit dans le magasin de fournisseurs actuel du client. Ne modifie que son propre bloc YAML. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| MCode | — | YAML | Oui | Anthropic Messages | implemented | — | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| ZCode | — | JSON | Oui | Responses (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | Écoute locale uniquement. Écrit dans le magasin de fournisseurs actuel du client. Le client choisit le point de terminaison à partir de ce réglage (vérifié en revue d’après le comportement documenté du client). | +| Command Code | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| Prime | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | Écoute locale uniquement. | +| Aside | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | Écoute locale uniquement. | +| Raycast | — | YAML | Oui | Chat Completions (revu) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | Écoute locale uniquement. Le client choisit le point de terminaison à partir de ce réglage (vérifié en revue d’après le comportement documenté du client). | +| omo | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | Écoute locale uniquement. | +| Cline | — | JSON | Oui | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | Écoute locale uniquement. Les écritures passent par un fichier de verrou placé à côté de la configuration. | +| Kilo Code | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | L’export prend aussi en charge une écoute distante (il ajoute l’en-tête de clé API). | +| Factory Droid | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | Écoute locale uniquement. [guide](/fr/guides/factory-droid/) | +| Gentle Shell | — | JSON | Oui | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | Écoute locale uniquement. | +| T3 Code | — | JSON | Oui | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | Écoute locale uniquement. L’application exécute d’abord une vérification propre au client et peut refuser. | ## Points de terminaison entrants diff --git a/docs-site/src/content/docs/ja/reference/client-support.md b/docs-site/src/content/docs/ja/reference/client-support.md index d87c84ade3c..4e5a7ce8e00 100644 --- a/docs-site/src/content/docs/ja/reference/client-support.md +++ b/docs-site/src/content/docs/ja/reference/client-support.md @@ -12,39 +12,40 @@ opencodex は、AI コーディングツールとモデルをつなぐ 1 つの - **implemented**: レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*は、エンドポイントが設定そのものではなくレビューに基づくことを示します(ネイティブクライアントと、設定を特定のエンドポイントに変換するクライアント)。レビューしたコードが残っているかを生成時に確認します。「OpenAI 互換」の行は特定のエンドポイントを主張しません。 - **test-covered**: implemented であり、レビューで選んだこのクライアントのテストケースへのリンクがあります。生成時に確認するのはテストが存在し無効化されていないことだけです。何を検証しているかはテストを読んでください。 +- **実行確認**: ある時点の観察記録です。表のクライアント製品・バージョンを表の opencodex バージョンで、記載のシナリオにより、その日付に人が実行し、証拠をリンクしました。ほかのバージョンや製品については何も示しません。証拠は結果の追加時にレビューされ、生成時に再実行はされません。ダッシュは記録がないことを示します。 ## クライアント -| クライアント | ネイティブ | 設定エクスポート | 管理ファイル連携 | 受信プロトコル | サポートレベル | 備考 | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | はい | — | — | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/codex-integration/) | -| Claude Code | はい | — | — | Anthropic Messages (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/claude-code/) | -| Claude Desktop | はい | — | — | Anthropic Messages (レビュー済み) | implemented | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 | -| Grok Build | はい | — | — | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/grok-build/) | -| Qoder | — | JSON | はい | OpenAI 互換(エンドポイントはクライアントが選択) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | ローカルリスナーのみ。 | -| Qoder CN | — | JSON | はい | OpenAI 互換(エンドポイントはクライアントが選択) | implemented | ローカルリスナーのみ。 | -| WorkBuddy | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | -| WorkBuddy AI | — | JSON | はい | Chat Completions | implemented | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | -| OpenCode | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。 [ガイド](/ja/guides/opencode/) | -| Pi | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | ローカルリスナーのみ。 [ガイド](/ja/guides/pi/) | -| Oh My Pi (omp) | — | YAML | はい | Chat Completions; Responses (ネイティブ OpenAI モデルのみ) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | ローカルリスナーのみ。自分の YAML ブロックだけを編集します。 | -| Hermes | — | YAML | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。自分の YAML ブロックだけを編集します。 | -| OpenClaw | — | JSON5 | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。 | -| Kimi CLI | — | TOML | はい | OpenAI 互換(エンドポイントはクライアントが選択) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | ローカルリスナーのみ。 | -| Gajae | — | YAML | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | ローカルリスナーのみ。 | -| DSH | — | YAML | はい | Responses | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | ローカルリスナーのみ。クライアントの現在のプロバイダーストアに書き込みます。自分の YAML ブロックだけを編集します。設定ファイル横のロックファイルで書き込みを調整します。 | -| MCode | — | YAML | はい | Anthropic Messages | implemented | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | -| ZCode | — | JSON | はい | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | ローカルリスナーのみ。クライアントの現在のプロバイダーストアに書き込みます。クライアントがこの設定からエンドポイントを選びます(クライアントの文書化された動作に基づきレビュー済み)。 | -| Command Code | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | -| Prime | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | ローカルリスナーのみ。 | -| Aside | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | ローカルリスナーのみ。 | -| Raycast | — | YAML | はい | Chat Completions (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | ローカルリスナーのみ。クライアントがこの設定からエンドポイントを選びます(クライアントの文書化された動作に基づきレビュー済み)。 | -| omo | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | ローカルリスナーのみ。 | -| Cline | — | JSON | はい | Responses | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | -| Kilo Code | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | リモートリスナーにも対応します(API キーヘッダーを追加)。 | -| Factory Droid | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | ローカルリスナーのみ。 [ガイド](/ja/guides/factory-droid/) | -| Gentle Shell | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | ローカルリスナーのみ。 | -| T3 Code | — | JSON | はい | Anthropic Messages | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | +| クライアント | ネイティブ | 設定エクスポート | 管理ファイル連携 | 受信プロトコル | サポートレベル | 実行確認 | 備考 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | はい | — | — | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/codex-integration/) | +| Claude Code | はい | — | — | Anthropic Messages (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/claude-code/) | +| Claude Desktop | はい | — | — | Anthropic Messages (レビュー済み) | implemented | — | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 | +| Grok Build | はい | — | — | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | opencodex が直接設定します。エクスポートされる設定ファイルはありません。 [ガイド](/ja/guides/grok-build/) | +| Qoder | — | JSON | はい | OpenAI 互換(エンドポイントはクライアントが選択) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | ローカルリスナーのみ。 | +| Qoder CN | — | JSON | はい | OpenAI 互換(エンドポイントはクライアントが選択) | implemented | — | ローカルリスナーのみ。 | +| WorkBuddy | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | +| WorkBuddy AI | — | JSON | はい | Chat Completions | implemented | — | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | +| OpenCode | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | リモートリスナーにも対応します(API キーヘッダーを追加)。 [ガイド](/ja/guides/opencode/) | +| Pi | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | ローカルリスナーのみ。 [ガイド](/ja/guides/pi/) | +| Oh My Pi (omp) | — | YAML | はい | Chat Completions; Responses (ネイティブ OpenAI モデルのみ) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | ローカルリスナーのみ。自分の YAML ブロックだけを編集します。 | +| Hermes | — | YAML | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | リモートリスナーにも対応します(API キーヘッダーを追加)。自分の YAML ブロックだけを編集します。 | +| OpenClaw | — | JSON5 | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | リモートリスナーにも対応します(API キーヘッダーを追加)。 | +| Kimi CLI | — | TOML | はい | OpenAI 互換(エンドポイントはクライアントが選択) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | ローカルリスナーのみ。 | +| Gajae | — | YAML | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | ローカルリスナーのみ。 | +| DSH | — | YAML | はい | Responses | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | ローカルリスナーのみ。クライアントの現在のプロバイダーストアに書き込みます。自分の YAML ブロックだけを編集します。設定ファイル横のロックファイルで書き込みを調整します。 | +| MCode | — | YAML | はい | Anthropic Messages | implemented | — | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | +| ZCode | — | JSON | はい | Responses (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | ローカルリスナーのみ。クライアントの現在のプロバイダーストアに書き込みます。クライアントがこの設定からエンドポイントを選びます(クライアントの文書化された動作に基づきレビュー済み)。 | +| Command Code | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | +| Prime | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | ローカルリスナーのみ。 | +| Aside | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | ローカルリスナーのみ。 | +| Raycast | — | YAML | はい | Chat Completions (レビュー済み) | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | ローカルリスナーのみ。クライアントがこの設定からエンドポイントを選びます(クライアントの文書化された動作に基づきレビュー済み)。 | +| omo | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | ローカルリスナーのみ。 | +| Cline | — | JSON | はい | Responses | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | ローカルリスナーのみ。設定ファイル横のロックファイルで書き込みを調整します。 | +| Kilo Code | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | リモートリスナーにも対応します(API キーヘッダーを追加)。 | +| Factory Droid | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | ローカルリスナーのみ。 [ガイド](/ja/guides/factory-droid/) | +| Gentle Shell | — | JSON | はい | Chat Completions | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | ローカルリスナーのみ。 | +| T3 Code | — | JSON | はい | Anthropic Messages | test-covered ([テスト](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | ローカルリスナーのみ。適用前にクライアント固有のチェックを実行し、拒否することがあります。 | ## 受信エンドポイント diff --git a/docs-site/src/content/docs/ko/reference/client-support.md b/docs-site/src/content/docs/ko/reference/client-support.md index 0f4c1462941..a6730e905f0 100644 --- a/docs-site/src/content/docs/ko/reference/client-support.md +++ b/docs-site/src/content/docs/ko/reference/client-support.md @@ -12,39 +12,40 @@ opencodex는 AI 코딩 도구와 모델을 잇는 하나의 로컬 게이트웨 - **implemented**: 레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시는 설정 자체가 아니라 리뷰에서 나온 특정 엔드포인트를 뜻합니다(네이티브 클라이언트, 설정값을 특정 엔드포인트로 바꾸는 클라이언트). 생성기는 리뷰한 코드가 그대로 있는지 확인합니다. 'OpenAI 호환' 행은 엔드포인트를 단정하지 않습니다. - **test-covered**: implemented이면서, 리뷰로 고른 이 클라이언트의 테스트 케이스가 링크되어 있습니다. 생성기는 테스트가 존재하고 비활성화되지 않았는지만 확인하므로, 무엇을 검증하는지는 테스트를 직접 읽어 보세요. +- **실행 검증**: 특정 시점의 관찰 기록입니다. 표에 적힌 클라이언트 제품·버전을 표에 적힌 opencodex 버전과 함께, 명시한 시나리오로, 그 날짜에 사람이 실행하고 증거를 링크했습니다. 다른 버전이나 제품에 대해서는 아무것도 보장하지 않습니다. 증거는 결과를 추가할 때 리뷰하며, 생성기가 다시 실행하지 않습니다. 대시(—)는 기록된 결과가 없다는 뜻입니다. ## 클라이언트 -| 클라이언트 | 네이티브 | 설정 내보내기 | 관리형 파일 연동 | 수신 프로토콜 | 지원 수준 | 참고 | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | 예 | — | — | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/codex-integration/) | -| Claude Code | 예 | — | — | Anthropic Messages (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/claude-code/) | -| Claude Desktop | 예 | — | — | Anthropic Messages (검토됨) | implemented | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. | -| Grok Build | 예 | — | — | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/grok-build/) | -| Qoder | — | JSON | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | 로컬 리스너 전용. | -| Qoder CN | — | JSON | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | implemented | 로컬 리스너 전용. | -| WorkBuddy | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | -| WorkBuddy AI | — | JSON | 예 | Chat Completions | implemented | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | -| OpenCode | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). [가이드](/ko/guides/opencode/) | -| Pi | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 로컬 리스너 전용. [가이드](/ko/guides/pi/) | -| Oh My Pi (omp) | — | YAML | 예 | Chat Completions; Responses (네이티브 OpenAI 모델만) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 로컬 리스너 전용. 자기 YAML 블록만 수정합니다. | -| Hermes | — | YAML | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). 자기 YAML 블록만 수정합니다. | -| OpenClaw | — | JSON5 | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). | -| Kimi CLI | — | TOML | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | 로컬 리스너 전용. | -| Gajae | — | YAML | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | 로컬 리스너 전용. | -| DSH | — | YAML | 예 | Responses | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 로컬 리스너 전용. 클라이언트의 현재 provider 저장소에 씁니다. 자기 YAML 블록만 수정합니다. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | -| MCode | — | YAML | 예 | Anthropic Messages | implemented | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | -| ZCode | — | JSON | 예 | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | 로컬 리스너 전용. 클라이언트의 현재 provider 저장소에 씁니다. 클라이언트가 이 설정값으로 엔드포인트를 고릅니다(클라이언트 문서 동작 기준으로 리뷰함). | -| Command Code | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | -| Prime | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | 로컬 리스너 전용. | -| Aside | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | 로컬 리스너 전용. | -| Raycast | — | YAML | 예 | Chat Completions (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | 로컬 리스너 전용. 클라이언트가 이 설정값으로 엔드포인트를 고릅니다(클라이언트 문서 동작 기준으로 리뷰함). | -| omo | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | 로컬 리스너 전용. | -| Cline | — | JSON | 예 | Responses | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | -| Kilo Code | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | 원격 리스너도 지원합니다(API 키 헤더를 추가). | -| Factory Droid | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | 로컬 리스너 전용. [가이드](/ko/guides/factory-droid/) | -| Gentle Shell | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | 로컬 리스너 전용. | -| T3 Code | — | JSON | 예 | Anthropic Messages | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | +| 클라이언트 | 네이티브 | 설정 내보내기 | 관리형 파일 연동 | 수신 프로토콜 | 지원 수준 | 실행 검증 | 참고 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | 예 | — | — | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/codex-integration/) | +| Claude Code | 예 | — | — | Anthropic Messages (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/claude-code/) | +| Claude Desktop | 예 | — | — | Anthropic Messages (검토됨) | implemented | — | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. | +| Grok Build | 예 | — | — | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | opencodex가 직접 설정하며 내보내는 설정 파일은 없습니다. [가이드](/ko/guides/grok-build/) | +| Qoder | — | JSON | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | 로컬 리스너 전용. | +| Qoder CN | — | JSON | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | implemented | — | 로컬 리스너 전용. | +| WorkBuddy | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | +| WorkBuddy AI | — | JSON | 예 | Chat Completions | implemented | — | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | +| OpenCode | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 원격 리스너도 지원합니다(API 키 헤더를 추가). [가이드](/ko/guides/opencode/) | +| Pi | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 로컬 리스너 전용. [가이드](/ko/guides/pi/) | +| Oh My Pi (omp) | — | YAML | 예 | Chat Completions; Responses (네이티브 OpenAI 모델만) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 로컬 리스너 전용. 자기 YAML 블록만 수정합니다. | +| Hermes | — | YAML | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | 원격 리스너도 지원합니다(API 키 헤더를 추가). 자기 YAML 블록만 수정합니다. | +| OpenClaw | — | JSON5 | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | 원격 리스너도 지원합니다(API 키 헤더를 추가). | +| Kimi CLI | — | TOML | 예 | OpenAI 호환(엔드포인트는 클라이언트가 선택) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | 로컬 리스너 전용. | +| Gajae | — | YAML | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | 로컬 리스너 전용. | +| DSH | — | YAML | 예 | Responses | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 로컬 리스너 전용. 클라이언트의 현재 provider 저장소에 씁니다. 자기 YAML 블록만 수정합니다. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| MCode | — | YAML | 예 | Anthropic Messages | implemented | — | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| ZCode | — | JSON | 예 | Responses (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | 로컬 리스너 전용. 클라이언트의 현재 provider 저장소에 씁니다. 클라이언트가 이 설정값으로 엔드포인트를 고릅니다(클라이언트 문서 동작 기준으로 리뷰함). | +| Command Code | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| Prime | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | 로컬 리스너 전용. | +| Aside | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | 로컬 리스너 전용. | +| Raycast | — | YAML | 예 | Chat Completions (검토됨) | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | 로컬 리스너 전용. 클라이언트가 이 설정값으로 엔드포인트를 고릅니다(클라이언트 문서 동작 기준으로 리뷰함). | +| omo | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | 로컬 리스너 전용. | +| Cline | — | JSON | 예 | Responses | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | 로컬 리스너 전용. 설정 파일 옆 잠금 파일로 쓰기를 조율합니다. | +| Kilo Code | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | 원격 리스너도 지원합니다(API 키 헤더를 추가). | +| Factory Droid | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | 로컬 리스너 전용. [가이드](/ko/guides/factory-droid/) | +| Gentle Shell | — | JSON | 예 | Chat Completions | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | 로컬 리스너 전용. | +| T3 Code | — | JSON | 예 | Anthropic Messages | test-covered ([테스트](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | 로컬 리스너 전용. 적용 전에 클라이언트별 검사를 실행하며 거부할 수 있습니다. | ## 수신 엔드포인트 diff --git a/docs-site/src/content/docs/reference/client-support.md b/docs-site/src/content/docs/reference/client-support.md index cfebf129b24..e1522a3b0de 100644 --- a/docs-site/src/content/docs/reference/client-support.md +++ b/docs-site/src/content/docs/reference/client-support.md @@ -12,39 +12,40 @@ opencodex is one local gateway for your AI coding tools and models. Some tools i - **implemented**: the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. A protocol marked *curated* names an endpoint that comes from a review instead of the config itself (native clients, and clients that turn a setting into a specific endpoint); the generator checks that the reviewed code is still in place. "OpenAI-compatible" rows make no endpoint claim. - **test-covered**: implemented, and a test case chosen in review that exercises this client is linked. The generator checks only that the test still exists and is enabled; read it to see what it asserts. +- **Runtime verified**: a historical observation: a person ran the exact client product and version shown against the opencodex version shown, for the stated scenario, on that date, and linked the evidence. It says nothing about other versions or products. The evidence is reviewed when a result is added; the generator does not re-run it. A dash means no result is recorded. ## Clients -| Client | Native | Config export | Managed file integration | Inbound protocol | Support level | Notes | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | Yes | — | — | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | Set up by opencodex itself; no exported config file. [guide](/guides/codex-integration/) | -| Claude Code | Yes | — | — | Anthropic Messages (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | Set up by opencodex itself; no exported config file. [guide](/guides/claude-code/) | -| Claude Desktop | Yes | — | — | Anthropic Messages (curated) | implemented | Set up by opencodex itself; no exported config file. | -| Grok Build | Yes | — | — | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | Set up by opencodex itself; no exported config file. [guide](/guides/grok-build/) | -| Qoder | — | JSON | Yes | OpenAI-compatible (endpoint chosen by the client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Local listener only. | -| Qoder CN | — | JSON | Yes | OpenAI-compatible (endpoint chosen by the client) | implemented | Local listener only. | -| WorkBuddy | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Local listener only. Apply runs a client-specific check first and can refuse. | -| WorkBuddy AI | — | JSON | Yes | Chat Completions | implemented | Local listener only. Apply runs a client-specific check first and can refuse. | -| OpenCode | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | The exporter also supports a remote listener (it adds the API key header). [guide](/guides/opencode/) | -| Pi | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Local listener only. [guide](/guides/pi/) | -| Oh My Pi (omp) | — | YAML | Yes | Chat Completions; Responses (native OpenAI models only) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Local listener only. Edits only its own YAML block. | -| Hermes | — | YAML | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | The exporter also supports a remote listener (it adds the API key header). Edits only its own YAML block. | -| OpenClaw | — | JSON5 | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | The exporter also supports a remote listener (it adds the API key header). | -| Kimi CLI | — | TOML | Yes | OpenAI-compatible (endpoint chosen by the client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Local listener only. | -| Gajae | — | YAML | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Local listener only. | -| DSH | — | YAML | Yes | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Local listener only. Writes the client's current provider store. Edits only its own YAML block. Writes are coordinated through a lock file next to the config. | -| MCode | — | YAML | Yes | Anthropic Messages | implemented | Local listener only. Writes are coordinated through a lock file next to the config. | -| ZCode | — | JSON | Yes | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Local listener only. Writes the client's current provider store. The client picks the endpoint from this setting (reviewed against the client's documented behavior). | -| Command Code | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Local listener only. Writes are coordinated through a lock file next to the config. | -| Prime | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Local listener only. | -| Aside | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Local listener only. | -| Raycast | — | YAML | Yes | Chat Completions (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Local listener only. The client picks the endpoint from this setting (reviewed against the client's documented behavior). | -| omo | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Local listener only. | -| Cline | — | JSON | Yes | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Local listener only. Writes are coordinated through a lock file next to the config. | -| Kilo Code | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | The exporter also supports a remote listener (it adds the API key header). | -| Factory Droid | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Local listener only. [guide](/guides/factory-droid/) | -| Gentle Shell | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Local listener only. | -| T3 Code | — | JSON | Yes | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Local listener only. Apply runs a client-specific check first and can refuse. | +| Client | Native | Config export | Managed file integration | Inbound protocol | Support level | Runtime verified | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Yes | — | — | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | Set up by opencodex itself; no exported config file. [guide](/guides/codex-integration/) | +| Claude Code | Yes | — | — | Anthropic Messages (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | Set up by opencodex itself; no exported config file. [guide](/guides/claude-code/) | +| Claude Desktop | Yes | — | — | Anthropic Messages (curated) | implemented | — | Set up by opencodex itself; no exported config file. | +| Grok Build | Yes | — | — | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | Set up by opencodex itself; no exported config file. [guide](/guides/grok-build/) | +| Qoder | — | JSON | Yes | OpenAI-compatible (endpoint chosen by the client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | Local listener only. | +| Qoder CN | — | JSON | Yes | OpenAI-compatible (endpoint chosen by the client) | implemented | — | Local listener only. | +| WorkBuddy | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | Local listener only. Apply runs a client-specific check first and can refuse. | +| WorkBuddy AI | — | JSON | Yes | Chat Completions | implemented | — | Local listener only. Apply runs a client-specific check first and can refuse. | +| OpenCode | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | The exporter also supports a remote listener (it adds the API key header). [guide](/guides/opencode/) | +| Pi | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Local listener only. [guide](/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Yes | Chat Completions; Responses (native OpenAI models only) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Local listener only. Edits only its own YAML block. | +| Hermes | — | YAML | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | The exporter also supports a remote listener (it adds the API key header). Edits only its own YAML block. | +| OpenClaw | — | JSON5 | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | The exporter also supports a remote listener (it adds the API key header). | +| Kimi CLI | — | TOML | Yes | OpenAI-compatible (endpoint chosen by the client) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | Local listener only. | +| Gajae | — | YAML | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | Local listener only. | +| DSH | — | YAML | Yes | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Local listener only. Writes the client's current provider store. Edits only its own YAML block. Writes are coordinated through a lock file next to the config. | +| MCode | — | YAML | Yes | Anthropic Messages | implemented | — | Local listener only. Writes are coordinated through a lock file next to the config. | +| ZCode | — | JSON | Yes | Responses (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | Local listener only. Writes the client's current provider store. The client picks the endpoint from this setting (reviewed against the client's documented behavior). | +| Command Code | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | Local listener only. Writes are coordinated through a lock file next to the config. | +| Prime | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | Local listener only. | +| Aside | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | Local listener only. | +| Raycast | — | YAML | Yes | Chat Completions (curated) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | Local listener only. The client picks the endpoint from this setting (reviewed against the client's documented behavior). | +| omo | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | Local listener only. | +| Cline | — | JSON | Yes | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | Local listener only. Writes are coordinated through a lock file next to the config. | +| Kilo Code | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | The exporter also supports a remote listener (it adds the API key header). | +| Factory Droid | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | Local listener only. [guide](/guides/factory-droid/) | +| Gentle Shell | — | JSON | Yes | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | Local listener only. | +| T3 Code | — | JSON | Yes | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | Local listener only. Apply runs a client-specific check first and can refuse. | ## Inbound endpoints diff --git a/docs-site/src/content/docs/ru/reference/client-support.md b/docs-site/src/content/docs/ru/reference/client-support.md index 6724ebe7016..95f1fcc881a 100644 --- a/docs-site/src/content/docs/ru/reference/client-support.md +++ b/docs-site/src/content/docs/ru/reference/client-support.md @@ -12,39 +12,40 @@ opencodex — единый локальный шлюз для ваших ИИ-и - **implemented**: клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Пометка *проверено* означает, что эндпоинт взят из ревью, а не из самой конфигурации (нативные клиенты и клиенты, которые превращают настройку в конкретный эндпоинт); генератор проверяет, что проверенный код на месте. Строки «Совместим с OpenAI» не утверждают конкретный эндпоинт. - **test-covered**: implemented, и есть ссылка на выбранный при ревью тест, который задействует этого клиента. Генератор проверяет только, что тест существует и не отключён; что именно он проверяет, смотрите в самом тесте. +- **Проверено в работе**: историческое наблюдение: человек запустил указанные продукт и версию клиента с указанной версией opencodex по описанному сценарию в эту дату и приложил ссылку на подтверждение. Это ничего не говорит о других версиях или продуктах. Подтверждение проверяется при добавлении результата; генератор его не перезапускает. Прочерк означает, что результатов нет. ## Клиенты -| Клиент | Нативно | Экспорт конфигурации | Управляемая интеграция файла | Входящий протокол | Уровень | Примечания | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | Да | — | — | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/codex-integration/) | -| Claude Code | Да | — | — | Anthropic Messages (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/claude-code/) | -| Claude Desktop | Да | — | — | Anthropic Messages (проверено) | implemented | Настраивается самим opencodex, без экспортируемого файла конфигурации. | -| Grok Build | Да | — | — | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/grok-build/) | -| Qoder | — | JSON | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Только локальный слушатель. | -| Qoder CN | — | JSON | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | implemented | Только локальный слушатель. | -| WorkBuddy | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | -| WorkBuddy AI | — | JSON | Да | Chat Completions | implemented | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | -| OpenCode | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). [руководство](/ru/guides/opencode/) | -| Pi | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Только локальный слушатель. [руководство](/ru/guides/pi/) | -| Oh My Pi (omp) | — | YAML | Да | Chat Completions; Responses (только нативные модели OpenAI) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Только локальный слушатель. Изменяет только свой YAML-блок. | -| Hermes | — | YAML | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). Изменяет только свой YAML-блок. | -| OpenClaw | — | JSON5 | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). | -| Kimi CLI | — | TOML | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Только локальный слушатель. | -| Gajae | — | YAML | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Только локальный слушатель. | -| DSH | — | YAML | Да | Responses | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Только локальный слушатель. Пишет в текущее хранилище провайдеров клиента. Изменяет только свой YAML-блок. Запись согласуется через файл блокировки рядом с конфигурацией. | -| MCode | — | YAML | Да | Anthropic Messages | implemented | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | -| ZCode | — | JSON | Да | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Только локальный слушатель. Пишет в текущее хранилище провайдеров клиента. Клиент выбирает эндпоинт по этой настройке (проверено при ревью по документированному поведению клиента). | -| Command Code | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | -| Prime | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Только локальный слушатель. | -| Aside | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Только локальный слушатель. | -| Raycast | — | YAML | Да | Chat Completions (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Только локальный слушатель. Клиент выбирает эндпоинт по этой настройке (проверено при ревью по документированному поведению клиента). | -| omo | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Только локальный слушатель. | -| Cline | — | JSON | Да | Responses | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | -| Kilo Code | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). | -| Factory Droid | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Только локальный слушатель. [руководство](/ru/guides/factory-droid/) | -| Gentle Shell | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Только локальный слушатель. | -| T3 Code | — | JSON | Да | Anthropic Messages | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | +| Клиент | Нативно | Экспорт конфигурации | Управляемая интеграция файла | Входящий протокол | Уровень | Проверено в работе | Примечания | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Да | — | — | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/codex-integration/) | +| Claude Code | Да | — | — | Anthropic Messages (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/claude-code/) | +| Claude Desktop | Да | — | — | Anthropic Messages (проверено) | implemented | — | Настраивается самим opencodex, без экспортируемого файла конфигурации. | +| Grok Build | Да | — | — | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | Настраивается самим opencodex, без экспортируемого файла конфигурации. [руководство](/ru/guides/grok-build/) | +| Qoder | — | JSON | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | Только локальный слушатель. | +| Qoder CN | — | JSON | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | implemented | — | Только локальный слушатель. | +| WorkBuddy | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | +| WorkBuddy AI | — | JSON | Да | Chat Completions | implemented | — | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | +| OpenCode | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). [руководство](/ru/guides/opencode/) | +| Pi | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Только локальный слушатель. [руководство](/ru/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Да | Chat Completions; Responses (только нативные модели OpenAI) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Только локальный слушатель. Изменяет только свой YAML-блок. | +| Hermes | — | YAML | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). Изменяет только свой YAML-блок. | +| OpenClaw | — | JSON5 | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). | +| Kimi CLI | — | TOML | Да | Совместим с OpenAI (эндпоинт выбирает клиент) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | Только локальный слушатель. | +| Gajae | — | YAML | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | Только локальный слушатель. | +| DSH | — | YAML | Да | Responses | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Только локальный слушатель. Пишет в текущее хранилище провайдеров клиента. Изменяет только свой YAML-блок. Запись согласуется через файл блокировки рядом с конфигурацией. | +| MCode | — | YAML | Да | Anthropic Messages | implemented | — | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | +| ZCode | — | JSON | Да | Responses (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | Только локальный слушатель. Пишет в текущее хранилище провайдеров клиента. Клиент выбирает эндпоинт по этой настройке (проверено при ревью по документированному поведению клиента). | +| Command Code | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | +| Prime | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | Только локальный слушатель. | +| Aside | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | Только локальный слушатель. | +| Raycast | — | YAML | Да | Chat Completions (проверено) | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | Только локальный слушатель. Клиент выбирает эндпоинт по этой настройке (проверено при ревью по документированному поведению клиента). | +| omo | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | Только локальный слушатель. | +| Cline | — | JSON | Да | Responses | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | Только локальный слушатель. Запись согласуется через файл блокировки рядом с конфигурацией. | +| Kilo Code | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | Экспорт поддерживает и удалённого слушателя (добавляет заголовок API-ключа). | +| Factory Droid | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | Только локальный слушатель. [руководство](/ru/guides/factory-droid/) | +| Gentle Shell | — | JSON | Да | Chat Completions | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | Только локальный слушатель. | +| T3 Code | — | JSON | Да | Anthropic Messages | test-covered ([тест](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | Только локальный слушатель. Перед применением выполняется проверка, специфичная для клиента, и возможен отказ. | ## Входящие эндпоинты diff --git a/docs-site/src/content/docs/tr/reference/client-support.md b/docs-site/src/content/docs/tr/reference/client-support.md index cb1a8db2887..c70d2247739 100644 --- a/docs-site/src/content/docs/tr/reference/client-support.md +++ b/docs-site/src/content/docs/tr/reference/client-support.md @@ -12,39 +12,40 @@ opencodex, yapay zekâ kodlama araçlarınız ve modelleriniz için tek bir yere - **implemented**: istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işareti, uç noktanın yapılandırmanın kendisinden değil bir incelemeden geldiğini belirtir (yerel istemciler ve bir ayarı belirli bir uç noktaya çeviren istemciler); üretici, incelenen kodun hâlâ yerinde olduğunu denetler. "OpenAI uyumlu" satırlar belirli bir uç nokta iddia etmez. - **test-covered**: implemented olmanın yanında, incelemede seçilmiş ve bu istemciyi çalıştıran bir test durumuna bağlantı verilir. Üretici yalnızca testin hâlâ var ve etkin olduğunu denetler; neyi doğruladığını görmek için testi okuyun. +- **Çalışırken doğrulandı**: tarihli bir gözlem: bir kişi gösterilen istemci ürününü ve sürümünü gösterilen opencodex sürümüyle, belirtilen senaryo için o tarihte çalıştırdı ve kanıtı bağladı. Diğer sürümler veya ürünler hakkında bir şey söylemez. Kanıt sonuç eklenirken incelenir; üretici yeniden çalıştırmaz. Tire, kayıtlı sonuç olmadığı anlamına gelir. ## İstemciler -| İstemci | Yerel | Yapılandırma dışa aktarımı | Yönetilen dosya entegrasyonu | Gelen protokol | Destek düzeyi | Notlar | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | Evet | — | — | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/codex-integration/) | -| Claude Code | Evet | — | — | Anthropic Messages (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/claude-code/) | -| Claude Desktop | Evet | — | — | Anthropic Messages (incelendi) | implemented | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. | -| Grok Build | Evet | — | — | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/grok-build/) | -| Qoder | — | JSON | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | Yalnızca yerel dinleyici. | -| Qoder CN | — | JSON | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | implemented | Yalnızca yerel dinleyici. | -| WorkBuddy | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | -| WorkBuddy AI | — | JSON | Evet | Chat Completions | implemented | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | -| OpenCode | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). [kılavuz](/tr/guides/opencode/) | -| Pi | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Yalnızca yerel dinleyici. [kılavuz](/tr/guides/pi/) | -| Oh My Pi (omp) | — | YAML | Evet | Chat Completions; Responses (yalnızca yerel OpenAI modelleri) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Yalnızca yerel dinleyici. Yalnızca kendi YAML bloğunu düzenler. | -| Hermes | — | YAML | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). Yalnızca kendi YAML bloğunu düzenler. | -| OpenClaw | — | JSON5 | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). | -| Kimi CLI | — | TOML | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | Yalnızca yerel dinleyici. | -| Gajae | — | YAML | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | Yalnızca yerel dinleyici. | -| DSH | — | YAML | Evet | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | Yalnızca yerel dinleyici. İstemcinin güncel sağlayıcı deposuna yazar. Yalnızca kendi YAML bloğunu düzenler. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | -| MCode | — | YAML | Evet | Anthropic Messages | implemented | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | -| ZCode | — | JSON | Evet | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | Yalnızca yerel dinleyici. İstemcinin güncel sağlayıcı deposuna yazar. İstemci uç noktayı bu ayardan seçer (istemcinin belgelenmiş davranışına göre incelendi). | -| Command Code | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | -| Prime | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | Yalnızca yerel dinleyici. | -| Aside | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | Yalnızca yerel dinleyici. | -| Raycast | — | YAML | Evet | Chat Completions (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | Yalnızca yerel dinleyici. İstemci uç noktayı bu ayardan seçer (istemcinin belgelenmiş davranışına göre incelendi). | -| omo | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | Yalnızca yerel dinleyici. | -| Cline | — | JSON | Evet | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | -| Kilo Code | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). | -| Factory Droid | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | Yalnızca yerel dinleyici. [kılavuz](/tr/guides/factory-droid/) | -| Gentle Shell | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | Yalnızca yerel dinleyici. | -| T3 Code | — | JSON | Evet | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | +| İstemci | Yerel | Yapılandırma dışa aktarımı | Yönetilen dosya entegrasyonu | Gelen protokol | Destek düzeyi | Çalışırken doğrulandı | Notlar | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | Evet | — | — | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/codex-integration/) | +| Claude Code | Evet | — | — | Anthropic Messages (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/claude-code/) | +| Claude Desktop | Evet | — | — | Anthropic Messages (incelendi) | implemented | — | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. | +| Grok Build | Evet | — | — | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | opencodex tarafından doğrudan kurulur; dışa aktarılan yapılandırma dosyası yoktur. [kılavuz](/tr/guides/grok-build/) | +| Qoder | — | JSON | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | Yalnızca yerel dinleyici. | +| Qoder CN | — | JSON | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | implemented | — | Yalnızca yerel dinleyici. | +| WorkBuddy | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | +| WorkBuddy AI | — | JSON | Evet | Chat Completions | implemented | — | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | +| OpenCode | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). [kılavuz](/tr/guides/opencode/) | +| Pi | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Yalnızca yerel dinleyici. [kılavuz](/tr/guides/pi/) | +| Oh My Pi (omp) | — | YAML | Evet | Chat Completions; Responses (yalnızca yerel OpenAI modelleri) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Yalnızca yerel dinleyici. Yalnızca kendi YAML bloğunu düzenler. | +| Hermes | — | YAML | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). Yalnızca kendi YAML bloğunu düzenler. | +| OpenClaw | — | JSON5 | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). | +| Kimi CLI | — | TOML | Evet | OpenAI uyumlu (uç noktayı istemci seçer) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | Yalnızca yerel dinleyici. | +| Gajae | — | YAML | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | Yalnızca yerel dinleyici. | +| DSH | — | YAML | Evet | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | Yalnızca yerel dinleyici. İstemcinin güncel sağlayıcı deposuna yazar. Yalnızca kendi YAML bloğunu düzenler. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| MCode | — | YAML | Evet | Anthropic Messages | implemented | — | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| ZCode | — | JSON | Evet | Responses (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | Yalnızca yerel dinleyici. İstemcinin güncel sağlayıcı deposuna yazar. İstemci uç noktayı bu ayardan seçer (istemcinin belgelenmiş davranışına göre incelendi). | +| Command Code | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| Prime | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | Yalnızca yerel dinleyici. | +| Aside | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | Yalnızca yerel dinleyici. | +| Raycast | — | YAML | Evet | Chat Completions (incelendi) | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | Yalnızca yerel dinleyici. İstemci uç noktayı bu ayardan seçer (istemcinin belgelenmiş davranışına göre incelendi). | +| omo | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | Yalnızca yerel dinleyici. | +| Cline | — | JSON | Evet | Responses | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | Yalnızca yerel dinleyici. Yazmalar yapılandırmanın yanındaki bir kilit dosyasıyla eşgüdümlenir. | +| Kilo Code | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | Dışa aktarım uzak dinleyiciyi de destekler (API anahtarı başlığını ekler). | +| Factory Droid | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | Yalnızca yerel dinleyici. [kılavuz](/tr/guides/factory-droid/) | +| Gentle Shell | — | JSON | Evet | Chat Completions | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | Yalnızca yerel dinleyici. | +| T3 Code | — | JSON | Evet | Anthropic Messages | test-covered ([test](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | Yalnızca yerel dinleyici. Uygulamadan önce istemciye özgü bir denetim çalışır ve reddedebilir. | ## Gelen uç noktalar diff --git a/docs-site/src/content/docs/zh-cn/reference/client-support.md b/docs-site/src/content/docs/zh-cn/reference/client-support.md index 2391c33ee69..a757e151b7a 100644 --- a/docs-site/src/content/docs/zh-cn/reference/client-support.md +++ b/docs-site/src/content/docs/zh-cn/reference/client-support.md @@ -12,39 +12,40 @@ opencodex 是一个本地网关,连接你的 AI 编程工具与模型。部分 - **implemented**: 客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议表示其端点来自审阅而非配置本身(原生客户端,以及会把某个设置转换为特定端点的客户端);生成器会检查被审阅的代码是否仍然存在。“OpenAI 兼容”的行不声明具体端点。 - **test-covered**: 满足 implemented,并链接了一个经评审选定、覆盖该客户端的测试用例。生成器只检查该测试仍然存在且未被禁用;它具体断言了什么,请阅读测试本身。 +- **运行验证**: 一次历史观察:有人在该日期,用表中所列的 opencodex 版本运行了表中所列的客户端产品与版本,执行了所述场景,并附上证据链接。它不代表其他版本或产品。证据在添加结果时经过评审,生成器不会重新运行。短横线表示尚无记录。 ## 客户端 -| 客户端 | 原生 | 配置导出 | 托管文件集成 | 入站协议 | 支持级别 | 说明 | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | 是 | — | — | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/codex-integration/) | -| Claude Code | 是 | — | — | Anthropic Messages (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/claude-code/) | -| Claude Desktop | 是 | — | — | Anthropic Messages (已审阅) | implemented | 由 opencodex 直接配置,没有导出的配置文件。 | -| Grok Build | 是 | — | — | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/grok-build/) | -| Qoder | — | JSON | 是 | OpenAI 兼容(端点由客户端选择) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | 仅限本地监听。 | -| Qoder CN | — | JSON | 是 | OpenAI 兼容(端点由客户端选择) | implemented | 仅限本地监听。 | -| WorkBuddy | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | -| WorkBuddy AI | — | JSON | 是 | Chat Completions | implemented | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | -| OpenCode | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 [指南](/zh-cn/guides/opencode/) | -| Pi | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 仅限本地监听。 [指南](/zh-cn/guides/pi/) | -| Oh My Pi (omp) | — | YAML | 是 | Chat Completions; Responses (仅原生 OpenAI 模型) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 仅限本地监听。只修改自己的 YAML 区块。 | -| Hermes | — | YAML | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。只修改自己的 YAML 区块。 | -| OpenClaw | — | JSON5 | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 | -| Kimi CLI | — | TOML | 是 | OpenAI 兼容(端点由客户端选择) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | 仅限本地监听。 | -| Gajae | — | YAML | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | 仅限本地监听。 | -| DSH | — | YAML | 是 | Responses | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 仅限本地监听。写入客户端当前的提供商存储。只修改自己的 YAML 区块。通过配置文件旁的锁文件协调写入。 | -| MCode | — | YAML | 是 | Anthropic Messages | implemented | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | -| ZCode | — | JSON | 是 | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | 仅限本地监听。写入客户端当前的提供商存储。客户端根据此设置选择端点(已依据客户端的文档行为评审)。 | -| Command Code | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | -| Prime | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | 仅限本地监听。 | -| Aside | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | 仅限本地监听。 | -| Raycast | — | YAML | 是 | Chat Completions (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | 仅限本地监听。客户端根据此设置选择端点(已依据客户端的文档行为评审)。 | -| omo | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | 仅限本地监听。 | -| Cline | — | JSON | 是 | Responses | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | -| Kilo Code | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 | -| Factory Droid | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | 仅限本地监听。 [指南](/zh-cn/guides/factory-droid/) | -| Gentle Shell | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | 仅限本地监听。 | -| T3 Code | — | JSON | 是 | Anthropic Messages | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | +| 客户端 | 原生 | 配置导出 | 托管文件集成 | 入站协议 | 支持级别 | 运行验证 | 说明 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | 是 | — | — | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/codex-integration/) | +| Claude Code | 是 | — | — | Anthropic Messages (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/claude-code/) | +| Claude Desktop | 是 | — | — | Anthropic Messages (已审阅) | implemented | — | 由 opencodex 直接配置,没有导出的配置文件。 | +| Grok Build | 是 | — | — | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | 由 opencodex 直接配置,没有导出的配置文件。 [指南](/zh-cn/guides/grok-build/) | +| Qoder | — | JSON | 是 | OpenAI 兼容(端点由客户端选择) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | 仅限本地监听。 | +| Qoder CN | — | JSON | 是 | OpenAI 兼容(端点由客户端选择) | implemented | — | 仅限本地监听。 | +| WorkBuddy | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | +| WorkBuddy AI | — | JSON | 是 | Chat Completions | implemented | — | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | +| OpenCode | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 [指南](/zh-cn/guides/opencode/) | +| Pi | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 仅限本地监听。 [指南](/zh-cn/guides/pi/) | +| Oh My Pi (omp) | — | YAML | 是 | Chat Completions; Responses (仅原生 OpenAI 模型) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 仅限本地监听。只修改自己的 YAML 区块。 | +| Hermes | — | YAML | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | 导出的配置也支持远程监听(会添加 API 密钥请求头)。只修改自己的 YAML 区块。 | +| OpenClaw | — | JSON5 | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 | +| Kimi CLI | — | TOML | 是 | OpenAI 兼容(端点由客户端选择) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | 仅限本地监听。 | +| Gajae | — | YAML | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | 仅限本地监听。 | +| DSH | — | YAML | 是 | Responses | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 仅限本地监听。写入客户端当前的提供商存储。只修改自己的 YAML 区块。通过配置文件旁的锁文件协调写入。 | +| MCode | — | YAML | 是 | Anthropic Messages | implemented | — | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | +| ZCode | — | JSON | 是 | Responses (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | 仅限本地监听。写入客户端当前的提供商存储。客户端根据此设置选择端点(已依据客户端的文档行为评审)。 | +| Command Code | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | +| Prime | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | 仅限本地监听。 | +| Aside | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | 仅限本地监听。 | +| Raycast | — | YAML | 是 | Chat Completions (已审阅) | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | 仅限本地监听。客户端根据此设置选择端点(已依据客户端的文档行为评审)。 | +| omo | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | 仅限本地监听。 | +| Cline | — | JSON | 是 | Responses | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | 仅限本地监听。通过配置文件旁的锁文件协调写入。 | +| Kilo Code | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | 导出的配置也支持远程监听(会添加 API 密钥请求头)。 | +| Factory Droid | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | 仅限本地监听。 [指南](/zh-cn/guides/factory-droid/) | +| Gentle Shell | — | JSON | 是 | Chat Completions | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | 仅限本地监听。 | +| T3 Code | — | JSON | 是 | Anthropic Messages | test-covered ([测试](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | 仅限本地监听。应用前先运行针对该客户端的检查,可能拒绝。 | ## 入站端点 diff --git a/docs-site/src/content/docs/zh-tw/reference/client-support.md b/docs-site/src/content/docs/zh-tw/reference/client-support.md index 8ea833b89ac..6439288f139 100644 --- a/docs-site/src/content/docs/zh-tw/reference/client-support.md +++ b/docs-site/src/content/docs/zh-tw/reference/client-support.md @@ -12,39 +12,40 @@ opencodex 是一個本機閘道,連接你的 AI 程式開發工具與模型。 - **implemented**: 用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定表示其端點來自審查而非設定本身(原生用戶端,以及會把某個設定轉換為特定端點的用戶端);產生器會檢查被審查的程式碼是否仍然存在。「OpenAI 相容」的列不聲明具體端點。 - **test-covered**: 符合 implemented,並連結了一個經審查選定、涵蓋該用戶端的測試案例。產生器只檢查該測試仍然存在且未被停用;它實際驗證什麼,請閱讀測試本身。 +- **執行驗證**: 一次歷史觀察:有人在該日期,以表中所列的 opencodex 版本執行了表中所列的用戶端產品與版本,執行了所述情境,並附上證據連結。它不代表其他版本或產品。證據在新增結果時經過審查,產生器不會重新執行。短橫線表示尚無紀錄。 ## 用戶端 -| 用戶端 | 原生 | 設定匯出 | 代管檔案整合 | 入站協定 | 支援等級 | 說明 | -| --- | --- | --- | --- | --- | --- | --- | -| Codex (CLI, app, SDK) | 是 | — | — | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/codex-integration/) | -| Claude Code | 是 | — | — | Anthropic Messages (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/claude-code/) | -| Claude Desktop | 是 | — | — | Anthropic Messages (已審查) | implemented | 由 opencodex 直接設定,沒有匯出的設定檔。 | -| Grok Build | 是 | — | — | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/grok-build/) | -| Qoder | — | JSON | 是 | OpenAI 相容(端點由用戶端選擇) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | 僅限本機監聽。 | -| Qoder CN | — | JSON | 是 | OpenAI 相容(端點由用戶端選擇) | implemented | 僅限本機監聽。 | -| WorkBuddy | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | -| WorkBuddy AI | — | JSON | 是 | Chat Completions | implemented | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | -| OpenCode | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 [指南](/zh-tw/guides/opencode/) | -| Pi | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 僅限本機監聽。 [指南](/zh-tw/guides/pi/) | -| Oh My Pi (omp) | — | YAML | 是 | Chat Completions; Responses (僅原生 OpenAI 模型) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 僅限本機監聽。只修改自己的 YAML 區塊。 | -| Hermes | — | YAML | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。只修改自己的 YAML 區塊。 | -| OpenClaw | — | JSON5 | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 | -| Kimi CLI | — | TOML | 是 | OpenAI 相容(端點由用戶端選擇) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | 僅限本機監聽。 | -| Gajae | — | YAML | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | 僅限本機監聽。 | -| DSH | — | YAML | 是 | Responses | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | 僅限本機監聽。寫入用戶端目前的供應商儲存區。只修改自己的 YAML 區塊。透過設定檔旁的鎖定檔協調寫入。 | -| MCode | — | YAML | 是 | Anthropic Messages | implemented | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | -| ZCode | — | JSON | 是 | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | 僅限本機監聽。寫入用戶端目前的供應商儲存區。用戶端依此設定選擇端點(已依用戶端的文件行為審查)。 | -| Command Code | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | -| Prime | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | 僅限本機監聽。 | -| Aside | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | 僅限本機監聽。 | -| Raycast | — | YAML | 是 | Chat Completions (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | 僅限本機監聽。用戶端依此設定選擇端點(已依用戶端的文件行為審查)。 | -| omo | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | 僅限本機監聽。 | -| Cline | — | JSON | 是 | Responses | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | -| Kilo Code | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 | -| Factory Droid | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | 僅限本機監聽。 [指南](/zh-tw/guides/factory-droid/) | -| Gentle Shell | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | 僅限本機監聽。 | -| T3 Code | — | JSON | 是 | Anthropic Messages | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | +| 用戶端 | 原生 | 設定匯出 | 代管檔案整合 | 入站協定 | 支援等級 | 執行驗證 | 說明 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Codex (CLI, app, SDK) | 是 | — | — | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/codex-integration/codex-inject-missing-config.test.ts)) | — | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/codex-integration/) | +| Claude Code | 是 | — | — | Anthropic Messages (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/claude-integration/claude-cli.test.ts)) | — | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/claude-code/) | +| Claude Desktop | 是 | — | — | Anthropic Messages (已審查) | implemented | — | 由 opencodex 直接設定,沒有匯出的設定檔。 | +| Grok Build | 是 | — | — | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/providers/xai/grok-config-inject.test.ts)) | — | 由 opencodex 直接設定,沒有匯出的設定檔。 [指南](/zh-tw/guides/grok-build/) | +| Qoder | — | JSON | 是 | OpenAI 相容(端點由用戶端選擇) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/qoder-client.test.ts)) | — | 僅限本機監聽。 | +| Qoder CN | — | JSON | 是 | OpenAI 相容(端點由用戶端選擇) | implemented | — | 僅限本機監聽。 | +| WorkBuddy | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/agent-clients.test.ts)) | — | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | +| WorkBuddy AI | — | JSON | 是 | Chat Completions | implemented | — | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | +| OpenCode | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 [指南](/zh-tw/guides/opencode/) | +| Pi | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 僅限本機監聽。 [指南](/zh-tw/guides/pi/) | +| Oh My Pi (omp) | — | YAML | 是 | Chat Completions; Responses (僅原生 OpenAI 模型) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 僅限本機監聽。只修改自己的 YAML 區塊。 | +| Hermes | — | YAML | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/hermes-catalog-refresh.test.ts)) | — | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。只修改自己的 YAML 區塊。 | +| OpenClaw | — | JSON5 | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/gui/integrations-invariants.test.ts)) | — | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 | +| Kimi CLI | — | TOML | 是 | OpenAI 相容(端點由用戶端選擇) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export-new-clients.test.ts)) | — | 僅限本機監聽。 | +| Gajae | — | YAML | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-modality-enum.test.ts)) | — | 僅限本機監聽。 | +| DSH | — | YAML | 是 | Responses | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/config/client-config-export.test.ts)) | — | 僅限本機監聽。寫入用戶端目前的供應商儲存區。只修改自己的 YAML 區塊。透過設定檔旁的鎖定檔協調寫入。 | +| MCode | — | YAML | 是 | Anthropic Messages | implemented | — | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | +| ZCode | — | JSON | 是 | Responses (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/integrations-current-store.test.ts)) | — | 僅限本機監聽。寫入用戶端目前的供應商儲存區。用戶端依此設定選擇端點(已依用戶端的文件行為審查)。 | +| Command Code | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/command-code-client.test.ts)) | — | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | +| Prime | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/prime-client.test.ts)) | — | 僅限本機監聽。 | +| Aside | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/aside-profiles-catalog-refresh.test.ts)) | — | 僅限本機監聽。 | +| Raycast | — | YAML | 是 | Chat Completions (已審查) | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/raycast-client.test.ts)) | — | 僅限本機監聽。用戶端依此設定選擇端點(已依用戶端的文件行為審查)。 | +| omo | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/omo-client.test.ts)) | — | 僅限本機監聽。 | +| Cline | — | JSON | 是 | Responses | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/cline-client.test.ts)) | — | 僅限本機監聽。透過設定檔旁的鎖定檔協調寫入。 | +| Kilo Code | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/client-export-reasoning-controls.test.ts)) | — | 匯出的設定也支援遠端監聽(會加入 API 金鑰標頭)。 | +| Factory Droid | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/droid-client.test.ts)) | — | 僅限本機監聽。 [指南](/zh-tw/guides/factory-droid/) | +| Gentle Shell | — | JSON | 是 | Chat Completions | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/gentle-shell-client.test.ts)) | — | 僅限本機監聽。 | +| T3 Code | — | JSON | 是 | Anthropic Messages | test-covered ([測試](https://github.com/lidge-jun/opencodex/blob/main/tests/clients/t3code-client.test.ts)) | — | 僅限本機監聽。套用前先執行該用戶端專屬的檢查,可能拒絕。 | ## 入站端點 diff --git a/scripts/client-support-matrix/locales.ts b/scripts/client-support-matrix/locales.ts index 814c2ca9def..537f68b5548 100644 --- a/scripts/client-support-matrix/locales.ts +++ b/scripts/client-support-matrix/locales.ts @@ -13,7 +13,10 @@ export interface Strings { implementedDefinition: string; testCoveredDefinition: string; clientsHeading: string; - columns: { client: string; native: string; configExport: string; managed: string; protocol: string; level: string; notes: string }; + columns: { client: string; native: string; configExport: string; managed: string; protocol: string; level: string; runtime: string; notes: string }; + runtimeDefinition: string; + evidenceLink: string; + runtimeMore: string; yes: string; curated: string; nativeOpenaiCondition: string; @@ -45,7 +48,10 @@ export const LOCALES = { implementedDefinition: "the client is in the registry. For exported configs, the protocol shown is read from the config opencodex generates, and generation fails if it changes. A protocol marked *curated* names an endpoint that comes from a review instead of the config itself (native clients, and clients that turn a setting into a specific endpoint); the generator checks that the reviewed code is still in place. \"OpenAI-compatible\" rows make no endpoint claim.", testCoveredDefinition: "implemented, and a test case chosen in review that exercises this client is linked. The generator checks only that the test still exists and is enabled; read it to see what it asserts.", clientsHeading: "Clients", - columns: { client: "Client", native: "Native", configExport: "Config export", managed: "Managed file integration", protocol: "Inbound protocol", level: "Support level", notes: "Notes" }, + columns: { client: "Client", native: "Native", configExport: "Config export", managed: "Managed file integration", protocol: "Inbound protocol", level: "Support level", runtime: "Runtime verified", notes: "Notes" }, + runtimeDefinition: "a historical observation: a person ran the exact client product and version shown against the opencodex version shown, for the stated scenario, on that date, and linked the evidence. It says nothing about other versions or products. The evidence is reviewed when a result is added; the generator does not re-run it. A dash means no result is recorded.", + evidenceLink: "evidence", + runtimeMore: "(+{n} more recorded)", yes: "Yes", curated: "curated", nativeOpenaiCondition: "native OpenAI models only", @@ -75,7 +81,10 @@ export const LOCALES = { implementedDefinition: "le client figure dans le registre. Pour les configurations exportées, le protocole indiqué est lu dans la configuration générée par opencodex, et la génération échoue s’il change. Un protocole marqué *revu* désigne un point de terminaison issu d’une revue plutôt que de la configuration elle-même (clients natifs, et clients qui déduisent un point de terminaison précis d’un réglage) ; le générateur vérifie que le code revu est toujours en place. Les lignes « Compatible OpenAI » n’affirment aucun point de terminaison.", testCoveredDefinition: "implemented, et un cas de test choisi en revue qui exerce ce client est lié. Le générateur vérifie seulement que le test existe toujours et qu’il est actif ; lisez-le pour savoir ce qu’il vérifie.", clientsHeading: "Clients", - columns: { client: "Client", native: "Natif", configExport: "Export de configuration", managed: "Intégration de fichier gérée", protocol: "Protocole entrant", level: "Niveau", notes: "Remarques" }, + columns: { client: "Client", native: "Natif", configExport: "Export de configuration", managed: "Intégration de fichier gérée", protocol: "Protocole entrant", level: "Niveau", runtime: "Vérifié à l’exécution", notes: "Remarques" }, + runtimeDefinition: "une observation datée : une personne a exécuté le produit et la version du client indiqués avec la version d’opencodex indiquée, pour le scénario décrit, à cette date, et a lié la preuve. Cela ne dit rien des autres versions ou produits. La preuve est revue lors de l’ajout du résultat ; le générateur ne la rejoue pas. Un tiret signifie qu’aucun résultat n’est enregistré.", + evidenceLink: "preuve", + runtimeMore: "(+{n} autres enregistrés)", yes: "Oui", curated: "revu", nativeOpenaiCondition: "modèles OpenAI natifs uniquement", @@ -105,7 +114,10 @@ export const LOCALES = { implementedDefinition: "레지스트리에 등록된 클라이언트입니다. 내보내는 설정의 경우 표의 프로토콜은 opencodex가 생성하는 설정에서 읽으며, 바뀌면 생성이 실패합니다. *검토됨* 표시는 설정 자체가 아니라 리뷰에서 나온 특정 엔드포인트를 뜻합니다(네이티브 클라이언트, 설정값을 특정 엔드포인트로 바꾸는 클라이언트). 생성기는 리뷰한 코드가 그대로 있는지 확인합니다. 'OpenAI 호환' 행은 엔드포인트를 단정하지 않습니다.", testCoveredDefinition: "implemented이면서, 리뷰로 고른 이 클라이언트의 테스트 케이스가 링크되어 있습니다. 생성기는 테스트가 존재하고 비활성화되지 않았는지만 확인하므로, 무엇을 검증하는지는 테스트를 직접 읽어 보세요.", clientsHeading: "클라이언트", - columns: { client: "클라이언트", native: "네이티브", configExport: "설정 내보내기", managed: "관리형 파일 연동", protocol: "수신 프로토콜", level: "지원 수준", notes: "참고" }, + columns: { client: "클라이언트", native: "네이티브", configExport: "설정 내보내기", managed: "관리형 파일 연동", protocol: "수신 프로토콜", level: "지원 수준", runtime: "실행 검증", notes: "참고" }, + runtimeDefinition: "특정 시점의 관찰 기록입니다. 표에 적힌 클라이언트 제품·버전을 표에 적힌 opencodex 버전과 함께, 명시한 시나리오로, 그 날짜에 사람이 실행하고 증거를 링크했습니다. 다른 버전이나 제품에 대해서는 아무것도 보장하지 않습니다. 증거는 결과를 추가할 때 리뷰하며, 생성기가 다시 실행하지 않습니다. 대시(—)는 기록된 결과가 없다는 뜻입니다.", + evidenceLink: "증거", + runtimeMore: "(외 {n}건 기록됨)", yes: "예", curated: "검토됨", nativeOpenaiCondition: "네이티브 OpenAI 모델만", @@ -135,7 +147,10 @@ export const LOCALES = { implementedDefinition: "客户端已在注册表中。对于导出的配置,表中协议读取自 opencodex 生成的配置,一旦变化生成就会失败。标有*已审阅*的协议表示其端点来自审阅而非配置本身(原生客户端,以及会把某个设置转换为特定端点的客户端);生成器会检查被审阅的代码是否仍然存在。“OpenAI 兼容”的行不声明具体端点。", testCoveredDefinition: "满足 implemented,并链接了一个经评审选定、覆盖该客户端的测试用例。生成器只检查该测试仍然存在且未被禁用;它具体断言了什么,请阅读测试本身。", clientsHeading: "客户端", - columns: { client: "客户端", native: "原生", configExport: "配置导出", managed: "托管文件集成", protocol: "入站协议", level: "支持级别", notes: "说明" }, + columns: { client: "客户端", native: "原生", configExport: "配置导出", managed: "托管文件集成", protocol: "入站协议", level: "支持级别", runtime: "运行验证", notes: "说明" }, + runtimeDefinition: "一次历史观察:有人在该日期,用表中所列的 opencodex 版本运行了表中所列的客户端产品与版本,执行了所述场景,并附上证据链接。它不代表其他版本或产品。证据在添加结果时经过评审,生成器不会重新运行。短横线表示尚无记录。", + evidenceLink: "证据", + runtimeMore: "(另有 {n} 条记录)", yes: "是", curated: "已审阅", nativeOpenaiCondition: "仅原生 OpenAI 模型", @@ -165,7 +180,10 @@ export const LOCALES = { implementedDefinition: "用戶端已在登錄表中。對於匯出的設定,表中協定讀取自 opencodex 產生的設定,一旦變更產生就會失敗。標示*已審查*的協定表示其端點來自審查而非設定本身(原生用戶端,以及會把某個設定轉換為特定端點的用戶端);產生器會檢查被審查的程式碼是否仍然存在。「OpenAI 相容」的列不聲明具體端點。", testCoveredDefinition: "符合 implemented,並連結了一個經審查選定、涵蓋該用戶端的測試案例。產生器只檢查該測試仍然存在且未被停用;它實際驗證什麼,請閱讀測試本身。", clientsHeading: "用戶端", - columns: { client: "用戶端", native: "原生", configExport: "設定匯出", managed: "代管檔案整合", protocol: "入站協定", level: "支援等級", notes: "說明" }, + columns: { client: "用戶端", native: "原生", configExport: "設定匯出", managed: "代管檔案整合", protocol: "入站協定", level: "支援等級", runtime: "執行驗證", notes: "說明" }, + runtimeDefinition: "一次歷史觀察:有人在該日期,以表中所列的 opencodex 版本執行了表中所列的用戶端產品與版本,執行了所述情境,並附上證據連結。它不代表其他版本或產品。證據在新增結果時經過審查,產生器不會重新執行。短橫線表示尚無紀錄。", + evidenceLink: "證據", + runtimeMore: "(另有 {n} 筆紀錄)", yes: "是", curated: "已審查", nativeOpenaiCondition: "僅原生 OpenAI 模型", @@ -195,7 +213,10 @@ export const LOCALES = { implementedDefinition: "клиент есть в реестре. Для экспортируемых конфигураций протокол берётся из конфигурации, которую генерирует opencodex, и генерация падает, если он меняется. Пометка *проверено* означает, что эндпоинт взят из ревью, а не из самой конфигурации (нативные клиенты и клиенты, которые превращают настройку в конкретный эндпоинт); генератор проверяет, что проверенный код на месте. Строки «Совместим с OpenAI» не утверждают конкретный эндпоинт.", testCoveredDefinition: "implemented, и есть ссылка на выбранный при ревью тест, который задействует этого клиента. Генератор проверяет только, что тест существует и не отключён; что именно он проверяет, смотрите в самом тесте.", clientsHeading: "Клиенты", - columns: { client: "Клиент", native: "Нативно", configExport: "Экспорт конфигурации", managed: "Управляемая интеграция файла", protocol: "Входящий протокол", level: "Уровень", notes: "Примечания" }, + columns: { client: "Клиент", native: "Нативно", configExport: "Экспорт конфигурации", managed: "Управляемая интеграция файла", protocol: "Входящий протокол", level: "Уровень", runtime: "Проверено в работе", notes: "Примечания" }, + runtimeDefinition: "историческое наблюдение: человек запустил указанные продукт и версию клиента с указанной версией opencodex по описанному сценарию в эту дату и приложил ссылку на подтверждение. Это ничего не говорит о других версиях или продуктах. Подтверждение проверяется при добавлении результата; генератор его не перезапускает. Прочерк означает, что результатов нет.", + evidenceLink: "подтверждение", + runtimeMore: "(ещё записей: {n})", yes: "Да", curated: "проверено", nativeOpenaiCondition: "только нативные модели OpenAI", @@ -225,7 +246,10 @@ export const LOCALES = { implementedDefinition: "レジストリに登録されたクライアントです。エクスポートされる設定では、表のプロトコルは opencodex が生成する設定から読み取られ、変わると生成が失敗します。*レビュー済み*は、エンドポイントが設定そのものではなくレビューに基づくことを示します(ネイティブクライアントと、設定を特定のエンドポイントに変換するクライアント)。レビューしたコードが残っているかを生成時に確認します。「OpenAI 互換」の行は特定のエンドポイントを主張しません。", testCoveredDefinition: "implemented であり、レビューで選んだこのクライアントのテストケースへのリンクがあります。生成時に確認するのはテストが存在し無効化されていないことだけです。何を検証しているかはテストを読んでください。", clientsHeading: "クライアント", - columns: { client: "クライアント", native: "ネイティブ", configExport: "設定エクスポート", managed: "管理ファイル連携", protocol: "受信プロトコル", level: "サポートレベル", notes: "備考" }, + columns: { client: "クライアント", native: "ネイティブ", configExport: "設定エクスポート", managed: "管理ファイル連携", protocol: "受信プロトコル", level: "サポートレベル", runtime: "実行確認", notes: "備考" }, + runtimeDefinition: "ある時点の観察記録です。表のクライアント製品・バージョンを表の opencodex バージョンで、記載のシナリオにより、その日付に人が実行し、証拠をリンクしました。ほかのバージョンや製品については何も示しません。証拠は結果の追加時にレビューされ、生成時に再実行はされません。ダッシュは記録がないことを示します。", + evidenceLink: "証拠", + runtimeMore: "(ほか {n} 件の記録)", yes: "はい", curated: "レビュー済み", nativeOpenaiCondition: "ネイティブ OpenAI モデルのみ", @@ -255,7 +279,10 @@ export const LOCALES = { implementedDefinition: "istemci kayıtta yer alır. Dışa aktarılan yapılandırmalarda gösterilen protokol, opencodex'in ürettiği yapılandırmadan okunur ve değişirse üretim başarısız olur. *İncelendi* işareti, uç noktanın yapılandırmanın kendisinden değil bir incelemeden geldiğini belirtir (yerel istemciler ve bir ayarı belirli bir uç noktaya çeviren istemciler); üretici, incelenen kodun hâlâ yerinde olduğunu denetler. \"OpenAI uyumlu\" satırlar belirli bir uç nokta iddia etmez.", testCoveredDefinition: "implemented olmanın yanında, incelemede seçilmiş ve bu istemciyi çalıştıran bir test durumuna bağlantı verilir. Üretici yalnızca testin hâlâ var ve etkin olduğunu denetler; neyi doğruladığını görmek için testi okuyun.", clientsHeading: "İstemciler", - columns: { client: "İstemci", native: "Yerel", configExport: "Yapılandırma dışa aktarımı", managed: "Yönetilen dosya entegrasyonu", protocol: "Gelen protokol", level: "Destek düzeyi", notes: "Notlar" }, + columns: { client: "İstemci", native: "Yerel", configExport: "Yapılandırma dışa aktarımı", managed: "Yönetilen dosya entegrasyonu", protocol: "Gelen protokol", level: "Destek düzeyi", runtime: "Çalışırken doğrulandı", notes: "Notlar" }, + runtimeDefinition: "tarihli bir gözlem: bir kişi gösterilen istemci ürününü ve sürümünü gösterilen opencodex sürümüyle, belirtilen senaryo için o tarihte çalıştırdı ve kanıtı bağladı. Diğer sürümler veya ürünler hakkında bir şey söylemez. Kanıt sonuç eklenirken incelenir; üretici yeniden çalıştırmaz. Tire, kayıtlı sonuç olmadığı anlamına gelir.", + evidenceLink: "kanıt", + runtimeMore: "(+{n} kayıt daha)", yes: "Evet", curated: "incelendi", nativeOpenaiCondition: "yalnızca yerel OpenAI modelleri", diff --git a/scripts/client-support-matrix/runtime-verified.json b/scripts/client-support-matrix/runtime-verified.json new file mode 100644 index 00000000000..c8f7ae06d78 --- /dev/null +++ b/scripts/client-support-matrix/runtime-verified.json @@ -0,0 +1,4 @@ +{ + "schemaVersion": 1, + "results": [] +} diff --git a/scripts/client-support-matrix/runtime.ts b/scripts/client-support-matrix/runtime.ts new file mode 100644 index 00000000000..85f8b0c1d54 --- /dev/null +++ b/scripts/client-support-matrix/runtime.ts @@ -0,0 +1,146 @@ +/** + * Runtime verification results for the client support page. + * + * scripts/client-support-matrix/runtime-verified.json records that a person ran one exact client + * product version against one opencodex version for a stated scenario, on a stated date, with a link to + * the evidence. It is a reviewed human claim: the generator validates its shape and renders it, but it + * cannot re-run the scenario or prove that the linked page is public or shows success. The file is + * required and starts empty; adding a result is a reviewed change like any other. + */ + +export const RUNTIME_SCHEMA_VERSION = 1; + +export interface RuntimeResult { + client: string; + /** The product variant that was run, e.g. "Codex CLI"; one row can group several products. */ + product: string; + clientVersion: string; + opencodexVersion: string; + /** Calendar date, YYYY-MM-DD. */ + verifiedOn: string; + /** The operation that was exercised, e.g. "streamed chat with one tool call". */ + scenario: string; + /** https URL of the evidence (a pull request, issue or CI run). */ + evidence: string; +} + +export interface SelectedRuntimeResult { + result: RuntimeResult; + /** Other recorded results for the same client that are not shown. */ + others: number; +} + +const TEXT_FIELDS = ["client", "product", "clientVersion", "opencodexVersion", "verifiedOn", "scenario", "evidence"] as const; +// Characters that would break or inject Markdown/HTML in a table cell or link label. +const UNSAFE_TEXT = /[\u0000-\u001f\u007f|[\]<>`()\\]/; + +function isRealDate(value: string): boolean { + const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value); + if (!match) return false; + const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])]; + const date = new Date(Date.UTC(year, month - 1, day)); + return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day; +} + +function evidenceProblem(value: string): string | undefined { + if (/[\s<>()\u0000-\u001f\u007f]/.test(value)) return "contains whitespace, angle brackets, parentheses or control characters"; + let url: URL; + try { + url = new URL(value); + } catch { + return "is not a URL"; + } + if (url.protocol !== "https:") return "must use https"; + if (url.username || url.password) return "must not carry credentials"; + if (!url.hostname.includes(".")) return "must name a public host"; + return undefined; +} + +/** + * Parse and validate the runtime results file. `knownIds` is the set of rows the page renders. + * Returns every problem found rather than stopping at the first. + */ +export function parseRuntimeResults(text: string | undefined, knownIds: ReadonlySet, source = "runtime-verified.json"): { results: RuntimeResult[]; problems: string[] } { + if (text === undefined) return { results: [], problems: [`${source} is missing; it is required (an empty "results" array is valid)`] }; + let parsed: unknown; + try { + parsed = JSON.parse(text); + } catch (error) { + return { results: [], problems: [`${source} is not valid JSON: ${(error as Error).message}`] }; + } + if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) { + return { results: [], problems: [`${source} must be an object with schemaVersion and results`] }; + } + const root = parsed as Record; + const problems: string[] = []; + if (root.schemaVersion !== RUNTIME_SCHEMA_VERSION) problems.push(`${source}: schemaVersion must be ${RUNTIME_SCHEMA_VERSION}`); + const extraKeys = Object.keys(root).filter(key => key !== "schemaVersion" && key !== "results"); + if (extraKeys.length > 0) problems.push(`${source}: unknown top-level keys ${extraKeys.join(", ")}`); + if (!Array.isArray(root.results)) { + problems.push(`${source}: results must be an array`); + return { results: [], problems }; + } + const results: RuntimeResult[] = []; + const seen = new Set(); + root.results.forEach((entry, index) => { + const at = `${source} results[${index}]`; + if (entry === null || typeof entry !== "object" || Array.isArray(entry)) { + problems.push(`${at} must be an object`); + return; + } + const record = entry as Record; + const unknown = Object.keys(record).filter(key => !(TEXT_FIELDS as readonly string[]).includes(key)); + if (unknown.length > 0) problems.push(`${at}: unknown keys ${unknown.join(", ")}`); + let valid = unknown.length === 0; + for (const field of TEXT_FIELDS) { + const value = record[field]; + if (typeof value !== "string" || value.trim() === "" || value !== value.trim()) { + problems.push(`${at}.${field} must be a non-empty string without surrounding whitespace`); + valid = false; + } else if (field !== "evidence" && UNSAFE_TEXT.test(value)) { + problems.push(`${at}.${field} contains characters that cannot appear in the table (| [ ] < > \` ( ) \\ or control characters)`); + valid = false; + } + } + if (!valid) return; + const result = record as unknown as RuntimeResult; + if (!knownIds.has(result.client)) { + problems.push(`${at}.client "${result.client}" is not a row on the client support page`); + return; + } + if (!isRealDate(result.verifiedOn)) { + problems.push(`${at}.verifiedOn "${result.verifiedOn}" is not a real YYYY-MM-DD date`); + return; + } + const evidence = evidenceProblem(result.evidence); + if (evidence) { + problems.push(`${at}.evidence ${evidence}`); + return; + } + const key = [result.client, result.product, result.clientVersion, result.opencodexVersion, result.scenario, result.verifiedOn].join("\u0000"); + if (seen.has(key)) { + problems.push(`${at} duplicates an earlier result for ${result.client} (same product, versions, scenario and date)`); + return; + } + seen.add(key); + results.push(result); + }); + return { results, problems }; +} + +/** The most recently verified result per client; ties break on product, versions and scenario. */ +export function selectRuntimeResults(results: readonly RuntimeResult[]): Map { + const byClient = new Map(); + for (const result of results) byClient.set(result.client, [...(byClient.get(result.client) ?? []), result]); + const selected = new Map(); + for (const [client, list] of byClient) { + const ordered = [...list].sort((a, b) => + b.verifiedOn.localeCompare(a.verifiedOn) + || a.product.localeCompare(b.product) + || a.clientVersion.localeCompare(b.clientVersion) + || a.opencodexVersion.localeCompare(b.opencodexVersion) + || a.scenario.localeCompare(b.scenario)); + selected.set(client, { result: ordered[0]!, others: ordered.length - 1 }); + } + return selected; +} diff --git a/scripts/generate-client-support-matrix.ts b/scripts/generate-client-support-matrix.ts index 42495cc0f08..5ae87f685e1 100644 --- a/scripts/generate-client-support-matrix.ts +++ b/scripts/generate-client-support-matrix.ts @@ -27,8 +27,11 @@ import { type TestReference, } from "./client-support-matrix/metadata"; import { LOCALES, type MatrixLocale, type Strings } from "./client-support-matrix/locales"; +import { parseRuntimeResults, selectRuntimeResults, type SelectedRuntimeResult } from "./client-support-matrix/runtime"; export const NATIVE_ROSTER_FILE = "src/server/management/native-integration-routes.ts"; +/** Reviewed runtime results; OCX_CLIENT_SUPPORT_RUNTIME_DATA points the CLI at another file (tests only). */ +export const RUNTIME_RESULTS_FILE = "scripts/client-support-matrix/runtime-verified.json"; const PAGE = "reference/client-support.md"; const OWNER = "GENERATED by scripts/generate-client-support-matrix.ts"; const REPO_BLOB = "https://github.com/lidge-jun/opencodex/blob/main/"; @@ -77,6 +80,14 @@ export interface MatrixInputs { readRepoFile: (path: string) => string | undefined; exportMetadata: Readonly>; nativeMetadata: Readonly>; + /** Text of the runtime results file; undefined when the file is missing. */ + runtimeResultsText: string | undefined; +} + +function readRuntimeResultsText(): string | undefined { + const override = process.env.OCX_CLIENT_SUPPORT_RUNTIME_DATA; + const file = override && override.trim() !== "" ? override : join(REPO_ROOT, RUNTIME_RESULTS_FILE); + return existsSync(file) ? readFileSync(file, "utf8") : undefined; } export function defaultInputs(): MatrixInputs { @@ -92,6 +103,7 @@ export function defaultInputs(): MatrixInputs { }, exportMetadata: EXPORT_CLIENT_METADATA, nativeMetadata: NATIVE_CLIENT_METADATA, + runtimeResultsText: readRuntimeResultsText(), }; } @@ -108,6 +120,7 @@ export interface MatrixRow { test?: TestReference; notes: NoteKey[]; guide?: string; + runtime?: SelectedRuntimeResult; } export class MatrixError extends Error { @@ -324,6 +337,14 @@ export function collectRows(inputs: MatrixInputs = defaultInputs()): MatrixRow[] rows.push({ id, label: meta.label, native: false, configExport: spec.format.toUpperCase(), managed: integration !== undefined, protocols, curated: meta.clientSelected !== undefined, test: meta.test, notes, guide: meta.guide }); } + const runtime = parseRuntimeResults(inputs.runtimeResultsText, new Set(rows.map(row => row.id)), RUNTIME_RESULTS_FILE); + problems.push(...runtime.problems); + const selected = selectRuntimeResults(runtime.results); + for (const row of rows) { + const result = selected.get(row.id); + if (result) row.runtime = result; + } + if (problems.length > 0) throw new MatrixError(problems); return rows; } @@ -341,6 +362,14 @@ function protocolText(entry: ProtocolCell, strings: Strings): string { return entry.nativeOpenaiOnly ? `${name} (${strings.nativeOpenaiCondition})` : name; } +/** Validated fields carry no Markdown-breaking characters, so they are rendered as written. */ +function runtimeCell(entry: SelectedRuntimeResult | undefined, s: Strings): string { + if (!entry) return "—"; + const r = entry.result; + const more = entry.others > 0 ? ` ${s.runtimeMore.replace("{n}", String(entry.others))}` : ""; + return `${r.product} ${r.clientVersion} + opencodex ${r.opencodexVersion}: ${r.scenario} (${r.verifiedOn}, [${s.evidenceLink}](${r.evidence}))${more}`; +} + export function renderPage(rows: readonly MatrixRow[], locale: MatrixLocale): string { const s: Strings = LOCALES[locale]; const c = s.columns; @@ -359,18 +388,19 @@ export function renderPage(rows: readonly MatrixRow[], locale: MatrixLocale): st "", `- **implemented**: ${s.implementedDefinition}`, `- **test-covered**: ${s.testCoveredDefinition}`, + `- **${s.columns.runtime}**: ${s.runtimeDefinition}`, "", `## ${s.clientsHeading}`, "", - `| ${c.client} | ${c.native} | ${c.configExport} | ${c.managed} | ${c.protocol} | ${c.level} | ${c.notes} |`, - "| --- | --- | --- | --- | --- | --- | --- |", + `| ${c.client} | ${c.native} | ${c.configExport} | ${c.managed} | ${c.protocol} | ${c.level} | ${c.runtime} | ${c.notes} |`, + "| --- | --- | --- | --- | --- | --- | --- | --- |", ]; for (const row of rows) { const protocol = row.protocols.map(entry => protocolText(entry, s)).join("; ") + (row.curated ? ` (${s.curated})` : ""); const level = row.test ? `test-covered ([${s.testLink}](${REPO_BLOB}${row.test.file}))` : "implemented"; const separator = locale === "zh-cn" || locale === "zh-tw" || locale === "ja" ? "" : " "; const notes = row.notes.map(key => s.notes[key]).join(separator) + (row.guide ? ` [${s.guideLink}](${localePrefix(locale)}${row.guide}/)` : ""); - lines.push(`| ${cell(row.label)} | ${row.native ? s.yes : "—"} | ${row.configExport ?? "—"} | ${row.managed ? s.yes : "—"} | ${cell(protocol)} | ${level} | ${cell(notes)} |`); + lines.push(`| ${cell(row.label)} | ${row.native ? s.yes : "—"} | ${row.configExport ?? "—"} | ${row.managed ? s.yes : "—"} | ${cell(protocol)} | ${level} | ${runtimeCell(row.runtime, s)} | ${cell(notes)} |`); } lines.push("", `## ${s.endpointsHeading}`, "", s.endpointsIntro, ""); for (const { protocol, route } of ENDPOINTS) lines.push(`- \`POST ${route}\`: ${PROTOCOL_NAMES[protocol]}`); diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index ef9f1b516c5..3bd3d718ffd 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -44,7 +44,7 @@ "cli-provider-result.test.ts": "cli", "cli-provider-sync-result.test.ts": "cli", "cli-capability-integration-workflows.test.ts": "cli", "cli-capability-account-workflows.test.ts": "cli", "cli-capability-agent-workflows.test.ts": "cli", "cli-capability-operation-workflows.test.ts": "cli", "cli-capability-provider-workflows.test.ts": "cli", "cli-capability-access-workflows.test.ts": "cli", "cli-capability-lab-workflows.test.ts": "cli", "skill-ocx-workflows.test.ts": "ci-workflows", - "cli-capability-data.test.ts": "cli", "skill-ocx-generated.test.ts": "ci-workflows", "client-support-matrix.test.ts": "ci-workflows", + "cli-capability-data.test.ts": "cli", "skill-ocx-generated.test.ts": "ci-workflows", "client-support-matrix.test.ts": "ci-workflows", "client-support-runtime.test.ts": "ci-workflows", "cli-update-restart.test.ts": "cli", "cli-update-restart-child.test.ts": "cli", "cli-update-restart-transport.test.ts": "cli", "cli-update-restart-home.test.ts": "cli", "cli-update-restart-service-record.test.ts": "cli", "cli-update-restart-supervision.test.ts": "cli", "cli-update-restart-admission.test.ts": "cli", "cli-update-restart-desktop-service.test.ts": "cli", "update-reclaim-authorizer.test.ts": "update", "update-windows-wrapper-authority.test.ts": "update", "update-mutation-scanner.test.ts": "update", "update-mutation-boundaries.test.ts": "update", "update-mutation-guard.test.ts": "update", "update-home-launcher.test.ts": "update", "update-home-recovery.test.ts": "update", "update-recovery-authority.test.ts": "update", "update-recovery-boundaries.test.ts": "update", "update-home-worker.test.ts": "update", "update-service-home-policy.test.ts": "update", "update-service-home.test.ts": "update", "update-guarded-ops.test.ts": "update", "update-effect-authority.test.ts": "update", "update-capability-policy.test.ts": "update", "codex-shim-overlay.test.ts": "codex-integration", "codex-shim-migration.test.ts": "codex-integration", diff --git a/structure/clients/client-support-matrix.md b/structure/clients/client-support-matrix.md index 2b7d4764420..2051f8f8b0f 100644 --- a/structure/clients/client-support-matrix.md +++ b/structure/clients/client-support-matrix.md @@ -34,6 +34,19 @@ fields, reviewed claims and linked tests. Generation fails when one no longer ho Locale chrome lives in `scripts/client-support-matrix/locales.ts`; every locale defines every string. +## Runtime results + +`scripts/client-support-matrix/runtime-verified.json` is required and starts with an empty `results` array. +Each result is a reviewed human claim: one exact client product and version, run against one opencodex +version, for a stated scenario, on a real calendar date, with an https evidence link. +`scripts/client-support-matrix/runtime.ts` refuses malformed files, unknown rows, impossible dates, +Markdown-breaking text, credentials in links and exact duplicates. The page shows the most recent result per +client in its own column (ties break on product, versions and scenario) and counts the rest; the support +level stays independent of it. Generation cannot prove that a linked page is public or shows success, and the +privacy scan cannot either, so evidence is reviewed before a result is committed. +`tests/ci-workflows/client-support-runtime.test.ts` covers the empty state, rendering, selection and every +refusal, including through the CLI via `OCX_CLIENT_SUPPORT_RUNTIME_DATA`. + ## Changing a client Adding or removing a client means adding or removing its metadata and running `bun run docs:clients`. diff --git a/tests/ci-workflows/client-support-runtime.test.ts b/tests/ci-workflows/client-support-runtime.test.ts new file mode 100644 index 00000000000..e579d47bef5 --- /dev/null +++ b/tests/ci-workflows/client-support-runtime.test.ts @@ -0,0 +1,127 @@ +import { describe, expect, test } from "bun:test"; +import { mkdtempSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { collectRows, defaultInputs, renderPage, type MatrixInputs } from "../../scripts/generate-client-support-matrix"; +import { LOCALES, type MatrixLocale } from "../../scripts/client-support-matrix/locales"; +import { parseRuntimeResults, selectRuntimeResults, type RuntimeResult } from "../../scripts/client-support-matrix/runtime"; +import { repoRoot } from "../helpers/repo-root"; + +const BASE = defaultInputs(); +const LOCALE_IDS = Object.keys(LOCALES) as MatrixLocale[]; + +function result(overrides: Partial = {}): RuntimeResult { + return { + client: "codex", product: "Codex CLI", clientVersion: "0.150.0", opencodexVersion: "2.40.0", + verifiedOn: "2026-10-20", scenario: "streamed reply with one tool call", + evidence: "https://github.com/lidge-jun/opencodex/pull/1", ...overrides, + }; +} + +function fileWith(results: unknown[], extra: Record = {}): string { + return JSON.stringify({ schemaVersion: 1, results, ...extra }); +} + +function rowsWith(text: string | undefined) { + return collectRows({ ...BASE, runtimeResultsText: text } satisfies MatrixInputs); +} + +function problems(text: string | undefined, ids = new Set(["codex", "qoder-cn"])): string[] { + return parseRuntimeResults(text, ids, "data.json").problems; +} + +/** The "Runtime verified" cell of every table row on a rendered page. */ +function runtimeCells(page: string): string[] { + return page.split("\n").filter(line => line.startsWith("| ") && !line.startsWith("| ---")) + .slice(1).map(line => line.split(" | ")[6]!); +} + +describe("client support runtime results", () => { + test("the committed file is empty, so no row claims a runtime result in any locale", () => { + const rows = collectRows(); + expect(rows.some(row => row.runtime)).toBe(false); + for (const locale of LOCALE_IDS) { + const page = renderPage(rows, locale); + expect(page).toContain(LOCALES[locale].runtimeDefinition); + expect(new Set(runtimeCells(page))).toEqual(new Set(["—"])); + } + }); + + test("a result renders product, both versions, scenario, date and evidence on native and export rows", () => { + const rows = rowsWith(fileWith([ + result(), + result({ client: "qoder-cn", product: "Qoder CN CLI", clientVersion: "1.1.70", scenario: "single chat turn" }), + ])); + const codex = rows.find(row => row.id === "codex")!; + const qoderCn = rows.find(row => row.id === "qoder-cn")!; + expect(codex.test).toBeDefined(); + expect(qoderCn.test).toBeUndefined(); + for (const locale of LOCALE_IDS) { + const page = renderPage(rows, locale); + const s = LOCALES[locale]; + expect(page).toContain(`Codex CLI 0.150.0 + opencodex 2.40.0: streamed reply with one tool call (2026-10-20, [${s.evidenceLink}](https://github.com/lidge-jun/opencodex/pull/1))`); + expect(page).toContain("Qoder CN CLI 1.1.70 + opencodex 2.40.0: single chat turn (2026-10-20,"); + // The support level is independent of the runtime column: Qoder CN stays implemented. + expect(page.split("\n").find(line => line.startsWith("| Qoder CN |"))).toContain("| implemented |"); + } + }); + + test("the most recent result is shown, ties break on product, and the rest are counted", () => { + const older = result({ verifiedOn: "2026-09-01", opencodexVersion: "2.30.0" }); + const recheck = result({ verifiedOn: "2026-10-25", opencodexVersion: "2.41.0" }); + const otherProduct = result({ verifiedOn: "2026-10-25", product: "Codex App", scenario: "picker shows routed models" }); + const forward = selectRuntimeResults([older, recheck, otherProduct]).get("codex")!; + const reversed = selectRuntimeResults([otherProduct, recheck, older]).get("codex")!; + expect(forward).toEqual(reversed); + expect(forward.result.product).toBe("Codex App"); + expect(forward.others).toBe(2); + const page = renderPage(rowsWith(fileWith([older, recheck])), "root"); + expect(page).toContain("Codex CLI 0.150.0 + opencodex 2.41.0"); + expect(page).toContain("(+1 more recorded)"); + }); + + test("every malformed file or result is refused with a reason", () => { + expect(problems(undefined)).toEqual(['data.json is missing; it is required (an empty "results" array is valid)']); + expect(problems("{")[0]).toStartWith("data.json is not valid JSON"); + expect(problems("[]")).toEqual(["data.json must be an object with schemaVersion and results"]); + expect(problems(JSON.stringify({ schemaVersion: 2, results: [] }))).toEqual(["data.json: schemaVersion must be 1"]); + expect(problems(JSON.stringify({ schemaVersion: 1, results: {} }))).toEqual(["data.json: results must be an array"]); + expect(problems(fileWith([], { note: "x" }))).toEqual(["data.json: unknown top-level keys note"]); + const cases: Array<[Partial | Record, string]> = [ + [{ scenario: " padded" }, "data.json results[0].scenario must be a non-empty string without surrounding whitespace"], + [{ product: "" }, "data.json results[0].product must be a non-empty string without surrounding whitespace"], + [{ clientVersion: 150 } as unknown as Partial, "data.json results[0].clientVersion must be a non-empty string without surrounding whitespace"], + [{ scenario: "chat | tools" }, "data.json results[0].scenario contains characters that cannot appear in the table (| [ ] < > ` ( ) \\ or control characters)"], + [{ notes: "x" }, "data.json results[0]: unknown keys notes"], + [{ client: "nope" }, 'data.json results[0].client "nope" is not a row on the client support page'], + [{ verifiedOn: "2026-02-30" }, 'data.json results[0].verifiedOn "2026-02-30" is not a real YYYY-MM-DD date'], + [{ verifiedOn: "26-10-01" }, 'data.json results[0].verifiedOn "26-10-01" is not a real YYYY-MM-DD date'], + [{ evidence: "http://github.com/x" }, "data.json results[0].evidence must use https"], + // Assembled at runtime: the privacy scan rejects an address-shaped literal. + [{ evidence: ["https://user:pw", "github.com/x"].join("@") }, "data.json results[0].evidence must not carry credentials"], + [{ evidence: "https://github.com/x_(y)" }, "data.json results[0].evidence contains whitespace, angle brackets, parentheses or control characters"], + [{ evidence: "https://localhost/x" }, "data.json results[0].evidence must name a public host"], + ]; + for (const [overrides, message] of cases) { + expect(problems(fileWith([{ ...result(), ...overrides }]))).toEqual([message]); + } + expect(problems(fileWith([result(), result({ evidence: "https://github.com/lidge-jun/opencodex/pull/2" })]))) + .toEqual(["data.json results[1] duplicates an earlier result for codex (same product, versions, scenario and date)"]); + expect(problems(fileWith([result(), result({ scenario: "voice call" })]))).toEqual([]); + }); + + test("the CLI check refuses a bad results file and names the problem", () => { + const dir = mkdtempSync(join(tmpdir(), "ocx-client-support-")); + const bad = join(dir, "runtime.json"); + writeFileSync(bad, fileWith([result({ verifiedOn: "2026-13-01" })])); + const run = (data: string) => Bun.spawnSync(["bun", "scripts/generate-client-support-matrix.ts", "--check"], { + cwd: repoRoot(), env: { ...process.env, OCX_CLIENT_SUPPORT_RUNTIME_DATA: data }, + }); + const invalid = run(bad); + expect(invalid.exitCode).toBe(1); + expect(invalid.stderr.toString()).toContain('verifiedOn "2026-13-01" is not a real YYYY-MM-DD date'); + const missing = run(join(dir, "absent.json")); + expect(missing.exitCode).toBe(1); + expect(missing.stderr.toString()).toContain("is missing; it is required"); + }); +}); diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json index c6fca6464eb..27a55cb5f59 100644 --- a/tests/fixtures/test-layout-expected.json +++ b/tests/fixtures/test-layout-expected.json @@ -175,6 +175,7 @@ "cli-capability-data.test.ts": "cli", "skill-ocx-generated.test.ts": "ci-workflows", "client-support-matrix.test.ts": "ci-workflows", + "client-support-runtime.test.ts": "ci-workflows", "cli-update-restart.test.ts": "cli", "cli-update-restart-child.test.ts": "cli", "cli-update-restart-transport.test.ts": "cli", From 55832c29e9da12720888a284b5225f931fdd9b6d Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 08:05:41 +0900 Subject: [PATCH 6/8] docs: tighten runtime result validation and selection Text and evidence links use allowlists so a cell renders exactly as recorded, evidence must name a public host, ties are ordered by code units, and another data file is only accepted by --check --runtime-data. --- scripts/client-support-matrix/runtime.ts | 38 ++++++++++++----- scripts/generate-client-support-matrix.ts | 15 +++++-- structure/clients/client-support-matrix.md | 5 ++- .../client-support-runtime.test.ts | 42 +++++++++++++++---- 4 files changed, 75 insertions(+), 25 deletions(-) diff --git a/scripts/client-support-matrix/runtime.ts b/scripts/client-support-matrix/runtime.ts index 85f8b0c1d54..45f5a01c80c 100644 --- a/scripts/client-support-matrix/runtime.ts +++ b/scripts/client-support-matrix/runtime.ts @@ -31,8 +31,14 @@ export interface SelectedRuntimeResult { } const TEXT_FIELDS = ["client", "product", "clientVersion", "opencodexVersion", "verifiedOn", "scenario", "evidence"] as const; -// Characters that would break or inject Markdown/HTML in a table cell or link label. -const UNSAFE_TEXT = /[\u0000-\u001f\u007f|[\]<>`()\\]/; +// Allowlist for text cells: letters (with combining marks), digits, spaces and plain punctuation. It keeps +// out Markdown emphasis, table delimiters, links, HTML entities, and control or bidirectional characters, +// so the rendered cell reads exactly as the recorded claim. +const SAFE_TEXT = /^[\p{L}\p{M}\p{N} .,:;/+'’!?#%=-]+$/u; +// Allowlist for evidence links: an https URL with a named host and no characters that are significant in a +// Markdown table or link destination. +const SAFE_URL = /^https:\/\/[A-Za-z0-9.-]+(?::[0-9]+)?(?:\/[A-Za-z0-9._~%!$&'*+,;=:@/-]*)?(?:\?[A-Za-z0-9._~%!$&'*+,;=:@/?-]*)?(?:#[A-Za-z0-9._~%!$&'*+,;=:@/?-]*)?$/; +const LOCAL_HOST_SUFFIXES = [".local", ".localhost", ".internal", ".lan", ".home", ".corp", ".test", ".example", ".invalid", ".arpa"]; function isRealDate(value: string): boolean { const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value); @@ -43,7 +49,7 @@ function isRealDate(value: string): boolean { } function evidenceProblem(value: string): string | undefined { - if (/[\s<>()\u0000-\u001f\u007f]/.test(value)) return "contains whitespace, angle brackets, parentheses or control characters"; + if (/[\s<>()|[\]`\\\u0000-\u001f\u007f]/.test(value)) return "contains whitespace or characters that break the table (| [ ] ( ) < > ` \\)"; let url: URL; try { url = new URL(value); @@ -52,7 +58,12 @@ function evidenceProblem(value: string): string | undefined { } if (url.protocol !== "https:") return "must use https"; if (url.username || url.password) return "must not carry credentials"; - if (!url.hostname.includes(".")) return "must name a public host"; + if (!SAFE_URL.test(value)) return "may only use letters, digits and URL punctuation that is safe in a Markdown link"; + const host = url.hostname.toLowerCase(); + const ipLiteral = /^[0-9.]+$/.test(host) || host.startsWith("["); + if (ipLiteral || !host.includes(".") || host === "localhost" || LOCAL_HOST_SUFFIXES.some(suffix => host.endsWith(suffix))) { + return "must name a public host (not an IP address or a local or internal name)"; + } return undefined; } @@ -97,8 +108,8 @@ export function parseRuntimeResults(text: string | undefined, knownIds: Readonly if (typeof value !== "string" || value.trim() === "" || value !== value.trim()) { problems.push(`${at}.${field} must be a non-empty string without surrounding whitespace`); valid = false; - } else if (field !== "evidence" && UNSAFE_TEXT.test(value)) { - problems.push(`${at}.${field} contains characters that cannot appear in the table (| [ ] < > \` ( ) \\ or control characters)`); + } else if (field !== "evidence" && !SAFE_TEXT.test(value)) { + problems.push(`${at}.${field} may only use letters, digits, spaces and . , : ; / + ' ! ? # % = -`); valid = false; } } @@ -128,6 +139,11 @@ export function parseRuntimeResults(text: string | undefined, knownIds: Readonly return { results, problems }; } +/** Locale-independent total order (UTF-16 code units), so selection never depends on the runtime locale. */ +function byCodeUnits(a: string, b: string): number { + return a < b ? -1 : a > b ? 1 : 0; +} + /** The most recently verified result per client; ties break on product, versions and scenario. */ export function selectRuntimeResults(results: readonly RuntimeResult[]): Map { const byClient = new Map(); @@ -135,11 +151,11 @@ export function selectRuntimeResults(results: readonly RuntimeResult[]): Map(); for (const [client, list] of byClient) { const ordered = [...list].sort((a, b) => - b.verifiedOn.localeCompare(a.verifiedOn) - || a.product.localeCompare(b.product) - || a.clientVersion.localeCompare(b.clientVersion) - || a.opencodexVersion.localeCompare(b.opencodexVersion) - || a.scenario.localeCompare(b.scenario)); + byCodeUnits(b.verifiedOn, a.verifiedOn) + || byCodeUnits(a.product, b.product) + || byCodeUnits(a.clientVersion, b.clientVersion) + || byCodeUnits(a.opencodexVersion, b.opencodexVersion) + || byCodeUnits(a.scenario, b.scenario)); selected.set(client, { result: ordered[0]!, others: ordered.length - 1 }); } return selected; diff --git a/scripts/generate-client-support-matrix.ts b/scripts/generate-client-support-matrix.ts index 5ae87f685e1..dc98b8fa416 100644 --- a/scripts/generate-client-support-matrix.ts +++ b/scripts/generate-client-support-matrix.ts @@ -30,7 +30,7 @@ import { LOCALES, type MatrixLocale, type Strings } from "./client-support-matri import { parseRuntimeResults, selectRuntimeResults, type SelectedRuntimeResult } from "./client-support-matrix/runtime"; export const NATIVE_ROSTER_FILE = "src/server/management/native-integration-routes.ts"; -/** Reviewed runtime results; OCX_CLIENT_SUPPORT_RUNTIME_DATA points the CLI at another file (tests only). */ +/** Reviewed runtime results. `--check --runtime-data ` validates another file as well (tests only). */ export const RUNTIME_RESULTS_FILE = "scripts/client-support-matrix/runtime-verified.json"; const PAGE = "reference/client-support.md"; const OWNER = "GENERATED by scripts/generate-client-support-matrix.ts"; @@ -84,9 +84,7 @@ export interface MatrixInputs { runtimeResultsText: string | undefined; } -function readRuntimeResultsText(): string | undefined { - const override = process.env.OCX_CLIENT_SUPPORT_RUNTIME_DATA; - const file = override && override.trim() !== "" ? override : join(REPO_ROOT, RUNTIME_RESULTS_FILE); +function readRuntimeResultsText(file = join(REPO_ROOT, RUNTIME_RESULTS_FILE)): string | undefined { return existsSync(file) ? readFileSync(file, "utf8") : undefined; } @@ -422,8 +420,17 @@ export function renderAll(inputs: MatrixInputs = defaultInputs()): Record= 0 && (!check || !process.argv[dataFlag + 1])) { + console.error("--runtime-data is only accepted together with --check; pages are always generated from the committed file."); + process.exit(1); + } let pages: Record; try { + if (dataFlag >= 0) { + // Validate the extra file against the same rows; the committed file is still checked below. + collectRows({ ...defaultInputs(), runtimeResultsText: readRuntimeResultsText(process.argv[dataFlag + 1]!) }); + } pages = renderAll(); } catch (error) { console.error(error instanceof MatrixError ? error.message : error); diff --git a/structure/clients/client-support-matrix.md b/structure/clients/client-support-matrix.md index 2051f8f8b0f..a2dd114b591 100644 --- a/structure/clients/client-support-matrix.md +++ b/structure/clients/client-support-matrix.md @@ -40,12 +40,13 @@ Locale chrome lives in `scripts/client-support-matrix/locales.ts`; every locale Each result is a reviewed human claim: one exact client product and version, run against one opencodex version, for a stated scenario, on a real calendar date, with an https evidence link. `scripts/client-support-matrix/runtime.ts` refuses malformed files, unknown rows, impossible dates, -Markdown-breaking text, credentials in links and exact duplicates. The page shows the most recent result per +text outside a plain-punctuation allowlist, links that are not https on a named public host or that carry +credentials or table-breaking characters, and exact duplicates. Ties are ordered by code units, not locale. The page shows the most recent result per client in its own column (ties break on product, versions and scenario) and counts the rest; the support level stays independent of it. Generation cannot prove that a linked page is public or shows success, and the privacy scan cannot either, so evidence is reviewed before a result is committed. `tests/ci-workflows/client-support-runtime.test.ts` covers the empty state, rendering, selection and every -refusal, including through the CLI via `OCX_CLIENT_SUPPORT_RUNTIME_DATA`. +refusal, including through the CLI (`--check --runtime-data `, which never writes pages). ## Changing a client diff --git a/tests/ci-workflows/client-support-runtime.test.ts b/tests/ci-workflows/client-support-runtime.test.ts index e579d47bef5..8aa331c9a7d 100644 --- a/tests/ci-workflows/client-support-runtime.test.ts +++ b/tests/ci-workflows/client-support-runtime.test.ts @@ -36,6 +36,10 @@ function runtimeCells(page: string): string[] { .slice(1).map(line => line.split(" | ")[6]!); } +const TEXT_RULE = (field: string) => `data.json results[0].${field} may only use letters, digits, spaces and . , : ; / + ' ! ? # % = -`; +const URL_BREAKS = "data.json results[0].evidence contains whitespace or characters that break the table (| [ ] ( ) < > ` \\)"; +const PUBLIC_HOST = "data.json results[0].evidence must name a public host (not an IP address or a local or internal name)"; + describe("client support runtime results", () => { test("the committed file is empty, so no row claims a runtime result in any locale", () => { const rows = collectRows(); @@ -75,6 +79,10 @@ describe("client support runtime results", () => { expect(forward).toEqual(reversed); expect(forward.result.product).toBe("Codex App"); expect(forward.others).toBe(2); + // Canonically equivalent strings are still distinct, and the order does not depend on the locale. + const composed = result({ product: "Caf\u00e9 CLI" }); + const decomposed = result({ product: "Cafe\u0301 CLI" }); + expect(selectRuntimeResults([composed, decomposed]).get("codex")).toEqual(selectRuntimeResults([decomposed, composed]).get("codex")); const page = renderPage(rowsWith(fileWith([older, recheck])), "root"); expect(page).toContain("Codex CLI 0.150.0 + opencodex 2.41.0"); expect(page).toContain("(+1 more recorded)"); @@ -91,7 +99,11 @@ describe("client support runtime results", () => { [{ scenario: " padded" }, "data.json results[0].scenario must be a non-empty string without surrounding whitespace"], [{ product: "" }, "data.json results[0].product must be a non-empty string without surrounding whitespace"], [{ clientVersion: 150 } as unknown as Partial, "data.json results[0].clientVersion must be a non-empty string without surrounding whitespace"], - [{ scenario: "chat | tools" }, "data.json results[0].scenario contains characters that cannot appear in the table (| [ ] < > ` ( ) \\ or control characters)"], + [{ scenario: "chat | tools" }, TEXT_RULE("scenario")], + [{ clientVersion: "*1.2*" }, TEXT_RULE("clientVersion")], + [{ scenario: "a | b" }, TEXT_RULE("scenario")], + [{ product: "Codex\u202eILC" }, TEXT_RULE("product")], + [{ scenario: "tool_call" }, TEXT_RULE("scenario")], [{ notes: "x" }, "data.json results[0]: unknown keys notes"], [{ client: "nope" }, 'data.json results[0].client "nope" is not a row on the client support page'], [{ verifiedOn: "2026-02-30" }, 'data.json results[0].verifiedOn "2026-02-30" is not a real YYYY-MM-DD date'], @@ -99,8 +111,15 @@ describe("client support runtime results", () => { [{ evidence: "http://github.com/x" }, "data.json results[0].evidence must use https"], // Assembled at runtime: the privacy scan rejects an address-shaped literal. [{ evidence: ["https://user:pw", "github.com/x"].join("@") }, "data.json results[0].evidence must not carry credentials"], - [{ evidence: "https://github.com/x_(y)" }, "data.json results[0].evidence contains whitespace, angle brackets, parentheses or control characters"], - [{ evidence: "https://localhost/x" }, "data.json results[0].evidence must name a public host"], + [{ evidence: "https://github.com/x_(y)" }, URL_BREAKS], + [{ evidence: "https://example.com/a|b" }, URL_BREAKS], + [{ evidence: "https://localhost/x" }, PUBLIC_HOST], + [{ evidence: "https://127.0.0.1/a" }, PUBLIC_HOST], + [{ evidence: "https://10.0.0.8/a" }, PUBLIC_HOST], + // IPv6 literals need brackets, which the table rule already refuses. + [{ evidence: "https://[::1]/a" }, URL_BREAKS], + [{ evidence: "https://service.local/a" }, PUBLIC_HOST], + [{ evidence: "https://ci.internal/run" }, PUBLIC_HOST], ]; for (const [overrides, message] of cases) { expect(problems(fileWith([{ ...result(), ...overrides }]))).toEqual([message]); @@ -114,14 +133,21 @@ describe("client support runtime results", () => { const dir = mkdtempSync(join(tmpdir(), "ocx-client-support-")); const bad = join(dir, "runtime.json"); writeFileSync(bad, fileWith([result({ verifiedOn: "2026-13-01" })])); - const run = (data: string) => Bun.spawnSync(["bun", "scripts/generate-client-support-matrix.ts", "--check"], { - cwd: repoRoot(), env: { ...process.env, OCX_CLIENT_SUPPORT_RUNTIME_DATA: data }, - }); - const invalid = run(bad); + const run = (...args: string[]) => Bun.spawnSync(["bun", "scripts/generate-client-support-matrix.ts", ...args], { cwd: repoRoot() }); + const invalid = run("--check", "--runtime-data", bad); expect(invalid.exitCode).toBe(1); expect(invalid.stderr.toString()).toContain('verifiedOn "2026-13-01" is not a real YYYY-MM-DD date'); - const missing = run(join(dir, "absent.json")); + const missing = run("--check", "--runtime-data", join(dir, "absent.json")); expect(missing.exitCode).toBe(1); expect(missing.stderr.toString()).toContain("is missing; it is required"); + // Pages are never written from another file. + const write = run("--runtime-data", bad); + expect(write.exitCode).toBe(1); + expect(write.stderr.toString()).toContain("only accepted together with --check"); + // The environment variable from an earlier revision has no effect. + const ambient = Bun.spawnSync(["bun", "scripts/generate-client-support-matrix.ts", "--check"], { + cwd: repoRoot(), env: { ...process.env, OCX_CLIENT_SUPPORT_RUNTIME_DATA: bad }, + }); + expect(ambient.exitCode).toBe(0); }); }); From c7b2f7051069b26b2d6fd9632b9c0dee4fb19aed Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 08:19:37 +0900 Subject: [PATCH 7/8] docs: refuse local evidence hosts written with a trailing dot --- scripts/client-support-matrix/runtime.ts | 3 ++- tests/ci-workflows/client-support-runtime.test.ts | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/client-support-matrix/runtime.ts b/scripts/client-support-matrix/runtime.ts index 45f5a01c80c..cf046477435 100644 --- a/scripts/client-support-matrix/runtime.ts +++ b/scripts/client-support-matrix/runtime.ts @@ -59,7 +59,8 @@ function evidenceProblem(value: string): string | undefined { if (url.protocol !== "https:") return "must use https"; if (url.username || url.password) return "must not carry credentials"; if (!SAFE_URL.test(value)) return "may only use letters, digits and URL punctuation that is safe in a Markdown link"; - const host = url.hostname.toLowerCase(); + // A trailing root dot ("localhost.") names the same host; drop it before the checks below. + const host = url.hostname.toLowerCase().replace(/\.$/, ""); const ipLiteral = /^[0-9.]+$/.test(host) || host.startsWith("["); if (ipLiteral || !host.includes(".") || host === "localhost" || LOCAL_HOST_SUFFIXES.some(suffix => host.endsWith(suffix))) { return "must name a public host (not an IP address or a local or internal name)"; diff --git a/tests/ci-workflows/client-support-runtime.test.ts b/tests/ci-workflows/client-support-runtime.test.ts index 8aa331c9a7d..dd560a83052 100644 --- a/tests/ci-workflows/client-support-runtime.test.ts +++ b/tests/ci-workflows/client-support-runtime.test.ts @@ -120,6 +120,9 @@ describe("client support runtime results", () => { [{ evidence: "https://[::1]/a" }, URL_BREAKS], [{ evidence: "https://service.local/a" }, PUBLIC_HOST], [{ evidence: "https://ci.internal/run" }, PUBLIC_HOST], + [{ evidence: "https://localhost./x" }, PUBLIC_HOST], + [{ evidence: "https://service.local./x" }, PUBLIC_HOST], + [{ evidence: "https://ci.internal./x" }, PUBLIC_HOST], ]; for (const [overrides, message] of cases) { expect(problems(fileWith([{ ...result(), ...overrides }]))).toEqual([message]); From f723d3e3e49d86d27367c56cf9ff4c836f51b8fc Mon Sep 17 00:00:00 2001 From: JUN Date: Thu, 29 Oct 2026 08:35:06 +0900 Subject: [PATCH 8/8] docs: reject autolinks in text fields, repeated dots in hosts, and uncouple empty-state test --- scripts/client-support-matrix/runtime.ts | 10 ++++++++-- tests/ci-workflows/client-support-runtime.test.ts | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/client-support-matrix/runtime.ts b/scripts/client-support-matrix/runtime.ts index cf046477435..517a53ed88c 100644 --- a/scripts/client-support-matrix/runtime.ts +++ b/scripts/client-support-matrix/runtime.ts @@ -35,6 +35,8 @@ const TEXT_FIELDS = ["client", "product", "clientVersion", "opencodexVersion", " // out Markdown emphasis, table delimiters, links, HTML entities, and control or bidirectional characters, // so the rendered cell reads exactly as the recorded claim. const SAFE_TEXT = /^[\p{L}\p{M}\p{N} .,:;/+'’!?#%=-]+$/u; +// Reject tokens that GitHub-flavored Markdown would autolink inside a free-text cell. +const AUTOLINK_LIKE = /:\/\/|\bwww\./i; // Allowlist for evidence links: an https URL with a named host and no characters that are significant in a // Markdown table or link destination. const SAFE_URL = /^https:\/\/[A-Za-z0-9.-]+(?::[0-9]+)?(?:\/[A-Za-z0-9._~%!$&'*+,;=:@/-]*)?(?:\?[A-Za-z0-9._~%!$&'*+,;=:@/?-]*)?(?:#[A-Za-z0-9._~%!$&'*+,;=:@/?-]*)?$/; @@ -60,9 +62,10 @@ function evidenceProblem(value: string): string | undefined { if (url.username || url.password) return "must not carry credentials"; if (!SAFE_URL.test(value)) return "may only use letters, digits and URL punctuation that is safe in a Markdown link"; // A trailing root dot ("localhost.") names the same host; drop it before the checks below. - const host = url.hostname.toLowerCase().replace(/\.$/, ""); + const rawHost = url.hostname.toLowerCase(); + const host = rawHost.replace(/\.+$/, ""); const ipLiteral = /^[0-9.]+$/.test(host) || host.startsWith("["); - if (ipLiteral || !host.includes(".") || host === "localhost" || LOCAL_HOST_SUFFIXES.some(suffix => host.endsWith(suffix))) { + if (rawHost.startsWith(".") || rawHost.includes("..") || ipLiteral || !host.includes(".") || host === "localhost" || LOCAL_HOST_SUFFIXES.some(suffix => host.endsWith(suffix))) { return "must name a public host (not an IP address or a local or internal name)"; } return undefined; @@ -112,6 +115,9 @@ export function parseRuntimeResults(text: string | undefined, knownIds: Readonly } else if (field !== "evidence" && !SAFE_TEXT.test(value)) { problems.push(`${at}.${field} may only use letters, digits, spaces and . , : ; / + ' ! ? # % = -`); valid = false; + } else if (field !== "evidence" && AUTOLINK_LIKE.test(value)) { + problems.push(`${at}.${field} must not contain a URL; put the link in evidence`); + valid = false; } } if (!valid) return; diff --git a/tests/ci-workflows/client-support-runtime.test.ts b/tests/ci-workflows/client-support-runtime.test.ts index dd560a83052..b4af057dc9a 100644 --- a/tests/ci-workflows/client-support-runtime.test.ts +++ b/tests/ci-workflows/client-support-runtime.test.ts @@ -41,8 +41,9 @@ const URL_BREAKS = "data.json results[0].evidence contains whitespace or charact const PUBLIC_HOST = "data.json results[0].evidence must name a public host (not an IP address or a local or internal name)"; describe("client support runtime results", () => { - test("the committed file is empty, so no row claims a runtime result in any locale", () => { - const rows = collectRows(); + test("an empty results file leaves every row with a dash in every locale, and the committed file is valid", () => { + expect(() => collectRows()).not.toThrow(); + const rows = rowsWith(fileWith([])); expect(rows.some(row => row.runtime)).toBe(false); for (const locale of LOCALE_IDS) { const page = renderPage(rows, locale); @@ -104,6 +105,8 @@ describe("client support runtime results", () => { [{ scenario: "a | b" }, TEXT_RULE("scenario")], [{ product: "Codex\u202eILC" }, TEXT_RULE("product")], [{ scenario: "tool_call" }, TEXT_RULE("scenario")], + [{ scenario: "see https://10.0.0.8/a" }, "data.json results[0].scenario must not contain a URL; put the link in evidence"], + [{ product: "www.internal.example/x" }, "data.json results[0].product must not contain a URL; put the link in evidence"], [{ notes: "x" }, "data.json results[0]: unknown keys notes"], [{ client: "nope" }, 'data.json results[0].client "nope" is not a row on the client support page'], [{ verifiedOn: "2026-02-30" }, 'data.json results[0].verifiedOn "2026-02-30" is not a real YYYY-MM-DD date'], @@ -123,6 +126,9 @@ describe("client support runtime results", () => { [{ evidence: "https://localhost./x" }, PUBLIC_HOST], [{ evidence: "https://service.local./x" }, PUBLIC_HOST], [{ evidence: "https://ci.internal./x" }, PUBLIC_HOST], + [{ evidence: "https://localhost../x" }, PUBLIC_HOST], + [{ evidence: "https://service.local../x" }, PUBLIC_HOST], + [{ evidence: "https://example..com/x" }, PUBLIC_HOST], ]; for (const [overrides, message] of cases) { expect(problems(fileWith([{ ...result(), ...overrides }]))).toEqual([message]);