mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
# 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
This commit is contained in:
@@ -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)
|
# v0.5.40 (2026-07-20)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "9router",
|
"name": "9router",
|
||||||
"version": "0.5.40",
|
"version": "0.5.45",
|
||||||
"description": "9Router CLI - Start and manage 9Router server",
|
"description": "9Router CLI - Start and manage 9Router server",
|
||||||
"bin": {
|
"bin": {
|
||||||
"9router": "./cli.js"
|
"9router": "./cli.js"
|
||||||
|
|||||||
@@ -32,26 +32,34 @@ function resolveDevinBin() {
|
|||||||
const envBin = process.env.CLI_DEVIN_BIN?.trim();
|
const envBin = process.env.CLI_DEVIN_BIN?.trim();
|
||||||
if (envBin) return envBin;
|
if (envBin) return envBin;
|
||||||
|
|
||||||
// 2. Common name (PATH lookup handled by spawn shell option)
|
|
||||||
const isWin = process.platform === "win32";
|
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();
|
const home = os.homedir();
|
||||||
for (const candidate of [
|
|
||||||
path.join(home, ".local", "share", "devin", "bin", "devin"),
|
// 2. Known installer / package-manager locations. spawn uses shell:false on
|
||||||
path.join(home, ".devin", "bin", "devin"),
|
// 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;
|
if (fs.existsSync(candidate)) return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback — rely on PATH
|
// 3. Fallback — rely on process.env.PATH
|
||||||
return isWin ? "devin.exe" : "devin";
|
return isWin ? "devin.exe" : "devin";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,11 +174,10 @@ export const PROVIDER_CAPABILITIES = {
|
|||||||
"deepseek-v4-flash": { vision: true, reasoning: true, thinkingFormat: "openai", thinkingCanDisable: false, contextWindow: 1000000, maxOutput: 50000 },
|
"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 },
|
"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": {
|
"poolside": {
|
||||||
"laguna-s-2.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: 262000, maxOutput: 32000 },
|
"laguna-xs-2.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 200000, maxOutput: 32000 },
|
||||||
"laguna-m.1": { reasoning: true, thinkingFormat: "openai", contextWindow: 262000, maxOutput: 32000 },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,6 +301,13 @@ export const PATTERN_CAPABILITIES = [
|
|||||||
{ pattern: "*pplx*", caps: { search: true, contextWindow: 128000 } },
|
{ pattern: "*pplx*", caps: { search: true, contextWindow: 128000 } },
|
||||||
{ pattern: "*perplexity*", 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 ───────────────────────────────────────────────────────
|
// ── Others ───────────────────────────────────────────────────────
|
||||||
{ pattern: "*hunyuan*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } },
|
{ pattern: "*hunyuan*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } },
|
||||||
{ pattern: "hy3*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } },
|
{ pattern: "hy3*", caps: { reasoning: true, thinkingFormat: "hunyuan", contextWindow: 262144, maxOutput: 262144 } },
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export default {
|
|||||||
alias: "dv",
|
alias: "dv",
|
||||||
aliases: ["devin"],
|
aliases: ["devin"],
|
||||||
uiAlias: "dv",
|
uiAlias: "dv",
|
||||||
|
hidden: true,
|
||||||
display: {
|
display: {
|
||||||
name: "Devin CLI",
|
name: "Devin CLI",
|
||||||
icon: "smart_toy",
|
icon: "smart_toy",
|
||||||
@@ -11,7 +12,7 @@ export default {
|
|||||||
website: "https://devin.ai",
|
website: "https://devin.ai",
|
||||||
notice: {
|
notice: {
|
||||||
signupUrl: "https://cli.devin.ai",
|
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",
|
category: "free",
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ import p110 from "./llm7.js";
|
|||||||
import p111 from "./sambanova.js";
|
import p111 from "./sambanova.js";
|
||||||
import p112 from "./tencent.js";
|
import p112 from "./tencent.js";
|
||||||
import p113 from "./morph.js";
|
import p113 from "./morph.js";
|
||||||
import p114 from "./devin-cli.js";
|
// import p114 from "./devin-cli.js";
|
||||||
// import p104 from "./windsurf.js";
|
// import p104 from "./windsurf.js";
|
||||||
import p115 from "./poolside.js";
|
import p115 from "./poolside.js";
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ export default [
|
|||||||
p111,
|
p111,
|
||||||
p112,
|
p112,
|
||||||
p113,
|
p113,
|
||||||
p114,
|
// p114, // devin-cli — hidden, spawns local agent with shell/fs access
|
||||||
// p104, // windsurf — hidden, no tool calling
|
// p104, // windsurf — hidden, no tool calling
|
||||||
p115,
|
p115,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -26,6 +26,5 @@ export default {
|
|||||||
models: [
|
models: [
|
||||||
{ id: "poolside/laguna-s-2.1", name: "Laguna S 2.1" },
|
{ 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-xs-2.1", name: "Laguna XS 2.1" },
|
||||||
{ id: "poolside/laguna-m.1", name: "Laguna M.1" },
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "9router-app",
|
"name": "9router-app",
|
||||||
"version": "0.5.40",
|
"version": "0.5.45",
|
||||||
"description": "9Router web dashboard",
|
"description": "9Router web dashboard",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -13,15 +13,25 @@ const execAsync = promisify(exec);
|
|||||||
// matches what the runtime actually spawns.
|
// matches what the runtime actually spawns.
|
||||||
const candidateDevinPaths = () => {
|
const candidateDevinPaths = () => {
|
||||||
const home = os.homedir();
|
const home = os.homedir();
|
||||||
const paths = [
|
const isWin = os.platform() === "win32";
|
||||||
path.join(home, ".local", "share", "devin", "bin", "devin"),
|
const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
||||||
path.join(home, ".devin", "bin", "devin"),
|
// Mirror resolveDevinBin in the executor — cover installer + common
|
||||||
];
|
// package-manager locations so detection matches runtime resolution.
|
||||||
if (process.platform === "win32") {
|
return isWin
|
||||||
const localAppData = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
|
? [
|
||||||
paths.push(path.join(localAppData, "devin", "cli", "bin", "devin.exe"));
|
path.join(localAppData, "devin", "cli", "bin", "devin.exe"),
|
||||||
}
|
path.join(home, ".local", "bin", "devin.exe"),
|
||||||
return paths;
|
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 () => {
|
const checkDevinInstalled = async () => {
|
||||||
|
|||||||
@@ -302,9 +302,26 @@ export default function Sidebar({ onClose }) {
|
|||||||
<span className="material-symbols-outlined text-[18px] group-hover:text-primary transition-colors">
|
<span className="material-symbols-outlined text-[18px] group-hover:text-primary transition-colors">
|
||||||
computer
|
computer
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[13px] font-medium">Remote</span>
|
<span className="text-[13px] font-medium">9Remote</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{/* 9English */}
|
||||||
|
<a
|
||||||
|
href="https://9english.net/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
onClick={onClose}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-3 px-3 py-1 rounded-lg transition-all group w-full",
|
||||||
|
"text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="material-symbols-outlined text-[18px] group-hover:text-primary transition-colors">
|
||||||
|
translate
|
||||||
|
</span>
|
||||||
|
<span className="text-[13px] font-medium">9English</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/profile"
|
href="/dashboard/profile"
|
||||||
|
|||||||
Reference in New Issue
Block a user