From 8b9abcb6ccbfe8ee2e6abf60c66d1af6aab518b9 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sun, 22 Mar 2026 13:31:56 -0300 Subject: [PATCH] fix(3.0.0-rc.2): resolve issues #536, #535, #524 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(providers): LongCat AI key validation — correct base URL and auth header (#536) - baseUrl: longcat.chat/api/v1/chat/completions -> api.longcat.chat/openai - authHeader: 'bearer' -> 'Authorization' + authPrefix: 'Bearer' fix(combo): implement pinnedModel override in comboAgentMiddleware (#535) - Previously: pinnedModel was detected but body.model was never updated - Now: body = { ...body, model: pinnedModel } when context_cache_protection fires fix(cli-tools): add OpenCode config save to guide-settings endpoint (#524) - Added 'opencode' case to switch in guide-settings/[toolId]/route.ts - saveOpenCodeConfig(): XDG_CONFIG_HOME aware, writes [provider.omniroute] TOML block --- CHANGELOG.md | 10 ++++ docs/openapi.yaml | 2 +- open-sse/config/providerRegistry.ts | 8 ++- open-sse/services/comboAgentMiddleware.ts | 6 +- package-lock.json | 4 +- package.json | 2 +- .../guide-settings/[toolId]/route.ts | 57 +++++++++++++++++++ 7 files changed, 82 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 945daaa8b2..ea19bb9d32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ --- +## [3.0.0-rc.2] - 2026-03-22 + +### 🔧 Bug Fixes + +- **#536** — LongCat AI key validation: fixed baseUrl (`api.longcat.chat/openai`) and authHeader (`Authorization: Bearer`) +- **#535** — Pinned model override: `body.model` is now set to `pinnedModel` when context-cache protection detects a pinned model +- **#524** — OpenCode config now saved correctly: added `saveOpenCodeConfig()` handler (XDG_CONFIG_HOME aware, writes TOML) + +--- + ## [3.0.0-rc.1] - 2026-03-22 ### 🔧 Bug Fixes diff --git a/docs/openapi.yaml b/docs/openapi.yaml index bb0f63fd0b..b9a883140a 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: OmniRoute API - version: 3.0.0-rc.1 + version: 3.0.0-rc.2 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, diff --git a/open-sse/config/providerRegistry.ts b/open-sse/config/providerRegistry.ts index 1df5ac72c0..60a32a5b86 100644 --- a/open-sse/config/providerRegistry.ts +++ b/open-sse/config/providerRegistry.ts @@ -1205,9 +1205,13 @@ export const REGISTRY: Record = { alias: "lc", format: "openai", executor: "default", - baseUrl: "https://longcat.chat/api/v1/chat/completions", + // (#536) Correct OpenAI-compatible base URL — was longcat.chat/api/v1/chat/completions + // which is the chat endpoint directly, not the base. Key validation and routing must + // use https://api.longcat.chat/openai which resolves /v1/models and /v1/chat/completions + baseUrl: "https://api.longcat.chat/openai", authType: "apikey", - authHeader: "bearer", + authHeader: "Authorization", + authPrefix: "Bearer", // Free tier: 50M tokens/day (Flash-Lite) + 500K/day (Chat/Thinking) — 100% free while public beta models: [ { id: "LongCat-Flash-Lite", name: "LongCat Flash-Lite (50M tok/day 🆓)" }, diff --git a/open-sse/services/comboAgentMiddleware.ts b/open-sse/services/comboAgentMiddleware.ts index 1e97e9e096..aa06211c8f 100644 --- a/open-sse/services/comboAgentMiddleware.ts +++ b/open-sse/services/comboAgentMiddleware.ts @@ -169,7 +169,11 @@ export function applyComboAgentMiddleware( if (comboConfig.context_cache_protection) { pinnedModel = extractPinnedModel(messages); if (pinnedModel) { - // Model is pinned — caller should override model selection + // (#535) Model is pinned via tag — override body.model so the combo + // router uses exactly this model instead of picking a different one. Without this, + // the extracted pinnedModel is returned but body.model is unchanged, breaking + // context cache sessions by sending subsequent turns to a different model. + body = { ...body, model: pinnedModel }; } } diff --git a/package-lock.json b/package-lock.json index 1d79ed4e01..d59ab40e11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute", - "version": "3.0.0-rc.1", + "version": "3.0.0-rc.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute", - "version": "3.0.0-rc.1", + "version": "3.0.0-rc.2", "hasInstallScript": true, "license": "MIT", "workspaces": [ diff --git a/package.json b/package.json index 63f58253f5..85a737f0ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "omniroute", - "version": "3.0.0-rc.1", + "version": "3.0.0-rc.2", "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": { diff --git a/src/app/api/cli-tools/guide-settings/[toolId]/route.ts b/src/app/api/cli-tools/guide-settings/[toolId]/route.ts index 9927a960eb..2f0fd1fc15 100644 --- a/src/app/api/cli-tools/guide-settings/[toolId]/route.ts +++ b/src/app/api/cli-tools/guide-settings/[toolId]/route.ts @@ -39,6 +39,10 @@ export async function POST(request, { params }) { switch (toolId) { case "continue": return await saveContinueConfig({ baseUrl, apiKey, model }); + case "opencode": + // (#524) OpenCode config was never saved because only 'continue' was handled here. + // opencode reads ~/.config/opencode/config.toml — write the OmniRoute settings there. + return await saveOpenCodeConfig({ baseUrl, apiKey, model }); default: return NextResponse.json( { error: `Direct config save not supported for: ${toolId}` }, @@ -125,3 +129,56 @@ async function saveContinueConfig({ baseUrl, apiKey, model }) { configPath, }); } + +/** + * Save OpenCode config to ~/.config/opencode/config.toml (XDG_CONFIG_HOME aware). + * (#524) OpenCode was silently failing because this handler was missing. + */ +async function saveOpenCodeConfig({ baseUrl, apiKey, model }) { + const { apiPort } = getRuntimePorts(); + // Honour $XDG_CONFIG_HOME if set, otherwise use ~/.config per the XDG Base Directory spec + const xdgConfigHome = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"); + const configPath = path.join(xdgConfigHome, "opencode", "config.toml"); + const configDir = path.dirname(configPath); + + // Ensure ~/.config/opencode/ exists + await fs.mkdir(configDir, { recursive: true }); + + const normalizedBaseUrl = String(baseUrl || "") + .trim() + .replace(/\/+$/, ""); + + // Read existing TOML to preserve any user settings outside our block + let existingContent = ""; + try { + existingContent = await fs.readFile(configPath, "utf-8"); + } catch { + // File doesn't exist yet — start fresh + } + + // Build the OmniRoute TOML block. + // opencode config.toml uses the [provider.X] table format. + void apiPort; // available for future port-based detection + const omniBlock = ` +# OmniRoute managed — updated automatically by OmniRoute CLI Tools +[provider.omniroute] +api_key = "${apiKey || "sk_omniroute"}" +base_url = "${normalizedBaseUrl}" +model = "${model}" +`; + + // Remove old OmniRoute-managed block (if any) then append fresh one + const cleanedContent = existingContent + .replace(/\n?# OmniRoute managed[\s\S]*?(?=\n\[|$)/, "") + .trimEnd(); + + const newContent = (cleanedContent ? cleanedContent + "\n" : "") + omniBlock; + + await fs.writeFile(configPath, newContent, "utf-8"); + + return NextResponse.json({ + success: true, + message: `OpenCode config saved to ${configPath}`, + configPath, + }); +}