import { execFile } from "node:child_process"; import fs from "node:fs/promises"; import path from "node:path"; import { promisify } from "node:util"; const execFileAsync = promisify(execFile); const HOME = process.env.HOME || "/home/ai"; const CC_SWITCH_DATABASE = path.join(HOME, ".cc-switch", "cc-switch.db"); const ANSI = /\u001b\[[0-?]*[ -/]*[@-~]/g; const SAFE_ID = /^[a-zA-Z0-9._-]{1,128}$/; export const CC_SWITCH_APPS = Object.freeze([ { id: "claude", toolId: "claude", name: "Claude Code" }, { id: "codex", toolId: "codex", name: "Codex" }, { id: "open-code", toolId: "opencode", name: "OpenCode" }, ]); export const MULTICA_TOOL_IDS = Object.freeze(["claude", "codex", "codebuddy", "kimi", "opencode"]); function clean(value) { return String(value || "").replace(ANSI, "").trim(); } function publicEndpoint(value) { const candidate = clean(value); if (!candidate || candidate === "N/A") return null; try { const url = new URL(candidate); return url.origin; } catch { return "configured"; } } export function parseCcProviderList(output) { const providers = []; let currentProviderId = null; for (const rawLine of clean(output).split(/\r?\n/)) { const line = rawLine.trim(); const current = line.match(/^→\s*Current:\s*(\S+)/); if (current) currentProviderId = current[1]; if (!line.startsWith("│") || !line.includes("┆")) continue; const columns = line.slice(1, line.endsWith("│") ? -1 : undefined).split("┆").map(clean); if (columns.length < 4 || columns[1] === "ID") continue; const [marker, id, name, endpoint] = columns; if (!SAFE_ID.test(id)) continue; providers.push({ id, name: name || id, endpoint: publicEndpoint(endpoint), active: marker.includes("✓"), }); } if (!currentProviderId) currentProviderId = providers.find((provider) => provider.active)?.id || null; return { currentProviderId, providers }; } export function parseMulticaConfig(output) { const values = {}; for (const line of clean(output).split(/\r?\n/)) { const match = line.match(/^([a-z_]+):\s*(.*)$/); if (match) values[match[1]] = match[2] === "(not set)" ? null : match[2]; } return { serverUrl: values.server_url || null, appUrl: values.app_url || null, workspaceId: values.workspace_id || null, configured: Boolean(values.server_url && values.workspace_id), }; } async function run(command, args, options = {}) { const { stdout, stderr } = await execFileAsync(command, args, { timeout: options.timeout || 10000, maxBuffer: options.maxBuffer || 1024 * 1024, env: { ...process.env, NO_COLOR: "1", TERM: "dumb" }, }); return clean(stdout || stderr); } async function versionOf(command, args = ["--version"]) { try { const output = await run(command, args, { timeout: 8000 }); return { installed: true, version: output.split(/\r?\n/)[0] || "installed" }; } catch (error) { if (error.code === "ENOENT") return { installed: false, version: null }; return { installed: true, version: "unknown", error: error.message }; } } async function ccSwitchInitialized() { try { await fs.access(CC_SWITCH_DATABASE); return true; } catch { return false; } } async function ccSwitchSnapshot() { const availability = await versionOf("cc-switch"); const initialized = availability.installed && await ccSwitchInitialized(); const apps = await Promise.all(CC_SWITCH_APPS.map(async (app) => { if (!initialized) return { ...app, currentProviderId: null, providers: [] }; try { return { ...app, ...parseCcProviderList(await run("cc-switch", ["--app", app.id, "provider", "list"])) }; } catch (error) { return { ...app, currentProviderId: null, providers: [], error: error.message }; } })); return { ...availability, initialized, apps, unsupportedToolIds: ["codebuddy", "kimi", "qwen", "dsh"], }; } async function multicaSnapshot() { const availability = await versionOf("multica", ["version"]); if (!availability.installed) { return { ...availability, daemon: { status: "unavailable" }, config: { configured: false }, supportedToolIds: MULTICA_TOOL_IDS }; } let daemon = { status: "unknown" }; let config = { configured: false, serverUrl: null, appUrl: null, workspaceId: null }; try { daemon = JSON.parse(await run("multica", ["daemon", "status", "--output", "json"])); } catch (error) { daemon = { status: "unknown", error: error.message }; } try { config = parseMulticaConfig(await run("multica", ["config", "show"])); } catch (error) { config = { ...config, error: error.message }; } return { ...availability, daemon, config, supportedToolIds: MULTICA_TOOL_IDS }; } let cachedSnapshot = null; let cachedAt = 0; export async function integrationSnapshot({ force = false } = {}) { if (!force && cachedSnapshot && Date.now() - cachedAt < 5000) return cachedSnapshot; const [ccSwitch, multica] = await Promise.all([ccSwitchSnapshot(), multicaSnapshot()]); cachedSnapshot = { ccSwitch, multica }; cachedAt = Date.now(); return cachedSnapshot; } function validateCcSwitchApp(appId) { if (!CC_SWITCH_APPS.some((app) => app.id === appId)) throw new Error("Unsupported CC Switch application"); } let mutationQueue = Promise.resolve(); function serializeMutation(operation) { const result = mutationQueue.then(operation, operation); mutationQueue = result.catch(() => {}); return result; } export async function switchCcProvider(appId, providerId) { validateCcSwitchApp(appId); if (!SAFE_ID.test(providerId || "")) throw new Error("Invalid provider ID"); return serializeMutation(async () => { const output = await run("cc-switch", ["--app", appId, "provider", "switch", providerId], { timeout: 30000 }); cachedSnapshot = null; return { ok: true, output: output.split(/\r?\n/).slice(0, 4).join("\n") }; }); } export async function importCcLiveConfig(appId) { validateCcSwitchApp(appId); return serializeMutation(async () => { const output = await run("cc-switch", ["--app", appId, "provider", "import-live"], { timeout: 30000 }); cachedSnapshot = null; return { ok: true, output: output.split(/\r?\n/).slice(0, 6).join("\n") }; }); }