Compare commits

...

5 Commits

39 changed files with 97 additions and 56 deletions

View File

@@ -4,6 +4,15 @@
---
## [3.3.7] - 2026-03-30
### 🐛 Bug Fixes
- **OpenCode Config:** Restructured generated `opencode.json` to use the `@ai-sdk/openai-compatible` record-based schema with `options` and `models` as object maps instead of flat arrays, fixing config validation failures (#816)
- **i18n Missing Keys:** Added missing `cloudflaredUrlNotice` translation key across all 30 language files to prevent `MISSING_MESSAGE` console errors in the Endpoint page (#823)
---
## [3.3.6] - 2026-03-30
### 🐛 Bug Fixes

View File

@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: OmniRoute API
version: 3.3.6
version: 3.3.7
description: |
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
endpoint that routes requests to multiple AI providers with load balancing,

View File

@@ -1,6 +1,6 @@
{
"name": "omniroute-desktop",
"version": "3.3.6",
"version": "3.3.7",
"description": "OmniRoute Desktop Application",
"main": "main.js",
"author": {

View File

@@ -1,6 +1,6 @@
{
"name": "@omniroute/open-sse",
"version": "3.3.6",
"version": "3.3.7",
"description": "Express SSE sidecar for OmniRoute — handles streaming, protocol translation, and provider orchestration",
"type": "module",
"main": "index.js",

6
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "omniroute",
"version": "3.3.6",
"version": "3.3.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute",
"version": "3.3.6",
"version": "3.3.7",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [
@@ -20324,7 +20324,7 @@
},
"open-sse": {
"name": "@omniroute/open-sse",
"version": "3.3.6"
"version": "3.3.7"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "omniroute",
"version": "3.3.6",
"version": "3.3.7",
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
"type": "module",
"bin": {

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "جارٍ تحميل لوحة تحكم MCP...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Зареждане на таблото за управление на MCP...",

View File

@@ -1062,7 +1062,8 @@
"a2aQuickStartStep2": "Odešlete požadavky JSON-RPC na `POST /a2a` pomocí `message/send` nebo `message/stream`.",
"a2aQuickStartStep3": "Sledujte a ovládejte úkoly pomocí příkazů `tasks/get` a `tasks/cancel`.",
"completionsLegacy": "Completions (Zastaralé)",
"completionsLegacyDesc": "Zastaralé OpenAI text completion akceptuje oba formáty, prompt string i messages array."
"completionsLegacyDesc": "Zastaralé OpenAI text completion akceptuje oba formáty, prompt string i messages array.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"endpoints": {
"tabProxy": "Koncová Proxy",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Indlæser MCP-dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "MCP-Dashboard wird geladen...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Crea un Quick Tunnel temporal de Cloudflare. La URL cambia después de cada reinicio."
},
"mcpDashboard": {
"loading": "Cargando el panel de MCP...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Ladataan MCP-hallintapaneelia...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Chargement du tableau de bord MCP...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -919,7 +919,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1062,7 +1062,8 @@
"a2aQuickStartStep2": "`message/send` या `message/stream` का उपयोग करके JSON-RPC अनुरोधों को `POST /a2a` पर भेजें।",
"a2aQuickStartStep3": "`कार्य/प्राप्त करें` और `कार्य/रद्द करें` का उपयोग करके कार्यों को ट्रैक और नियंत्रित करें।",
"completionsLegacy": "पूर्णताएँ (विरासत)",
"completionsLegacyDesc": "लीगेसी ओपनएआई टेक्स्ट पूर्णताएँ - शीघ्र स्ट्रिंग और संदेश सरणी प्रारूप दोनों को स्वीकार करती हैं"
"completionsLegacyDesc": "लीगेसी ओपनएआई टेक्स्ट पूर्णताएँ - शीघ्र स्ट्रिंग और संदेश सरणी प्रारूप दोनों को स्वीकार करती हैं",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"endpoints": {
"tabProxy": "समापन बिंदु प्रॉक्सी",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Laster inn MCP-dashbordet ...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Nilo-load ang MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1023,7 +1023,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Cria um Quick Tunnel temporário do Cloudflare. A URL muda a cada reinício."
},
"mcpDashboard": {
"loading": "Carregando painel MCP...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"endpoints": {
"tabProxy": "Endpoint Proxy",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1060,7 +1060,8 @@
"a2aQuickStartStep2": "JSON-RPC isteklerini `message/send` veya `message/stream` kullanarak `POST /a2a` adresine gönderin.",
"a2aQuickStartStep3": "Görevleri `tasks/get` ve `tasks/cancel` ile izleyin ve yönetin.",
"completionsLegacy": "Tamamlamalar (Eski)",
"completionsLegacyDesc": "Eski OpenAI metin tamamlamaları — hem bilgi istemi dizesini hem de mesaj dizisi biçimini kabul eder"
"completionsLegacyDesc": "Eski OpenAI metin tamamlamaları — hem bilgi istemi dizesini hem de mesaj dizisi biçimini kabul eder",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"endpoints": {
"tabProxy": "Uç Nokta Proxy",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -1011,7 +1011,8 @@
"webSearch": "Web Search",
"webSearchDesc": "Unified web search across multiple providers with automatic failover and caching",
"searchProvider": "Search Provider",
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."
"searchProviderDesc": "This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected.",
"cloudflaredUrlNotice": "Creates a temporary Cloudflare Quick Tunnel. The URL changes after every restart."
},
"mcpDashboard": {
"loading": "Loading MCP dashboard...",

View File

@@ -4,14 +4,6 @@ type OpenCodeConfigInput = {
model?: string;
};
type OpenCodeProviderConfig = {
name: string;
api: "openai";
baseURL: string;
apiKey: string;
models: string[];
};
const OPENCODE_DEFAULT_MODELS = [
"claude-opus-4-5-thinking",
"claude-sonnet-4-5-thinking",
@@ -28,7 +20,7 @@ export const buildOpenCodeProviderConfig = ({
baseUrl,
apiKey,
model,
}: OpenCodeConfigInput): OpenCodeProviderConfig => {
}: OpenCodeConfigInput): Record<string, any> => {
const normalizedBaseUrl = String(baseUrl || "")
.trim()
.replace(/\/+$/, "");
@@ -36,12 +28,21 @@ export const buildOpenCodeProviderConfig = ({
const uniqueModels = [...new Set([normalizedModel, ...OPENCODE_DEFAULT_MODELS].filter(Boolean))];
const modelsRecord: Record<string, { name: string }> = {};
for (const m of uniqueModels) {
if (m) {
modelsRecord[m] = { name: m };
}
}
return {
npm: "@ai-sdk/openai-compatible",
name: "OmniRoute",
api: "openai",
baseURL: normalizedBaseUrl,
apiKey: apiKey || "sk_omniroute",
models: uniqueModels,
options: {
baseURL: normalizedBaseUrl,
apiKey: apiKey || "sk_omniroute",
},
models: modelsRecord,
};
};

View File

@@ -49,9 +49,9 @@ test("T40: OpenCode config generator includes endpoint and selected API key", ()
apiKey: "sk_test_opencode",
model: "claude-sonnet-4-5-thinking",
});
assert.equal(providerConfig.baseURL, "http://localhost:20128/v1");
assert.equal(providerConfig.apiKey, "sk_test_opencode");
assert.ok(providerConfig.models.includes("claude-sonnet-4-5-thinking"));
assert.equal(providerConfig.options.baseURL, "http://localhost:20128/v1");
assert.equal(providerConfig.options.apiKey, "sk_test_opencode");
assert.ok(providerConfig.models["claude-sonnet-4-5-thinking"]);
const mergedConfig = mergeOpenCodeConfig(
{ provider: { custom: { name: "Custom Provider" } } },
@@ -62,8 +62,8 @@ test("T40: OpenCode config generator includes endpoint and selected API key", ()
}
);
assert.ok(mergedConfig.provider.custom);
assert.equal(mergedConfig.provider.omniroute.baseURL, "http://localhost:20128/v1");
assert.equal(mergedConfig.provider.omniroute.apiKey, "sk_test_opencode");
assert.equal(mergedConfig.provider.omniroute.options.baseURL, "http://localhost:20128/v1");
assert.equal(mergedConfig.provider.omniroute.options.apiKey, "sk_test_opencode");
});
test("T40: Windsurf card documents current official limitations honestly", () => {