mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
feat(cli-tools): add Devin CLI provider with ACP stdio executor
Wire Devin CLI as a routed provider that spawns the local `devin acp` binary. Add the DevinCliExecutor, register it in the executor map, expose its status through the cli-tools batch endpoint and devin-settings route, and document setup in cliTools constants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
de2da19a9e
commit
72ec06a81d
@@ -14,6 +14,7 @@ import { GET as kiloGet } from "../kilo-settings/route";
|
||||
import { GET as deepseekTuiGet } from "../deepseek-tui-settings/route";
|
||||
import { GET as jcodeGet } from "../jcode-settings/route";
|
||||
import { GET as grokBuildGet } from "../grok-build-settings/route";
|
||||
import { GET as devinGet } from "../devin-settings/route";
|
||||
|
||||
const STATUS_GETTERS = {
|
||||
claude: claudeGet,
|
||||
@@ -29,6 +30,7 @@ const STATUS_GETTERS = {
|
||||
"deepseek-tui": deepseekTuiGet,
|
||||
jcode: jcodeGet,
|
||||
"grok-build": grokBuildGet,
|
||||
devin: devinGet,
|
||||
};
|
||||
|
||||
// Batch endpoint: gather all CLI tool statuses in one round-trip
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
"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 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 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 });
|
||||
}
|
||||
}
|
||||
@@ -390,6 +390,32 @@ amp --model "{{model}}"
|
||||
},
|
||||
],
|
||||
},
|
||||
devin: {
|
||||
id: "devin",
|
||||
name: "Devin CLI",
|
||||
image: "/providers/devin-cli.png",
|
||||
color: "#6366F1",
|
||||
description: "Cognition Devin CLI — local binary called by the Devin CLI provider via ACP/stdio",
|
||||
configType: "guide",
|
||||
installUrl: "https://cli.devin.ai",
|
||||
notes: [
|
||||
{ type: "info", text: "This is a local dependency, not a routed CLI. The Devin CLI provider spawns `devin acp --agent-type summarizer` and relays its output." },
|
||||
{ type: "warning", text: "Install the Devin CLI and run `devin auth login` — without it, the provider returns a spawn error on first request." },
|
||||
],
|
||||
guideSteps: [
|
||||
{ step: 1, title: "Install Devin CLI", desc: "Install via the official installer at cli.devin.ai.", docsUrl: "https://cli.devin.ai" },
|
||||
{ step: 2, title: "Authenticate", desc: "Log in once so the binary stores its own credentials." },
|
||||
{ step: 3, title: "Use the provider", desc: "Pick any Devin CLI model under the Providers tab — no API key field needed." },
|
||||
],
|
||||
codeBlock: {
|
||||
language: "bash",
|
||||
code: `# Install Devin CLI (see https://cli.devin.ai for options)
|
||||
devin auth login
|
||||
|
||||
# Verify detection (optional)
|
||||
devin --version`,
|
||||
},
|
||||
},
|
||||
// HIDDEN: gemini-cli
|
||||
// "gemini-cli": {
|
||||
// id: "gemini-cli",
|
||||
|
||||
Reference in New Issue
Block a user