mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
## 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
89 lines
2.8 KiB
JavaScript
89 lines
2.8 KiB
JavaScript
"use server";
|
|
|
|
import { NextResponse } from "next/server";
|
|
import { exec } from "child_process";
|
|
import { promisify } from "util";
|
|
import fs from "fs/promises";
|
|
import path from "path";
|
|
import os from "os";
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
// Mirror the executor's resolveDevinBin discovery so the dashboard's status
|
|
// matches what the runtime actually spawns.
|
|
const candidateDevinPaths = () => {
|
|
const home = os.homedir();
|
|
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 () => {
|
|
// 1. PATH lookup
|
|
try {
|
|
const isWindows = os.platform() === "win32";
|
|
const command = isWindows ? "where devin" : "which devin";
|
|
await execAsync(command, { windowsHide: true });
|
|
return { installed: true, source: "path" };
|
|
} catch {
|
|
// fall through to filesystem probes
|
|
}
|
|
// 2. Known installer paths
|
|
for (const candidate of candidateDevinPaths()) {
|
|
try {
|
|
await fs.access(candidate);
|
|
return { installed: true, source: candidate };
|
|
} catch { /* keep probing */ }
|
|
}
|
|
return { installed: false, source: null };
|
|
};
|
|
|
|
const readDevinVersion = async () => {
|
|
try {
|
|
const { stdout } = await execAsync("devin --version", { windowsHide: true });
|
|
return stdout.trim().split("\n")[0] || null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
// GET — install detection only. No config to write: the binary handles its own auth.
|
|
export async function GET() {
|
|
try {
|
|
const { installed, source } = await checkDevinInstalled();
|
|
if (!installed) {
|
|
return NextResponse.json({
|
|
installed: false,
|
|
message: "Devin CLI is not installed. Install it from https://cli.devin.ai and run `devin auth login`.",
|
|
installUrl: "https://cli.devin.ai",
|
|
});
|
|
}
|
|
const version = await readDevinVersion();
|
|
return NextResponse.json({
|
|
installed: true,
|
|
source,
|
|
version,
|
|
message: "Devin CLI detected. Make sure `devin auth login` has been run.",
|
|
});
|
|
} catch (error) {
|
|
console.log("Error checking devin settings:", error);
|
|
return NextResponse.json({ error: "Failed to check devin settings" }, { status: 500 });
|
|
}
|
|
}
|