Files
9router/src/app/api/cli-tools/devin-settings/route.js
T
decolua 6fcd27337a # 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
2026-07-30 09:43:55 +07:00

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 });
}
}