From 906db25e2fbe445b069d151332f506e24e167065 Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:13:36 -0300 Subject: [PATCH 1/3] feat(cli-tools): write an OmniRoute model catalog when applying Codex settings Codex's /model picker only lists its bundled catalog, so picking a bare slug sends e.g. gpt-5.5 to OmniRoute, which may not route it. On Apply (opt-out checkbox, on by default) the Codex settings route now runs `codex debug models --bundled`, clones each bundled entry (they carry version-specific system prompts Codex validates strictly) into ~/.codex/omniroute-models.json under OmniRoute's provider-prefixed ids, and points `model_catalog_json` at it. - non-Codex upstreams get code-mode flags cleared; inherited upgrade notices are dropped; bundled entries stay as hidden so internal slugs resolve - selected models lead the picker; listed block capped at 300 entries - fail-open: if Codex is missing/old, Apply still succeeds and returns catalogWarning (shown as a warning in the dashboard) - Reset removes the key only when it points to OmniRoute's file, then deletes the file - spawn via execFile with an argv array (Hard Rule #13) and CODEX_HOME pinned to the directory OmniRoute writes into Co-authored-by: Kalfian Inspired-by: https://github.com/decolua/9router/pull/4406 --- .../cli-code/components/CodexToolCard.tsx | 29 +- src/app/api/cli-tools/codex-settings/route.ts | 45 ++- src/i18n/messages/am.json | 3 + src/i18n/messages/ar.json | 3 + src/i18n/messages/az.json | 3 + src/i18n/messages/bg.json | 3 + src/i18n/messages/bn.json | 3 + src/i18n/messages/bs.json | 3 + src/i18n/messages/cs.json | 3 + src/i18n/messages/da.json | 3 + src/i18n/messages/de.json | 3 + src/i18n/messages/el.json | 3 + src/i18n/messages/en.json | 3 + src/i18n/messages/es.json | 3 + src/i18n/messages/et.json | 3 + src/i18n/messages/fa.json | 3 + src/i18n/messages/fi.json | 3 + src/i18n/messages/fr.json | 3 + src/i18n/messages/ga.json | 3 + src/i18n/messages/gu.json | 3 + src/i18n/messages/ha.json | 3 + src/i18n/messages/he.json | 3 + src/i18n/messages/hi.json | 3 + src/i18n/messages/hr.json | 3 + src/i18n/messages/hu.json | 3 + src/i18n/messages/hy.json | 3 + src/i18n/messages/id.json | 3 + src/i18n/messages/ig.json | 3 + src/i18n/messages/it.json | 3 + src/i18n/messages/ja.json | 3 + src/i18n/messages/ka.json | 3 + src/i18n/messages/km.json | 3 + src/i18n/messages/kn.json | 3 + src/i18n/messages/ko.json | 3 + src/i18n/messages/lt.json | 3 + src/i18n/messages/lv.json | 3 + src/i18n/messages/ml.json | 3 + src/i18n/messages/mr.json | 3 + src/i18n/messages/ms.json | 3 + src/i18n/messages/mt.json | 3 + src/i18n/messages/my.json | 3 + src/i18n/messages/ne.json | 3 + src/i18n/messages/nl.json | 3 + src/i18n/messages/no.json | 3 + src/i18n/messages/or.json | 3 + src/i18n/messages/pa.json | 3 + src/i18n/messages/phi.json | 3 + src/i18n/messages/pl.json | 3 + src/i18n/messages/pt-BR.json | 3 + src/i18n/messages/pt.json | 3 + src/i18n/messages/ro.json | 3 + src/i18n/messages/ru.json | 3 + src/i18n/messages/si.json | 3 + src/i18n/messages/sk.json | 3 + src/i18n/messages/sl.json | 3 + src/i18n/messages/sr.json | 3 + src/i18n/messages/sv.json | 3 + src/i18n/messages/sw.json | 3 + src/i18n/messages/ta.json | 3 + src/i18n/messages/te.json | 3 + src/i18n/messages/th.json | 3 + src/i18n/messages/tr.json | 3 + src/i18n/messages/uk-UA.json | 3 + src/i18n/messages/ur.json | 3 + src/i18n/messages/uz.json | 3 + src/i18n/messages/vi.json | 3 + src/i18n/messages/yo.json | 3 + src/i18n/messages/zh-CN.json | 3 + src/i18n/messages/zh-TW.json | 3 + src/lib/cliTools/codexModelCatalog.ts | 207 ++++++++++++ src/lib/cliTools/codexModelCatalogWriter.ts | 141 +++++++++ src/shared/validation/schemas/cli.ts | 8 + tests/unit/codex-model-catalog.test.ts | 299 ++++++++++++++++++ .../unit/codex-settings-model-catalog.test.ts | 206 ++++++++++++ .../ui/codex-tool-card-model-catalog.test.tsx | 150 +++++++++ 75 files changed, 1280 insertions(+), 6 deletions(-) create mode 100644 src/lib/cliTools/codexModelCatalog.ts create mode 100644 src/lib/cliTools/codexModelCatalogWriter.ts create mode 100644 tests/unit/codex-model-catalog.test.ts create mode 100644 tests/unit/codex-settings-model-catalog.test.ts create mode 100644 tests/unit/ui/codex-tool-card-model-catalog.test.tsx diff --git a/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx b/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx index 3e589aca84c9..29c4c4c79e39 100644 --- a/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx +++ b/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx @@ -33,6 +33,7 @@ export default function CodexToolCard({ const [modelMappings, setModelMappings] = useState>({}); const [reasoningEffort, setReasoningEffort] = useState("xhigh"); const [wireApi, setWireApi] = useState("responses"); + const [writeModelCatalog, setWriteModelCatalog] = useState(true); const [modalOpen, setModalOpen] = useState(false); const [modalTarget, setModalTarget] = useState(null); // null = default model, string = mapping key const [modelAliases, setModelAliases] = useState({}); @@ -190,11 +191,16 @@ export default function CodexToolCard({ reasoningEffort, wireApi, modelMappings, + modelCatalog: writeModelCatalog, }), }); const data = await res.json(); if (res.ok) { - setMessage({ type: "success", text: t("settingsApplied") }); + setMessage( + data.catalogWarning + ? { type: "warning", text: `${t("settingsApplied")} ${t("codexCatalogWarning")}` } + : { type: "success", text: t("settingsApplied") } + ); checkCodexStatus(); } else { setMessage({ @@ -652,6 +658,19 @@ openai_base_url = "${getEffectiveBaseUrl()}" + +
@@ -703,10 +722,14 @@ openai_base_url = "${getEffectiveBaseUrl()}" {message && (
- {message.type === "success" ? "check_circle" : "error"} + {message.type === "success" + ? "check_circle" + : message.type === "warning" + ? "warning" + : "error"} {message.text}
diff --git a/src/app/api/cli-tools/codex-settings/route.ts b/src/app/api/cli-tools/codex-settings/route.ts index d106dc5f27aa..5c50fc105c7d 100644 --- a/src/app/api/cli-tools/codex-settings/route.ts +++ b/src/app/api/cli-tools/codex-settings/route.ts @@ -12,11 +12,18 @@ import { } from "@/shared/services/cliRuntime"; import { createMultiBackup } from "@/shared/services/backupService"; import { saveCliToolLastConfigured, deleteCliToolLastConfigured } from "@/lib/db/cliToolState"; -import { cliModelConfigSchema } from "@/shared/validation/schemas"; +import { codexSettingsConfigSchema } from "@/shared/validation/schemas"; import { isValidationFailure, validateBody } from "@/shared/validation/helpers"; import { resolveApiKey } from "@/shared/services/apiKeyResolver"; import { normalizeCodexBaseUrl } from "@/shared/utils/codexBaseUrl"; import { migrateCodexFeatureFlags } from "@/shared/utils/codexConfig"; +import { + getCodexCatalogPath, + listOmniRouteChatModels, + removeCodexModelCatalog, + writeCodexModelCatalog, + type CodexCatalogWriteResult, +} from "@/lib/cliTools/codexModelCatalogWriter"; import { configClobberRefusalResponse, parseJsonObject, @@ -26,6 +33,9 @@ import { const getCodexConfigPath = () => getCliConfigPaths("codex").config; const getCodexAuthPath = () => getCliConfigPaths("codex").auth; const getCodexDir = () => path.dirname(getCodexConfigPath()); +// TOML basic strings treat a backslash as an escape and this file's writer does not escape, +// so the catalog path is stored with forward slashes (Codex accepts them on Windows). +const toTomlPath = (filePath: string) => filePath.replace(/\\/g, "/"); // Parse TOML config to object (simple parser for codex config) const parseToml = (content: string) => { @@ -224,11 +234,12 @@ export async function POST(request: Request) { // we always write the full key value to the config file. const keyId = typeof rawBody?.keyId === "string" ? rawBody.keyId.trim() : null; - const validation = validateBody(cliModelConfigSchema, rawBody); + const validation = validateBody(codexSettingsConfigSchema, rawBody); if (isValidationFailure(validation)) { return NextResponse.json({ error: validation.error }, { status: 400 }); } - const { baseUrl, model, reasoningEffort, wireApi, modelMappings } = validation.data; + const { baseUrl, model, reasoningEffort, wireApi, modelMappings, modelCatalog } = + validation.data; // Canonical key resolution (#13563): by keyId -> submitted apiKey -> sk_omniroute. // Matches cline/forge/openclaw/grok-build/jcode-settings. const apiKey = await resolveApiKey(keyId, validation.data.apiKey); @@ -295,6 +306,18 @@ export async function POST(request: Request) { delete parsed._sections["notice.model_migrations"]; } + // Point Codex's /model picker at an OmniRoute catalog. Fail-open: on any failure + // Apply still succeeds, a previous value is left alone and a warning is returned. + let catalog: CodexCatalogWriteResult | null = null; + if (modelCatalog === true) { + catalog = await writeCodexModelCatalog({ + codexDir, + selectedModels: [model, ...Object.values(modelMappings ?? {})], + listModels: () => listOmniRouteChatModels(request), + }); + if (catalog.catalogPath) parsed._root.model_catalog_json = toTomlPath(catalog.catalogPath); + } + // Write merged config const configContent = toToml(parsed); await fs.writeFile(configPath, configContent); @@ -314,6 +337,10 @@ export async function POST(request: Request) { success: true, message: "Codex settings applied successfully!", configPath, + ...(catalog?.catalogPath + ? { catalogPath: catalog.catalogPath, catalogModelCount: catalog.count } + : {}), + ...(catalog?.warning ? { catalogWarning: catalog.warning } : {}), }); } catch (error) { const refusal = configClobberRefusalResponse(error); @@ -368,6 +395,16 @@ export async function DELETE(request: Request) { // Remove omniroute provider section delete parsed._sections["model_providers.omniroute"]; + // Remove the model catalog only when it is the one OmniRoute wrote + const codexDir = getCodexDir(); + const catalogValue = parsed._root.model_catalog_json; + if ( + typeof catalogValue === "string" && + toTomlPath(catalogValue) === toTomlPath(getCodexCatalogPath(codexDir)) + ) { + delete parsed._root.model_catalog_json; + } + // Write updated config const configContent = toToml(parsed); await fs.writeFile(configPath, configContent); @@ -389,6 +426,8 @@ export async function DELETE(request: Request) { /* No auth file */ } + await removeCodexModelCatalog(codexDir); + // Clear last-configured timestamp try { deleteCliToolLastConfigured("codex"); diff --git a/src/i18n/messages/am.json b/src/i18n/messages/am.json index cfd757bdedf4..fe8ef546a61b 100644 --- a/src/i18n/messages/am.json +++ b/src/i18n/messages/am.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ከዚያ VS Codeን ዳግም ያስጀምሩ እና በግቤት መጠየቂያው ውስጥ የAPI ቁልፉን ያስገቡ።", "wireApiChatCompletions": "የውይይት ማጠናቀቂያዎች (/chat/completions)", "wireApiResponses": "የምላሾች API (/responses)", + "codexModelCatalog": "የOmniRoute ሞዴሎችን በCodex /model መምረጫ ውስጥ ዘርዝር", + "codexModelCatalogHint": "ከተጫነው Codex የራሱ የሞዴል ካታሎግ ~/.codex/omniroute-models.json ይጽፋል (በአንድ ሞዴል 60 KB ገደማ)። Codexን ካሻሻሉ በኋላ እንደገና ይተግብሩ።", + "codexCatalogWarning": "የCodex /model መምረጫ አልዘመነም፦ OmniRoute የተጫነውን የCodex ሞዴል ካታሎግ ማንበብ አልቻለም።", "cliproxyapiActionFailed": "እርምጃው አልተሳካም", "cliproxyapiActionSucceeded": "እርምጃው ተሳክቷል", "cliproxyapiCheckUpdates": "ዝማኔዎችን ይፈትሹ", diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index ccb731d451b4..5a55ca45749d 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ثم أعد تحميل VS Code وعيّن مفتاح API في موجه الإدخال.", "wireApiChatCompletions": "مكتملات الدردشة (/chat/مكتملات)", "wireApiResponses": "واجهة برمجة تطبيقات الاستجابات (/ الاستجابات)", + "codexModelCatalog": "عرض نماذج OmniRoute في منتقي ‎/model في Codex", + "codexModelCatalogHint": "يكتب ~/.codex/omniroute-models.json انطلاقًا من كتالوج النماذج الخاص بـ Codex المثبت (نحو 60 KB لكل نموذج). أعد التطبيق بعد ترقية Codex.", + "codexCatalogWarning": "لم يتم تحديث منتقي ‎/model في Codex: تعذّر على OmniRoute قراءة كتالوج نماذج Codex المثبت.", "cliproxyapiActionFailed": "فشل الإجراء", "cliproxyapiActionSucceeded": "تمت العملية بنجاح", "cliproxyapiCheckUpdates": "تحقق من التحديثات", diff --git a/src/i18n/messages/az.json b/src/i18n/messages/az.json index 3261f7210a34..8b6d0da82eea 100644 --- a/src/i18n/messages/az.json +++ b/src/i18n/messages/az.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Sonra VS Code-u yenidən yükləyin və daxiletmə sorğusunda API açarını təyin edin.", "wireApiChatCompletions": "Söhbət Tamamlamaları (/chat/tamamlamalar)", "wireApiResponses": "Responses API (/cavablar)", + "codexModelCatalog": "OmniRoute modellərini Codex-in /model seçicisində göstər", + "codexModelCatalogHint": "Quraşdırılmış Codex-in öz model kataloqundan ~/.codex/omniroute-models.json faylını yazır (hər model üçün təxminən 60 KB). Codex-i yenilədikdən sonra yenidən tətbiq edin.", + "codexCatalogWarning": "Codex-in /model seçicisi yenilənmədi: OmniRoute quraşdırılmış Codex model kataloqunu oxuya bilmədi.", "cliproxyapiActionFailed": "Əməliyyat uğursuz oldu", "cliproxyapiActionSucceeded": "Əməliyyat uğurla başa çatdı", "cliproxyapiCheckUpdates": "Yeniləmələri Yoxla", diff --git a/src/i18n/messages/bg.json b/src/i18n/messages/bg.json index fce9febb9f79..5679a2415400 100644 --- a/src/i18n/messages/bg.json +++ b/src/i18n/messages/bg.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "След това рестартирайте VS Code и задайте API ключа в полето за въвеждане.", "wireApiChatCompletions": "Завършвания на чат (/chat/completions)", "wireApiResponses": "API за отговори (/отговори)", + "codexModelCatalog": "Показване на моделите на OmniRoute в избора /model на Codex", + "codexModelCatalogHint": "Записва ~/.codex/omniroute-models.json от собствения каталог с модели на инсталирания Codex (около 60 KB на модел). Приложете отново след обновяване на Codex.", + "codexCatalogWarning": "Изборът /model на Codex не беше обновен: OmniRoute не успя да прочете каталога с модели на инсталирания Codex.", "cliproxyapiActionFailed": "Действието не успя", "cliproxyapiActionSucceeded": "Действието беше успешно", "cliproxyapiCheckUpdates": "Провери актуализации", diff --git a/src/i18n/messages/bn.json b/src/i18n/messages/bn.json index 1209749d7225..8f1fe6bbfd57 100644 --- a/src/i18n/messages/bn.json +++ b/src/i18n/messages/bn.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "তারপর VS Code রিলোড করুন এবং ইনপুট প্রম্পটে API কী সেট করুন।", "wireApiChatCompletions": "চ্যাট সমাপ্তি (/চ্যাট/সম্পূর্ণতা)", "wireApiResponses": "প্রতিক্রিয়া API (/প্রতিক্রিয়া)", + "codexModelCatalog": "Codex-এর /model পিকারে OmniRoute মডেলগুলি দেখান", + "codexModelCatalogHint": "ইনস্টল করা Codex-এর নিজস্ব মডেল ক্যাটালগ থেকে ~/.codex/omniroute-models.json লেখে (প্রতি মডেলে প্রায় 60 KB)। Codex আপগ্রেড করার পরে আবার প্রয়োগ করুন।", + "codexCatalogWarning": "Codex-এর /model পিকার আপডেট হয়নি: OmniRoute ইনস্টল করা Codex মডেল ক্যাটালগ পড়তে পারেনি।", "cliproxyapiActionFailed": "কার্য সম্পন্ন হয়নি", "cliproxyapiActionSucceeded": "কার্য সফল হয়েছে", "cliproxyapiCheckUpdates": "আপডেট পরীক্ষা করুন", diff --git a/src/i18n/messages/bs.json b/src/i18n/messages/bs.json index 350932f4febe..88eddc382ead 100644 --- a/src/i18n/messages/bs.json +++ b/src/i18n/messages/bs.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Zatim ponovo učitajte VS Code i postavite API ključ u promptu za unos.", "wireApiChatCompletions": "Dovršavanja razgovora (/chat/completions)", "wireApiResponses": "API odgovora (/responses)", + "codexModelCatalog": "Prikaži OmniRoute modele u Codex /model biraču", + "codexModelCatalogHint": "Upisuje ~/.codex/omniroute-models.json iz vlastitog kataloga modela instaliranog Codexa (oko 60 KB po modelu). Ponovo primijenite nakon nadogradnje Codexa.", + "codexCatalogWarning": "Codex /model birač nije ažuriran: OmniRoute nije mogao pročitati katalog modela instaliranog Codexa.", "cliproxyapiActionFailed": "Akcija nije uspjela", "cliproxyapiActionSucceeded": "Akcija je uspjela", "cliproxyapiCheckUpdates": "Provjeri ažuriranja", diff --git a/src/i18n/messages/cs.json b/src/i18n/messages/cs.json index 7bb91536cc51..0f9a746e91f1 100644 --- a/src/i18n/messages/cs.json +++ b/src/i18n/messages/cs.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Poté znovu načtěte VS Code a nastavte klíč API ve vstupním poli.", "wireApiChatCompletions": "Dokončení chatu (/chat/completions)", "wireApiResponses": "API odpovědí (/responses)", + "codexModelCatalog": "Zobrazit modely OmniRoute ve výběru /model v Codexu", + "codexModelCatalogHint": "Zapíše ~/.codex/omniroute-models.json z vlastního katalogu modelů nainstalovaného Codexu (asi 60 KB na model). Po aktualizaci Codexu použijte znovu.", + "codexCatalogWarning": "Výběr /model v Codexu nebyl aktualizován: OmniRoute nedokázal načíst katalog modelů nainstalovaného Codexu.", "cliproxyapiActionFailed": "Akce selhala", "cliproxyapiActionSucceeded": "Akce byla úspěšná", "cliproxyapiCheckUpdates": "Zkontrolovat aktualizace", diff --git a/src/i18n/messages/da.json b/src/i18n/messages/da.json index a841a278b3f6..dc93a2869fdf 100644 --- a/src/i18n/messages/da.json +++ b/src/i18n/messages/da.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Genstart derefter VS Code, og angiv API-nøglen i inputfeltet.", "wireApiChatCompletions": "Chatafslutninger (/chat/afslutninger)", "wireApiResponses": "Svar-API (/responses)", + "codexModelCatalog": "Vis OmniRoute-modeller i Codex' /model-vælger", + "codexModelCatalogHint": "Skriver ~/.codex/omniroute-models.json ud fra den installerede Codex' egen modelkatalog (ca. 60 KB pr. model). Anvend igen efter opgradering af Codex.", + "codexCatalogWarning": "Codex' /model-vælger blev ikke opdateret: OmniRoute kunne ikke læse den installerede Codex' modelkatalog.", "cliproxyapiActionFailed": "Handlingen mislykkedes", "cliproxyapiActionSucceeded": "Handlingen lykkedes", "cliproxyapiCheckUpdates": "Tjek Opdateringer", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 9a326321af45..f896602b221f 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Laden Sie dann VS Code neu und legen Sie den API-Schlüssel in der Eingabeaufforderung fest.", "wireApiChatCompletions": "Chat-Abschlüsse (/chat/completions)", "wireApiResponses": "Antwort-API (/responses)", + "codexModelCatalog": "OmniRoute-Modelle in der /model-Auswahl von Codex anzeigen", + "codexModelCatalogHint": "Schreibt ~/.codex/omniroute-models.json aus dem eigenen Modellkatalog des installierten Codex (ca. 60 KB pro Modell). Nach einem Codex-Update erneut anwenden.", + "codexCatalogWarning": "Die /model-Auswahl von Codex wurde nicht aktualisiert: OmniRoute konnte den Modellkatalog des installierten Codex nicht lesen.", "cliproxyapiActionFailed": "Aktion fehlgeschlagen", "cliproxyapiActionSucceeded": "Aktion erfolgreich", "cliproxyapiCheckUpdates": "Updates überprüfen", diff --git a/src/i18n/messages/el.json b/src/i18n/messages/el.json index d55928c5a70c..4c4793859f16 100644 --- a/src/i18n/messages/el.json +++ b/src/i18n/messages/el.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Στη συνέχεια, επαναφορτώστε το VS Code και ορίστε το κλειδί API στο πεδίο εισαγωγής.", "wireApiChatCompletions": "Ολοκληρώσεις συνομιλίας (/chat/completions)", "wireApiResponses": "API αποκρίσεων (/responses)", + "codexModelCatalog": "Εμφάνιση μοντέλων OmniRoute στον επιλογέα /model του Codex", + "codexModelCatalogHint": "Γράφει το ~/.codex/omniroute-models.json από τον κατάλογο μοντέλων του εγκατεστημένου Codex (περίπου 60 KB ανά μοντέλο). Εφαρμόστε ξανά μετά από αναβάθμιση του Codex.", + "codexCatalogWarning": "Ο επιλογέας /model του Codex δεν ενημερώθηκε: το OmniRoute δεν μπόρεσε να διαβάσει τον κατάλογο μοντέλων του εγκατεστημένου Codex.", "cliproxyapiActionFailed": "Η ενέργεια απέτυχε", "cliproxyapiActionSucceeded": "Η ενέργεια ολοκληρώθηκε με επιτυχία", "cliproxyapiCheckUpdates": "Έλεγχος ενημερώσεων", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index d8442d0fdf92..cdaa7dee3548 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Then reload VS Code and set the API key in the input prompt.", "wireApiChatCompletions": "Chat Completions (/chat/completions)", "wireApiResponses": "Responses API (/responses)", + "codexModelCatalog": "List OmniRoute models in Codex's /model picker", + "codexModelCatalogHint": "Writes ~/.codex/omniroute-models.json from your installed Codex's own model catalog (about 60 KB per model). Re-apply after upgrading Codex.", + "codexCatalogWarning": "Codex's /model picker was not updated: OmniRoute could not read the installed Codex model catalog.", "cliproxyapiActionFailed": "Action failed", "cliproxyapiActionSucceeded": "Action succeeded", "cliproxyapiCheckUpdates": "Check Updates", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index f89fce4b9523..0ad6bdd10c5c 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "A continuación, vuelve a cargar VS Code y establece la clave de API en el campo de entrada.", "wireApiChatCompletions": "Finalizaciones de chat (/chat/completions)", "wireApiResponses": "API de respuestas (/respuestas)", + "codexModelCatalog": "Mostrar los modelos de OmniRoute en el selector /model de Codex", + "codexModelCatalogHint": "Escribe ~/.codex/omniroute-models.json a partir del catálogo de modelos del propio Codex instalado (unos 60 KB por modelo). Vuelve a aplicar tras actualizar Codex.", + "codexCatalogWarning": "No se actualizó el selector /model de Codex: OmniRoute no pudo leer el catálogo de modelos del Codex instalado.", "cliproxyapiActionFailed": "Acción fallida", "cliproxyapiActionSucceeded": "Acción exitosa", "cliproxyapiCheckUpdates": "Buscar Actualizaciones", diff --git a/src/i18n/messages/et.json b/src/i18n/messages/et.json index baba137cac98..fe28ee459d7c 100644 --- a/src/i18n/messages/et.json +++ b/src/i18n/messages/et.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Seejärel laadige VS Code uuesti ja määrake sisestusviibas API-võti.", "wireApiChatCompletions": "Vestluse lõpuleviimised (/chat/completions)", "wireApiResponses": "Vastuste API (/responses)", + "codexModelCatalog": "Näita OmniRoute'i mudeleid Codexi /model valijas", + "codexModelCatalogHint": "Kirjutab ~/.codex/omniroute-models.json paigaldatud Codexi enda mudelikataloogist (umbes 60 KB mudeli kohta). Rakenda uuesti pärast Codexi uuendamist.", + "codexCatalogWarning": "Codexi /model valijat ei uuendatud: OmniRoute ei saanud paigaldatud Codexi mudelikataloogi lugeda.", "cliproxyapiActionFailed": "Toiming nurjus", "cliproxyapiActionSucceeded": "Toiming õnnestus", "cliproxyapiCheckUpdates": "Kontrolli värskendusi", diff --git a/src/i18n/messages/fa.json b/src/i18n/messages/fa.json index 9b22f48287fc..0f2015938940 100644 --- a/src/i18n/messages/fa.json +++ b/src/i18n/messages/fa.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "سپس VS Code را مجدداً بارگیری کرده و کلید API را در اعلان ورودی تنظیم کنید.", "wireApiChatCompletions": "تکمیل چت (/chat/completions)", "wireApiResponses": "API پاسخها (/responses)", + "codexModelCatalog": "نمایش مدل‌های OmniRoute در انتخابگر ‎/model در Codex", + "codexModelCatalogHint": "فایل ~/.codex/omniroute-models.json را از کاتالوگ مدل خودِ Codex نصب‌شده می‌نویسد (حدود 60 KB برای هر مدل). پس از ارتقای Codex دوباره اعمال کنید.", + "codexCatalogWarning": "انتخابگر ‎/model در Codex به‌روزرسانی نشد: OmniRoute نتوانست کاتالوگ مدل Codex نصب‌شده را بخواند.", "cliproxyapiActionFailed": "عملیات ناموفق بود", "cliproxyapiActionSucceeded": "عملیات با موفقیت انجام شد", "cliproxyapiCheckUpdates": "بررسی به‌روزرسانی‌ها", diff --git a/src/i18n/messages/fi.json b/src/i18n/messages/fi.json index 1661e871bf67..8c646e378d40 100644 --- a/src/i18n/messages/fi.json +++ b/src/i18n/messages/fi.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Käynnistä sitten VS Code uudelleen ja aseta API-avain syötekehotteessa.", "wireApiChatCompletions": "Keskustelun päättymiset (/chat/completions)", "wireApiResponses": "Responses API (/responses)", + "codexModelCatalog": "Näytä OmniRoute-mallit Codexin /model-valitsimessa", + "codexModelCatalogHint": "Kirjoittaa tiedoston ~/.codex/omniroute-models.json asennetun Codexin omasta mallikatalogista (noin 60 KB mallia kohden). Ota uudelleen käyttöön Codexin päivityksen jälkeen.", + "codexCatalogWarning": "Codexin /model-valitsinta ei päivitetty: OmniRoute ei voinut lukea asennetun Codexin mallikatalogia.", "cliproxyapiActionFailed": "Toiminto epäonnistui", "cliproxyapiActionSucceeded": "Toiminto onnistui", "cliproxyapiCheckUpdates": "Tarkista päivitykset", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 8062e8112755..446440491889 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Rechargez ensuite VS Code et définissez la clé API dans l'invite de saisie.", "wireApiChatCompletions": "Achèvements de chat (/chat/completions)", "wireApiResponses": "API de réponses (/réponses)", + "codexModelCatalog": "Afficher les modèles OmniRoute dans le sélecteur /model de Codex", + "codexModelCatalogHint": "Écrit ~/.codex/omniroute-models.json à partir du catalogue de modèles du Codex installé (environ 60 Ko par modèle). Réappliquez après une mise à jour de Codex.", + "codexCatalogWarning": "Le sélecteur /model de Codex n'a pas été mis à jour : OmniRoute n'a pas pu lire le catalogue de modèles du Codex installé.", "cliproxyapiActionFailed": "Échec de l’action", "cliproxyapiActionSucceeded": "Action réussie", "cliproxyapiCheckUpdates": "Vérifier les mises à jour", diff --git a/src/i18n/messages/ga.json b/src/i18n/messages/ga.json index 1b623b05ef5c..5c4e98487a6c 100644 --- a/src/i18n/messages/ga.json +++ b/src/i18n/messages/ga.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Ansin athlódáil VS Code agus socraigh an eochair API sa leid ionchuir.", "wireApiChatCompletions": "Comhlánuithe Comhrá (/chat/completions)", "wireApiResponses": "API Freagairtí (/responses)", + "codexModelCatalog": "Taispeáin samhlacha OmniRoute i roghnóir /model Codex", + "codexModelCatalogHint": "Scríobhann sé ~/.codex/omniroute-models.json ó chatalóg samhlacha an Codex suiteáilte féin (thart ar 60 KB in aghaidh na samhla). Cuir i bhfeidhm arís tar éis Codex a uasghrádú.", + "codexCatalogWarning": "Níor nuashonraíodh roghnóir /model Codex: níorbh fhéidir le OmniRoute catalóg samhlacha an Codex suiteáilte a léamh.", "cliproxyapiActionFailed": "Theip ar an ngníomh", "cliproxyapiActionSucceeded": "D'éirigh leis an ngníomh", "cliproxyapiCheckUpdates": "Seiceáil nuashonruithe", diff --git a/src/i18n/messages/gu.json b/src/i18n/messages/gu.json index bb03dc1b2056..650cc6c5e435 100644 --- a/src/i18n/messages/gu.json +++ b/src/i18n/messages/gu.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "પછી VS Code ફરીથી લોડ કરો અને ઇનપુટ પ્રોમ્પ્ટમાં API કી સેટ કરો.", "wireApiChatCompletions": "ચેટ પૂર્ણતા (/ચેટ/પૂર્ણતા)", "wireApiResponses": "પ્રતિભાવ API (/પ્રતિસાદો)", + "codexModelCatalog": "Codex ના /model પિકરમાં OmniRoute મોડલ્સ બતાવો", + "codexModelCatalogHint": "ઇન્સ્ટોલ કરેલા Codex ના પોતાના મોડલ કેટલોગમાંથી ~/.codex/omniroute-models.json લખે છે (દરેક મોડલ માટે આશરે 60 KB). Codex અપગ્રેડ કર્યા પછી ફરીથી લાગુ કરો.", + "codexCatalogWarning": "Codex નો /model પિકર અપડેટ થયો નથી: OmniRoute ઇન્સ્ટોલ કરેલા Codex મોડલ કેટલોગને વાંચી શક્યું નથી.", "cliproxyapiActionFailed": "ક્રિયા નિષ્ફળ ગઈ", "cliproxyapiActionSucceeded": "ક્રિયા સફળ થઈ ગઈ", "cliproxyapiCheckUpdates": "અપડેટ્સ તપાસો", diff --git a/src/i18n/messages/ha.json b/src/i18n/messages/ha.json index 2a5c68843053..2b1e5b165f22 100644 --- a/src/i18n/messages/ha.json +++ b/src/i18n/messages/ha.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Sannan ka sake loda VS Code kuma ka saita maɓallin API a filin shigarwa.", "wireApiChatCompletions": "Kammalawar Taɗi (/chat/completions)", "wireApiResponses": "API na Amsoshi (/responses)", + "codexModelCatalog": "Nuna samfuran OmniRoute a cikin mai zaɓin /model na Codex", + "codexModelCatalogHint": "Yana rubuta ~/.codex/omniroute-models.json daga kundin samfuran Codex da aka shigar (kusan 60 KB kowane samfuri). Sake aiwatarwa bayan haɓaka Codex.", + "codexCatalogWarning": "Ba a sabunta mai zaɓin /model na Codex ba: OmniRoute ya kasa karanta kundin samfuran Codex da aka shigar.", "cliproxyapiActionFailed": "Aikin bai yi nasara ba", "cliproxyapiActionSucceeded": "Aikin ya yi nasara", "cliproxyapiCheckUpdates": "Duba Sabuntawa", diff --git a/src/i18n/messages/he.json b/src/i18n/messages/he.json index d7f02f7d968d..cf1cf420ac5e 100644 --- a/src/i18n/messages/he.json +++ b/src/i18n/messages/he.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "לאחר מכן טען מחדש את VS Code והגדר את מפתח ה-API בשורת הקלט.", "wireApiChatCompletions": "השלמות של צ'אט (/chat/completions)", "wireApiResponses": "ממשק API של תגובות (/תגובות)", + "codexModelCatalog": "הצג את מודלי OmniRoute בבורר ‎/model של Codex", + "codexModelCatalogHint": "כותב את ~/.codex/omniroute-models.json מתוך קטלוג המודלים של Codex המותקן (כ־60 KB לכל מודל). יש להחיל שוב לאחר שדרוג Codex.", + "codexCatalogWarning": "בורר ‎/model של Codex לא עודכן: OmniRoute לא הצליח לקרוא את קטלוג המודלים של Codex המותקן.", "cliproxyapiActionFailed": "הפעולה נכשלה", "cliproxyapiActionSucceeded": "הפעולה הצליחה", "cliproxyapiCheckUpdates": "בדוק עדכונים", diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json index 9baa3a6521db..86b071a02df7 100644 --- a/src/i18n/messages/hi.json +++ b/src/i18n/messages/hi.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "फिर VS Code को पुनः लोड करें और इनपुट प्रॉम्प्ट में API कुंजी सेट करें।", "wireApiChatCompletions": "चैट पूर्णताएँ (/चैट/पूर्णियाँ)", "wireApiResponses": "प्रतिक्रियाएँ एपीआई (/प्रतिक्रियाएँ)", + "codexModelCatalog": "Codex के /model पिकर में OmniRoute मॉडल दिखाएँ", + "codexModelCatalogHint": "इंस्टॉल किए गए Codex के अपने मॉडल कैटलॉग से ~/.codex/omniroute-models.json लिखता है (प्रति मॉडल लगभग 60 KB)। Codex अपग्रेड करने के बाद फिर से लागू करें।", + "codexCatalogWarning": "Codex का /model पिकर अपडेट नहीं हुआ: OmniRoute इंस्टॉल किए गए Codex का मॉडल कैटलॉग नहीं पढ़ सका।", "cliproxyapiActionFailed": "क्रिया विफल हुई", "cliproxyapiActionSucceeded": "कार्य सफल हुआ", "cliproxyapiCheckUpdates": "अपडेट्स की जांच करें", diff --git a/src/i18n/messages/hr.json b/src/i18n/messages/hr.json index 7f684216525a..a66d8f3cc6e1 100644 --- a/src/i18n/messages/hr.json +++ b/src/i18n/messages/hr.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Zatim ponovno učitajte VS Code i postavite API ključ u polje za unos.", "wireApiChatCompletions": "Dovršavanje razgovora (/chat/completions)", "wireApiResponses": "API za odgovore (/responses)", + "codexModelCatalog": "Prikaži OmniRoute modele u Codexovom /model biraču", + "codexModelCatalogHint": "Zapisuje ~/.codex/omniroute-models.json iz vlastitog kataloga modela instaliranog Codexa (oko 60 KB po modelu). Ponovno primijenite nakon nadogradnje Codexa.", + "codexCatalogWarning": "Codexov /model birač nije ažuriran: OmniRoute nije mogao pročitati katalog modela instaliranog Codexa.", "cliproxyapiActionFailed": "Radnja nije uspjela", "cliproxyapiActionSucceeded": "Radnja je uspjela", "cliproxyapiCheckUpdates": "Provjeri ima li ažuriranja", diff --git a/src/i18n/messages/hu.json b/src/i18n/messages/hu.json index 7d5b3fe5303d..5f428f08d36b 100644 --- a/src/i18n/messages/hu.json +++ b/src/i18n/messages/hu.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Ezután töltse be újra a VS Code-ot, és adja meg az API-kulcsot a beviteli mezőben.", "wireApiChatCompletions": "Csevegés befejezése (/chat/completions)", "wireApiResponses": "Responses API (/responses)", + "codexModelCatalog": "OmniRoute-modellek megjelenítése a Codex /model választójában", + "codexModelCatalogHint": "Megírja a(z) ~/.codex/omniroute-models.json fájlt a telepített Codex saját modellkatalógusából (modellenként kb. 60 KB). A Codex frissítése után alkalmazza újra.", + "codexCatalogWarning": "A Codex /model választója nem frissült: az OmniRoute nem tudta beolvasni a telepített Codex modellkatalógusát.", "cliproxyapiActionFailed": "A művelet meghiúsult", "cliproxyapiActionSucceeded": "A művelet sikeres volt", "cliproxyapiCheckUpdates": "Frissítések ellenőrzése", diff --git a/src/i18n/messages/hy.json b/src/i18n/messages/hy.json index b88cef2a30ec..4d8f03e1140b 100644 --- a/src/i18n/messages/hy.json +++ b/src/i18n/messages/hy.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Այնուհետև վերաբեռնեք VS Code-ը և մուտքագրման հուշման մեջ սահմանեք API բանալին։", "wireApiChatCompletions": "Զրույցի լրացումներ (/chat/completions)", "wireApiResponses": "Պատասխանների API (/responses)", + "codexModelCatalog": "Ցուցադրել OmniRoute-ի մոդելները Codex-ի /model ընտրիչում", + "codexModelCatalogHint": "Գրում է ~/.codex/omniroute-models.json ֆայլը տեղադրված Codex-ի սեփական մոդելների կատալոգից (մոտ 60 KB յուրաքանչյուր մոդելի համար)։ Codex-ը թարմացնելուց հետո կրկին կիրառեք։", + "codexCatalogWarning": "Codex-ի /model ընտրիչը չթարմացվեց․ OmniRoute-ը չկարողացավ կարդալ տեղադրված Codex-ի մոդելների կատալոգը։", "cliproxyapiActionFailed": "Գործողությունը ձախողվեց", "cliproxyapiActionSucceeded": "Գործողությունը հաջողվեց", "cliproxyapiCheckUpdates": "Ստուգել թարմացումները", diff --git a/src/i18n/messages/id.json b/src/i18n/messages/id.json index ab017b5ff19c..6440ae0d7ccc 100644 --- a/src/i18n/messages/id.json +++ b/src/i18n/messages/id.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Kemudian muat ulang VS Code dan atur kunci API di prompt input.", "wireApiChatCompletions": "Penyelesaian Obrolan (/chat/penyelesaian)", "wireApiResponses": "API Respons (/respons)", + "codexModelCatalog": "Tampilkan model OmniRoute di pemilih /model Codex", + "codexModelCatalogHint": "Menulis ~/.codex/omniroute-models.json dari katalog model milik Codex yang terpasang (sekitar 60 KB per model). Terapkan ulang setelah memperbarui Codex.", + "codexCatalogWarning": "Pemilih /model Codex tidak diperbarui: OmniRoute tidak dapat membaca katalog model Codex yang terpasang.", "cliproxyapiActionFailed": "Tindakan gagal", "cliproxyapiActionSucceeded": "Tindakan berhasil", "cliproxyapiCheckUpdates": "Periksa Pembaruan", diff --git a/src/i18n/messages/ig.json b/src/i18n/messages/ig.json index d65b0a8b40a5..753b34be7d87 100644 --- a/src/i18n/messages/ig.json +++ b/src/i18n/messages/ig.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Mgbe ahụ, malitegharịa VS Code ma tọọ igodo API n'oghere ntinye.", "wireApiChatCompletions": "Mmecha Mkparịta ụka (/chat/completions)", "wireApiResponses": "API Nzaghachi (/responses)", + "codexModelCatalog": "Gosi ụdị OmniRoute na onye nhọrọ /model nke Codex", + "codexModelCatalogHint": "Ọ na-ede ~/.codex/omniroute-models.json site na katalọgụ ụdị nke Codex etinyere (ihe dị ka 60 KB maka ụdị ọ bụla). Tinye ya ọzọ mgbe ị kwalitere Codex.", + "codexCatalogWarning": "Emelitebeghị onye nhọrọ /model nke Codex: OmniRoute enweghị ike ịgụ katalọgụ ụdị nke Codex etinyere.", "cliproxyapiActionFailed": "Omume ahụ dara", "cliproxyapiActionSucceeded": "Omume ahụ gara nke ọma", "cliproxyapiCheckUpdates": "Lelee Mmelite", diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json index bdcc1cb5bbb0..e27fa1e8ef50 100644 --- a/src/i18n/messages/it.json +++ b/src/i18n/messages/it.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Quindi ricarica VS Code e imposta la chiave API nel prompt di input.", "wireApiChatCompletions": "Completamenti chat (/chat/completamenti)", "wireApiResponses": "API delle risposte (/responses)", + "codexModelCatalog": "Mostra i modelli OmniRoute nel selettore /model di Codex", + "codexModelCatalogHint": "Scrive ~/.codex/omniroute-models.json a partire dal catalogo modelli del Codex installato (circa 60 KB per modello). Riapplica dopo aver aggiornato Codex.", + "codexCatalogWarning": "Il selettore /model di Codex non è stato aggiornato: OmniRoute non è riuscito a leggere il catalogo modelli del Codex installato.", "cliproxyapiActionFailed": "Azione non riuscita", "cliproxyapiActionSucceeded": "Azione riuscita", "cliproxyapiCheckUpdates": "Controlla Aggiornamenti", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index b7bc1aef4c03..3466922a573d 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "その後、VS Code を再読み込みし、入力プロンプトで API キーを設定します。", "wireApiChatCompletions": "チャットの完了 (/chat/completions)", "wireApiResponses": "レスポンス API (/responses)", + "codexModelCatalog": "Codex の /model ピッカーに OmniRoute のモデルを表示する", + "codexModelCatalogHint": "インストール済み Codex 自身のモデルカタログから ~/.codex/omniroute-models.json を書き出します(1 モデルあたり約 60 KB)。Codex を更新したら再適用してください。", + "codexCatalogWarning": "Codex の /model ピッカーは更新されませんでした: OmniRoute がインストール済み Codex のモデルカタログを読み取れませんでした。", "cliproxyapiActionFailed": "アクションが失敗しました", "cliproxyapiActionSucceeded": "アクションが成功しました", "cliproxyapiCheckUpdates": "更新を確認", diff --git a/src/i18n/messages/ka.json b/src/i18n/messages/ka.json index 1de1997fbdc9..07dbd6addd7a 100644 --- a/src/i18n/messages/ka.json +++ b/src/i18n/messages/ka.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "შემდეგ ხელახლა ჩატვირთეთ VS Code და შეყვანის ველში მიუთითეთ API გასაღები.", "wireApiChatCompletions": "ჩატის დასრულებები (/chat/completions)", "wireApiResponses": "პასუხების API (/responses)", + "codexModelCatalog": "OmniRoute-ის მოდელების ჩვენება Codex-ის /model ამრჩევში", + "codexModelCatalogHint": "წერს ~/.codex/omniroute-models.json ფაილს დაინსტალირებული Codex-ის საკუთარი მოდელების კატალოგიდან (დაახლოებით 60 KB თითო მოდელზე). Codex-ის განახლების შემდეგ ხელახლა გამოიყენეთ.", + "codexCatalogWarning": "Codex-ის /model ამრჩევი არ განახლდა: OmniRoute-მა ვერ წაიკითხა დაინსტალირებული Codex-ის მოდელების კატალოგი.", "cliproxyapiActionFailed": "მოქმედება ვერ შესრულდა", "cliproxyapiActionSucceeded": "მოქმედება წარმატებით შესრულდა", "cliproxyapiCheckUpdates": "განახლებების შემოწმება", diff --git a/src/i18n/messages/km.json b/src/i18n/messages/km.json index 682bcfd0841d..da7ef4d7391a 100644 --- a/src/i18n/messages/km.json +++ b/src/i18n/messages/km.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "បន្ទាប់មក ផ្ទុក VS Code ឡើងវិញ ហើយកំណត់សោ API នៅក្នុងប្រអប់បញ្ចូល។", "wireApiChatCompletions": "ការបំពេញការជជែក (/chat/completions)", "wireApiResponses": "API ការឆ្លើយតប (/responses)", + "codexModelCatalog": "បង្ហាញម៉ូដែល OmniRoute នៅក្នុងកម្មវិធីជ្រើស /model របស់ Codex", + "codexModelCatalogHint": "សរសេរ ~/.codex/omniroute-models.json ពីកាតាឡុកម៉ូដែលផ្ទាល់របស់ Codex ដែលបានដំឡើង (ប្រហែល 60 KB ក្នុងមួយម៉ូដែល)។ សូមអនុវត្តម្តងទៀតបន្ទាប់ពីធ្វើបច្ចុប្បន្នភាព Codex។", + "codexCatalogWarning": "កម្មវិធីជ្រើស /model របស់ Codex មិនត្រូវបានធ្វើបច្ចុប្បន្នភាពទេ៖ OmniRoute មិនអាចអានកាតាឡុកម៉ូដែលរបស់ Codex ដែលបានដំឡើងបានទេ។", "cliproxyapiActionFailed": "សកម្មភាពបានបរាជ័យ", "cliproxyapiActionSucceeded": "សកម្មភាពបានជោគជ័យ", "cliproxyapiCheckUpdates": "ពិនិត្យមើលបច្ចុប្បន្នភាព", diff --git a/src/i18n/messages/kn.json b/src/i18n/messages/kn.json index 78d03a7378a6..89bb01f96ffe 100644 --- a/src/i18n/messages/kn.json +++ b/src/i18n/messages/kn.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ನಂತರ VS Code ಅನ್ನು ಮರುಲೋಡ್ ಮಾಡಿ ಮತ್ತು ಇನ್ಪುಟ್ ಪ್ರಾಂಪ್ಟ್ನಲ್ಲಿ API ಕೀ ಅನ್ನು ಹೊಂದಿಸಿ.", "wireApiChatCompletions": "ಚಾಟ್ ಪೂರ್ಣಗೊಳಿಸುವಿಕೆಗಳು (/chat/completions)", "wireApiResponses": "ಪ್ರತಿಕ್ರಿಯೆಗಳ API (/responses)", + "codexModelCatalog": "Codex ನ /model ಪಿಕರ್‌ನಲ್ಲಿ OmniRoute ಮಾದರಿಗಳನ್ನು ತೋರಿಸಿ", + "codexModelCatalogHint": "ಸ್ಥಾಪಿಸಲಾದ Codex ನ ಸ್ವಂತ ಮಾದರಿ ಕ್ಯಾಟಲಾಗ್‌ನಿಂದ ~/.codex/omniroute-models.json ಬರೆಯುತ್ತದೆ (ಪ್ರತಿ ಮಾದರಿಗೆ ಸುಮಾರು 60 KB). Codex ಅಪ್‌ಗ್ರೇಡ್ ಮಾಡಿದ ನಂತರ ಮತ್ತೆ ಅನ್ವಯಿಸಿ.", + "codexCatalogWarning": "Codex ನ /model ಪಿಕರ್ ನವೀಕರಿಸಲಾಗಿಲ್ಲ: ಸ್ಥಾಪಿಸಲಾದ Codex ಮಾದರಿ ಕ್ಯಾಟಲಾಗ್ ಅನ್ನು OmniRoute ಓದಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ.", "cliproxyapiActionFailed": "ಕ್ರಿಯೆ ವಿಫಲವಾಗಿದೆ", "cliproxyapiActionSucceeded": "ಕ್ರಿಯೆ ಯಶಸ್ವಿಯಾಗಿದೆ", "cliproxyapiCheckUpdates": "ನವೀಕರಣಗಳನ್ನು ಪರಿಶೀಲಿಸಿ", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 69349f34e1e2..b93e4d1dbfcf 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "그런 다음 VS Code를 다시 로드하고 입력 프롬프트에 API 키를 설정하세요.", "wireApiChatCompletions": "채팅 완료(/chat/completions)", "wireApiResponses": "응답 API(/responses)", + "codexModelCatalog": "Codex의 /model 선택기에 OmniRoute 모델 표시", + "codexModelCatalogHint": "설치된 Codex 자체 모델 카탈로그에서 ~/.codex/omniroute-models.json 파일을 생성합니다(모델당 약 60 KB). Codex를 업그레이드한 후 다시 적용하세요.", + "codexCatalogWarning": "Codex의 /model 선택기가 업데이트되지 않았습니다: OmniRoute가 설치된 Codex 모델 카탈로그를 읽지 못했습니다.", "cliproxyapiActionFailed": "작업 실패", "cliproxyapiActionSucceeded": "작업이 성공했습니다", "cliproxyapiCheckUpdates": "업데이트 확인", diff --git a/src/i18n/messages/lt.json b/src/i18n/messages/lt.json index a54bf8ef61b9..53155f5c3d14 100644 --- a/src/i18n/messages/lt.json +++ b/src/i18n/messages/lt.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Tada iš naujo įkelkite VS Code ir nustatykite API raktą įvesties lauke.", "wireApiChatCompletions": "Pokalbių užbaigimai (/chat/completions)", "wireApiResponses": "Atsakymų API (/responses)", + "codexModelCatalog": "Rodyti OmniRoute modelius Codex /model parinkiklyje", + "codexModelCatalogHint": "Įrašo ~/.codex/omniroute-models.json iš įdiegto Codex modelių katalogo (apie 60 KB vienam modeliui). Atnaujinę Codex pritaikykite iš naujo.", + "codexCatalogWarning": "Codex /model parinkiklis neatnaujintas: OmniRoute nepavyko perskaityti įdiegto Codex modelių katalogo.", "cliproxyapiActionFailed": "Veiksmas nepavyko", "cliproxyapiActionSucceeded": "Veiksmas pavyko", "cliproxyapiCheckUpdates": "Tikrinti atnaujinimus", diff --git a/src/i18n/messages/lv.json b/src/i18n/messages/lv.json index 039151391c81..7f65569a9728 100644 --- a/src/i18n/messages/lv.json +++ b/src/i18n/messages/lv.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Pēc tam pārlādējiet VS Code un ievades uzvednē iestatiet API atslēgu.", "wireApiChatCompletions": "Tērzēšanas pabeigšana (/chat/completions)", "wireApiResponses": "Atbilžu API (/responses)", + "codexModelCatalog": "Rādīt OmniRoute modeļus Codex /model atlasītājā", + "codexModelCatalogHint": "Ieraksta ~/.codex/omniroute-models.json no instalētā Codex paša modeļu kataloga (aptuveni 60 KB uz modeli). Pēc Codex atjaunināšanas lietojiet vēlreiz.", + "codexCatalogWarning": "Codex /model atlasītājs netika atjaunināts: OmniRoute neizdevās nolasīt instalētā Codex modeļu katalogu.", "cliproxyapiActionFailed": "Darbība neizdevās", "cliproxyapiActionSucceeded": "Darbība veiksmīga", "cliproxyapiCheckUpdates": "Pārbaudīt atjauninājumu pieejamību", diff --git a/src/i18n/messages/ml.json b/src/i18n/messages/ml.json index 1e803c382bce..9cb65826296b 100644 --- a/src/i18n/messages/ml.json +++ b/src/i18n/messages/ml.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "തുടർന്ന് VS Code റീലോഡ് ചെയ്ത് ഇൻപുട്ട് പ്രോംപ്റ്റിൽ API കീ സജ്ജമാക്കുക.", "wireApiChatCompletions": "ചാറ്റ് പൂർത്തീകരണങ്ങൾ (/chat/completions)", "wireApiResponses": "പ്രതികരണ API (/responses)", + "codexModelCatalog": "Codex-ന്റെ /model പിക്കറിൽ OmniRoute മോഡലുകൾ കാണിക്കുക", + "codexModelCatalogHint": "ഇൻസ്റ്റാൾ ചെയ്ത Codex-ന്റെ സ്വന്തം മോഡൽ കാറ്റലോഗിൽ നിന്ന് ~/.codex/omniroute-models.json എഴുതുന്നു (ഓരോ മോഡലിനും ഏകദേശം 60 KB). Codex അപ്‌ഗ്രേഡ് ചെയ്ത ശേഷം വീണ്ടും പ്രയോഗിക്കുക.", + "codexCatalogWarning": "Codex-ന്റെ /model പിക്കർ അപ്‌ഡേറ്റ് ചെയ്തില്ല: ഇൻസ്റ്റാൾ ചെയ്ത Codex മോഡൽ കാറ്റലോഗ് വായിക്കാൻ OmniRoute-ന് കഴിഞ്ഞില്ല.", "cliproxyapiActionFailed": "പ്രവർത്തനം പരാജയപ്പെട്ടു", "cliproxyapiActionSucceeded": "പ്രവർത്തനം വിജയിച്ചു", "cliproxyapiCheckUpdates": "അപ്ഡേറ്റുകൾ പരിശോധിക്കുക", diff --git a/src/i18n/messages/mr.json b/src/i18n/messages/mr.json index 87022481e33b..cc020bd1e726 100644 --- a/src/i18n/messages/mr.json +++ b/src/i18n/messages/mr.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "नंतर VS Code रीलोड करा आणि इनपुट प्रॉम्प्टमध्ये API की सेट करा.", "wireApiChatCompletions": "चॅट पूर्णता (/चॅट/पूर्णता)", "wireApiResponses": "प्रतिसाद API (/प्रतिसाद)", + "codexModelCatalog": "Codex च्या /model पिकरमध्ये OmniRoute मॉडेल्स दाखवा", + "codexModelCatalogHint": "स्थापित Codex च्या स्वतःच्या मॉडेल कॅटलॉगमधून ~/.codex/omniroute-models.json लिहिते (प्रति मॉडेल सुमारे 60 KB). Codex अपग्रेड केल्यानंतर पुन्हा लागू करा.", + "codexCatalogWarning": "Codex चा /model पिकर अपडेट झाला नाही: OmniRoute स्थापित Codex चा मॉडेल कॅटलॉग वाचू शकले नाही.", "cliproxyapiActionFailed": "क्रिया अयशस्वी झाली", "cliproxyapiActionSucceeded": "क्रिया यशस्वी झाली", "cliproxyapiCheckUpdates": "अपडेट्स तपासा", diff --git a/src/i18n/messages/ms.json b/src/i18n/messages/ms.json index c00a742a58fb..fe077008e089 100644 --- a/src/i18n/messages/ms.json +++ b/src/i18n/messages/ms.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Kemudian muat semula VS Code dan tetapkan kunci API dalam prompt input.", "wireApiChatCompletions": "Selesai Sembang (/sembang/penyelesaian)", "wireApiResponses": "API Respons (/respons)", + "codexModelCatalog": "Paparkan model OmniRoute dalam pemilih /model Codex", + "codexModelCatalogHint": "Menulis ~/.codex/omniroute-models.json daripada katalog model Codex yang dipasang sendiri (kira-kira 60 KB bagi setiap model). Guna semula selepas menaik taraf Codex.", + "codexCatalogWarning": "Pemilih /model Codex tidak dikemas kini: OmniRoute tidak dapat membaca katalog model Codex yang dipasang.", "cliproxyapiActionFailed": "Tindakan gagal", "cliproxyapiActionSucceeded": "Tindakan berjaya", "cliproxyapiCheckUpdates": "Semak Kemas Kini", diff --git a/src/i18n/messages/mt.json b/src/i18n/messages/mt.json index 568368bf98cd..d7261dbf0552 100644 --- a/src/i18n/messages/mt.json +++ b/src/i18n/messages/mt.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Imbagħad erġa' għabbi VS Code u ssettja ċ-ċavetta tal-API fil-prompt tal-input.", "wireApiChatCompletions": "Kompletamenti taċ-Chat (/chat/completions)", "wireApiResponses": "API tat-Tweġibiet (/responses)", + "codexModelCatalog": "Uri l-mudelli ta' OmniRoute fl-għażla /model ta' Codex", + "codexModelCatalogHint": "Jikteb ~/.codex/omniroute-models.json mill-katalgu tal-mudelli tal-Codex installat innifsu (madwar 60 KB għal kull mudell). Erġa' applika wara li taġġorna Codex.", + "codexCatalogWarning": "L-għażla /model ta' Codex ma ġietx aġġornata: OmniRoute ma setax jaqra l-katalgu tal-mudelli tal-Codex installat.", "cliproxyapiActionFailed": "L-azzjoni falliet", "cliproxyapiActionSucceeded": "L-azzjoni rnexxiet", "cliproxyapiCheckUpdates": "Iċċekkja għal Aġġornamenti", diff --git a/src/i18n/messages/my.json b/src/i18n/messages/my.json index b4ae271a0c97..17011846e578 100644 --- a/src/i18n/messages/my.json +++ b/src/i18n/messages/my.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ထို့နောက် VS Code ကို ပြန်လည်ဖွင့်ပြီး ထည့်သွင်းရန်အကွက်တွင် API သော့ကို သတ်မှတ်ပါ။", "wireApiChatCompletions": "ချတ်ပြီးစီးမှုများ (/chat/completions)", "wireApiResponses": "တုံ့ပြန်မှု API (/responses)", + "codexModelCatalog": "Codex ၏ /model ရွေးချယ်ကိရိယာတွင် OmniRoute မော်ဒယ်များကို ပြပါ", + "codexModelCatalogHint": "ထည့်သွင်းထားသော Codex ၏ ကိုယ်ပိုင် မော်ဒယ်ကတ်တလောက်မှ ~/.codex/omniroute-models.json ကို ရေးသားသည် (မော်ဒယ်တစ်ခုလျှင် 60 KB ခန့်)။ Codex ကို အဆင့်မြှင့်ပြီးနောက် ထပ်မံအသုံးချပါ။", + "codexCatalogWarning": "Codex ၏ /model ရွေးချယ်ကိရိယာကို အပ်ဒိတ်မလုပ်ရသေးပါ- OmniRoute သည် ထည့်သွင်းထားသော Codex မော်ဒယ်ကတ်တလောက်ကို မဖတ်နိုင်ပါ။", "cliproxyapiActionFailed": "လုပ်ဆောင်ချက် မအောင်မြင်ပါ", "cliproxyapiActionSucceeded": "လုပ်ဆောင်ချက် အောင်မြင်ပါသည်", "cliproxyapiCheckUpdates": "အပ်ဒိတ်များကို စစ်ဆေးပါ", diff --git a/src/i18n/messages/ne.json b/src/i18n/messages/ne.json index 74d4de43b367..e1f4fc7f14c8 100644 --- a/src/i18n/messages/ne.json +++ b/src/i18n/messages/ne.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "त्यसपछि VS Code पुनः लोड गर्नुहोस् र इनपुट प्रम्प्टमा API कुञ्जी सेट गर्नुहोस्।", "wireApiChatCompletions": "च्याट पूर्णताहरू (/chat/completions)", "wireApiResponses": "प्रतिक्रिया एपीआई (/responses)", + "codexModelCatalog": "Codex को /model पिकरमा OmniRoute मोडेलहरू देखाउनुहोस्", + "codexModelCatalogHint": "स्थापित Codex को आफ्नै मोडेल क्याटलगबाट ~/.codex/omniroute-models.json लेख्छ (प्रति मोडेल लगभग 60 KB)। Codex अपग्रेड गरेपछि फेरि लागू गर्नुहोस्।", + "codexCatalogWarning": "Codex को /model पिकर अद्यावधिक भएन: OmniRoute ले स्थापित Codex को मोडेल क्याटलग पढ्न सकेन।", "cliproxyapiActionFailed": "कार्य असफल भयो", "cliproxyapiActionSucceeded": "कार्य सफल भयो", "cliproxyapiCheckUpdates": "अद्यावधिकहरू जाँच गर्नुहोस्", diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json index 70f8970cc8fb..dde95d213180 100644 --- a/src/i18n/messages/nl.json +++ b/src/i18n/messages/nl.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Herlaad vervolgens VS Code en stel de API-sleutel in bij de invoerprompt.", "wireApiChatCompletions": "Chatvoltooiingen (/chat/voltooiingen)", "wireApiResponses": "Reacties-API (/reacties)", + "codexModelCatalog": "OmniRoute-modellen tonen in de /model-kiezer van Codex", + "codexModelCatalogHint": "Schrijft ~/.codex/omniroute-models.json op basis van de eigen modelcatalogus van de geïnstalleerde Codex (ongeveer 60 KB per model). Pas opnieuw toe na een Codex-upgrade.", + "codexCatalogWarning": "De /model-kiezer van Codex is niet bijgewerkt: OmniRoute kon de modelcatalogus van de geïnstalleerde Codex niet lezen.", "cliproxyapiActionFailed": "Actie mislukt", "cliproxyapiActionSucceeded": "Actie geslaagd", "cliproxyapiCheckUpdates": "Controleer op updates", diff --git a/src/i18n/messages/no.json b/src/i18n/messages/no.json index ee625e8880e6..03c77368599a 100644 --- a/src/i18n/messages/no.json +++ b/src/i18n/messages/no.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Last deretter inn VS Code på nytt og angi API-nøkkelen i inndatafeltet.", "wireApiChatCompletions": "Chatfullføringer (/chat/fullføringer)", "wireApiResponses": "Svar-API (/responses)", + "codexModelCatalog": "Vis OmniRoute-modeller i Codex sin /model-velger", + "codexModelCatalogHint": "Skriver ~/.codex/omniroute-models.json fra den installerte Codex sin egen modellkatalog (omtrent 60 KB per modell). Bruk på nytt etter oppgradering av Codex.", + "codexCatalogWarning": "Codex sin /model-velger ble ikke oppdatert: OmniRoute kunne ikke lese modellkatalogen til den installerte Codex.", "cliproxyapiActionFailed": "Handling mislyktes", "cliproxyapiActionSucceeded": "Handling fullført", "cliproxyapiCheckUpdates": "Sjekk oppdateringer", diff --git a/src/i18n/messages/or.json b/src/i18n/messages/or.json index 40c2d4870428..6ca73063ed41 100644 --- a/src/i18n/messages/or.json +++ b/src/i18n/messages/or.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ତା’ପରେ VS Codeକୁ ପୁନଃଲୋଡ୍ କରନ୍ତୁ ଏବଂ ଇନ୍ପୁଟ୍ ପ୍ରମ୍ପ୍ଟରେ API କୀ ସେଟ୍ କରନ୍ତୁ।", "wireApiChatCompletions": "ଚାଟ୍ କମ୍ପ୍ଲିସନ୍ସ (/chat/completions)", "wireApiResponses": "ରେସ୍ପନ୍ସେସ୍ API (/responses)", + "codexModelCatalog": "Codex ର /model ପିକରରେ OmniRoute ମଡେଲଗୁଡ଼ିକ ଦେଖାନ୍ତୁ", + "codexModelCatalogHint": "ସଂସ୍ଥାପିତ Codex ର ନିଜସ୍ୱ ମଡେଲ କ୍ୟାଟାଲଗରୁ ~/.codex/omniroute-models.json ଲେଖେ (ପ୍ରତି ମଡେଲ ପାଇଁ ପ୍ରାୟ 60 KB)। Codex ଅପଗ୍ରେଡ୍ କରିବା ପରେ ପୁଣି ପ୍ରୟୋଗ କରନ୍ତୁ।", + "codexCatalogWarning": "Codex ର /model ପିକର ଅପଡେଟ୍ ହୋଇନାହିଁ: OmniRoute ସଂସ୍ଥାପିତ Codex ମଡେଲ କ୍ୟାଟାଲଗ ପଢ଼ିପାରିଲା ନାହିଁ।", "cliproxyapiActionFailed": "କାର୍ଯ୍ୟ ବିଫଳ ହେଲା", "cliproxyapiActionSucceeded": "କାର୍ଯ୍ୟ ସଫଳ ହେଲା", "cliproxyapiCheckUpdates": "ଅପଡେଟ୍ ଯାଞ୍ଚ କରନ୍ତୁ", diff --git a/src/i18n/messages/pa.json b/src/i18n/messages/pa.json index c0ce308aff6c..7385e38b5fa2 100644 --- a/src/i18n/messages/pa.json +++ b/src/i18n/messages/pa.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ਫਿਰ VS Code ਨੂੰ ਮੁੜ ਲੋਡ ਕਰੋ ਅਤੇ ਇਨਪੁੱਟ ਪ੍ਰੌਮਪਟ ਵਿੱਚ API ਕੁੰਜੀ ਸੈੱਟ ਕਰੋ।", "wireApiChatCompletions": "ਚੈਟ ਕੰਪਲੀਸ਼ਨਜ਼ (/chat/completions)", "wireApiResponses": "ਰਿਸਪਾਂਸਿਜ਼ API (/responses)", + "codexModelCatalog": "Codex ਦੇ /model ਪਿਕਰ ਵਿੱਚ OmniRoute ਮਾਡਲ ਦਿਖਾਓ", + "codexModelCatalogHint": "ਇੰਸਟਾਲ ਕੀਤੇ Codex ਦੇ ਆਪਣੇ ਮਾਡਲ ਕੈਟਾਲਾਗ ਤੋਂ ~/.codex/omniroute-models.json ਲਿਖਦਾ ਹੈ (ਹਰ ਮਾਡਲ ਲਈ ਲਗਭਗ 60 KB)। Codex ਅੱਪਗ੍ਰੇਡ ਕਰਨ ਤੋਂ ਬਾਅਦ ਦੁਬਾਰਾ ਲਾਗੂ ਕਰੋ।", + "codexCatalogWarning": "Codex ਦਾ /model ਪਿਕਰ ਅੱਪਡੇਟ ਨਹੀਂ ਹੋਇਆ: OmniRoute ਇੰਸਟਾਲ ਕੀਤੇ Codex ਦਾ ਮਾਡਲ ਕੈਟਾਲਾਗ ਨਹੀਂ ਪੜ੍ਹ ਸਕਿਆ।", "cliproxyapiActionFailed": "ਕਾਰਵਾਈ ਅਸਫਲ ਰਹੀ", "cliproxyapiActionSucceeded": "ਕਾਰਵਾਈ ਸਫਲ ਰਹੀ", "cliproxyapiCheckUpdates": "ਅੱਪਡੇਟਾਂ ਦੀ ਜਾਂਚ ਕਰੋ", diff --git a/src/i18n/messages/phi.json b/src/i18n/messages/phi.json index 4b0ecd0fdc64..81f5775c8526 100644 --- a/src/i18n/messages/phi.json +++ b/src/i18n/messages/phi.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Pagkatapos ay i-reload ang VS Code at i-set ang API key sa input prompt.", "wireApiChatCompletions": "Mga Pagkumpleto ng Chat (/chat/pagkumpleto)", "wireApiResponses": "Responses API (/mga tugon)", + "codexModelCatalog": "Ipakita ang mga model ng OmniRoute sa /model picker ng Codex", + "codexModelCatalogHint": "Isinusulat ang ~/.codex/omniroute-models.json mula sa sariling katalogo ng model ng naka-install na Codex (mga 60 KB bawat model). Ilapat muli pagkatapos i-upgrade ang Codex.", + "codexCatalogWarning": "Hindi na-update ang /model picker ng Codex: hindi nabasa ng OmniRoute ang katalogo ng model ng naka-install na Codex.", "cliproxyapiActionFailed": "Nabigong aksyon", "cliproxyapiActionSucceeded": "Matagumpay ang aksyon", "cliproxyapiCheckUpdates": "Suriin ang Mga Update", diff --git a/src/i18n/messages/pl.json b/src/i18n/messages/pl.json index 3d4b0e976619..71401c4ab26d 100644 --- a/src/i18n/messages/pl.json +++ b/src/i18n/messages/pl.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Następnie uruchom ponownie VS Code i ustaw klucz API w polu wprowadzania.", "wireApiChatCompletions": "Uzupełnienia czatu (/chat/completions)", "wireApiResponses": "API odpowiedzi (/responses)", + "codexModelCatalog": "Pokaż modele OmniRoute w selektorze /model w Codex", + "codexModelCatalogHint": "Zapisuje ~/.codex/omniroute-models.json na podstawie własnego katalogu modeli zainstalowanego Codex (około 60 KB na model). Zastosuj ponownie po aktualizacji Codex.", + "codexCatalogWarning": "Selektor /model w Codex nie został zaktualizowany: OmniRoute nie mógł odczytać katalogu modeli zainstalowanego Codex.", "cliproxyapiActionFailed": "Akcja nie powiodła się", "cliproxyapiActionSucceeded": "Akcja zakończona sukcesem", "cliproxyapiCheckUpdates": "Sprawdź aktualizacje", diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index beedf50bc909..18e2a007cae1 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Em seguida, recarregue o VS Code e defina a chave de API no prompt de entrada.", "wireApiChatCompletions": "Conclusões de bate-papo (/chat/completions)", "wireApiResponses": "API de respostas (/respostas)", + "codexModelCatalog": "Listar os modelos do OmniRoute no seletor /model do Codex", + "codexModelCatalogHint": "Grava ~/.codex/omniroute-models.json a partir do catálogo de modelos do próprio Codex instalado (cerca de 60 KB por modelo). Aplique de novo depois de atualizar o Codex.", + "codexCatalogWarning": "O seletor /model do Codex não foi atualizado: o OmniRoute não conseguiu ler o catálogo de modelos do Codex instalado.", "cliproxyapiActionFailed": "Ação falhou", "cliproxyapiActionSucceeded": "Ação bem-sucedida", "cliproxyapiCheckUpdates": "Verificar Atualizações", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index e1e7acc1c3d6..c016656bf8e2 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Depois, recarregue o VS Code e defina a chave de API no prompt de entrada.", "wireApiChatCompletions": "Conclusões de bate-papo (/chat/completions)", "wireApiResponses": "API de respostas (/respostas)", + "codexModelCatalog": "Listar os modelos do OmniRoute no seletor /model do Codex", + "codexModelCatalogHint": "Escreve ~/.codex/omniroute-models.json a partir do catálogo de modelos do próprio Codex instalado (cerca de 60 KB por modelo). Volte a aplicar depois de atualizar o Codex.", + "codexCatalogWarning": "O seletor /model do Codex não foi atualizado: o OmniRoute não conseguiu ler o catálogo de modelos do Codex instalado.", "cliproxyapiActionFailed": "Ação falhou", "cliproxyapiActionSucceeded": "Ação bem-sucedida", "cliproxyapiCheckUpdates": "Verificar Atualizações", diff --git a/src/i18n/messages/ro.json b/src/i18n/messages/ro.json index fb3e4a9b7f5e..d582db838f38 100644 --- a/src/i18n/messages/ro.json +++ b/src/i18n/messages/ro.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Apoi reîncărcați VS Code și setați cheia API în câmpul de introducere.", "wireApiChatCompletions": "Finalizări de chat (/chat/completions)", "wireApiResponses": "API-ul Responses (/responses)", + "codexModelCatalog": "Afișează modelele OmniRoute în selectorul /model din Codex", + "codexModelCatalogHint": "Scrie ~/.codex/omniroute-models.json pe baza catalogului de modele al Codex instalat (aproximativ 60 KB per model). Aplicați din nou după actualizarea Codex.", + "codexCatalogWarning": "Selectorul /model din Codex nu a fost actualizat: OmniRoute nu a putut citi catalogul de modele al Codex instalat.", "cliproxyapiActionFailed": "Acțiunea a eșuat", "cliproxyapiActionSucceeded": "Acțiune reușită", "cliproxyapiCheckUpdates": "Verifică actualizările", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index e1db170356ba..7d4e985bd3e8 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Затем перезапустите VS Code и укажите API-ключ в поле ввода.", "wireApiChatCompletions": "Завершения чата (/chat/completions)", "wireApiResponses": "API ответов (/responses)", + "codexModelCatalog": "Показывать модели OmniRoute в списке /model в Codex", + "codexModelCatalogHint": "Записывает ~/.codex/omniroute-models.json на основе собственного каталога моделей установленного Codex (около 60 KB на модель). Примените повторно после обновления Codex.", + "codexCatalogWarning": "Список /model в Codex не обновлён: OmniRoute не удалось прочитать каталог моделей установленного Codex.", "cliproxyapiActionFailed": "Действие не выполнено", "cliproxyapiActionSucceeded": "Действие выполнено успешно", "cliproxyapiCheckUpdates": "Проверить обновления", diff --git a/src/i18n/messages/si.json b/src/i18n/messages/si.json index ece842bdbfe8..47e4f3db2d4f 100644 --- a/src/i18n/messages/si.json +++ b/src/i18n/messages/si.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ඉන්පසු VS Code නැවත පූරණය කර ආදාන විමසුමේ API යතුර සකසන්න.", "wireApiChatCompletions": "චැට් සම්පූර්ණ කිරීම් (/chat/completions)", "wireApiResponses": "ප්රතිචාර API (/responses)", + "codexModelCatalog": "Codex හි /model තේරීමේ ලැයිස්තුවේ OmniRoute මාදිලි පෙන්වන්න", + "codexModelCatalogHint": "ස්ථාපිත Codex හි ම මාදිලි නාමාවලියෙන් ~/.codex/omniroute-models.json ලියයි (මාදිලියකට ආසන්න වශයෙන් 60 KB). Codex උත්ශ්‍රේණි කිරීමෙන් පසු නැවත යොදන්න.", + "codexCatalogWarning": "Codex හි /model තේරීමේ ලැයිස්තුව යාවත්කාලීන නොවීය: ස්ථාපිත Codex මාදිලි නාමාවලිය කියවීමට OmniRoute හට නොහැකි විය.", "cliproxyapiActionFailed": "ක්රියාව අසාර්ථක විය", "cliproxyapiActionSucceeded": "ක්රියාව සාර්ථක විය", "cliproxyapiCheckUpdates": "යාවත්කාලීන සඳහා පරීක්ෂා කරන්න", diff --git a/src/i18n/messages/sk.json b/src/i18n/messages/sk.json index a5d795d78c7f..6f8f737ade08 100644 --- a/src/i18n/messages/sk.json +++ b/src/i18n/messages/sk.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Potom znova načítajte VS Code a nastavte kľúč API vo vstupnej výzve.", "wireApiChatCompletions": "Dokončenia rozhovoru (/chat/completions)", "wireApiResponses": "API Responses (/responses)", + "codexModelCatalog": "Zobraziť modely OmniRoute vo výbere /model v Codexe", + "codexModelCatalogHint": "Zapíše ~/.codex/omniroute-models.json z vlastného katalógu modelov nainštalovaného Codexu (asi 60 KB na model). Po aktualizácii Codexu použite znova.", + "codexCatalogWarning": "Výber /model v Codexe nebol aktualizovaný: OmniRoute nedokázal prečítať katalóg modelov nainštalovaného Codexu.", "cliproxyapiActionFailed": "Akcia zlyhala", "cliproxyapiActionSucceeded": "Akcia bola úspešná", "cliproxyapiCheckUpdates": "Skontrolovať aktualizácie", diff --git a/src/i18n/messages/sl.json b/src/i18n/messages/sl.json index a83dc1c3757d..802dd97efd72 100644 --- a/src/i18n/messages/sl.json +++ b/src/i18n/messages/sl.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Nato znova naložite VS Code in v pozivu za vnos nastavite ključ API-ja.", "wireApiChatCompletions": "Dokončanja klepeta (/chat/completions)", "wireApiResponses": "API za odgovore (/responses)", + "codexModelCatalog": "Prikaži modele OmniRoute v izbirniku /model v Codexu", + "codexModelCatalogHint": "Zapiše ~/.codex/omniroute-models.json iz lastnega kataloga modelov nameščenega Codexa (približno 60 KB na model). Po nadgradnji Codexa znova uporabite.", + "codexCatalogWarning": "Izbirnik /model v Codexu ni bil posodobljen: OmniRoute ni mogel prebrati kataloga modelov nameščenega Codexa.", "cliproxyapiActionFailed": "Dejanje ni uspelo", "cliproxyapiActionSucceeded": "Dejanje je uspelo", "cliproxyapiCheckUpdates": "Preveri posodobitve", diff --git a/src/i18n/messages/sr.json b/src/i18n/messages/sr.json index d2d20290bff5..776bb507e017 100644 --- a/src/i18n/messages/sr.json +++ b/src/i18n/messages/sr.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Затим поново учитајте VS Code и подесите API кључ у пољу за унос.", "wireApiChatCompletions": "Довршавања ћаскања (/chat/completions)", "wireApiResponses": "API одговора (/responses)", + "codexModelCatalog": "Прикажи OmniRoute моделе у Codex /model бирачу", + "codexModelCatalogHint": "Уписује ~/.codex/omniroute-models.json из сопственог каталога модела инсталираног Codex-а (око 60 KB по моделу). Поново примените након надоградње Codex-а.", + "codexCatalogWarning": "Codex /model бирач није ажуриран: OmniRoute није могао да прочита каталог модела инсталираног Codex-а.", "cliproxyapiActionFailed": "Радња није успела", "cliproxyapiActionSucceeded": "Радња је успешно извршена", "cliproxyapiCheckUpdates": "Провери ажурирања", diff --git a/src/i18n/messages/sv.json b/src/i18n/messages/sv.json index e2a2f3f4e8c4..d18b182515aa 100644 --- a/src/i18n/messages/sv.json +++ b/src/i18n/messages/sv.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Ladda sedan om VS Code och ange API-nyckeln i inmatningsfältet.", "wireApiChatCompletions": "Chattavslut (/chat/kompletteringar)", "wireApiResponses": "Svars-API (/responses)", + "codexModelCatalog": "Visa OmniRoute-modeller i Codex /model-väljare", + "codexModelCatalogHint": "Skriver ~/.codex/omniroute-models.json utifrån den installerade Codex egen modellkatalog (cirka 60 KB per modell). Tillämpa igen efter att du uppgraderat Codex.", + "codexCatalogWarning": "Codex /model-väljare uppdaterades inte: OmniRoute kunde inte läsa den installerade Codex modellkatalog.", "cliproxyapiActionFailed": "Åtgärden misslyckades", "cliproxyapiActionSucceeded": "Åtgärden lyckades", "cliproxyapiCheckUpdates": "Kontrollera uppdateringar", diff --git a/src/i18n/messages/sw.json b/src/i18n/messages/sw.json index a377851f1de3..bee52cc318a2 100644 --- a/src/i18n/messages/sw.json +++ b/src/i18n/messages/sw.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Kisha pakia upya VS Code na uweke ufunguo wa API kwenye kidokezo cha kuingiza.", "wireApiChatCompletions": "Kukamilika kwa Gumzo (/kuzungumza/kukamilika)", "wireApiResponses": "API ya majibu (/majibu)", + "codexModelCatalog": "Onyesha miundo ya OmniRoute kwenye kiteuzi cha /model cha Codex", + "codexModelCatalogHint": "Huandika ~/.codex/omniroute-models.json kutoka kwenye katalogi ya miundo ya Codex iliyosakinishwa (takriban 60 KB kwa kila muundo). Tumia tena baada ya kusasisha Codex.", + "codexCatalogWarning": "Kiteuzi cha /model cha Codex hakikusasishwa: OmniRoute haikuweza kusoma katalogi ya miundo ya Codex iliyosakinishwa.", "cliproxyapiActionFailed": "Kitendo kimefeli", "cliproxyapiActionSucceeded": "Kitendo kimefanikiwa", "cliproxyapiCheckUpdates": "Angalia Sasisho", diff --git a/src/i18n/messages/ta.json b/src/i18n/messages/ta.json index beff4c09b29a..e4e4ba6ff45e 100644 --- a/src/i18n/messages/ta.json +++ b/src/i18n/messages/ta.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "பின்னர் VS Code-ஐ மீண்டும் ஏற்றி, உள்ளீட்டு வரியில் API விசையை அமைக்கவும்.", "wireApiChatCompletions": "அரட்டை நிறைவுகள் (/அரட்டை/நிறைவுகள்)", "wireApiResponses": "பதில்கள் API (/பதில்கள்)", + "codexModelCatalog": "Codex இன் /model தேர்வியில் OmniRoute மாதிரிகளைக் காட்டு", + "codexModelCatalogHint": "நிறுவப்பட்ட Codex இன் சொந்த மாதிரி அட்டவணையிலிருந்து ~/.codex/omniroute-models.json ஐ எழுதுகிறது (ஒரு மாதிரிக்குச் சுமார் 60 KB). Codex ஐ மேம்படுத்திய பின் மீண்டும் பயன்படுத்தவும்.", + "codexCatalogWarning": "Codex இன் /model தேர்வி புதுப்பிக்கப்படவில்லை: நிறுவப்பட்ட Codex மாதிரி அட்டவணையை OmniRoute ஆல் படிக்க முடியவில்லை.", "cliproxyapiActionFailed": "செயல் தோல்வியுற்றது", "cliproxyapiActionSucceeded": "செயல் வெற்றிகரமாக நிறைவுற்றது", "cliproxyapiCheckUpdates": "புதுப்பிப்புகளை சரிபார்க்கவும்", diff --git a/src/i18n/messages/te.json b/src/i18n/messages/te.json index 6c7726c49133..773a83438194 100644 --- a/src/i18n/messages/te.json +++ b/src/i18n/messages/te.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "ఆపై VS Codeని రీలోడ్ చేసి, ఇన్‌పుట్ ప్రాంప్ట్‌లో API కీని సెట్ చేయండి.", "wireApiChatCompletions": "చాట్ పూర్తిలు (/చాట్/పూర్తి)", "wireApiResponses": "ప్రతిస్పందనల API (/స్పందనలు)", + "codexModelCatalog": "Codex యొక్క /model పికర్‌లో OmniRoute మోడల్‌లను చూపించు", + "codexModelCatalogHint": "ఇన్‌స్టాల్ చేసిన Codex స్వంత మోడల్ కేటలాగ్ నుండి ~/.codex/omniroute-models.json ను రాస్తుంది (ఒక్కో మోడల్‌కు సుమారు 60 KB). Codex ను అప్‌గ్రేడ్ చేసిన తర్వాత మళ్లీ వర్తింపజేయండి.", + "codexCatalogWarning": "Codex యొక్క /model పికర్ అప్‌డేట్ కాలేదు: ఇన్‌స్టాల్ చేసిన Codex మోడల్ కేటలాగ్‌ను OmniRoute చదవలేకపోయింది.", "cliproxyapiActionFailed": "చర్య విఫలమైంది", "cliproxyapiActionSucceeded": "చర్య విజయవంతమైంది", "cliproxyapiCheckUpdates": "అప్‌డేట్స్‌ను తనిఖీ చేయండి", diff --git a/src/i18n/messages/th.json b/src/i18n/messages/th.json index ab795b3b7a98..bb2024c94673 100644 --- a/src/i18n/messages/th.json +++ b/src/i18n/messages/th.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "จากนั้นโหลด VS Code ใหม่ และตั้งค่าคีย์ API ในช่องป้อนข้อมูล", "wireApiChatCompletions": "เสร็จสิ้นการแชท (/แชท/เสร็จสิ้น)", "wireApiResponses": "API การตอบกลับ (/การตอบกลับ)", + "codexModelCatalog": "แสดงโมเดลของ OmniRoute ในตัวเลือก /model ของ Codex", + "codexModelCatalogHint": "เขียน ~/.codex/omniroute-models.json จากแคตตาล็อกโมเดลของ Codex ที่ติดตั้งไว้เอง (ประมาณ 60 KB ต่อโมเดล) ให้ใช้อีกครั้งหลังอัปเกรด Codex", + "codexCatalogWarning": "ตัวเลือก /model ของ Codex ไม่ได้รับการอัปเดต: OmniRoute อ่านแคตตาล็อกโมเดลของ Codex ที่ติดตั้งไว้ไม่ได้", "cliproxyapiActionFailed": "การดำเนินการล้มเหลว", "cliproxyapiActionSucceeded": "ดำเนินการสำเร็จ", "cliproxyapiCheckUpdates": "ตรวจสอบการอัปเดต", diff --git a/src/i18n/messages/tr.json b/src/i18n/messages/tr.json index 73e583687599..9eabfae5164e 100644 --- a/src/i18n/messages/tr.json +++ b/src/i18n/messages/tr.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Ardından VS Code'u yeniden yükleyin ve giriş isteminde API anahtarını ayarlayın.", "wireApiChatCompletions": "Sohbet Tamamlamaları (/sohbet/tamamlamalar)", "wireApiResponses": "Yanıtlar API'si (/yanıtlar)", + "codexModelCatalog": "OmniRoute modellerini Codex'in /model seçicisinde listele", + "codexModelCatalogHint": "Yüklü Codex'in kendi model kataloğundan ~/.codex/omniroute-models.json dosyasını yazar (model başına yaklaşık 60 KB). Codex'i yükselttikten sonra yeniden uygulayın.", + "codexCatalogWarning": "Codex'in /model seçicisi güncellenmedi: OmniRoute yüklü Codex model kataloğunu okuyamadı.", "cliproxyapiActionFailed": "Eylem başarısız oldu", "cliproxyapiActionSucceeded": "İşlem başarılı oldu", "cliproxyapiCheckUpdates": "Güncellemeleri Kontrol Et", diff --git a/src/i18n/messages/uk-UA.json b/src/i18n/messages/uk-UA.json index 732e194a20ae..f509e48d2254 100644 --- a/src/i18n/messages/uk-UA.json +++ b/src/i18n/messages/uk-UA.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Потім перезавантажте VS Code та вкажіть ключ API у полі введення.", "wireApiChatCompletions": "Завершення чату (/chat/completions)", "wireApiResponses": "Responses API (/responses)", + "codexModelCatalog": "Показувати моделі OmniRoute у списку /model у Codex", + "codexModelCatalogHint": "Записує ~/.codex/omniroute-models.json на основі власного каталогу моделей встановленого Codex (близько 60 KB на модель). Застосуйте повторно після оновлення Codex.", + "codexCatalogWarning": "Список /model у Codex не оновлено: OmniRoute не вдалося прочитати каталог моделей встановленого Codex.", "cliproxyapiActionFailed": "Дію не виконано", "cliproxyapiActionSucceeded": "Дію виконано успішно", "cliproxyapiCheckUpdates": "Перевірити оновлення", diff --git a/src/i18n/messages/ur.json b/src/i18n/messages/ur.json index 6850f9cac54b..bbc0e899bf38 100644 --- a/src/i18n/messages/ur.json +++ b/src/i18n/messages/ur.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "پھر VS Code کو دوبارہ لوڈ کریں اور ان پٹ پرامپٹ میں API key سیٹ کریں۔", "wireApiChatCompletions": "چیٹ کی تکمیل (/چیٹ/کمپلیشنز)", "wireApiResponses": "Responses API (/responses)", + "codexModelCatalog": "Codex کے ‎/model پِکر میں OmniRoute ماڈلز دکھائیں", + "codexModelCatalogHint": "انسٹال شدہ Codex کے اپنے ماڈل کیٹلاگ سے ~/.codex/omniroute-models.json لکھتا ہے (فی ماڈل تقریباً 60 KB)۔ Codex اپ گریڈ کرنے کے بعد دوبارہ لاگو کریں۔", + "codexCatalogWarning": "Codex کا ‎/model پِکر اپ ڈیٹ نہیں ہوا: OmniRoute انسٹال شدہ Codex کا ماڈل کیٹلاگ نہیں پڑھ سکا۔", "cliproxyapiActionFailed": "عمل ناکام ہوگیا", "cliproxyapiActionSucceeded": "عمل کامیاب ہوا", "cliproxyapiCheckUpdates": "اپ ڈیٹس چیک کریں", diff --git a/src/i18n/messages/uz.json b/src/i18n/messages/uz.json index 68f21cf40a63..1a6c6222f801 100644 --- a/src/i18n/messages/uz.json +++ b/src/i18n/messages/uz.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Soʻng VS Code’ni qayta yuklang va kiritish soʻrovida API kalitini kiriting.", "wireApiChatCompletions": "Chat yakunlari (/chat/completions)", "wireApiResponses": "Javoblar API’si (/responses)", + "codexModelCatalog": "OmniRoute modellarini Codex /model tanlagichida ko'rsatish", + "codexModelCatalogHint": "O'rnatilgan Codex'ning o'z model katalogidan ~/.codex/omniroute-models.json faylini yozadi (har bir model uchun taxminan 60 KB). Codex'ni yangilagandan keyin qayta qo'llang.", + "codexCatalogWarning": "Codex /model tanlagichi yangilanmadi: OmniRoute o'rnatilgan Codex model katalogini o'qiy olmadi.", "cliproxyapiActionFailed": "Amal bajarilmadi", "cliproxyapiActionSucceeded": "Amal muvaffaqiyatli bajarildi", "cliproxyapiCheckUpdates": "Yangilanishlarni tekshirish", diff --git a/src/i18n/messages/vi.json b/src/i18n/messages/vi.json index 01006da534fa..6a1d3e926476 100644 --- a/src/i18n/messages/vi.json +++ b/src/i18n/messages/vi.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Sau đó tải lại VS Code và nhập khóa API khi được yêu cầu.", "wireApiChatCompletions": "Hoàn tất trò chuyện (/chat/completions)", "wireApiResponses": "API phản hồi (/responses)", + "codexModelCatalog": "Hiển thị các mô hình OmniRoute trong bộ chọn /model của Codex", + "codexModelCatalogHint": "Ghi ~/.codex/omniroute-models.json từ danh mục mô hình riêng của Codex đã cài đặt (khoảng 60 KB mỗi mô hình). Áp dụng lại sau khi nâng cấp Codex.", + "codexCatalogWarning": "Bộ chọn /model của Codex chưa được cập nhật: OmniRoute không đọc được danh mục mô hình của Codex đã cài đặt.", "cliproxyapiActionFailed": "Thao tác thất bại", "cliproxyapiActionSucceeded": "Thao tác thành công", "cliproxyapiCheckUpdates": "Kiểm tra bản cập nhật", diff --git a/src/i18n/messages/yo.json b/src/i18n/messages/yo.json index 6d3170818fb6..43ed35edd6c8 100644 --- a/src/i18n/messages/yo.json +++ b/src/i18n/messages/yo.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "Lẹ́yìn náà, tún VS Code rù kí o sì ṣètò kọ́kọ́rọ́ API nínú ààyè ìtẹ̀wọlé.", "wireApiChatCompletions": "Àwọn Ìparí Ìfọ̀rọ̀wérọ̀ (/chat/completions)", "wireApiResponses": "API Àwọn Ìdáhùn (/responses)", + "codexModelCatalog": "Ṣàfihàn àwọn àwòṣe OmniRoute nínú olùyàn /model ti Codex", + "codexModelCatalogHint": "Ó ń kọ ~/.codex/omniroute-models.json láti inú àkójọ àwòṣe ti Codex tí a fi sórí ẹ̀rọ (nǹkan bí 60 KB fún àwòṣe kọ̀ọ̀kan). Tún un lò lẹ́yìn tí o bá ṣe àtúnṣe Codex.", + "codexCatalogWarning": "A kò ṣe àtúnṣe olùyàn /model ti Codex: OmniRoute kò lè ka àkójọ àwòṣe ti Codex tí a fi sórí ẹ̀rọ.", "cliproxyapiActionFailed": "Ìṣe náà kùnà", "cliproxyapiActionSucceeded": "Ìṣe náà ṣàṣeyọrí", "cliproxyapiCheckUpdates": "Ṣàyẹ̀wò Àwọn Ìmúdójúìwọ̀n", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index ff5cc4c2e967..5aa0971f21d9 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "然后重新加载 VS Code 并在输入提示框中设置 API 密钥。", "wireApiChatCompletions": "聊天完成 (/chat/completions)", "wireApiResponses": "响应 API (/responses)", + "codexModelCatalog": "在 Codex 的 /model 选择器中列出 OmniRoute 模型", + "codexModelCatalogHint": "根据已安装 Codex 自带的模型目录写入 ~/.codex/omniroute-models.json(每个模型约 60 KB)。升级 Codex 后请重新应用。", + "codexCatalogWarning": "未更新 Codex 的 /model 选择器:OmniRoute 无法读取已安装 Codex 的模型目录。", "cliproxyapiActionFailed": "操作失败", "cliproxyapiActionSucceeded": "操作成功", "cliproxyapiCheckUpdates": "检查更新", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 0def58a86a2e..3ee40b96b48e 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -3240,6 +3240,9 @@ "copilotReloadInstruction": "然後重新載入 VS Code 並在輸入提示中設定 API 金鑰。", "wireApiChatCompletions": "聊天完成 (/chat/completions)", "wireApiResponses": "回應 API (/responses)", + "codexModelCatalog": "在 Codex 的 /model 選擇器中列出 OmniRoute 模型", + "codexModelCatalogHint": "根據已安裝 Codex 內建的模型目錄寫入 ~/.codex/omniroute-models.json(每個模型約 60 KB)。升級 Codex 後請重新套用。", + "codexCatalogWarning": "未更新 Codex 的 /model 選擇器:OmniRoute 無法讀取已安裝 Codex 的模型目錄。", "cliproxyapiActionFailed": "操作失敗", "cliproxyapiActionSucceeded": "操作成功", "cliproxyapiCheckUpdates": "檢查更新", diff --git a/src/lib/cliTools/codexModelCatalog.ts b/src/lib/cliTools/codexModelCatalog.ts new file mode 100644 index 000000000000..4f70f1730642 --- /dev/null +++ b/src/lib/cliTools/codexModelCatalog.ts @@ -0,0 +1,207 @@ +import { execFile } from "child_process"; +import { promisify } from "util"; +import { resolveProviderId } from "@/shared/constants/providers"; +import { + getCliToolCommandCandidates, + getLookupEnv, + locateCommandCandidate, + shouldUseShellForCommand, +} from "@/shared/services/cliRuntime"; + +const execFileAsync = promisify(execFile); + +// Codex validates `model_catalog_json` entries strictly, and every bundled entry +// carries a version-specific system prompt (base_instructions / model_messages). +// OmniRoute entries are therefore cloned from the installed Codex's own bundled +// catalog, never synthesized. +const BUNDLED_CATALOG_ARGS = ["debug", "models", "--bundled"]; +const BUNDLED_CATALOG_TIMEOUT_MS = 15_000; +// The bundled dump is ~450 KB and grows with every Codex release. +const BUNDLED_CATALOG_MAX_BUFFER = 16 * 1024 * 1024; + +// Each listed entry carries a full system prompt (~60 KB with Codex 0.162), so the +// picker is capped to keep the catalog file bounded on deployments with thousands of +// models. Callers order their input so the models the user selected come first. +export const MAX_LISTED_CATALOG_MODELS = 300; + +// Preferred fallback template: a plain shell-tool entry. Code-mode entries expose +// tools that Codex rejects as "unsupported call" when a non-Codex model uses them. +const DEFAULT_TEMPLATE_SLUG = "gpt-5.5"; + +// Code-mode / responses-lite behavior only the Codex upstream understands. +const CODE_MODE_OVERRIDES = { + tool_mode: null, + use_responses_lite: false, + multi_agent_version: null, + multi_agent_reasoning_effort: null, + experimental_supported_tools: [], +}; + +export type CodexCatalogEntry = Record & { + slug: string; + priority?: number; + visibility?: string; +}; + +export type CodexModelCatalog = { models: CodexCatalogEntry[] }; + +export type CatalogModelInput = string | { id?: unknown; context_length?: unknown } | null; + +const isValidEntry = (entry: unknown): entry is CodexCatalogEntry => + !!entry && + typeof entry === "object" && + typeof (entry as { slug?: unknown }).slug === "string" && + (entry as { slug: string }).slug !== ""; + +const isCodeModeEntry = (entry: CodexCatalogEntry) => + entry.tool_mode === "code_mode_only" || entry.use_responses_lite === true; + +const priorityOf = (entry: CodexCatalogEntry) => + typeof entry.priority === "number" && Number.isFinite(entry.priority) ? entry.priority : Infinity; + +// gpt-5.5 if bundled, else the first plain listed entry, else the entry Codex itself +// leads its picker with (lowest-priority listed), else the lowest-priority entry. +const pickDefaultTemplate = (entries: CodexCatalogEntry[]): CodexCatalogEntry | null => { + const preferred = entries.find((e) => e.slug === DEFAULT_TEMPLATE_SLUG); + if (preferred) return preferred; + const byPriority = (a: CodexCatalogEntry, b: CodexCatalogEntry) => priorityOf(a) - priorityOf(b); + const listed = entries.filter((e) => e.visibility === "list").sort(byPriority); + const plain = listed.find((e) => !isCodeModeEntry(e)); + if (plain) return plain; + if (listed.length) return listed[0]; + return [...entries].sort(byPriority)[0] || null; +}; + +// Only `codex/…` (or one of its registry aliases, e.g. `cx/…`) reaches the real Codex +// upstream; unprefixed ids (combos) and every other provider are non-Codex. +const isCodexProviderModel = (id: string) => { + const slash = id.indexOf("/"); + return slash > 0 && resolveProviderId(id.slice(0, slash)) === "codex"; +}; + +const normalizeModel = (model: CatalogModelInput) => { + if (typeof model === "string") return { id: model.trim(), contextLength: null }; + if (model && typeof model === "object" && typeof model.id === "string") { + const contextLength = + typeof model.context_length === "number" && + Number.isFinite(model.context_length) && + model.context_length > 0 + ? model.context_length + : null; + return { id: model.id.trim(), contextLength }; + } + return null; +}; + +/** + * Build a Codex `model_catalog_json` document listing OmniRoute models. + * + * Each OmniRoute model clones the bundled entry whose slug matches the part after the + * last "/" (`cx/gpt-5.5` ← `gpt-5.5`), else a default template. Bundled entries are + * kept but hidden so Codex's internal slugs still resolve. + */ +export function buildCodexModelCatalog( + bundledCatalog: unknown, + models: CatalogModelInput[] +): CodexModelCatalog { + const rawBundled = (bundledCatalog as { models?: unknown } | null)?.models; + const bundled = (Array.isArray(rawBundled) ? rawBundled : []).filter(isValidEntry); + const defaultTemplate = pickDefaultTemplate(bundled); + if (!defaultTemplate) return { models: [] }; + + const bySlug = new Map(bundled.map((e) => [e.slug, e])); + const listedEntries: CodexCatalogEntry[] = []; + const seen = new Set(); + + for (const raw of Array.isArray(models) ? models : []) { + if (listedEntries.length >= MAX_LISTED_CATALOG_MODELS) break; + const model = normalizeModel(raw); + if (!model?.id || seen.has(model.id)) continue; + seen.add(model.id); + + const baseSlug = model.id.slice(model.id.lastIndexOf("/") + 1); + const matched = bySlug.get(baseSlug); + const entry = structuredClone(matched || defaultTemplate); + + entry.slug = model.id; + entry.display_name = model.id; + entry.description = matched + ? `${model.id} via OmniRoute` + : `${model.id} via OmniRoute (Codex profile: ${defaultTemplate.slug})`; + entry.visibility = "list"; + entry.priority = listedEntries.length + 1; + // Retirement notices point at bare OpenAI slugs; an inherited upgrade would push + // the user off a working prefixed model. + entry.upgrade = null; + entry.availability_nux = null; + + // Non-Codex upstreams get the plain shell toolset even when the name matches a + // code-mode bundled model. + if (!isCodexProviderModel(model.id)) { + Object.assign(entry, structuredClone(CODE_MODE_OVERRIDES)); + } + + if (!matched) { + // OpenAI service tiers (Fast/priority) do not exist on arbitrary upstreams. + entry.service_tiers = []; + entry.additional_speed_tiers = []; + // The template's window belongs to a GPT model; prefer OmniRoute's own figure so + // Codex compacts at the right point instead of overflowing upstream. + if (model.contextLength) { + entry.context_window = model.contextLength; + entry.max_context_window = model.contextLength; + } + } + + listedEntries.push(entry); + } + + // Shift hidden entries past the OmniRoute block, keeping their relative order. + const offset = listedEntries.length + 1; + const hiddenBundled = bundled + .filter((e) => !seen.has(e.slug)) + .map((e) => { + const priority = + typeof e.priority === "number" && Number.isFinite(e.priority) ? e.priority : 0; + return { + ...structuredClone(e), + visibility: "hide", + priority: offset + Math.max(0, priority), + }; + }); + + return { models: [...listedEntries, ...hiddenBundled] }; +} + +/** + * Load Codex's bundled model catalog by running the installed CLI + * (`codex debug models --bundled`). Throws when Codex is not runnable, is too old to + * have the subcommand, or prints something that is not a catalog. + * + * `codexHome` pins `CODEX_HOME` for the child so it reads the same directory OmniRoute + * writes the config into. + */ +export async function loadBundledCodexCatalog(codexHome: string): Promise { + const env = getLookupEnv(); + const commands = getCliToolCommandCandidates("codex"); + const hasEnvOverride = !!String(process.env.CLI_CODEX_BIN || "").trim(); + const located = await locateCommandCandidate(commands, env, hasEnvOverride ? undefined : "codex"); + if (!located.installed || !located.commandPath || located.reason === "not_executable") { + throw new Error(`Codex CLI not available (${located.reason || "not_found"})`); + } + + // Hard Rule #13: argv array, no shell string. Windows .cmd/.bat wrappers still need + // cmd.exe, which Node quotes itself when `shell` is set. + const { stdout } = await execFileAsync(located.commandPath, BUNDLED_CATALOG_ARGS, { + windowsHide: true, + env: { ...env, CODEX_HOME: codexHome }, + timeout: BUNDLED_CATALOG_TIMEOUT_MS, + maxBuffer: BUNDLED_CATALOG_MAX_BUFFER, + ...(shouldUseShellForCommand(located.commandPath) ? { shell: true } : {}), + }); + const parsed = JSON.parse(String(stdout)); + if (!Array.isArray(parsed?.models) || parsed.models.length === 0) { + throw new Error("Codex bundled catalog has no models"); + } + return parsed as CodexModelCatalog; +} diff --git a/src/lib/cliTools/codexModelCatalogWriter.ts b/src/lib/cliTools/codexModelCatalogWriter.ts new file mode 100644 index 000000000000..bfe7b014484c --- /dev/null +++ b/src/lib/cliTools/codexModelCatalogWriter.ts @@ -0,0 +1,141 @@ +import fs from "fs/promises"; +import path from "path"; +import { + buildCodexModelCatalog, + loadBundledCodexCatalog, + type CatalogModelInput, + type CodexModelCatalog, +} from "./codexModelCatalog"; + +export const CODEX_CATALOG_FILENAME = "omniroute-models.json"; + +export const getCodexCatalogPath = (codexDir: string) => + path.join(codexDir, CODEX_CATALOG_FILENAME); + +export type CodexCatalogWriteResult = + | { catalogPath: string; count: number; warning?: undefined } + | { warning: string; catalogPath?: undefined; count?: undefined }; + +type CatalogListEntry = { id?: unknown; type?: unknown; context_length?: unknown }; + +/** + * List OmniRoute's chat models through the same unified catalog `/v1/models` serves + * (hidden models excluded, combos included). The caller's own credentials (session + * cookie, peer stamp, bearer) are forwarded so the catalog's auth gate sees the + * same identity that already passed the CLI-tools management check. + */ +export async function listOmniRouteChatModels(request: Request): Promise { + const { getUnifiedModelsResponse } = await import("@/app/api/v1/models/catalog"); + const headers = new Headers(request.headers); + headers.delete("content-type"); + headers.delete("content-length"); + const response = await getUnifiedModelsResponse( + new Request(new URL("/v1/models", request.url), { method: "GET", headers }) + ); + if (!response.ok) return []; + const body = (await response.json()) as { data?: unknown }; + if (!Array.isArray(body?.data)) return []; + return (body.data as CatalogListEntry[]) + .filter((m) => typeof m?.id === "string" && (!m.type || m.type === "chat")) + .map((m) => ({ id: m.id, context_length: m.context_length })); +} + +type WriteCatalogOptions = { + codexDir: string; + /** Models the user picked in this Apply; listed first and always included. */ + selectedModels: string[]; + listModels: () => Promise; + loadBundled?: (codexHome: string) => Promise; +}; + +/** + * Write the OmniRoute model catalog so Codex's `/model` picker lists routable, + * provider-prefixed ids instead of bare bundled slugs. + * + * Fail-open: every failure returns `{ warning }` instead of throwing, so Apply still + * succeeds without the catalog. Catalog content is never logged. + */ +export async function writeCodexModelCatalog({ + codexDir, + selectedModels, + listModels, + loadBundled = loadBundledCodexCatalog, +}: WriteCatalogOptions): Promise { + let bundled: CodexModelCatalog; + try { + bundled = await loadBundled(codexDir); + } catch (error) { + console.log( + "Codex bundled model catalog unavailable:", + error instanceof Error ? error.message : String(error) + ); + return { + warning: "Could not read Codex's bundled model catalog; the /model picker was not updated", + }; + } + + let listed: CatalogModelInput[] = []; + try { + listed = await listModels(); + } catch (error) { + console.log( + "Could not build the OmniRoute model list for Codex:", + error instanceof Error ? error.message : String(error) + ); + } + + // Selected models go first (they survive the listing cap) and may be custom ids + // that are absent from the unified catalog. + const models: CatalogModelInput[] = []; + const byId = new Map(); + for (const entry of listed) { + if (entry && typeof entry === "object" && typeof entry.id === "string") { + byId.set(entry.id, entry); + } + } + for (const id of selectedModels) { + if (typeof id === "string" && id.trim()) models.push(byId.get(id.trim()) ?? id.trim()); + } + models.push(...listed); + + if (models.length === 0) { + return { warning: "No OmniRoute models available; the /model picker was not updated" }; + } + + const catalog = buildCodexModelCatalog(bundled, models); + const count = catalog.models.filter((m) => m.visibility === "list").length; + if (count === 0) { + return { + warning: "Codex's bundled catalog has no usable template; the /model picker was not updated", + }; + } + + const catalogPath = getCodexCatalogPath(codexDir); + try { + await fs.writeFile(catalogPath, JSON.stringify(catalog)); + } catch (error) { + console.log( + "Could not write the Codex model catalog:", + error instanceof Error ? error.message : String(error) + ); + return { + warning: "Could not write the Codex model catalog; the /model picker was not updated", + }; + } + return { catalogPath, count }; +} + +/** Remove the catalog file OmniRoute wrote; a missing file is not an error. */ +export async function removeCodexModelCatalog(codexDir: string): Promise { + try { + await fs.unlink(getCodexCatalogPath(codexDir)); + } catch (error) { + const code = (error as NodeJS.ErrnoException)?.code; + if (code !== "ENOENT") { + console.log( + "Could not remove the Codex model catalog:", + error instanceof Error ? error.message : String(error) + ); + } + } +} diff --git a/src/shared/validation/schemas/cli.ts b/src/shared/validation/schemas/cli.ts index 0c192bfab1c9..ff605c154f66 100644 --- a/src/shared/validation/schemas/cli.ts +++ b/src/shared/validation/schemas/cli.ts @@ -85,6 +85,14 @@ export const cliModelConfigSchema = z.object({ modelMappings: z.record(z.string().trim().min(1), z.string().trim().min(1)).optional(), }); +/** + * Codex variant of `cliModelConfigSchema`. `modelCatalog: true` also writes an + * OmniRoute `model_catalog_json` so Codex's `/model` picker lists routable ids. + */ +export const codexSettingsConfigSchema = cliModelConfigSchema.extend({ + modelCatalog: z.boolean().optional(), +}); + /** * Multi-model variant of `cliModelConfigSchema`. Adds optional `models` * (array of strings, takes precedence over `model`) and `activeModel` diff --git a/tests/unit/codex-model-catalog.test.ts b/tests/unit/codex-model-catalog.test.ts new file mode 100644 index 000000000000..a757dca4d299 --- /dev/null +++ b/tests/unit/codex-model-catalog.test.ts @@ -0,0 +1,299 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; + +import { + buildCodexModelCatalog, + MAX_LISTED_CATALOG_MODELS, + type CodexCatalogEntry, + type CodexModelCatalog, +} from "../../src/lib/cliTools/codexModelCatalog.ts"; + +// Minimal stand-ins for `codex debug models --bundled` entries; real ones carry ~60 KB of +// instructions, which is exactly why the builder clones instead of synthesizing. +const entry = (slug: string, overrides: Record = {}): CodexCatalogEntry => ({ + slug, + display_name: slug.toUpperCase(), + description: `${slug} description`, + supported_reasoning_levels: [{ effort: "medium", description: "Balanced" }], + support_verbosity: true, + shell_type: "unified_exec", + truncation_policy: { mode: "tokens", limit: 10000 }, + priority: 1, + visibility: "list", + supported_in_api: true, + experimental_supported_tools: [], + base_instructions: `instructions for ${slug}`, + model_messages: { instructions_template: `template for ${slug}` }, + service_tiers: [{ id: "priority", name: "Fast" }], + additional_speed_tiers: ["fast"], + availability_nux: null, + upgrade: null, + context_window: 272000, + max_context_window: 872000, + ...overrides, +}); + +// Code-mode flags carried by gpt-6-astra / gpt-5.6-* in the real bundled catalog +const codeMode = { + tool_mode: "code_mode_only", + use_responses_lite: true, + multi_agent_version: "v2", +}; + +const bundled: CodexModelCatalog = { + models: [ + entry("gpt-5.6-sol", { priority: 6, ...codeMode }), + entry("gpt-6-astra", { + priority: 1, + ...codeMode, + multi_agent_reasoning_effort: "xhigh", + experimental_supported_tools: ["send_user_message_async", "clock"], + }), + entry("gpt-5.6-terra", { priority: 7, ...codeMode }), + entry("gpt-5.5", { priority: 12 }), + entry("gpt-hidden-first", { priority: 0, visibility: "hide" }), + entry("gpt-5.4", { + priority: 16, + visibility: "hide", + upgrade: { + model: "gpt-5.6-terra", + migration_markdown: "retired", + retirement_at: "2026-08-31T19:00:00Z", + }, + }), + entry("codex-auto-review", { priority: 43, visibility: "hide" }), + ], +}; + +const withoutGpt55 = () => bundled.models.filter((m) => m.slug !== "gpt-5.5"); +const listed = (catalog: CodexModelCatalog) => + catalog.models.filter((m) => m.visibility === "list"); +const bySlug = (catalog: CodexModelCatalog, slug: string) => { + const found = catalog.models.find((m) => m.slug === slug); + assert.ok(found, `expected ${slug} in catalog`); + return found; +}; +const pick = (obj: Record, keys: string[]) => + Object.fromEntries(keys.map((k) => [k, obj[k]])); + +describe("buildCodexModelCatalog", () => { + it("clones the bundled entry matching the part after the last slash", () => { + const catalog = buildCodexModelCatalog(bundled, ["mirbud/gpt-5.6-terra", "a/b/gpt-5.6-sol"]); + const terra = bySlug(catalog, "mirbud/gpt-5.6-terra"); + assert.equal(terra.base_instructions, "instructions for gpt-5.6-terra"); + assert.deepEqual(terra.model_messages, { instructions_template: "template for gpt-5.6-terra" }); + assert.equal(terra.display_name, "mirbud/gpt-5.6-terra"); + assert.equal(terra.description, "mirbud/gpt-5.6-terra via OmniRoute"); + assert.equal(terra.visibility, "list"); + // Matched templates keep Codex's own tiers and window for that model + assert.deepEqual(terra.service_tiers, [{ id: "priority", name: "Fast" }]); + assert.equal(terra.context_window, 272000); + assert.equal( + bySlug(catalog, "a/b/gpt-5.6-sol").base_instructions, + "instructions for gpt-5.6-sol" + ); + }); + + it("falls back to bundled gpt-5.5 for unknown models", () => { + const catalog = buildCodexModelCatalog(bundled, ["cc/claude-opus", "Opus_like"]); + for (const slug of ["cc/claude-opus", "Opus_like"]) { + const clone = bySlug(catalog, slug); + assert.equal(clone.base_instructions, "instructions for gpt-5.5"); + assert.deepEqual(clone.service_tiers, []); + assert.deepEqual(clone.additional_speed_tiers, []); + assert.match(String(clone.description), /via OmniRoute \(Codex profile: gpt-5\.5\)$/); + } + }); + + it("without gpt-5.5, falls back to the lowest-priority plain listed entry", () => { + const models = [ + ...withoutGpt55(), + entry("gpt-plain-late", { priority: 30 }), + entry("gpt-plain-early", { priority: 20 }), + entry("gpt-plain-hidden", { priority: 2, visibility: "hide" }), + ]; + const clone = bySlug(buildCodexModelCatalog({ models }, ["cc/claude-opus"]), "cc/claude-opus"); + assert.equal(clone.base_instructions, "instructions for gpt-plain-early"); + }); + + it("with only code-mode entries, falls back to the lowest-priority listed entry", () => { + const clone = bySlug( + buildCodexModelCatalog({ models: withoutGpt55() }, ["cc/claude-opus"]), + "cc/claude-opus" + ); + // gpt-hidden-first has priority 0 but is hidden, so astra wins + assert.equal(clone.base_instructions, "instructions for gpt-6-astra"); + }); + + it("neutralizes code-mode flags for non-Codex providers and combos", () => { + const ids = ["kr/gpt-5.6-terra", "mirbud/gpt-6-astra", "Opus_like", "cc/claude-opus"]; + const catalog = buildCodexModelCatalog({ models: withoutGpt55() }, ids); + for (const slug of ids) { + assert.deepEqual( + pick(bySlug(catalog, slug), [ + "tool_mode", + "use_responses_lite", + "multi_agent_version", + "multi_agent_reasoning_effort", + "experimental_supported_tools", + ]), + { + tool_mode: null, + use_responses_lite: false, + multi_agent_version: null, + multi_agent_reasoning_effort: null, + experimental_supported_tools: [], + }, + slug + ); + } + // Instructions still come from the matched template + assert.equal( + bySlug(catalog, "kr/gpt-5.6-terra").base_instructions, + "instructions for gpt-5.6-terra" + ); + }); + + it("keeps Codex values for Codex-provider entries (id and registry alias)", () => { + const catalog = buildCodexModelCatalog(bundled, ["cx/gpt-6-astra", "codex/gpt-5.6-terra"]); + assert.deepEqual( + pick(bySlug(catalog, "cx/gpt-6-astra"), [ + "tool_mode", + "use_responses_lite", + "multi_agent_version", + "multi_agent_reasoning_effort", + "experimental_supported_tools", + ]), + { + ...codeMode, + multi_agent_reasoning_effort: "xhigh", + experimental_supported_tools: ["send_user_message_async", "clock"], + } + ); + assert.deepEqual(pick(bySlug(catalog, "codex/gpt-5.6-terra"), Object.keys(codeMode)), codeMode); + // Hidden bundled originals are never neutralized + assert.deepEqual(pick(bySlug(catalog, "gpt-5.6-sol"), Object.keys(codeMode)), codeMode); + }); + + it("uses the OmniRoute context length only for default-template clones", () => { + const catalog = buildCodexModelCatalog(bundled, [ + { id: "cc/claude-opus", context_length: 200000 }, + { id: "cx/gpt-5.6-terra", context_length: 128000 }, + { id: "kr/unknown" }, + { id: "kr/bogus-window", context_length: -1 }, + ]); + const windowOf = (slug: string) => + pick(bySlug(catalog, slug), ["context_window", "max_context_window"]); + assert.deepEqual(windowOf("cc/claude-opus"), { + context_window: 200000, + max_context_window: 200000, + }); + assert.deepEqual(windowOf("cx/gpt-5.6-terra"), { + context_window: 272000, + max_context_window: 872000, + }); + assert.deepEqual(windowOf("kr/unknown"), { + context_window: 272000, + max_context_window: 872000, + }); + assert.deepEqual(windowOf("kr/bogus-window"), { + context_window: 272000, + max_context_window: 872000, + }); + }); + + it("clears inherited upgrade prompts on OmniRoute entries only", () => { + const catalog = buildCodexModelCatalog(bundled, ["mirbud/gpt-5.4"]); + const clone = bySlug(catalog, "mirbud/gpt-5.4"); + assert.equal(clone.base_instructions, "instructions for gpt-5.4"); + assert.equal(clone.upgrade, null); + assert.equal(clone.availability_nux, null); + assert.equal((bySlug(catalog, "gpt-5.4").upgrade as { model: string }).model, "gpt-5.6-terra"); + }); + + it("keeps every bundled entry but hides it from the picker", () => { + const catalog = buildCodexModelCatalog(bundled, ["cx/gpt-5.6-terra"]); + assert.deepEqual( + listed(catalog).map((m) => m.slug), + ["cx/gpt-5.6-terra"] + ); + for (const original of bundled.models) { + const kept = bySlug(catalog, original.slug); + assert.equal(kept.visibility, "hide"); + assert.equal(kept.base_instructions, original.base_instructions); + } + // Input catalog is not mutated + assert.equal(bundled.models.find((m) => m.slug === "gpt-6-astra")?.visibility, "list"); + }); + + it("lets an OmniRoute id win over a bundled slug without duplicating it", () => { + const catalog = buildCodexModelCatalog(bundled, ["gpt-5.6-terra", "gpt-5.6-terra"]); + const matches = catalog.models.filter((m) => m.slug === "gpt-5.6-terra"); + assert.equal(matches.length, 1); + assert.deepEqual(pick(matches[0], ["visibility", "priority", "display_name"]), { + visibility: "list", + priority: 1, + display_name: "gpt-5.6-terra", + }); + const slugs = catalog.models.map((m) => m.slug); + assert.equal(new Set(slugs).size, slugs.length); + }); + + it("orders OmniRoute entries by input order ahead of hidden bundled entries", () => { + const ids = ["Opus_like", "cx/gpt-5.6-terra", "cc/claude-opus"]; + const catalog = buildCodexModelCatalog(bundled, ids); + assert.deepEqual( + listed(catalog).map((m) => [m.slug, m.priority]), + [ + ["Opus_like", 1], + ["cx/gpt-5.6-terra", 2], + ["cc/claude-opus", 3], + ] + ); + const hiddenPriorities = catalog.models + .filter((m) => m.visibility === "hide") + .map((m) => Number(m.priority)); + assert.ok(Math.min(...hiddenPriorities) > ids.length); + }); + + it("preserves the fields Codex requires to parse an entry", () => { + const required = [ + "slug", + "display_name", + "supported_reasoning_levels", + "support_verbosity", + "shell_type", + "truncation_policy", + "priority", + "visibility", + "supported_in_api", + "experimental_supported_tools", + ]; + const catalog = buildCodexModelCatalog(bundled, ["cx/gpt-5.6-terra", "cc/claude-opus"]); + for (const model of catalog.models) { + for (const key of required) assert.ok(key in model, `${model.slug} is missing ${key}`); + const messages = model.model_messages as { instructions_template?: string } | undefined; + assert.ok(model.base_instructions || messages?.instructions_template); + } + }); + + it("caps the listed block so the catalog file stays bounded", () => { + const ids = Array.from({ length: MAX_LISTED_CATALOG_MODELS + 25 }, (_, i) => `kr/m-${i}`); + const catalog = buildCodexModelCatalog(bundled, ids); + const listedSlugs = listed(catalog).map((m) => m.slug); + assert.equal(listedSlugs.length, MAX_LISTED_CATALOG_MODELS); + assert.equal(listedSlugs[0], "kr/m-0"); + assert.equal(listedSlugs.at(-1), `kr/m-${MAX_LISTED_CATALOG_MODELS - 1}`); + // Bundled entries are still all kept (hidden) + assert.equal(catalog.models.length, MAX_LISTED_CATALOG_MODELS + bundled.models.length); + }); + + it("returns an empty catalog when there is nothing to clone from", () => { + assert.deepEqual(buildCodexModelCatalog({ models: [] }, ["cx/gpt-5.6-terra"]), { models: [] }); + assert.deepEqual(buildCodexModelCatalog(null, ["cx/gpt-5.6-terra"]), { models: [] }); + assert.deepEqual( + listed(buildCodexModelCatalog(bundled, [" ", null, 42 as unknown as string, { id: 7 }])), + [] + ); + }); +}); diff --git a/tests/unit/codex-settings-model-catalog.test.ts b/tests/unit/codex-settings-model-catalog.test.ts new file mode 100644 index 000000000000..3a5f82f5bc1d --- /dev/null +++ b/tests/unit/codex-settings-model-catalog.test.ts @@ -0,0 +1,206 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { SignJWT } from "jose"; + +// Hermetic: a temp HOME for the route AND for the spawned fake `codex`, which is a shell +// script selected through CLI_CODEX_BIN. The real ~/.codex is never read or written. +const TEST_ROOT = path.join(os.tmpdir(), `omniroute-codex-catalog-${process.pid}-${Date.now()}`); +const TEST_HOME = path.join(TEST_ROOT, "home"); +const BIN_DIR = path.join(TEST_ROOT, "bin"); +const CODEX_DIR = path.join(TEST_HOME, ".codex"); +const CONFIG_PATH = path.join(CODEX_DIR, "config.toml"); +const CATALOG_PATH = path.join(CODEX_DIR, "omniroute-models.json"); +const BUNDLED_PATH = path.join(TEST_ROOT, "bundled.json"); +const SEEN_CODEX_HOME = path.join(TEST_ROOT, "seen-codex-home.txt"); +const FAKE_CODEX_OK = path.join(BIN_DIR, "codex-ok"); +const FAKE_CODEX_BROKEN = path.join(BIN_DIR, "codex-broken"); + +const saved = { + homedir: os.homedir, + HOME: process.env.HOME, + JWT_SECRET: process.env.JWT_SECRET, + CLI_ALLOW_CONFIG_WRITES: process.env.CLI_ALLOW_CONFIG_WRITES, + CLI_CODEX_BIN: process.env.CLI_CODEX_BIN, +}; + +os.homedir = () => TEST_HOME; +process.env.HOME = TEST_HOME; +process.env.CLI_ALLOW_CONFIG_WRITES = "true"; + +await fs.mkdir(BIN_DIR, { recursive: true }); +await fs.writeFile( + BUNDLED_PATH, + JSON.stringify({ + models: [ + { + slug: "gpt-5.5", + display_name: "GPT-5.5", + supported_reasoning_levels: [], + support_verbosity: true, + shell_type: "unified_exec", + truncation_policy: { mode: "tokens", limit: 10000 }, + priority: 3, + visibility: "list", + supported_in_api: true, + experimental_supported_tools: [], + base_instructions: "bundled prompt", + upgrade: null, + }, + ], + }) +); +await fs.writeFile( + FAKE_CODEX_OK, + `#!/bin/sh\nprintf '%s' "$CODEX_HOME" > '${SEEN_CODEX_HOME}'\n[ "$1 $2 $3" = "debug models --bundled" ] || exit 2\ncat '${BUNDLED_PATH}'\n`, + { mode: 0o755 } +); +await fs.writeFile(FAKE_CODEX_BROKEN, "#!/bin/sh\necho 'unknown subcommand' >&2\nexit 1\n", { + mode: 0o755, +}); + +const route = await import("../../src/app/api/cli-tools/codex-settings/route.ts"); +const { resetDbInstance } = await import("../../src/lib/db/core.ts"); + +const authCookie = async (): Promise => { + process.env.JWT_SECRET = "codex-model-catalog-test-secret"; + const token = await new SignJWT({ authenticated: true, sub: "codex-model-catalog-test" }) + .setProtectedHeader({ alg: "HS256" }) + .setIssuedAt() + .setExpirationTime("1h") + .sign(new TextEncoder().encode(process.env.JWT_SECRET)); + return `auth_token=${token}`; +}; + +const post = async (body: Record) => + route.POST( + new Request("http://localhost/api/cli-tools/codex-settings", { + method: "POST", + headers: { cookie: await authCookie(), "Content-Type": "application/json" }, + body: JSON.stringify({ + baseUrl: "http://localhost:20128/v1", + apiKey: "sk-test-only", + model: "cx/gpt-5.5", + ...body, + }), + }) + ); + +const reset = async () => + route.DELETE( + new Request("http://localhost/api/cli-tools/codex-settings", { + method: "DELETE", + headers: { cookie: await authCookie() }, + }) + ); + +const exists = async (filePath: string) => + fs.access(filePath).then( + () => true, + () => false + ); + +const freshCodexDir = async () => { + await fs.rm(CODEX_DIR, { recursive: true, force: true }); + await fs.rm(SEEN_CODEX_HOME, { force: true }); +}; + +test.after(async () => { + resetDbInstance(); + os.homedir = saved.homedir; + for (const key of ["HOME", "JWT_SECRET", "CLI_ALLOW_CONFIG_WRITES", "CLI_CODEX_BIN"] as const) { + if (saved[key] === undefined) delete process.env[key]; + else process.env[key] = saved[key]; + } + await fs.rm(TEST_ROOT, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); +}); + +test("POST with modelCatalog writes an OmniRoute catalog and points Codex at it", async () => { + await freshCodexDir(); + process.env.CLI_CODEX_BIN = FAKE_CODEX_OK; + + const response = await post({ + modelCatalog: true, + modelMappings: { "gpt-5.6-sol": "kr/claude-sonnet" }, + }); + assert.equal(response.status, 200); + const body = await response.json(); + assert.equal(body.success, true); + assert.equal(body.catalogPath, CATALOG_PATH); + assert.equal(body.catalogWarning, undefined); + assert.ok(body.catalogModelCount >= 2); + + const config = await fs.readFile(CONFIG_PATH, "utf8"); + assert.match(config, new RegExp(`^model_catalog_json = "${CATALOG_PATH}"$`, "m")); + assert.match(config, /^model = "cx\/gpt-5\.5"$/m); + + // The child ran against the same Codex home OmniRoute writes into + assert.equal(await fs.readFile(SEEN_CODEX_HOME, "utf8"), CODEX_DIR); + + const catalog = JSON.parse(await fs.readFile(CATALOG_PATH, "utf8")); + const listedSlugs = catalog.models + .filter((m: { visibility: string }) => m.visibility === "list") + .map((m: { slug: string }) => m.slug); + // The models picked in this Apply lead the picker + assert.deepEqual(listedSlugs.slice(0, 2), ["cx/gpt-5.5", "kr/claude-sonnet"]); + const bundledCopy = catalog.models.find((m: { slug: string }) => m.slug === "gpt-5.5"); + assert.equal(bundledCopy.visibility, "hide"); + // The API key never lands in the catalog + assert.ok(!JSON.stringify(catalog).includes("sk-test-only")); +}); + +test("POST without modelCatalog leaves Codex's catalog untouched", async () => { + await freshCodexDir(); + process.env.CLI_CODEX_BIN = FAKE_CODEX_OK; + + const response = await post({}); + assert.equal(response.status, 200); + const body = await response.json(); + assert.equal(body.catalogPath, undefined); + assert.equal(body.catalogWarning, undefined); + assert.doesNotMatch(await fs.readFile(CONFIG_PATH, "utf8"), /model_catalog_json/); + assert.equal(await exists(CATALOG_PATH), false); + assert.equal(await exists(SEEN_CODEX_HOME), false, "codex must not be spawned"); +}); + +test("POST fails open with catalogWarning when Codex cannot dump its catalog", async () => { + await freshCodexDir(); + process.env.CLI_CODEX_BIN = FAKE_CODEX_BROKEN; + + const response = await post({ modelCatalog: true }); + assert.equal(response.status, 200); + const body = await response.json(); + assert.equal(body.success, true); + assert.equal(typeof body.catalogWarning, "string"); + assert.equal(body.catalogPath, undefined); + + const config = await fs.readFile(CONFIG_PATH, "utf8"); + assert.doesNotMatch(config, /model_catalog_json/); + assert.match(config, /^model_provider = "omniroute"$/m); + assert.equal(await exists(CATALOG_PATH), false); +}); + +test("DELETE removes the catalog key and file only when the key points to OmniRoute's file", async () => { + await freshCodexDir(); + process.env.CLI_CODEX_BIN = FAKE_CODEX_OK; + assert.equal((await post({ modelCatalog: true })).status, 200); + assert.equal(await exists(CATALOG_PATH), true); + + assert.equal((await reset()).status, 200); + assert.doesNotMatch(await fs.readFile(CONFIG_PATH, "utf8"), /model_catalog_json/); + assert.equal(await exists(CATALOG_PATH), false); + + // A user-owned catalog is left alone + const userCatalog = path.join(CODEX_DIR, "my-models.json"); + await fs.writeFile( + CONFIG_PATH, + `model_catalog_json = "${userCatalog}"\nmodel = "cx/gpt-5.5"\nmodel_provider = "omniroute"\n` + ); + assert.equal((await reset()).status, 200); + assert.match( + await fs.readFile(CONFIG_PATH, "utf8"), + new RegExp(`^model_catalog_json = "${userCatalog}"$`, "m") + ); +}); diff --git a/tests/unit/ui/codex-tool-card-model-catalog.test.tsx b/tests/unit/ui/codex-tool-card-model-catalog.test.tsx new file mode 100644 index 000000000000..ba2ac2bef594 --- /dev/null +++ b/tests/unit/ui/codex-tool-card-model-catalog.test.tsx @@ -0,0 +1,150 @@ +import React, { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = + true; + +const translate = (key: string) => key; + +vi.mock("next-intl", () => ({ useTranslations: () => translate })); +vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null })); +vi.mock("@/app/(dashboard)/dashboard/cli-code/components/CliStatusBadge", () => ({ + default: () => null, +})); +vi.mock("@/shared/components", () => ({ + Card: ({ children }: { children: React.ReactNode }) =>
{children}
, + Button: ({ + children, + onClick, + disabled, + loading, + }: { + children: React.ReactNode; + onClick?: () => void; + disabled?: boolean; + loading?: boolean; + }) => ( + + ), + ModelSelectModal: () => null, + ManualConfigModal: () => null, +})); + +import CodexToolCard from "@/app/(dashboard)/dashboard/cli-code/components/CodexToolCard"; + +const mounted: Array<{ container: HTMLDivElement; root: Root }> = []; + +const jsonResponse = (body: unknown) => ({ + ok: true, + json: async () => body, +}); + +const waitFor = async (predicate: () => boolean, timeoutMs = 2000) => { + const started = Date.now(); + while (!predicate()) { + if (Date.now() - started > timeoutMs) throw new Error("waitFor timed out"); + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + } +}; + +const catalogCheckbox = (container: HTMLElement): HTMLInputElement | null => + Array.from(container.querySelectorAll('input[type="checkbox"]')).find((input) => + input.closest("label")?.textContent?.includes("codexModelCatalog") + ) ?? null; + +const applyButton = (container: HTMLElement) => + Array.from(container.querySelectorAll("button")).find( + (button) => button.textContent?.trim() === "saveapply" + ); + +afterEach(() => { + for (const { container, root } of mounted.splice(0)) { + act(() => root.unmount()); + container.remove(); + } + vi.restoreAllMocks(); +}); + +const renderCard = async (applyResponse: Record) => { + const posts: Array> = []; + vi.stubGlobal( + "fetch", + vi.fn(async (input: string | URL | Request, init?: RequestInit) => { + const url = String(input); + if (url === "/api/cli-tools/codex-settings" && init?.method === "POST") { + posts.push(JSON.parse(String(init.body))); + return jsonResponse(applyResponse); + } + if (url === "/api/cli-tools/codex-settings") { + return jsonResponse({ installed: true, runnable: true, config: 'model = "gpt-5.6-sol"\n' }); + } + if (url === "/api/models/alias") return jsonResponse({ aliases: {} }); + if (url === "/api/cli-tools/codex-profiles") return jsonResponse({ profiles: [] }); + if (url === "/api/cli-tools/backups?tool=codex") return jsonResponse({ backups: [] }); + throw new Error(`Unexpected fetch: ${url}`); + }) + ); + + const container = document.createElement("div"); + document.body.appendChild(container); + const root = createRoot(container); + mounted.push({ container, root }); + + await act(async () => { + root.render( + + ); + }); + await waitFor(() => catalogCheckbox(container) !== null && !!applyButton(container)); + return { container, posts }; +}; + +describe("CodexToolCard model catalog", () => { + it("asks for the Codex model catalog by default and can opt out", async () => { + const { container, posts } = await renderCard({ success: true }); + expect(catalogCheckbox(container)?.checked).toBe(true); + + await act(async () => { + applyButton(container)!.click(); + }); + await waitFor(() => posts.length === 1); + expect(posts[0].modelCatalog).toBe(true); + + await act(async () => { + catalogCheckbox(container)!.click(); + }); + expect(catalogCheckbox(container)?.checked).toBe(false); + await act(async () => { + applyButton(container)!.click(); + }); + await waitFor(() => posts.length === 2); + expect(posts[1].modelCatalog).toBe(false); + }); + + it("shows a warning, not an error, when Apply succeeds without the catalog", async () => { + const { container } = await renderCard({ + success: true, + catalogWarning: "Could not read Codex's bundled model catalog", + }); + await act(async () => { + applyButton(container)!.click(); + }); + await waitFor(() => !!container.textContent?.includes("codexCatalogWarning")); + expect(container.textContent).toContain("settingsApplied codexCatalogWarning"); + expect(container.querySelector(".text-amber-600")).not.toBeNull(); + }); +}); From b6a7f16fe41022ac4ec5a074df482cc2cd92ea91 Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 16 Oct 2026 20:17:04 -0300 Subject: [PATCH 2/3] chore(changelog): fragment for #16910 --- changelog.d/features/16910-codex-model-catalog-on-apply.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/features/16910-codex-model-catalog-on-apply.md diff --git a/changelog.d/features/16910-codex-model-catalog-on-apply.md b/changelog.d/features/16910-codex-model-catalog-on-apply.md new file mode 100644 index 000000000000..764a73ea8b7d --- /dev/null +++ b/changelog.d/features/16910-codex-model-catalog-on-apply.md @@ -0,0 +1 @@ +- **feat(cli-tools):** applying Codex settings also writes an OmniRoute model catalog (`model_catalog_json`) so Codex's `/model` picker lists routable provider-prefixed models; fails open with a warning when Codex can't be read ([#16910](https://github.com/diegosouzapw/OmniRoute/pull/16910)) — thanks @kalfian From 7b273fca9f93c2980f195297b3313e9fb4c0e768 Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:10:37 -0300 Subject: [PATCH 3/3] fix(cli-tools): make the Codex model catalog opt-in (default off) The catalog file grows ~60 KB per model in ~/.codex, so the Apply checkbox now starts unchecked. The route already writes only on modelCatalog === true; an explicit modelCatalog: false test joins the omitted-flag test. --- .../features/16910-codex-model-catalog-on-apply.md | 2 +- .../dashboard/cli-code/components/CodexToolCard.tsx | 3 ++- src/shared/validation/schemas/cli.ts | 1 + tests/unit/codex-settings-model-catalog.test.ts | 13 +++++++++++++ .../unit/ui/codex-tool-card-model-catalog.test.tsx | 10 +++++----- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/changelog.d/features/16910-codex-model-catalog-on-apply.md b/changelog.d/features/16910-codex-model-catalog-on-apply.md index 764a73ea8b7d..56da8f3f5cce 100644 --- a/changelog.d/features/16910-codex-model-catalog-on-apply.md +++ b/changelog.d/features/16910-codex-model-catalog-on-apply.md @@ -1 +1 @@ -- **feat(cli-tools):** applying Codex settings also writes an OmniRoute model catalog (`model_catalog_json`) so Codex's `/model` picker lists routable provider-prefixed models; fails open with a warning when Codex can't be read ([#16910](https://github.com/diegosouzapw/OmniRoute/pull/16910)) — thanks @kalfian +- **feat(cli-tools):** applying Codex settings can also write an OmniRoute model catalog (`model_catalog_json`, opt-in checkbox, off by default) so Codex's `/model` picker lists routable provider-prefixed models; fails open with a warning when Codex can't be read ([#16910](https://github.com/diegosouzapw/OmniRoute/pull/16910)) — thanks @kalfian diff --git a/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx b/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx index 29c4c4c79e39..e870bb84ef1b 100644 --- a/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx +++ b/src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx @@ -33,7 +33,8 @@ export default function CodexToolCard({ const [modelMappings, setModelMappings] = useState>({}); const [reasoningEffort, setReasoningEffort] = useState("xhigh"); const [wireApi, setWireApi] = useState("responses"); - const [writeModelCatalog, setWriteModelCatalog] = useState(true); + // Opt-in: the catalog file is ~60 KB per model (MBs with a large catalog) in ~/.codex. + const [writeModelCatalog, setWriteModelCatalog] = useState(false); const [modalOpen, setModalOpen] = useState(false); const [modalTarget, setModalTarget] = useState(null); // null = default model, string = mapping key const [modelAliases, setModelAliases] = useState({}); diff --git a/src/shared/validation/schemas/cli.ts b/src/shared/validation/schemas/cli.ts index ff605c154f66..a6a396c472a5 100644 --- a/src/shared/validation/schemas/cli.ts +++ b/src/shared/validation/schemas/cli.ts @@ -88,6 +88,7 @@ export const cliModelConfigSchema = z.object({ /** * Codex variant of `cliModelConfigSchema`. `modelCatalog: true` also writes an * OmniRoute `model_catalog_json` so Codex's `/model` picker lists routable ids. + * Opt-in: omitted or `false` never writes the catalog. */ export const codexSettingsConfigSchema = cliModelConfigSchema.extend({ modelCatalog: z.boolean().optional(), diff --git a/tests/unit/codex-settings-model-catalog.test.ts b/tests/unit/codex-settings-model-catalog.test.ts index 3a5f82f5bc1d..2bd761a85511 100644 --- a/tests/unit/codex-settings-model-catalog.test.ts +++ b/tests/unit/codex-settings-model-catalog.test.ts @@ -165,6 +165,19 @@ test("POST without modelCatalog leaves Codex's catalog untouched", async () => { assert.equal(await exists(SEEN_CODEX_HOME), false, "codex must not be spawned"); }); +test("POST with modelCatalog: false leaves Codex's catalog untouched", async () => { + await freshCodexDir(); + process.env.CLI_CODEX_BIN = FAKE_CODEX_OK; + + const response = await post({ modelCatalog: false }); + assert.equal(response.status, 200); + const body = await response.json(); + assert.equal(body.catalogPath, undefined); + assert.doesNotMatch(await fs.readFile(CONFIG_PATH, "utf8"), /model_catalog_json/); + assert.equal(await exists(CATALOG_PATH), false); + assert.equal(await exists(SEEN_CODEX_HOME), false, "codex must not be spawned"); +}); + test("POST fails open with catalogWarning when Codex cannot dump its catalog", async () => { await freshCodexDir(); process.env.CLI_CODEX_BIN = FAKE_CODEX_BROKEN; diff --git a/tests/unit/ui/codex-tool-card-model-catalog.test.tsx b/tests/unit/ui/codex-tool-card-model-catalog.test.tsx index ba2ac2bef594..02091d30f323 100644 --- a/tests/unit/ui/codex-tool-card-model-catalog.test.tsx +++ b/tests/unit/ui/codex-tool-card-model-catalog.test.tsx @@ -114,25 +114,25 @@ const renderCard = async (applyResponse: Record) => { }; describe("CodexToolCard model catalog", () => { - it("asks for the Codex model catalog by default and can opt out", async () => { + it("does not ask for the Codex model catalog by default and can opt in", async () => { const { container, posts } = await renderCard({ success: true }); - expect(catalogCheckbox(container)?.checked).toBe(true); + expect(catalogCheckbox(container)?.checked).toBe(false); await act(async () => { applyButton(container)!.click(); }); await waitFor(() => posts.length === 1); - expect(posts[0].modelCatalog).toBe(true); + expect(posts[0].modelCatalog).toBe(false); await act(async () => { catalogCheckbox(container)!.click(); }); - expect(catalogCheckbox(container)?.checked).toBe(false); + expect(catalogCheckbox(container)?.checked).toBe(true); await act(async () => { applyButton(container)!.click(); }); await waitFor(() => posts.length === 2); - expect(posts[1].modelCatalog).toBe(false); + expect(posts[1].modelCatalog).toBe(true); }); it("shows a warning, not an error, when Apply succeeds without the catalog", async () => {