| "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); |
|
|
| |
| |
| |
| const resolveAgentModel = (m) => { |
| if (typeof m === "string") return m; |
| if (m && typeof m === "object") return m.primary ?? ""; |
| return ""; |
| }; |
|
|
| const getOpenClawDir = () => path.join(os.homedir(), ".openclaw"); |
| const getOpenClawSettingsPath = () => path.join(getOpenClawDir(), "openclaw.json"); |
|
|
| |
| const checkOpenClawInstalled = async () => { |
| try { |
| const isWindows = os.platform() === "win32"; |
| const command = isWindows ? "where openclaw" : "which openclaw"; |
| |
| const env = isWindows |
| ? { ...process.env, PATH: `${process.env.APPDATA}\\npm;${process.env.PATH}` } |
| : process.env; |
| await execAsync(command, { windowsHide: true, env }); |
| return true; |
| } catch { |
| try { |
| await fs.access(getOpenClawSettingsPath()); |
| return true; |
| } catch { |
| return false; |
| } |
| } |
| }; |
|
|
| |
| const readSettings = async () => { |
| try { |
| const settingsPath = getOpenClawSettingsPath(); |
| const content = await fs.readFile(settingsPath, "utf-8"); |
| return JSON.parse(content); |
| } catch (error) { |
| if (error.code === "ENOENT") return null; |
| throw error; |
| } |
| }; |
|
|
| |
| const has9RouterConfig = (settings) => { |
| if (!settings || !settings.models || !settings.models.providers) return false; |
| return !!settings.models.providers["9router"]; |
| }; |
|
|
| |
| const readAgentModel = async (agentDir) => { |
| try { |
| const modelsPath = path.join(agentDir, "models.json"); |
| const content = await fs.readFile(modelsPath, "utf-8"); |
| const data = JSON.parse(content); |
| const models = data?.providers?.["9router"]?.models; |
| return models?.[0]?.id || null; |
| } catch { |
| return null; |
| } |
| }; |
|
|
| |
| export async function GET() { |
| try { |
| const isInstalled = await checkOpenClawInstalled(); |
| |
| if (!isInstalled) { |
| return NextResponse.json({ |
| installed: false, |
| settings: null, |
| message: "Open Claw CLI is not installed", |
| }); |
| } |
|
|
| const settings = await readSettings(); |
|
|
| |
| |
| |
| const agentList = settings?.agents?.list || []; |
| const enrichedAgents = await Promise.all( |
| agentList.map(async (agent) => { |
| const agentModel = agent.agentDir ? await readAgentModel(agent.agentDir) : null; |
| return { ...agent, model: resolveAgentModel(agent.model), currentModel: agentModel }; |
| }) |
| ); |
|
|
| return NextResponse.json({ |
| installed: true, |
| settings, |
| agents: enrichedAgents, |
| has9Router: has9RouterConfig(settings), |
| settingsPath: getOpenClawSettingsPath(), |
| }); |
| } catch (error) { |
| console.log("Error checking openclaw settings:", error); |
| return NextResponse.json({ error: "Failed to check openclaw settings" }, { status: 500 }); |
| } |
| } |
|
|
| |
| const writeAgentModels = async (agentDir, model, baseUrl, apiKey) => { |
| await fs.mkdir(agentDir, { recursive: true }); |
| const modelsPath = path.join(agentDir, "models.json"); |
| let existing = {}; |
| try { |
| const content = await fs.readFile(modelsPath, "utf-8"); |
| existing = JSON.parse(content); |
| } catch { } |
|
|
| if (!existing.providers) existing.providers = {}; |
| existing.providers["9router"] = { |
| baseUrl, |
| apiKey: apiKey || "your_api_key", |
| api: "openai-completions", |
| models: [{ id: model, name: model.split("/").pop() || model }], |
| }; |
| await fs.writeFile(modelsPath, JSON.stringify(existing, null, 2)); |
| }; |
|
|
| |
| export async function POST(request) { |
| try { |
| |
| const { baseUrl, apiKey, model, agentModels = {} } = await request.json(); |
| |
| if (!baseUrl || !model) { |
| return NextResponse.json({ error: "baseUrl and model are required" }, { status: 400 }); |
| } |
|
|
| const openclawDir = getOpenClawDir(); |
| const settingsPath = getOpenClawSettingsPath(); |
|
|
| await fs.mkdir(openclawDir, { recursive: true }); |
|
|
| let settings = {}; |
| try { |
| const existingSettings = await fs.readFile(settingsPath, "utf-8"); |
| settings = JSON.parse(existingSettings); |
| } catch { } |
|
|
| if (!settings.agents) settings.agents = {}; |
| if (!settings.agents.defaults) settings.agents.defaults = {}; |
| if (!settings.agents.defaults.model) settings.agents.defaults.model = {}; |
| if (!settings.agents.defaults.models) settings.agents.defaults.models = {}; |
| if (!settings.models) settings.models = {}; |
| if (!settings.models.providers) settings.models.providers = {}; |
|
|
| const normalizedBaseUrl = baseUrl.endsWith("/v1") ? baseUrl : `${baseUrl}/v1`; |
| const fullModelId = `9router/${model}`; |
|
|
| |
| Object.keys(settings.agents.defaults.models) |
| .filter((k) => k.startsWith("9router/")) |
| .forEach((k) => { delete settings.agents.defaults.models[k]; }); |
|
|
| |
| settings.agents.defaults.model.primary = fullModelId; |
|
|
| |
| const allModelIds = new Set([model]); |
| Object.values(agentModels).forEach((m) => { if (m) allModelIds.add(m); }); |
|
|
| |
| allModelIds.forEach((m) => { |
| settings.agents.defaults.models[`9router/${m}`] = {}; |
| }); |
|
|
| |
| |
| if (settings.agents.list) { |
| settings.agents.list = settings.agents.list.map((agent) => { |
| if (resolveAgentModel(agent.model).startsWith("9router/")) { |
| const { model: _, ...rest } = agent; |
| return rest; |
| } |
| return agent; |
| }); |
| } |
|
|
| |
| settings.models.providers["9router"] = { |
| baseUrl: normalizedBaseUrl, |
| apiKey: apiKey || "your_api_key", |
| api: "openai-completions", |
| models: [...allModelIds].map((m) => ({ id: m, name: m.split("/").pop() || m })), |
| }; |
|
|
| |
| if (settings.agents.list) { |
| settings.agents.list = settings.agents.list.map((agent) => { |
| const agentModel = agentModels[agent.id]; |
| if (agentModel) return { ...agent, model: `9router/${agentModel}` }; |
| return agent; |
| }); |
|
|
| |
| await Promise.all( |
| settings.agents.list.map(async (agent) => { |
| if (!agent.agentDir) return; |
| const agentModel = agentModels[agent.id]; |
| const modelToWrite = agentModel || model; |
| await writeAgentModels(agent.agentDir, modelToWrite, normalizedBaseUrl, apiKey); |
| }) |
| ); |
| } |
|
|
| await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2)); |
|
|
| return NextResponse.json({ |
| success: true, |
| message: "Open Claw settings applied successfully!", |
| settingsPath, |
| }); |
| } catch (error) { |
| console.log("Error updating openclaw settings:", error); |
| return NextResponse.json({ error: "Failed to update openclaw settings" }, { status: 500 }); |
| } |
| } |
|
|
| |
| export async function DELETE() { |
| try { |
| const settingsPath = getOpenClawSettingsPath(); |
|
|
| |
| let settings = {}; |
| try { |
| const existingSettings = await fs.readFile(settingsPath, "utf-8"); |
| settings = JSON.parse(existingSettings); |
| } catch (error) { |
| if (error.code === "ENOENT") { |
| return NextResponse.json({ |
| success: true, |
| message: "No settings file to reset", |
| }); |
| } |
| throw error; |
| } |
|
|
| |
| if (settings.models && settings.models.providers) { |
| delete settings.models.providers["9router"]; |
| |
| |
| if (Object.keys(settings.models.providers).length === 0) { |
| delete settings.models.providers; |
| } |
| } |
|
|
| |
| if (settings.agents?.defaults?.models) { |
| const keysToRemove = Object.keys(settings.agents.defaults.models).filter((k) => k.startsWith("9router/")); |
| for (const key of keysToRemove) { |
| delete settings.agents.defaults.models[key]; |
| } |
| if (Object.keys(settings.agents.defaults.models).length === 0) { |
| delete settings.agents.defaults.models; |
| } |
| } |
|
|
| |
| if (settings.agents?.defaults?.model?.primary?.startsWith("9router/")) { |
| delete settings.agents.defaults.model.primary; |
| } |
|
|
| |
| await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2)); |
|
|
| return NextResponse.json({ |
| success: true, |
| message: "9Router settings removed successfully", |
| }); |
| } catch (error) { |
| console.log("Error resetting openclaw settings:", error); |
| return NextResponse.json({ error: "Failed to reset openclaw settings" }, { status: 500 }); |
| } |
| } |
|
|