"use server"; import { NextResponse } from "next/server"; import fs from "fs/promises"; import path from "path"; import os from "os"; import { exec } from "child_process"; import { promisify } from "util"; import { parseTOML, stringifyTOML } from "confbox"; const execAsync = promisify(exec); const getJcodeConfigDir = () => path.join(os.homedir(), ".jcode"); const getConfigPath = () => path.join(getJcodeConfigDir(), "config.toml"); const getProviderEnvPath = () => { const configDir = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"); return path.join(configDir, "jcode", "provider-9router.env"); }; const checkJcodeInstalled = async () => { try { const isWindows = os.platform() === "win32"; const command = isWindows ? "where jcode" : "which jcode"; await execAsync(command, { windowsHide: true }); return true; } catch { try { await fs.access(getJcodeConfigDir()); return true; } catch { return false; } } }; const readConfig = async () => { try { const configPath = getConfigPath(); const content = await fs.readFile(configPath, "utf-8"); return parseTOML(content); } catch (error) { return { providers: {} }; } }; const has9RouterConfig = (config) => { if (!config || !config.providers) return false; const providers = config.providers; if (providers["9router"]) return true; for (const [name, provider] of Object.entries(providers)) { if (provider.base_url && provider.base_url.includes("localhost:20128")) { return true; } } return false; }; const writeConfig = async (config) => { const configPath = getConfigPath(); const content = stringifyTOML(config); await fs.writeFile(configPath, content, "utf-8"); }; const readProviderEnv = async () => { try { const envPath = getProviderEnvPath(); const content = await fs.readFile(envPath, "utf-8"); const env = {}; for (const line of content.split("\n")) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith("#")) continue; const eqIndex = trimmed.indexOf("="); if (eqIndex > 0) { const key = trimmed.slice(0, eqIndex).trim(); let value = trimmed.slice(eqIndex + 1).trim(); if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { value = value.slice(1, -1); } env[key] = value; } } return env; } catch { return {}; } }; const writeProviderEnv = async (env) => { const envPath = getProviderEnvPath(); let content = "# jcode provider environment variables\n"; for (const [key, value] of Object.entries(env)) { content += `${key}="${value}"\n`; } await fs.writeFile(envPath, content, "utf-8"); }; export async function GET() { const isInstalled = await checkJcodeInstalled(); if (!isInstalled) { return NextResponse.json({ installed: false, message: "jcode not installed. Install via: curl -fsSL https://raw.githubusercontent.com/1jehuang/jcode/master/scripts/install.sh | bash", }); } const config = await readConfig(); const has9Router = has9RouterConfig(config); return NextResponse.json({ installed: true, config, has9Router, configPath: getConfigPath(), }); } export async function POST(request) { try { const { baseUrl, apiKey, models } = await request.json(); if (!baseUrl || !apiKey) { return NextResponse.json( { error: "baseUrl and apiKey are required" }, { status: 400 } ); } const normalizedBaseUrl = baseUrl.endsWith("/v1") ? baseUrl : `${baseUrl}/v1`; let config = await readConfig(); if (!config.providers) { config.providers = {}; } config.providers["9router"] = { type: "openai-compatible", base_url: normalizedBaseUrl, auth: "bearer", api_key_env: "JCODE_9ROUTER_API_KEY", env_file: "provider-9router.env", default_model: models && models.length > 0 ? models[0] : "cc/claude-opus-4-7", requires_api_key: true, }; const configDir = getJcodeConfigDir(); await fs.mkdir(configDir, { recursive: true }); await writeConfig(config); const xdgConfigDir = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"); const jcodeConfigDir = path.join(xdgConfigDir, "jcode"); await fs.mkdir(jcodeConfigDir, { recursive: true }); const env = await readProviderEnv(); env.JCODE_9ROUTER_API_KEY = apiKey; await writeProviderEnv(env); return NextResponse.json({ success: true, message: "jcode configured successfully. Use: jcode --provider-profile 9router", configPath: getConfigPath(), }); } catch (error) { console.error("Error configuring jcode:", error); return NextResponse.json( { error: error.message }, { status: 500 } ); } } export async function DELETE() { try { const config = await readConfig(); if (!config.providers) { return NextResponse.json({ success: true, message: "No configuration to remove" }); } delete config.providers["9router"]; await writeConfig(config); const env = await readProviderEnv(); delete env.JCODE_9ROUTER_API_KEY; await writeProviderEnv(env); return NextResponse.json({ success: true, message: "9router configuration removed from jcode", }); } catch (error) { console.error("Error removing jcode configuration:", error); return NextResponse.json( { error: error.message }, { status: 500 } ); } }