Spaces:
Runtime error
Runtime error
| "use server"; | |
| import { NextResponse } from "next/server"; | |
| import { requireCliToolsAuth } from "@/lib/api/requireCliToolsAuth"; | |
| import { getCliRuntimeStatus, CLI_TOOL_IDS } from "@/shared/services/cliRuntime"; | |
| import { getAllCliToolLastConfigured } from "@/lib/db/cliToolState"; | |
| import { checkToolConfigStatus } from "@/lib/cliTools/checkToolConfigStatus"; | |
| /** | |
| * GET /api/cli-tools/status | |
| * Returns runtime + config status for all CLI tools in one batch call. | |
| * Used by the CLI Tools page to show status badges in collapsed state. | |
| */ | |
| export async function GET(request: Request) { | |
| const authError = await requireCliToolsAuth(request); | |
| if (authError) return authError; | |
| try { | |
| const statuses = {}; | |
| // Run all runtime checks in parallel with individual timeouts | |
| const RUNTIME_CHECK_TIMEOUT = 5000; // 5s per tool max | |
| await Promise.all( | |
| CLI_TOOL_IDS.map(async (toolId) => { | |
| try { | |
| const runtime = (await Promise.race([ | |
| getCliRuntimeStatus(toolId), | |
| new Promise((_, reject) => | |
| setTimeout(() => reject(new Error("Timeout")), RUNTIME_CHECK_TIMEOUT) | |
| ), | |
| ])) as { | |
| installed: boolean; | |
| runnable: boolean; | |
| command?: string; | |
| commandPath?: string; | |
| reason?: string; | |
| }; | |
| statuses[toolId] = { | |
| installed: runtime.installed, | |
| runnable: runtime.runnable, | |
| command: runtime.command, | |
| commandPath: runtime.commandPath, | |
| reason: runtime.reason || null, | |
| }; | |
| } catch (error) { | |
| statuses[toolId] = { | |
| installed: false, | |
| runnable: false, | |
| reason: error.message || "Check failed", | |
| }; | |
| } | |
| }) | |
| ); | |
| // Check config status for installed+runnable tools via direct file reads | |
| const settingsTools = [ | |
| "claude", | |
| "codex", | |
| "droid", | |
| "openclaw", | |
| "cline", | |
| "kilo", | |
| "qwen", | |
| "hermes", | |
| ]; | |
| await Promise.all( | |
| settingsTools.map(async (toolId) => { | |
| if (!statuses[toolId]) { | |
| return; | |
| } | |
| if (!statuses[toolId].installed || !statuses[toolId].runnable) { | |
| statuses[toolId].configStatus = "not_installed"; | |
| return; | |
| } | |
| statuses[toolId].configStatus = await checkToolConfigStatus(toolId); | |
| }) | |
| ); | |
| // Merge last-configured timestamps from SQLite | |
| try { | |
| const lastConfigured = getAllCliToolLastConfigured(); | |
| for (const [toolId, timestamp] of Object.entries(lastConfigured)) { | |
| if (statuses[toolId]) { | |
| statuses[toolId].lastConfiguredAt = timestamp; | |
| } | |
| } | |
| } catch { | |
| /* non-critical */ | |
| } | |
| return NextResponse.json(statuses); | |
| } catch (error) { | |
| console.log("Error fetching CLI tool statuses:", error); | |
| return NextResponse.json({ error: "Failed to fetch statuses" }, { status: 500 }); | |
| } | |
| } | |