diff --git a/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md b/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md index b378545e45b..432161e84fe 100644 --- a/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/fr/reference/cli/providers-accounts.md @@ -441,18 +441,19 @@ déplacé car son AAD chiffré et l'identité du trousseau de clés du système ### `ocx models [subcommand]` · `ocx model ` -`ocx model` est un alias de `ocx models`. Sans sous-commande, répertoriez les modèles implantés statiquement dans -fournisseurs configurés. `--provider` filtre un fournisseur configuré et `--json` renvoie le modèle +`ocx model` est un alias de `ocx models`. Sans sous-commande, liste les ID enregistrés et connus du routeur, notamment les modèles du registre, les modèles personnalisés et le cache de découverte de ce processus CLI. Le cache d’un nouveau processus peut être vide : ce n’est pas le catalogue du proxy actif. Certains anciens fournisseurs à clé conservent les modèles du registre après un changement de destination. `--provider` filtre un fournisseur configuré et `--json` renvoie le modèle métadonnées. `live` lit le catalogue en cours d'exécution ; `add`, `edit`, `remove` et `list-custom` gèrent le manuel entrées de catalogue ; `enable`, `disable` et `provider` contrôlent la visibilité ; `selected` contrôle un liste d'autorisation des fournisseurs ; `context` contrôle les plafonds de contexte du fournisseur ; et `shadow` gère l'arrière-plan interception d'appel fantôme. +Le champ `reasoningEfforts` apparaît uniquement avec `--json`. La valeur `null` signifie que la liste est inconnue ou n’a pas été consultée, notamment si sa vérification nécessiterait d’ouvrir le trousseau du système. Le tableau vide `[]` indique une liste explicitement vide ou un effort de raisonnement désactivé pour le modèle, par exemple par `noReasoningModels`. Utilisez `ocx models live` pour consulter le catalogue du proxy actif. + `add` et `remove` enregistrent localement par défaut ; `--live` modifie le proxy actif. Sans proxy, un enregistrement local `--json` renvoie `sync.status: "not-attempted"`, `needsSync: true` et le code 0. Une synchronisation tentée mais échouée conserve la sauvegarde et renvoie un code non nul. `list-custom` reste local. `display-name` prend l’ID amont brut ; `order` prend les IDs publics du sélecteur. Voir les contraintes de permutation complète, de préfixe featured et de réinitialisation native dans le [guide anglais](/reference/cli/providers-accounts/#display-names-and-picker-identities). | Sous-commande | Drapeaux pris en charge | Actions | | --- | --- | --- | -| `list` (par défaut) | `--provider `, `--json` | Répertoriez les modèles prédéfinis dans les fournisseurs configurés. | +| `list` (par défaut) | `--provider `, `--json` | Liste les modèles locaux enregistrés et connus du routeur. | | `live` | `--provider `, `--json` | Lisez le catalogue en cours d'exécution, y compris les modèles découverts lors de l'exécution. Les lignes sont marquées `native`/`routed`, `custom` et `enabled`/`disabled`. | | `add ` | `--display-name `, `--context-window `, `--modalities `, `--live`, `--json` | Enregistre un modèle personnalisé localement, ou sur le proxy actif avec `--live`. | | `edit ` | `--model-id `, `--display-name `, `--context-window `, `--modalities `, `--json` | Modifiez un modèle personnalisé. `-` libère un champ ; `0` efface la fenêtre contextuelle. | diff --git a/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md index 0a939338274..24c16ec5c69 100644 --- a/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ja/reference/cli/providers-accounts.md @@ -343,13 +343,15 @@ native-main トラフィックまたはジャーナル復旧を受け入れる ### `ocx models [subcommand]`・`ocx model ` -`ocx model` は `ocx models` の別名です。サブコマンドを使用しない場合、構成されたプロバイダーに静的にシードされたモデルを一覧表示します。 `--provider` は 1 つの構成済みプロバイダーをフィルターし、`--json` はモデル メタデータを返します。 `live` は実行中のカタログを読み取ります。 `add`、`edit`、`remove`、および `list-custom` は手動カタログ エントリを管理します。 `enable`、`disable`、および `provider` は可視性を制御します。 `selected` はプロバイダー許可リストを制御します。 `context` はプロバイダーのコンテキストの上限を制御します。 `shadow` はバックグラウンドのシャドウ コール インターセプトを管理します。 +`ocx model` は `ocx models` の別名です。サブコマンドを使用しない場合、保存済みモデルとルーターが認識するモデル ID を一覧表示します。レジストリのシード、カスタムモデル、この CLI プロセス自身の検出キャッシュが含まれます。新しい CLI プロセスのキャッシュは空の場合があり、実行中のプロキシのカタログとは異なります。従来のキー方式プリセットでは、接続先を変更してもレジストリのシードが残る場合があります。 `--provider` は 1 つの構成済みプロバイダーをフィルターし、`--json` はモデル メタデータを返します。 `live` は実行中のカタログを読み取ります。 `add`、`edit`、`remove`、および `list-custom` は手動カタログ エントリを管理します。 `enable`、`disable`、および `provider` は可視性を制御します。 `selected` はプロバイダー許可リストを制御します。 `context` はプロバイダーのコンテキストの上限を制御します。 `shadow` はバックグラウンドのシャドウ コール インターセプトを管理します。 + +`reasoningEfforts` は `--json` でのみ表示されます。`null` は一覧が不明または未照会であることを示し、OS のキーチェーンを開く必要がある場合も含みます。空配列 `[]` は明示的な空の一覧、または `noReasoningModels` などによってそのモデルの推論 effort が無効であることを示します。稼働中のプロキシのカタログは `ocx models live` で確認できます。 `add` と `remove` は既定でローカル保存し、`--live` で稼働中のプロキシを変更します。ローカル `--json` 保存時にプロキシがなければ `sync.status: "not-attempted"`、`needsSync: true`、終了コード 0 を返します。試行した同期が失敗しても保存は維持され、終了コードは非ゼロです。`list-custom` はローカル一覧です。`display-name` は raw upstream ID、`order` は picker の public ID を使います。完全な並べ替え、featured 接頭部、native 順序のリセット制約は[英語版](/reference/cli/providers-accounts/#display-names-and-picker-identities)を参照してください。 |サブコマンド |サポートされているフラグ |アクション | | --- | --- | --- | -| `list` (デフォルト) | `--provider `、`--json` |構成されたプロバイダーにシードされたモデルをリストします。 | +| `list` (デフォルト) | `--provider `、`--json` | 保存済みモデルとローカルでルーターが認識するモデルを一覧表示します。 | | `live` | `--provider `、`--json` |実行時に検出されたモデルを含む、実行中のカタログを読み取ります。行には、`native`/`routed`、`custom`、および `enabled`/`disabled` というフラグが付けられます。 | | `add ` | `--display-name `、`--context-window `、`--modalities `, `--live`, `--json` | カスタムモデルをローカルに、または `--live` で稼働中のプロキシに登録します。 | | `edit ` | `--model-id `、`--display-name `、`--context-window `、`--modalities `、`--json` |カスタムモデルを編集します。 `-` はフィールドをクリアします。 `0` はコンテキスト ウィンドウをクリアします。 | diff --git a/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md index 8e77626c88f..e88c51adf6d 100644 --- a/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ko/reference/cli/providers-accounts.md @@ -469,13 +469,15 @@ native-main 트래픽이나 저널 복구를 허용하기 전에 수명 주기 ### `ocx models [subcommand]` · `ocx model ` -`ocx model`은 `ocx models`의 별칭입니다. 하위 명령이 없으면 설정된 제공자에 사전 등록된 모델을 나열합니다. `--provider`는 설정된 제공자 하나를 필터링하고 `--json`은 모델 메타데이터를 반환합니다. `live`는 실행 중인 카탈로그를 읽습니다. `add`, `edit`, `remove`, `list-custom`은 수동 카탈로그 항목을 관리합니다. `enable`, `disable`, `provider`는 가시성을 제어합니다. `selected`는 제공자 허용 목록을 제어합니다. `context`는 제공자 컨텍스트 한도를 제어합니다. `shadow`는 백그라운드 shadow-call 가로채기를 관리합니다. +`ocx model`은 `ocx models`의 별칭입니다. 하위 명령이 없으면 저장된 모델과 라우터가 아는 모델 ID를 나열합니다. 레지스트리 시드, 사용자 정의 모델, 현재 CLI 프로세스의 검색 캐시가 포함됩니다. 새 CLI 프로세스의 캐시는 비어 있을 수 있으며 실행 중인 프록시 카탈로그와 다릅니다. 일부 기존 키 방식 프리셋은 접속 주소를 바꾼 뒤에도 레지스트리 시드를 유지할 수 있습니다. `--provider`는 설정된 제공자 하나를 필터링하고 `--json`은 모델 메타데이터를 반환합니다. `live`는 실행 중인 카탈로그를 읽습니다. `add`, `edit`, `remove`, `list-custom`은 수동 카탈로그 항목을 관리합니다. `enable`, `disable`, `provider`는 가시성을 제어합니다. `selected`는 제공자 허용 목록을 제어합니다. `context`는 제공자 컨텍스트 한도를 제어합니다. `shadow`는 백그라운드 shadow-call 가로채기를 관리합니다. + +`reasoningEfforts` 목록은 `--json`에서만 표시됩니다. `null`은 목록을 알 수 없거나 조회하지 않았다는 뜻이며, 확인하려면 OS 키체인을 열어야 하는 경우도 포함합니다. 빈 배열 `[]`은 명시적으로 비어 있는 목록이거나 `noReasoningModels` 등으로 해당 모델의 reasoning effort가 비활성화됐다는 뜻입니다. 실행 중인 프록시의 카탈로그는 `ocx models live`로 확인합니다. `add`·`remove`는 기본적으로 로컬 저장이며 `--live`를 주면 실행 중인 프록시를 바꿉니다. 로컬 `--json` 저장은 프록시가 없으면 `sync.status: "not-attempted"`, `needsSync: true`, 종료 코드 0을 반환합니다. 실제 동기화 시도가 실패하면 저장은 유지하고 0이 아닌 코드로 끝납니다. `list-custom`은 로컬 목록입니다. 발견된 모델의 표시 이름은 raw upstream ID를 받는 `display-name`으로 바꾸고, picker 순서는 public ID를 받는 `order`로 바꿉니다. [영문 절차](/reference/cli/providers-accounts/#display-names-and-picker-identities)에서 전체 목록·featured 접두부·native 순서 초기화 제약을 확인하세요. | 하위 명령 | 지원 플래그 | 동작 | | --- | --- | --- | -| `list` (기본값) | `--provider `, `--json` | 설정된 제공자에 사전 등록된 모델을 나열합니다. | +| `list` (기본값) | `--provider `, `--json` | 로컬에 저장된 모델과 라우터가 아는 모델을 나열합니다. | | `live` | `--provider `, `--json` | 런타임에 발견된 모델을 포함해 실행 중인 카탈로그를 읽습니다. 행에는 `native`/`routed`, `custom`, `enabled`/`disabled` 표시가 붙습니다. | | `add ` | `--display-name `, `--context-window `, `--modalities `, `--live`, `--json` | 사용자 정의 모델을 로컬에 등록하거나 `--live`로 실행 중인 프록시에 등록합니다. | | `edit ` | `--model-id `, `--display-name `, `--context-window `, `--modalities `, `--json` | 사용자 지정 모델을 수정합니다. `-`는 필드를 지우고, `0`은 컨텍스트 창을 지웁니다. | diff --git a/docs-site/src/content/docs/reference/cli/providers-accounts.md b/docs-site/src/content/docs/reference/cli/providers-accounts.md index 092133e51de..edba232756a 100644 --- a/docs-site/src/content/docs/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/reference/cli/providers-accounts.md @@ -1200,13 +1200,21 @@ moved because its encrypted AAD and operating-system keyring identity are intent ### `ocx models [subcommand]` · `ocx model ` -`ocx model` is an alias of `ocx models`. With no subcommand, list the models statically seeded in -configured providers. `--provider` filters one configured provider and `--json` returns model +`ocx model` is an alias of `ocx models`. With no subcommand, list saved and router-known model IDs +for configured providers, including registry seeds, custom models and this process's discovery +cache. A fresh CLI process may have an empty cache; this is not the running proxy catalog. Legacy +key presets may retain registry seeds when their configured destination changes. `--provider` filters one configured provider and `--json` returns model metadata. `live` reads the running catalog; `add`, `edit`, `remove`, and `list-custom` manage manual catalog entries; `enable`, `disable`, and `provider` control visibility; `selected` controls a provider allowlist; `context` controls provider context caps; and `shadow` manages background shadow-call interception. +The `reasoningEfforts` ladder appears only with `--json`. A `null` value means the ladder is +unknown or was not looked up, including when credential-scoped learned refusals would require +opening the OS keychain. An empty array `[]` means an explicitly empty ladder or that reasoning +effort is disabled for the model, such as by `noReasoningModels`. Use `ocx models live` to inspect +the running proxy's catalog. + Model prices are estimates in USD per million tokens. `ocx models --json` includes a `price` object with `cost4` rates and their source; `ocx models price --json` keeps `cost` for the saved override and reports resolved rates in `effectiveCost`. @@ -1231,7 +1239,7 @@ from custom-model `edit`. | Subcommand | Supported flags | Action | | --- | --- | --- | -| `list` (default) | `--provider `, `--json` | List models seeded in configured providers, with estimated input/output prices. | +| `list` (default) | `--provider `, `--json` | List local saved and router-known models, with estimated input/output prices. | | `live` | `--provider `, `--json` | Read the running catalog, including models discovered at runtime. Rows are flagged `native`/`routed`, `custom`, and `enabled`/`disabled`. | | `price ` | `--json` | Read the saved manual override and effective price, including automatic catalog defaults. | | `set-price ` | `--input `, `--output `, `--cache-read `, `--cache-write `, `--auto`, `--json` | Set display prices in USD per 1M tokens. Input/output are required when setting; omitted cache rates become zero. `--auto` removes only this model's override. | diff --git a/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md b/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md index 8adca4b9a4e..584eacdb910 100644 --- a/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/ru/reference/cli/providers-accounts.md @@ -397,18 +397,19 @@ ocx account main recover [--rollback --yes] [--json] ### `ocx models [subcommand]` · `ocx model ` -`ocx model` — alias команды `ocx models`. Без подкоманды команда показывает модели, статически -засеянные в настроенных провайдерах. `--provider` фильтрует один провайдер, а `--json` возвращает +`ocx model` — alias команды `ocx models`. Без подкоманды команда показывает сохранённые и известные маршрутизатору ID моделей, включая записи реестра, пользовательские модели и кэш обнаружения этого процесса CLI. Кэш нового процесса может быть пустым и не является каталогом работающего прокси. Некоторые старые пресеты с ключом сохраняют записи реестра после смены адреса. `--provider` фильтрует один провайдер, а `--json` возвращает метаданные моделей. `live` читает работающий каталог; `add`, `edit`, `remove` и `list-custom` управляют ручными записями каталога; `enable`, `disable` и `provider` управляют видимостью; `selected` управляет allowlist'ом провайдера; `context` — provider context cap'ами; `shadow` управляет intercept'ом background shadow-call'ов. +Поле `reasoningEfforts` отображается только с `--json`. Значение `null` означает, что список неизвестен или не запрашивался, в том числе когда для проверки пришлось бы открыть системное хранилище ключей. Пустой массив `[]` означает явно пустой список либо отключённый для модели уровень рассуждения, например через `noReasoningModels`. Каталог работающего прокси доступен через `ocx models live`. + `add` и `remove` по умолчанию сохраняют локально; `--live` меняет работающий прокси. Без прокси локальное сохранение с `--json` возвращает `sync.status: "not-attempted"`, `needsSync: true` и код 0. Неудачная попытка синхронизации сохраняет запись и возвращает ненулевой код. `list-custom` читает локальный список. `display-name` использует исходный upstream ID, а `order` — публичные ID picker. Полная перестановка, префикс featured и сброс native-порядка описаны в [английской инструкции](/reference/cli/providers-accounts/#display-names-and-picker-identities). | Подкоманда | Поддерживаемые флаги | Действие | | --- | --- | --- | -| `list` (default) | `--provider `, `--json` | Показать модели, засеянные в настроенных провайдерах. | +| `list` (default) | `--provider `, `--json` | Показать локально сохранённые и известные маршрутизатору модели. | | `live` | `--provider `, `--json` | Прочитать работающий каталог, включая модели, обнаруженные во время выполнения. Строки помечаются как `native`/`routed`, `custom` и `enabled`/`disabled`. | | `add ` | `--display-name `, `--context-window `, `--modalities `, `--live`, `--json` | Регистрирует custom-модель локально или на работающем прокси с `--live`. | | `edit ` | `--model-id `, `--display-name `, `--context-window `, `--modalities `, `--json` | Изменить custom-модель. `-` очищает поле; `0` очищает context window. | diff --git a/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md b/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md index 1c3f0893a21..cf8e96e1320 100644 --- a/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md @@ -371,17 +371,21 @@ v1 恢复矩阵覆盖的是事务文件通过重命名发布后 OpenCodex 进程 ### `ocx models [subcommand]` · `ocx model ` -`ocx model` 是 `ocx models` 的别名。没有子命令时,列出已配置提供方中静态预置的模型。 +`ocx model` 是 `ocx models` 的别名。没有子命令时,列出已保存及路由器已知的模型 ID, +包括注册表预置、自定义模型和当前 CLI 进程的发现缓存。新 CLI 进程的缓存可能为空, +这不是运行中代理的目录。部分旧版密钥预设即使更改接入地址也可能保留注册表预置。 `--provider` 可过滤单个已配置提供方,而 `--json` 会返回模型元数据。`live` 读取运行中 的目录;`add`、`edit`、`remove` 和 `list-custom` 管理手动目录条目;`enable`、 `disable` 和 `provider` 控制可见性;`selected` 控制提供方允许列表;`context` 控制提供方 上下文上限;`shadow` 管理后台 shadow-call 拦截。 +`reasoningEfforts` 列表仅在使用 `--json` 时显示。`null` 表示列表未知或未查询,包括查询需要打开系统钥匙串的情况。空数组 `[]` 表示明确设置了空列表,或该模型的推理强度已被禁用,例如通过 `noReasoningModels` 禁用。使用 `ocx models live` 查看运行中代理的目录。 + `add` 和 `remove` 默认保存到本地;`--live` 修改运行中的代理。本地 `--json` 保存时若无代理,返回 `sync.status: "not-attempted"`、`needsSync: true`,退出码为 0。实际尝试的同步失败时保留保存结果,以非零码退出。`list-custom` 是本地列表。`display-name` 使用原始上游 ID,`order` 使用 picker 的 public ID。完整排列、featured 前缀与 native 顺序重置限制见[英文步骤](/reference/cli/providers-accounts/#display-names-and-picker-identities)。 | 子命令 | 支持的标志 | 操作 | | --- | --- | --- | -| `list` (默认) | `--provider `, `--json` | 列出已配置提供方中预置的模型。 | +| `list` (默认) | `--provider `, `--json` | 列出本地已保存及路由器已知的模型。 | | `live` | `--provider `, `--json` | 读取运行中的目录,包括运行时发现的模型。各行会标记为 `native`/`routed`、`custom`,以及 `enabled`/`disabled`。 | | `add ` | `--display-name `, `--context-window `, `--modalities `, `--live`, `--json` | 在本地注册自定义模型,或使用 `--live` 在运行中的代理上注册。 | | `edit ` | `--model-id `, `--display-name `, `--context-window `, `--modalities `, `--json` | 编辑自定义模型。`-` 会清空字段;`0` 会清空上下文窗口。 | diff --git a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md index c43aaade0e3..0ec96d57678 100644 --- a/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md +++ b/docs-site/src/content/docs/zh-tw/reference/cli/providers-accounts.md @@ -352,13 +352,15 @@ Preview 建置使用 `/native-main-profiles`。該配置絕不 ### `ocx models [subcommand]` · `ocx model ` -`ocx model` 是 `ocx models` 的別名。無子指令時,列出已設定供應商中靜態播種的模型。`--provider` 過濾一個已設定的供應商,而 `--json` 回傳模型中繼資料。`live` 讀取執行中的目錄;`add`、`edit`、`remove` 與 `list-custom` 管理手動目錄項目;`enable`、`disable` 與 `provider` 控制可見性;`selected` 控制供應商允許清單;`context` 控制供應商 context 上限;而 `shadow` 管理背景 shadow-call 攔截。 +`ocx model` 是 `ocx models` 的別名。無子指令時,列出已儲存及路由器已知的模型 ID,包括 registry 預設、自訂模型和目前 CLI 程序的探索快取。新 CLI 程序的快取可能為空,這不是執行中代理的目錄。部分舊版金鑰預設在變更連線位址後仍可能保留 registry 預設。`--provider` 過濾一個已設定的供應商,而 `--json` 回傳模型中繼資料。`live` 讀取執行中的目錄;`add`、`edit`、`remove` 與 `list-custom` 管理手動目錄項目;`enable`、`disable` 與 `provider` 控制可見性;`selected` 控制供應商允許清單;`context` 控制供應商 context 上限;而 `shadow` 管理背景 shadow-call 攔截。 + +`reasoningEfforts` 清單僅在使用 `--json` 時顯示。`null` 表示清單未知或未查詢,包括查詢需要開啟系統鑰匙圈的情況。空陣列 `[]` 表示明確設定了空清單,或該模型的推理強度已停用,例如透過 `noReasoningModels` 停用。使用 `ocx models live` 查看執行中代理的目錄。 `add` 與 `remove` 預設儲存至本機;`--live` 修改執行中的代理。本機 `--json` 儲存時若無代理,回傳 `sync.status: "not-attempted"`、`needsSync: true`,結束碼為 0。實際嘗試的同步失敗時保留儲存結果,以非零碼結束。`list-custom` 是本機清單。`display-name` 使用原始上游 ID,`order` 使用 picker 的 public ID。完整排列、featured 前綴與 native 順序重設限制請見[英文步驟](/reference/cli/providers-accounts/#display-names-and-picker-identities)。 | 子指令 | 支援的旗標 | 動作 | | --- | --- | --- | -| `list`(預設) | `--provider `, `--json` | 列出已設定供應商中播種的模型。 | +| `list`(預設) | `--provider `, `--json` | 列出本機已儲存及路由器已知的模型。 | | `live` | `--provider `, `--json` | 讀取執行中的目錄,包含 runtime 探索的模型。列標記為 `native`/`routed`、`custom` 與 `enabled`/`disabled`。 | | `add ` | `--display-name `, `--context-window `, `--modalities `, `--live`, `--json` | 在本機註冊自訂模型,或使用 `--live` 在執行中的代理上註冊。 | | `edit ` | `--model-id `, `--display-name `, `--context-window `, `--modalities `, `--json` | 編輯自訂模型。`-` 清除欄位;`0` 清除 context window。 | diff --git a/skills/ocx/references/01_surface_providers-models.md b/skills/ocx/references/01_surface_providers-models.md index 5f9804ef3fe..75571fe5f74 100644 --- a/skills/ocx/references/01_surface_providers-models.md +++ b/skills/ocx/references/01_surface_providers-models.md @@ -385,7 +385,7 @@ Drives no management route. Usage: `ocx models list [--provider ] [--json]` -List static configured models locally. +List local configured and router-known models. State-changing: no. JSON mode: `envelope`. @@ -394,9 +394,9 @@ Drives no management route. | Flag | Value | Meaning | |---|---|---| | `--provider` | string | Limit to a configured provider. | -| `--json` | boolean | Emit models and the static-catalog note. | +| `--json` | boolean | Emit models and the local-catalog note. | -- A bare models invocation is equivalent. Reads local config, never the live catalog; use models live for runtime discovery. +- A bare models invocation is equivalent. Includes saved IDs, registry seeds, custom models and this process's discovery cache; use models live for the running catalog. ### `ocx models add` diff --git a/src/cli/capabilities-provider-models.ts b/src/cli/capabilities-provider-models.ts index 871712a790e..56bd2fb2bd3 100644 --- a/src/cli/capabilities-provider-models.ts +++ b/src/cli/capabilities-provider-models.ts @@ -87,10 +87,10 @@ export const PROVIDER_MODEL_CAPABILITIES: readonly Capability[] = [ details: ["Projects values only from an explicit allowlist of non-secret metadata fields (provider IDs, app types, base URLs, wire dialects, and default models); source and embedded-config bytes are read transiently while secret fields, free-text names, and TOML comments are skipped at the byte level and never decoded into strings, written, or logged, and transient byte buffers are zeroed in finally.", "Only import from a trusted local CC-Switch source: apply prints every imported ID, model, and destination URL before writing, refuses SQLite hard links (nlink > 1) and non-empty -wal/-journal sidecars until CC-Switch is closed, and opens sources read-only with O_NOFOLLOW|O_NONBLOCK descriptor checks; restore inspects auth.json read-only at the byte level and retains any provider that was edited, selected as defaultProvider, or referenced by routing, combos, fallback policy, OAuth accounts, or API keys."], }, { - command: ["models", "list"], summary: "List static configured models locally.", + command: ["models", "list"], summary: "List local configured and router-known models.", usage: "ocx models list [--provider ] [--json]", routes: [], mutates: false, json: "envelope", - flags: [{ name: "--provider", value: "string", summary: "Limit to a configured provider." }, { name: "--json", value: "boolean", summary: "Emit models and the static-catalog note." }], - details: ["A bare models invocation is equivalent. Reads local config, never the live catalog; use models live for runtime discovery."], + flags: [{ name: "--provider", value: "string", summary: "Limit to a configured provider." }, { name: "--json", value: "boolean", summary: "Emit models and the local-catalog note." }], + details: ["A bare models invocation is equivalent. Includes saved IDs, registry seeds, custom models and this process's discovery cache; use models live for the running catalog."], }, { command: ["models","add"], diff --git a/src/cli/codex-shim-autorestore.ts b/src/cli/codex-shim-autorestore.ts index bac12067179..9b259cd914a 100644 --- a/src/cli/codex-shim-autorestore.ts +++ b/src/cli/codex-shim-autorestore.ts @@ -35,6 +35,11 @@ export function skipsCodexShimAutoRestore(command: string | undefined, args: str // `resolve` is read-only inspection for embedding shells: a lookup made to populate // a consent surface must not trigger a shim repair side effect first. if (command === "resolve") return true; + // Local model inventory is read-only; mutations and live commands keep preflight. + if (command === "models" || command === "model") { + const subcommand = args[1]; + if (!subcommand || subcommand.startsWith("-") || subcommand === "list" || subcommand === "list-custom") return true; + } // `service status --json` is a passive inventory read for peers such as GOAT: a shim repair // before dispatch would change the installation it was asked only to describe. // Flags may precede the verb (`ocx service --json status`), exactly as parseServiceArgs reads it. diff --git a/src/cli/models.ts b/src/cli/models.ts index 28d83536b49..1d0f0f4efa9 100644 --- a/src/cli/models.ts +++ b/src/cli/models.ts @@ -6,7 +6,7 @@ import { resolveMatchedPrice, type MatchedPrice } from "../usage/cost"; import { randomUUID } from "node:crypto"; import { createInterface } from "node:readline/promises"; import { syncModelsToCodex } from "../codex/sync"; -import { configuredContextWindow } from "../codex/catalog/provider-fetch"; +import { applyRegistryCapabilitySeedFill, configuredContextWindow } from "../codex/catalog/provider-fetch"; import { hasOwnProvider, isValidProviderName, loadConfig, saveConfig } from "../config"; import { readCliConfigForMutation } from "./config-mutation"; import { @@ -16,6 +16,20 @@ import { import { encodedModelIdCollides, resolveSlugSelection, routedSlug } from "../providers/slug-codec"; import { isModelsRuntimeSubcommand } from "./models-runtime-subcommands"; import { knownModelIdsForProvider } from "../router"; +import { azureVendorModelMetadata, enrichProviderFromRegistry, registryInputModalitiesForDestination } from "../providers/derive"; +import { getProviderRegistryEntry } from "../providers/registry"; +import { providerMatchesRegistryTransportWithStaticGuards } from "../providers/static-model-discovery"; +import { resolveModelPolicy } from "../providers/resolved-model-policy"; +import { customRowInputModalities } from "../vision/eligibility"; +import { applyProviderContextCap, providerContextCap, resolveUnknownRoutedContextWindow } from "../providers/context-cap"; +import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../providers/openai-tiers"; +import { reasoningCapabilitiesFromConfig } from "../providers/reasoning-capabilities"; +import { dropLearnedUnsupportedReasoningEfforts, providerUsesReasoningMetadata } from "../providers/reasoning-metadata"; +import { isKeychainReference } from "../providers/api-key-resolve"; +import { hasNativeOpenAiCapabilityMetadata, nativeContextLimits, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiContextWindow, nativeReasoningEfforts } from "../codex/catalog/metadata"; +import { canInferVendorContext, vendorContextInference } from "../codex/catalog/vendor-context"; +import { terminalSafeText } from "./runtime-api"; +import { runPassiveRead } from "../lib/passive-read"; import { findLiveProxy } from "../server/proxy-liveness"; import { modelInList, type OcxConfig, type OcxCustomModel } from "../types"; import type { RuntimeApiDeps } from "./runtime-api"; @@ -28,6 +42,13 @@ const REMOVE_USAGE = "Usage: ocx models remove [--ye const LIST_CUSTOM_USAGE = "Usage: ocx models list-custom [--json]"; const ALLOWED_MODALITIES = new Set(["text", "image", "audio"]); +function printJson(value: unknown): void { + const encoded = JSON.stringify(value, null, 2); + if (encoded === undefined) throw new TypeError("Cannot serialize model output"); + console.log(encoded.replace(/[\u007f-\u009f\u2028\u2029]/g, character => + `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`)); +} + import { parseReasoningArgs } from "./models-custom-input"; import { redactSecretArgs } from "./secret-args"; export { parseReasoningArgs } from "./models-custom-input"; @@ -56,35 +77,91 @@ function collectModels(config: OcxConfig, providerFilter?: string): ModelEntry[] for (const [provName, prov] of Object.entries(providers)) { if (!prov) continue; const seen = new Set(); - const inputModalities = prov.modelInputModalities ?? {}; + // Model listing is read-only: derive effective registry policy on a private copy. + const effective = structuredClone(prov); + const registryMatches = providerMatchesRegistryTransportWithStaticGuards(provName, prov); + if (registryMatches) applyRegistryCapabilitySeedFill(provName, effective); + enrichProviderFromRegistry(provName, effective); + if (registryMatches) { + const policy = resolveModelPolicy({ + providerName: provName, modelId: effective.defaultModel ?? "__catalog_capture__", provider: effective, + registryEntry: getProviderRegistryEntry(provName), transportMatchedRegistry: true, + ...(effective.authMode ? { effectiveAuth: { authMode: effective.authMode } } : {}), + }).provider; + Object.assign(effective, policy); + } + effective.inputModalities ??= registryInputModalitiesForDestination(provName, prov); + const routerKnown = new Set(knownModelIdsForProvider(provName, prov)); /** Append one model with resolved metadata, ignoring duplicates within this provider. */ const addModel = (model: string, isDefault: boolean) => { if (seen.has(model)) return; seen.add(model); - // Resolve exactly as the runtime does, or this command reports capabilities the - // proxy will not honour: `isModelTextOnly` matches noVisionModels with modelInList - // and reads modelInputModalities with modelRecordValue, so a `gpt-oss` entry covers - // `gpt-oss:120b`. A bare lookup reported that model as unclassified on every field. - // noVisionModels is checked first because `isModelTextOnly` returns true on that - // match before it ever reads modelInputModalities: a `gpt-oss` noVision entry beats - // an exact `gpt-oss:120b` entry that lists "image", and the proxy rejects the image. - const noVision = modelInList(prov.noVisionModels, model); - const modalities = noVision ? ["text"] : (modelRecordValue(inputModalities, model) ?? null); + const custom = config.customModels?.find(row => row.provider === provName && row.modelId === model); + const nativeMetadata = custom && hasNativeOpenAiCapabilityMetadata(model); + const canonicalProvider = provName === OPENAI_CODEX_PROVIDER_ID && prov.authMode === undefined + ? { ...prov, authMode: "forward" as const } : prov; + const canonicalNativeCustom = nativeMetadata && provName === OPENAI_CODEX_PROVIDER_ID + && isCanonicalOpenAiForwardProvider(canonicalProvider); + const canInfer = canInferVendorContext(provName, effective); + const modelListEmpty = (effective.models?.length ?? 0) === 0; + const seededDefault = effective.defaultModel === model && modelListEmpty + && (effective.liveModels === false || (effective.adapter === "google" && effective.googleMode === "vertex")); + const hintEligible = !custom || canInfer || routerKnown.has(model) || (effective.models ?? []).includes(model) + || (effective.retainModels ?? []).includes(model) || seededDefault; + const azureMetadata = hintEligible ? azureVendorModelMetadata(effective.baseUrl, model, getConfigDir()) : undefined; + const capability = Object.hasOwn(effective.modelCapabilities ?? {}, model) + ? effective.modelCapabilities?.[model]?.inputModalities : undefined; + const customModalities = customRowInputModalities(config, provName, model); + const modalities = capability ?? customModalities ?? (modelInList(effective.noVisionModels, model) + ? ["text"] : modelRecordValue(effective.modelInputModalities, model) ?? effective.inputModalities + ?? azureMetadata?.input ?? (canonicalNativeCustom ? nativeInputModalities(model) : null)); // Same reason, for the ladder: `configuredReasoningEfforts` is what the catalog // (`provider-fetch`) and the effort cap (`effort-policy`) resolve through, and it // does three things this expression did not — it returns [] for a noReasoningModels // match, drops levels Codex does not declare, and re-adds tiers the wire map proves // the model emits. Restating two of its five lines here reported a ladder the proxy // strips, and unsanitized junk as a supported level. - const efforts = configuredReasoningEfforts(prov, model) ?? null; + const declaredEfforts = custom?.reasoningEfforts ?? modelRecordValue(effective.modelReasoningEfforts, model) ?? effective.reasoningEfforts; + // A passive CLI cannot resolve an OS-keychain reference to select account-scoped refusals. + // Report unknown rather than opening the keychain or claiming an unverifiable ladder. + const unobservedCredential = providerUsesReasoningMetadata(effective) && isKeychainReference(effective.apiKey); + let efforts = reasoningCapabilitiesFromConfig(effective, model).supportsReasoningEffort === false || declaredEfforts?.length === 0 + ? [] : unobservedCredential ? null : custom?.reasoningEfforts ?? configuredReasoningEfforts(effective, model, { refreshMetadata: false }) + ?? (canonicalNativeCustom ? nativeReasoningEfforts(model) : null); + if (nativeMetadata && efforts && efforts.length > 0) { + const allowed = nativeReasoningEfforts(model); + if (allowed.length > 0) { + const declared = new Set(efforts); + const surviving = [...new Set(allowed)].filter(effort => declared.has(effort)); + const defaultEffort = nativeDefaultReasoningEffort(model); + efforts = surviving.length > 0 ? surviving : [defaultEffort && allowed.includes(defaultEffort) ? defaultEffort : allowed[0]!]; + } + } + if (efforts) efforts = dropLearnedUnsupportedReasoningEfforts(effective, model, efforts); + const nativeCustomContext = canonicalNativeCustom + ? nativeOpenAiContextWindow(model, { + ...nativeContextLimits(config), + ...(custom.contextWindow ? { modelWindows: { ...nativeContextLimits(config).modelWindows, [model]: custom.contextWindow } } : {}), + }) : undefined; + const cap = providerContextCap(config, provName); + const explicitContext = nativeCustomContext ?? custom?.contextWindow; + const configuredContext = hintEligible ? configuredContextWindow(effective, model) : undefined; + const azureWindow = explicitContext === undefined && configuredContext === undefined + ? azureMetadata?.contextWindow : undefined; + const azureContext = Number.isSafeInteger(azureWindow) && azureWindow! > 0 ? azureWindow : undefined; + const inferredContext = explicitContext === undefined && configuredContext === undefined && azureContext === undefined && canInfer + ? vendorContextInference(model)?.contextWindow : undefined; + const fallbackContext = hintEligible && cap !== undefined ? resolveUnknownRoutedContextWindow(cap) : undefined; + const contextWindow = explicitContext + ?? applyProviderContextCap(configuredContext ?? azureContext ?? inferredContext ?? fallbackContext, cap); entries.push({ provider: provName, model, isDefault, - contextWindow: configuredContextWindow(prov, model) ?? null, + contextWindow: contextWindow ?? null, inputModalities: modalities, reasoningEfforts: efforts, price: resolveMatchedPrice(provName, model), @@ -94,9 +171,17 @@ function collectModels(config: OcxConfig, providerFilter?: string): ModelEntry[] // defaultModel first if (prov.defaultModel) addModel(prov.defaultModel, true); - // models array - if (prov.models) { - for (const m of prov.models) addModel(m, m === prov.defaultModel); + // Keep the CLI list current when this config predates a provider's registry seed. + // knownModelIdsForProvider is the router's own derivation: config models first, then + // registry seeds, hint-map keys, and live-discovered cache ids (registry-derived + // sources only enter when the router's registry transport guard matches), then customModels. + // Some legacy key presets retain registry ownership even after a destination change. Because hint-map keys + // are included, wire-only ids a provider's collapsed picker hides still appear here; + // that is router parity. A model the user pruned from `models` reappears if the + // registry still seeds it -- the list is a union, not the user's curation. The + // user's saved models remain first and are never discarded. + for (const m of [...(prov.models ?? []), ...knownModelIdsForProvider(provName, prov, config)]) { + addModel(m, m === prov.defaultModel); } } @@ -119,8 +204,8 @@ function consumeFlagValue(args: string[], flag: string): string | undefined { } function fail(message: string, usage?: string): never { - console.error(`Error: ${message}`); - if (usage) console.error(usage); + console.error(`Error: ${terminalSafeText(message)}`); + if (usage) console.error(terminalSafeText(usage)); process.exit(1); } @@ -150,13 +235,13 @@ function printCustomMutation(action: "added" | "removed", model: OcxCustomModel, const complete = sync.configApplied === true && sync.ok; if (sync.status !== "not-attempted" && (!sync.ok || sync.status === "refused")) process.exitCode = 1; if (wantsJson) { - console.log(JSON.stringify({ action, + printJson({ action, model: action === "added" ? model : { id: model.id, provider: model.provider, modelId: model.modelId }, - needsSync: !complete, sync }, null, 2)); + needsSync: !complete, sync }); return; } - if (sync.warning) console.log(`Warning: ${sync.warning}`); - console.log(`${action === "added" ? "Added" : "Removed"} custom model ${routedSlug(model.provider, model.modelId)}${action === "added" ? ` (${model.id})` : ""}.`); + if (sync.warning) console.log(`Warning: ${terminalSafeText(sync.warning)}`); + console.log(`${action === "added" ? "Added" : "Removed"} custom model ${terminalSafeText(routedSlug(model.provider, model.modelId))}${action === "added" ? ` (${terminalSafeText(model.id)})` : ""}.`); if (sync.status === "skipped") console.log("Custom model saved; client sync was skipped by the integration policy."); else if (!complete && sync.status !== "not-attempted") console.log("Custom model saved; client/catalog sync remains incomplete. Inspect the target before retrying."); } @@ -241,7 +326,7 @@ async function confirmCustomRemoval(model: OcxCustomModel): Promise { } const rl = createInterface({ input: process.stdin, output: process.stdout }); try { - const answer = (await rl.question(`Remove custom model ${routedSlug(model.provider, model.modelId)}? [y/N] `)).trim().toLowerCase(); + const answer = (await rl.question(`Remove custom model ${terminalSafeText(routedSlug(model.provider, model.modelId))}? [y/N] `)).trim().toLowerCase(); return answer === "y" || answer === "yes"; } finally { rl.close(); @@ -331,11 +416,11 @@ function customModelCells(model: OcxCustomModel): string[] { } function printCustomModelGroup(provider: string, models: OcxCustomModel[]): void { - const rows = models.map(customModelCells); + const rows = models.map(model => customModelCells(model).map(terminalSafeText)); const headers = ["ID", "MODEL", "DISPLAY NAME", "CONTEXT", "MODALITIES", "EFFORTS", "DEFAULT EFFORT"]; const widths = headers.map((header, column) => Math.max(header.length, ...rows.map(row => row[column].length))); const line = (cells: string[]) => cells.map((cell, column) => cell.padEnd(widths[column])).join(" "); - console.log(`${provider}:`); + console.log(`${terminalSafeText(provider)}:`); console.log(` ${line(headers)}`); for (const row of rows) console.log(` ${line(row)}`); console.log(); @@ -347,7 +432,7 @@ function handleCustomList(args: string[]): void { rejectUnexpectedArgs(rest, LIST_CUSTOM_USAGE); const models = loadConfig().customModels ?? []; if (wantsJson) { - console.log(JSON.stringify(models, null, 2)); + printJson(models); return; } if (models.length === 0) { @@ -372,9 +457,9 @@ function handleConfiguredModels(args: string[]): void { const shown = redactSecretArgs(restArgs); const unknown = shown.filter(a => a.startsWith("-")); if (unknown.length > 0) { - console.error(`Unknown flag(s): ${unknown.join(", ")}`); + console.error(`Unknown flag(s): ${terminalSafeText(unknown.join(", "))}`); } else { - console.error(`Unexpected argument(s): ${shown.join(", ")}`); + console.error(`Unexpected argument(s): ${terminalSafeText(shown.join(", "))}`); } console.error("Usage: ocx models [--provider ] [--json]"); process.exit(1); @@ -383,23 +468,24 @@ function handleConfiguredModels(args: string[]): void { const config = loadConfig(); if (providerFilter && !hasOwnProvider(config.providers, providerFilter)) { - console.error(`Provider "${providerFilter}" is not configured. See: ocx provider list`); + console.error(`Provider "${terminalSafeText(providerFilter)}" is not configured. See: ocx provider list`); process.exit(1); } const models = collectModels(config, providerFilter ?? undefined); + const note = "Local saved and router-known IDs include registry seeds, custom models and this process's discovery cache. Reasoning may be unknown for keychain credentials. Use ocx models live for the running catalog."; if (wantsJson) { - console.log(JSON.stringify({ + printJson({ models, - note: "Static config models only. Providers with liveModels=true may have additional models at runtime.", - }, null, 2)); + note, + }); return; } if (models.length === 0) { console.log("No models found in configured providers."); - if (!providerFilter) console.log("Providers may discover models dynamically at runtime (liveModels)."); + console.log(`Note: ${note}`); return; } @@ -413,19 +499,19 @@ function handleConfiguredModels(args: string[]): void { for (const [provName, provModels] of byProvider) { const isDefaultProv = provName === config.defaultProvider ? " (default provider)" : ""; - console.log(`${provName}${isDefaultProv}:`); + console.log(`${terminalSafeText(provName)}${isDefaultProv}:`); for (const m of provModels) { const marker = m.isDefault ? " *" : ""; const ctx = m.contextWindow ? ` (${Math.round(m.contextWindow / 1000)}k)` : ""; const rates = m.price?.cost4; const pricing = rates ? ` ~$${rates.input}/$${rates.output} input/output per 1M tokens` : " price unknown"; - console.log(` ${m.model}${marker}${ctx}${pricing}`); + console.log(` ${terminalSafeText(m.model)}${marker}${ctx}${pricing}`); } console.log(); } console.log("* = default model for provider"); - console.log("Note: providers with liveModels may have additional models at runtime."); + console.log(`Note: ${note}`); } export async function handleModels(args: string[], deps: ModelsCommandDeps = {}): Promise { @@ -451,7 +537,7 @@ export async function handleModels(args: string[], deps: ModelsCommandDeps = {}) return; } if (subcommand === "list-custom") { - handleCustomList(rest); + await runPassiveRead(async () => { handleCustomList(rest); }, { onDiagnostic: message => console.error(message) }); return; } if (isModelsRuntimeSubcommand(subcommand)) { @@ -460,5 +546,5 @@ export async function handleModels(args: string[], deps: ModelsCommandDeps = {}) if (code !== null) process.exitCode = code; return; } - handleConfiguredModels(subcommand === "list" ? rest : args); + await runPassiveRead(async () => { handleConfiguredModels(subcommand === "list" ? rest : args); }, { onDiagnostic: message => console.error(message) }); } diff --git a/src/cli/registry.ts b/src/cli/registry.ts index 53db87d00e0..a9f48283878 100644 --- a/src/cli/registry.ts +++ b/src/cli/registry.ts @@ -379,7 +379,7 @@ export const CLI_COMMANDS: CliCommandEntry[] = [ usage: "ocx models ...", summary: "List models and manage custom (manually registered) models.", details: [ - "List available models from static config with no subcommand (liveModels may add more at runtime).", + "List available models: saved and router-known IDs include custom models and the process-local cache; models live reads the running proxy catalog.", "add/remove support --live for the selected proxy; live or JSON removal requires --yes.", "Local add/remove JSON reports saved state and opportunistic sync; no proxy keeps the save successful/pending.", "display-name --set