"use server"; import { NextResponse } from "next/server"; import fs from "fs/promises"; import { getCliRuntimeStatus, CLI_TOOL_IDS, getCliPrimaryConfigPath, } from "@/shared/services/cliRuntime"; import { getAllCliToolLastConfigured } from "@/lib/db/cliToolState"; // Check if a tool has OmniRoute configured by reading its config file directly // This replaces the expensive self-referential HTTP calls to /api/cli-tools/*-settings async function checkToolConfigStatus(toolId: string): Promise { try { const configPath = getCliPrimaryConfigPath(toolId); if (!configPath) return "unknown"; const content = await fs.readFile(configPath, "utf-8"); const config = JSON.parse(content); // Each tool stores OmniRoute config differently switch (toolId) { case "claude": return config?.env?.ANTHROPIC_BASE_URL ? "configured" : "not_configured"; case "codex": return config?.providers?.omniroute || config?.providers?.["openai-compatible"] ? "configured" : "not_configured"; case "droid": case "openclaw": case "cline": case "kilo": // Generic check: look for any OmniRoute-related URL in the config const configStr = JSON.stringify(config).toLowerCase(); return configStr.includes("omniroute") || configStr.includes("localhost") ? "configured" : "not_configured"; default: return "unknown"; } } catch { return "not_configured"; } } /** * 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() { try { const statuses = {}; // Run all runtime checks in parallel await Promise.all( CLI_TOOL_IDS.map(async (toolId) => { try { const runtime = await getCliRuntimeStatus(toolId); 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 config status for installed+runnable tools via direct file reads const settingsTools = ["claude", "codex", "droid", "openclaw", "cline", "kilo"]; await Promise.all( settingsTools.map(async (toolId) => { 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 }); } }