From 6fcd27337a7893642c7fe630840d0a641743f28f Mon Sep 17 00:00:00 2001 From: decolua Date: Thu, 30 Jul 2026 09:43:55 +0700 Subject: [PATCH] # v0.5.45 (2026-07-30) ## Features - **Providers**: add Poolside (OpenAI-compatible) - **Providers**: add api-airforce, baidu, bazaarlink, bluesminds, kilo-gateway, llm7, morph, sambanova, tencent - **OAuth**: zed / trae / windsurf providers + harden callback proxies - **CLI tools**: set Claude Code max context tokens - **Qoder**: PAT auth + refresh model list - **Gemini**: Gemini 3.6 Flash tier routing + Gemini 3.5 Flash Lite - **Claude**: bump default Opus to `claude-opus-5` - **Kiro**: add Claude Opus 5 models - **Usage**: Kimi and DeepSeek usage handlers - **Usage**: SuperGrok weekly pool via gRPC-web ## Fixes - **Refresh**: rotate `refresh_token` between retry attempts - **Kiro**: canonicalize tool history and route API keys correctly - **Kiro**: normalize dashboard thinking intensity models - **Cursor**: stop leaking agent tool errors as text - **Gemini**: fill empty tool schemas after `$ref` strip - **Antigravity**: strip `stream_options` from non-stream requests - **Jina-reader**: recover after transient errors, use JSON POST API - **Usage**: record exact embedding tokens - **Tunnel**: preserve successor cloudflared PID - **Console-log**: initialize capture at server boot + prevent SSE proxy buffering - **Dashboard**: count dual-auth, free-tier OAuth and API-key connections correctly - **Dashboard**: flex quota rows, thin global scrollbars, no hidden-row overflow ## Docs - **i18n**: expand pt-BR translation to 986 terms - README: Indonesian translation --- CHANGELOG.md | 32 ++++++++++++++++ cli/package.json | 2 +- open-sse/executors/devin-cli.js | 38 +++++++++++-------- open-sse/providers/capabilities.js | 14 +++++-- open-sse/providers/registry/devin-cli.js | 3 +- open-sse/providers/registry/index.js | 4 +- open-sse/providers/registry/poolside.js | 1 - package.json | 2 +- src/app/api/cli-tools/devin-settings/route.js | 28 +++++++++----- src/shared/components/Sidebar.js | 19 +++++++++- 10 files changed, 108 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dd3b81f..b970869d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +# v0.5.45 (2026-07-30) + +## Features +- **Providers**: add Poolside (OpenAI-compatible) +- **Providers**: add api-airforce, baidu, bazaarlink, bluesminds, kilo-gateway, llm7, morph, sambanova, tencent +- **OAuth**: zed / trae / windsurf providers + harden callback proxies +- **CLI tools**: set Claude Code max context tokens +- **Qoder**: PAT auth + refresh model list +- **Gemini**: Gemini 3.6 Flash tier routing + Gemini 3.5 Flash Lite +- **Claude**: bump default Opus to `claude-opus-5` +- **Kiro**: add Claude Opus 5 models +- **Usage**: Kimi and DeepSeek usage handlers +- **Usage**: SuperGrok weekly pool via gRPC-web + +## Fixes +- **Refresh**: rotate `refresh_token` between retry attempts +- **Kiro**: canonicalize tool history and route API keys correctly +- **Kiro**: normalize dashboard thinking intensity models +- **Cursor**: stop leaking agent tool errors as text +- **Gemini**: fill empty tool schemas after `$ref` strip +- **Antigravity**: strip `stream_options` from non-stream requests +- **Jina-reader**: recover after transient errors, use JSON POST API +- **Usage**: record exact embedding tokens +- **Tunnel**: preserve successor cloudflared PID +- **Console-log**: initialize capture at server boot + prevent SSE proxy buffering +- **Dashboard**: count dual-auth, free-tier OAuth and API-key connections correctly +- **Dashboard**: flex quota rows, thin global scrollbars, no hidden-row overflow + +## Docs +- **i18n**: expand pt-BR translation to 986 terms +- README: Indonesian translation + # v0.5.40 (2026-07-20) ## Features diff --git a/cli/package.json b/cli/package.json index cb24c4f0..efb786e4 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "9router", - "version": "0.5.40", + "version": "0.5.45", "description": "9Router CLI - Start and manage 9Router server", "bin": { "9router": "./cli.js" diff --git a/open-sse/executors/devin-cli.js b/open-sse/executors/devin-cli.js index dd452f9b..7cb35ff1 100644 --- a/open-sse/executors/devin-cli.js +++ b/open-sse/executors/devin-cli.js @@ -32,26 +32,34 @@ function resolveDevinBin() { const envBin = process.env.CLI_DEVIN_BIN?.trim(); if (envBin) return envBin; - // 2. Common name (PATH lookup handled by spawn shell option) const isWin = process.platform === "win32"; - - // 3. Windows installer default: %LOCALAPPDATA%\devin\cli\bin\devin.exe - if (isWin) { - const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"); - const winPath = path.join(localAppData, "devin", "cli", "bin", "devin.exe"); - if (fs.existsSync(winPath)) return winPath; - } - - // 4. Linux/macOS installer paths const home = os.homedir(); - for (const candidate of [ - path.join(home, ".local", "share", "devin", "bin", "devin"), - path.join(home, ".devin", "bin", "devin"), - ]) { + + // 2. Known installer / package-manager locations. spawn uses shell:false on + // macOS/Linux, so process.env.PATH alone may miss ~/.local/bin, Homebrew, + // Scoop, etc. when the server runs detached (tray/daemon/launchd) without + // a login shell — probe these explicitly before falling back to PATH. + const candidates = isWin + ? [ + // Official installer: %LOCALAPPDATA%\devin\cli\bin\devin.exe + path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "devin", "cli", "bin", "devin.exe"), + path.join(home, ".local", "bin", "devin.exe"), + path.join(home, "scoop", "shims", "devin.exe"), + path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "Programs", "devin", "devin.exe"), + ] + : [ + path.join(home, ".local", "share", "devin", "bin", "devin"), + path.join(home, ".devin", "bin", "devin"), + path.join(home, ".local", "bin", "devin"), // pipx / user install + "/opt/homebrew/bin/devin", // Homebrew (Apple Silicon) + "/usr/local/bin/devin", // Homebrew (Intel) / manual + "/usr/bin/devin", + ]; + for (const candidate of candidates) { if (fs.existsSync(candidate)) return candidate; } - // Fallback — rely on PATH + // 3. Fallback — rely on process.env.PATH return isWin ? "devin.exe" : "devin"; } diff --git a/open-sse/providers/capabilities.js b/open-sse/providers/capabilities.js index d999278d..526ceb9b 100644 --- a/open-sse/providers/capabilities.js +++ b/open-sse/providers/capabilities.js @@ -174,11 +174,10 @@ export const PROVIDER_CAPABILITIES = { "deepseek-v4-flash": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 1000000, maxOutput: 50000 }, "deepseek-v3-2-volc": { reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 96000, maxOutput: 32000 }, }, - // Poolside Laguna — OpenAI-compatible, all reasoning-capable (262K context, 32K max output). + // Poolside Laguna — OpenAI-compatible, all reasoning-capable (32K max output). "poolside": { - "laguna-s-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 262000, maxOutput: 32000 }, - "laguna-xs-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 262000, maxOutput: 32000 }, - "laguna-m.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 262000, maxOutput: 32000 }, + "laguna-s-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 1000000, maxOutput: 32000 }, + "laguna-xs-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 32000 }, }, }; @@ -302,6 +301,13 @@ export const PATTERN_CAPABILITIES = [ { pattern: "*pplx*", caps: { search: true, contextWindow: 128000 } }, { pattern: "*perplexity*", caps: { search: true, contextWindow: 128000 } }, + // ── Poolside Laguna (resellers: openrouter/nvidia/kilocode/vercel/...) ── + // Free tiers cap S 2.1 well below the paid 1M window → match the free suffix + // (":free" or "-free", depending on reseller) before the plain id. + { pattern: "*laguna-s-2.1*free*", caps: { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 32000 } }, + { pattern: "*laguna-s-2.1*", caps: { reasoning: true, thinkingFormat: "openai", contextWindow: 1000000, maxOutput: 32000 } }, + { pattern: "*laguna*", caps: { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 32000 } }, + // ── Others ─────────────────────────────────────────────────────── { pattern: "*hunyuan*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } }, { pattern: "hy3*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } }, diff --git a/open-sse/providers/registry/devin-cli.js b/open-sse/providers/registry/devin-cli.js index 3ff062fe..09391649 100644 --- a/open-sse/providers/registry/devin-cli.js +++ b/open-sse/providers/registry/devin-cli.js @@ -3,6 +3,7 @@ export default { alias: "dv", aliases: ["devin"], uiAlias: "dv", + hidden: true, display: { name: "Devin CLI", icon: "smart_toy", @@ -11,7 +12,7 @@ export default { website: "https://devin.ai", notice: { signupUrl: "https://cli.devin.ai", - text: "Install the Devin CLI and run `devin auth login` first. Spawns the `devin` binary via ACP/stdio — no API key field. Uses the default agent with built-in fs/shell tools (DEVIN_PERMISSION_MODE=bypass). Local use only. Set CLI_DEVIN_AGENT_TYPE=summarizer for a tool-less mode.", + text: "Install: `curl -fsSL https://cli.devin.ai/install.sh | bash` (macOS: `brew install --cask devin-cli`, Windows PowerShell: `irm https://static.devin.ai/cli/setup.ps1 | iex`). Then run `devin auth login`. No API key needed.", }, }, category: "free", diff --git a/open-sse/providers/registry/index.js b/open-sse/providers/registry/index.js index 8d29e2cb..7bfdd0d7 100644 --- a/open-sse/providers/registry/index.js +++ b/open-sse/providers/registry/index.js @@ -113,7 +113,7 @@ import p110 from "./llm7.js"; import p111 from "./sambanova.js"; import p112 from "./tencent.js"; import p113 from "./morph.js"; -import p114 from "./devin-cli.js"; +// import p114 from "./devin-cli.js"; // import p104 from "./windsurf.js"; import p115 from "./poolside.js"; @@ -230,7 +230,7 @@ export default [ p111, p112, p113, - p114, + // p114, // devin-cli — hidden, spawns local agent with shell/fs access // p104, // windsurf — hidden, no tool calling p115, ]; diff --git a/open-sse/providers/registry/poolside.js b/open-sse/providers/registry/poolside.js index 462bd3f4..02882ad9 100644 --- a/open-sse/providers/registry/poolside.js +++ b/open-sse/providers/registry/poolside.js @@ -26,6 +26,5 @@ export default { models: [ { id: "poolside/laguna-s-2.1", name: "Laguna S 2.1" }, { id: "poolside/laguna-xs-2.1", name: "Laguna XS 2.1" }, - { id: "poolside/laguna-m.1", name: "Laguna M.1" }, ], }; diff --git a/package.json b/package.json index dc89873d..fbedad57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "9router-app", - "version": "0.5.40", + "version": "0.5.45", "description": "9Router web dashboard", "private": true, "scripts": { diff --git a/src/app/api/cli-tools/devin-settings/route.js b/src/app/api/cli-tools/devin-settings/route.js index 1679ef92..240df0a8 100644 --- a/src/app/api/cli-tools/devin-settings/route.js +++ b/src/app/api/cli-tools/devin-settings/route.js @@ -13,15 +13,25 @@ const execAsync = promisify(exec); // matches what the runtime actually spawns. const candidateDevinPaths = () => { const home = os.homedir(); - const paths = [ - path.join(home, ".local", "share", "devin", "bin", "devin"), - path.join(home, ".devin", "bin", "devin"), - ]; - if (process.platform === "win32") { - const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"); - paths.push(path.join(localAppData, "devin", "cli", "bin", "devin.exe")); - } - return paths; + const isWin = os.platform() === "win32"; + const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"); + // Mirror resolveDevinBin in the executor — cover installer + common + // package-manager locations so detection matches runtime resolution. + return isWin + ? [ + path.join(localAppData, "devin", "cli", "bin", "devin.exe"), + path.join(home, ".local", "bin", "devin.exe"), + path.join(home, "scoop", "shims", "devin.exe"), + path.join(localAppData, "Programs", "devin", "devin.exe"), + ] + : [ + path.join(home, ".local", "share", "devin", "bin", "devin"), + path.join(home, ".devin", "bin", "devin"), + path.join(home, ".local", "bin", "devin"), + "/opt/homebrew/bin/devin", + "/usr/local/bin/devin", + "/usr/bin/devin", + ]; }; const checkDevinInstalled = async () => { diff --git a/src/shared/components/Sidebar.js b/src/shared/components/Sidebar.js index ef4fcba8..cded1824 100644 --- a/src/shared/components/Sidebar.js +++ b/src/shared/components/Sidebar.js @@ -302,9 +302,26 @@ export default function Sidebar({ onClose }) { computer - Remote + 9Remote + {/* 9English */} + + + translate + + 9English + + {/* Settings */}