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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions docs-site/src/content/docs/fr/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,10 @@ Si Kiro s’arrête sans appeler l’outil d’achèvement, l’adaptateur effec
## `azure-openai` (alias : `azure`)

**Cibles :** **Azure OpenAI**. Encapsule `openai-responses` (et utilise donc également `passthrough: true`).
**Authentification :** `key` au moyen de l’en-tête `api-key` (et non Bearer).
**Authentification :** `key` au moyen de l’en-tête `api-key` si `apiKey` est configuré. Sinon,
l’adaptateur utilise Microsoft Entra ID via `DefaultAzureCredential` et envoie `Authorization: Bearer`.

- Délègue la construction de la requête au relais Responses, vérifie que `baseUrl` ne contient aucun espace réservé de modèle non résolu et remplace `Authorization` par `api-key`. L’URL configurée cible directement l’API Responses v1 d’Azure ; l’adaptateur n’ajoute donc pas `api-version`.
- Délègue la construction de la requête au relais Responses et vérifie que `baseUrl` ne contient aucun espace réservé de modèle non résolu. Les requêtes avec clé utilisent `api-key`; les requêtes sans clé obtiennent un jeton Entra via `DefaultAzureCredential` et utilisent `Authorization: Bearer`. L’URL configurée cible directement l’API Responses v1 d’Azure ; l’adaptateur n’ajoute donc pas `api-version`.

## Utilitaires d’image (`image.ts`)

Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/content/docs/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ labels local presets separately; those normally omit both `authMode` and `apiKey
| `forward` | Relays **your incoming Codex auth headers** verbatim to the provider — no key stored. This is the ChatGPT-login passthrough. | OpenAI (`openai-responses` adapter). |
| `oauth` | Resolves a stored OAuth access token (auto-refreshed before expiry) and uses it as the bearer key. | xAI, Anthropic, Kimi, Kiro, Google Antigravity, Cursor, Command Code, GitHub Copilot, Nous Portal. |

Azure OpenAI has one provider-specific option: when its `apiKey` is omitted, the Azure adapter uses
Microsoft Entra ID through `DefaultAzureCredential` and sends a bearer token. Supplying `apiKey`
keeps the existing `api-key` authentication path.

The [`retryOn429`](/reference/configuration/) same-key 429 replay applies only to API-key
providers (`authMode: "key"`). OAuth, forward, and local presets are excluded — their
credentials must never be replayed on the same token, and local runtimes have no remote key to
Expand Down
5 changes: 3 additions & 2 deletions docs-site/src/content/docs/ja/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,10 @@ model discovery の両方に適用されます。
## `azure-openai`(別名: `azure`)

**対象:** **Azure OpenAI**。`openai-responses` を包むため、同じく `passthrough: true` です。
**認証:** `api-key` ヘッダーの `key`(Bearer ではない)。
**認証:** `apiKey` が設定されている場合は `api-key` ヘッダーの `key`。未設定の場合は
`DefaultAzureCredential` 経由で Microsoft Entra ID を使い、`Authorization: Bearer` を送信します。

- リクエスト構成は Responses passthrough に任せます。`baseUrl` に未解釈のテンプレート placeholder がないか検証し、`Authorization` を `api-key` に差し替えます。設定 URL が Azure v1 Responses API を直接指すため、`api-version` は追加しません。
- リクエスト構成は Responses passthrough に任せ、`baseUrl` に未解釈のテンプレート placeholder がないか検証します。API キー使用時は `api-key` を使い、キーがない場合は `DefaultAzureCredential` で Entra トークンを取得して `Authorization: Bearer` を使います。設定 URL が Azure v1 Responses API を直接指すため、`api-version` は追加しません。

## 画像ユーティリティ(`image.ts`)

Expand Down
8 changes: 5 additions & 3 deletions docs-site/src/content/docs/ko/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,12 @@ discovery에 모두 적용됩니다.
## `azure-openai` (별칭: `azure`)

**대상:** **Azure OpenAI**. `openai-responses`를 감싸므로 마찬가지로 `passthrough: true`입니다.
**인증:** `api-key` 헤더의 `key`(Bearer 아님).
**인증:** `apiKey`가 설정되면 `api-key` 헤더의 `key`를 사용합니다. 설정되지 않으면
`DefaultAzureCredential`을 통해 Microsoft Entra ID를 사용하고 `Authorization: Bearer`를 보냅니다.

- 요청 구성은 Responses passthrough에 맡깁니다. `baseUrl`에 해석되지 않은 템플릿 placeholder가
없는지 검증하고 `Authorization`을 `api-key`로 바꿉니다. 설정 URL이 Azure v1 Responses API를
- 요청 구성은 Responses passthrough에 맡기고 `baseUrl`에 해석되지 않은 템플릿 placeholder가
없는지 검증합니다. API 키 요청은 `api-key`를 사용하고, 키가 없으면 `DefaultAzureCredential`로
Entra 토큰을 가져와 `Authorization: Bearer`를 사용합니다. 설정 URL이 Azure v1 Responses API를
직접 가리키므로 `api-version`은 덧붙이지 않습니다.

## 이미지 유틸리티 (`image.ts`)
Expand Down
14 changes: 9 additions & 5 deletions docs-site/src/content/docs/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,15 @@ compatibility pair: `agent.v1.AgentService/RunSSE` for server output and
## `azure-openai` (alias: `azure`)

**Targets:** **Azure OpenAI**. Wraps `openai-responses` (so also `passthrough: true`).
**Auth:** `key` via the `api-key` header (not Bearer).

- Delegates request building to the Responses passthrough, validates that `baseUrl` contains no
unresolved template placeholder, and replaces `Authorization` with `api-key`. The configured URL
targets Azure's v1 Responses API directly, so the adapter does not append `api-version`.
**Auth:** `key` via the `api-key` header when `apiKey` is configured. When it is omitted, the
adapter uses Microsoft Entra ID through `DefaultAzureCredential` and sends `Authorization: Bearer`.

- Delegates request building to the Responses passthrough and validates that `baseUrl` contains no
unresolved template placeholder. API-key requests use `api-key`; keyless (Entra-authenticated)
requests acquire an Entra token with `DefaultAzureCredential` and use `Authorization: Bearer`.
Keyless authentication requires an HTTPS endpoint; HTTP endpoints are rejected before token
acquisition. The configured URL targets Azure's v1 Responses API directly, so the adapter does
not append `api-version`.

## Image utilities (`image.ts`)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ predictions. Explicit provider/model price overrides still take precedence.
| `promptCacheKey?` | `boolean` | Provider-wide `openai-chat` opt-in for forwarding a `prompt_cache_key`. The adapter forwards the key it is given and never invents one, but the key is not always the caller's: Claude Messages translation derives one from `metadata.user_id`, or from a model/system/tools cohort when no metadata is sent. Default off. Enable only when the upstream documents support, because strict gateways may reject the unknown field with HTTP 400. |
| `preserveResponsesReasoningContent?` | `boolean` | Keep plaintext reasoning content on replayed Responses reasoning items instead of blanking it (blanking is the ChatGPT backend's rule). Enable for upstreams whose contract accepts reasoning replay, such as DeepSeek. Proxy-minted `ocxr1` envelopes are always stripped. |
| `disabled?` | `boolean` | Keep the provider on disk but exclude it from routing and model/catalog listings. |
| `apiKey?` | `string` | API key, an `${ENV_VAR}` / `$ENV_VAR` reference, or a `keychain:<provider>` reference written by `ocx provider keychain <name> store`. References resolve at request time. See [Storing keys in the OS keychain](#storing-keys-in-the-os-keychain). |
| `apiKey?` | `string` | API key, an `${ENV_VAR}` / `$ENV_VAR` reference, or a `keychain:<provider>` reference written by `ocx provider keychain <name> store`. References resolve at request time. For `azure-openai` and its `azure` alias, omitting this field enables Microsoft Entra ID through `DefaultAzureCredential`. See [Storing keys in the OS keychain](#storing-keys-in-the-os-keychain). |
| `apiKeyTransport?` | `"x-api-key" \| "bearer"` | Anthropic key header style. Defaults to native `x-api-key`; valid only for key-auth `anthropic` providers. |
| `apiKeyPool?` | `ApiKeyPoolEntry[]` | Multi-key pool. `apiKey` mirrors the active entry; each item has `id`, `key`, optional `label`, and optional numeric `addedAt`. |
| `defaultModel?` | `string` | Model used when this provider is selected without an explicit model. |
Expand Down
14 changes: 8 additions & 6 deletions docs-site/src/content/docs/ru/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,14 @@ authorization.

**Назначение:** **Azure OpenAI**. Обёртка над `openai-responses` (поэтому тоже
`passthrough: true`).
**Аутентификация:** `key` через заголовок `api-key` (не Bearer).

- Делегирует построение запроса passthrough-адаптеру Responses, проверяет, что `baseUrl` не
содержит неразрешённых плейсхолдеров шаблона, и заменяет `Authorization` на `api-key`.
Настроенный URL указывает напрямую на Azure v1 Responses API, поэтому адаптер не добавляет
`api-version`.
**Аутентификация:** `key` через заголовок `api-key`, если задан `apiKey`. Если поле не задано,
адаптер использует Microsoft Entra ID через `DefaultAzureCredential` и отправляет `Authorization: Bearer`.

- Делегирует построение запроса passthrough-адаптеру Responses и проверяет, что `baseUrl` не
содержит неразрешённых плейсхолдеров шаблона. Запросы с API-ключом используют `api-key`, а
запросы без ключа получают токен Entra через `DefaultAzureCredential` и используют
`Authorization: Bearer`. Настроенный URL указывает напрямую на Azure v1 Responses API, поэтому
адаптер не добавляет `api-version`.

## Утилиты для изображений (`image.ts`)

Expand Down
14 changes: 8 additions & 6 deletions docs-site/src/content/docs/tr/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,15 @@ başlığından Cursor OAuth/erişim belirteci.

**Hedefler:** **Azure OpenAI**. `openai-responses`'ı sarar (bu nedenle
`passthrough: true`).
**Kimlik Doğrulama:** `api-key` başlığı aracılığıyla `key` (Bearer değil).
**Kimlik Doğrulama:** `apiKey` ayarlanmışsa `api-key` başlığı aracılığıyla `key` kullanılır.
Ayarlanmadığında adaptör `DefaultAzureCredential` üzerinden Microsoft Entra ID kullanır ve
`Authorization: Bearer` gönderir.

- İstek oluşturmayı Responses doğrudan geçişine devreder, `baseUrl`'in
çözümlenmemiş şablon yer tutucusu içermediğini doğrular ve `Authorization`'ı
`api-key` ile değiştirir. Yapılandırılan URL doğrudan Azure'un v1 Responses
API'sini hedefler, bu nedenle adaptör `api-version` eklemez.
- İstek oluşturmayı Responses doğrudan geçişine devreder ve `baseUrl`'in
çözümlenmemiş şablon yer tutucusu içermediğini doğrular. API anahtarlı istekler
`api-key` kullanır; anahtarsız istekler `DefaultAzureCredential` ile Entra belirteci
alır ve `Authorization: Bearer` kullanır. Yapılandırılan URL doğrudan Azure'un v1
Responses API'sini hedefler, bu nedenle adaptör `api-version` eklemez.

## Görsel yardımcıları (`image.ts`)

Expand All @@ -328,4 +331,3 @@ Vizyon duyarlı adaptörler tarafından kullanılan paylaşılan yardımcılar:
- `contentPartsToText(content)` — salt metin araç mesajları için içerik
parçalarını metne düzleştirir (açıklanmayan bir görsel kısa bir `[image]`
işaretçisi haline gelir, asla belirteç patlatan bir base64 bloğu olmaz).

10 changes: 6 additions & 4 deletions docs-site/src/content/docs/zh-cn/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,13 @@ Cursor 的 HTTP/1.1 兼容传输:通过 `agent.v1.AgentService/RunSSE` 接收
## `azure-openai`(别名:`azure`)

**目标:** **Azure OpenAI**。封装 `openai-responses`,因此同样是 `passthrough: true`。
**认证:** 用 `api-key` header 进行 `key` 认证,而非 Bearer。
**认证:** 配置 `apiKey` 时使用 `api-key` header 进行 `key` 认证。未配置时通过
`DefaultAzureCredential` 使用 Microsoft Entra ID,并发送 `Authorization: Bearer`。

- 把请求构建交给 Responses passthrough,验证 `baseUrl` 不含未解析的 template placeholder,
再用 `api-key` 替换 `Authorization`。配置的 URL 直接指向 Azure v1 Responses API,因此 adapter
不会追加 `api-version`。
- 把请求构建交给 Responses passthrough,验证 `baseUrl` 不含未解析的 template placeholder。
API key 请求使用 `api-key`;未配置 key 时通过 `DefaultAzureCredential` 获取 Entra 令牌并使用
`Authorization: Bearer`。配置的 URL 直接指向 Azure v1 Responses API,因此 adapter 不会追加
`api-version`。

## 图像工具(`image.ts`)

Expand Down
10 changes: 6 additions & 4 deletions docs-site/src/content/docs/zh-tw/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,13 @@ Kiro 的 assistant 文字本身沒有可靠的回合結束標記,但終止的
## `azure-openai`(別名:`azure`)

**目標:** **Azure OpenAI**。封裝 `openai-responses`,因此同樣是 `passthrough: true`。
**認證:** 用 `api-key` header 進行 `key` 認證,而非 Bearer。
**認證:** 設定 `apiKey` 時使用 `api-key` header 進行 `key` 認證。未設定時透過
`DefaultAzureCredential` 使用 Microsoft Entra ID,並傳送 `Authorization: Bearer`。

- 把請求建置交給 Responses passthrough,驗證 `baseUrl` 不含未解析的 template placeholder,
再用 `api-key` 替換 `Authorization`。設定的 URL 直接指向 Azure v1 Responses API,因此 adapter
不會追加 `api-version`。
- 把請求建置交給 Responses passthrough,驗證 `baseUrl` 不含未解析的 template placeholder。
API key 請求使用 `api-key`;未設定 key 時透過 `DefaultAzureCredential` 取得 Entra 權杖並使用
`Authorization: Bearer`。設定的 URL 直接指向 Azure v1 Responses API,因此 adapter 不會追加
`api-version`。

## 圖像工具(`image.ts`)

Expand Down
17 changes: 15 additions & 2 deletions gui/src/components/add-provider-form-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,23 @@ export function AddProviderFormPane({
onBack: () => void;
}) {
const t = useT();
const isAzureOpenAi = preset.adapter === "azure-openai";
const isFreeKeyless = preset.keyOptional === true && preset.freeTier !== false;

return (
<div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
{!isReservedForward && !isCustom && !isLocal && !preset.keyOptional && preset.note && (
{!isReservedForward && !isCustom && !isLocal && isAzureOpenAi ? (
<details className="setup-guide">
<summary>{t("modal.azureEntraSetupGuide")}</summary>
<div className="text-label leading-relaxed" style={{ margin: "8px 0 0", color: "var(--muted)" }}>
<p style={{ margin: 0 }}>{t("modal.azureEntraIntro")}</p>
<ol style={{ margin: "8px 0 0", paddingLeft: 18 }}>
<li>{t("modal.azureEntraResource")}</li>
<li>{t("modal.azureEntraCredential")}</li>
</ol>
</div>
</details>
) : !isReservedForward && !isCustom && !isLocal && !preset.keyOptional && preset.note && (
<details className="setup-guide">
<summary>{t("modal.setupGuide")}</summary>
<ol className="text-label leading-relaxed" style={{ margin: "8px 0 0", paddingLeft: 18, color: "var(--muted)" }}>
Expand Down Expand Up @@ -129,7 +142,7 @@ export function AddProviderFormPane({
<div className="text-label leading-relaxed" style={{ color: "var(--amber)", background: "var(--amber-soft)", border: "1px solid var(--amber)", borderRadius: "var(--radius-sm)", padding: "8px 10px" }}>
{t("modal.localHint")}
</div>
) : preset.keyOptional ? (
) : isFreeKeyless ? (
<div className="text-label leading-relaxed" style={{ color: "var(--green)", background: "var(--green-soft)", border: "1px solid var(--green)", borderRadius: "var(--radius-sm)", padding: "10px 12px" }}>
<strong>{t("modal.freeTierTitle")}</strong> — {preset.note ?? t("modal.freeTierDefault")}
</div>
Expand Down
4 changes: 2 additions & 2 deletions gui/src/components/provider-catalog/ProviderCatalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ export default function ProviderCatalog({
: p.auth === "oauth" ? <span className="badge badge-accent">{t("modal.badge.oauth")}</span>
: p.auth === "forward" ? <span className="badge badge-green">{t("modal.badge.codexLogin")}</span>
: p.auth === "local" ? <span className="badge badge-amber">{t("modal.badge.local")}</span>
: p.keyOptional ? null // keyless free: the Free badge alone says it all
: p.keyOptional && p.freeTier !== false ? null // keyless free: the Free badge alone says it all
: <span className="badge badge-muted">{t("modal.badge.apiKey")}</span>;
// Free pricing is orthogonal to auth: NVIDIA (freeTier + key required) shows BOTH
// the Free badge and the API-key badge — free pricing never hides a key requirement.
const free = (p.freeTier || p.keyOptional) && p.auth === "key"
const free = (p.freeTier === true || (p.freeTier !== false && p.keyOptional)) && p.auth === "key"
? <span className="badge badge-green">{t("modal.badge.free")}</span>
: null;
return <>{free}{auth}</>;
Expand Down
2 changes: 1 addition & 1 deletion gui/src/components/provider-catalog/provider-presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function presetTierInput(preset: CatalogPreset): WorkspaceProvider {
adapter: preset.adapter,
baseUrl: preset.baseUrl,
authMode: preset.auth,
freeTier: !!preset.freeTier,
...(preset.freeTier !== undefined ? { freeTier: preset.freeTier } : {}),
keyOptional: !!preset.keyOptional,
};
}
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,10 @@ export const de: Record<TKey, string> = {
"modal.oauthComingSoonShort": "OAuth-Login für diesen Anbieter kommt im nächsten Update — nutze vorerst einen API-Schlüssel.",
"modal.useApiKeyInstead": "Stattdessen API-Schlüssel verwenden",
"modal.setupGuide": "Einrichtungsanleitung",
"modal.azureEntraSetupGuide": "Azure-OpenAI-Einrichtung",
"modal.azureEntraIntro": "Azure OpenAI akzeptiert einen API-Schlüssel oder Microsoft Entra ID über DefaultAzureCredential.",
"modal.azureEntraResource": "Ersetzen Sie den Ressourcen-Platzhalter in der Basis-URL durch den Namen Ihrer Azure-OpenAI-Ressource.",
"modal.azureEntraCredential": "Für Microsoft Entra ID lassen Sie das API-Schlüsselfeld leer und konfigurieren Sie DefaultAzureCredential auf dem Rechner, auf dem OpenCodex läuft.",
"modal.setupStep1Prefix": "Gehe zu",
"modal.setupDashboardLink": "{label}-Dashboard",
"modal.setupStep1Suffix": "und kopiere deinen API-Schlüssel",
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,10 @@ export const en = {
"modal.oauthComingSoonShort": "OAuth login for this provider arrives in the next update — use an API key for now.",
"modal.useApiKeyInstead": "Use an API key instead",
"modal.setupGuide": "Setup guide",
"modal.azureEntraSetupGuide": "Azure OpenAI setup",
"modal.azureEntraIntro": "Azure OpenAI accepts an API key or Microsoft Entra ID through DefaultAzureCredential.",
"modal.azureEntraResource": "Replace the resource placeholder in the Base URL with your Azure OpenAI resource name.",
"modal.azureEntraCredential": "For Microsoft Entra ID, leave the API key blank and configure DefaultAzureCredential on the machine running OpenCodex.",
"modal.setupStep1Prefix": "Go to",
"modal.setupDashboardLink": "{label} dashboard",
"modal.setupStep1Suffix": "and copy your API key",
Expand Down
4 changes: 4 additions & 0 deletions gui/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1307,6 +1307,10 @@ export const fr: Record<TKey, string> = {
"modal.oauthComingSoonShort": "La connexion OAuth pour ce fournisseur sera disponible dans la prochaine mise à jour — utilisez une clé API pour le moment.",
"modal.useApiKeyInstead": "Utiliser plutôt une clé API",
"modal.setupGuide": "Guide de configuration",
"modal.azureEntraSetupGuide": "Configuration d’Azure OpenAI",
"modal.azureEntraIntro": "Azure OpenAI accepte une clé API ou Microsoft Entra ID via DefaultAzureCredential.",
"modal.azureEntraResource": "Remplacez le paramètre de ressource dans l’URL de base par le nom de votre ressource Azure OpenAI.",
"modal.azureEntraCredential": "Pour Microsoft Entra ID, laissez la clé API vide et configurez DefaultAzureCredential sur la machine qui exécute OpenCodex.",
"modal.setupStep1Prefix": "Accédez au",
"modal.setupDashboardLink": "tableau de bord {label}",
"modal.setupStep1Suffix": "et copiez votre clé API",
Expand Down
Loading
Loading