import { NextRequest, NextResponse } from "next/server"; import { exec } from "child_process"; import { promisify } from "util"; import { readFile, writeFile, mkdir, rm, readdir } from "fs/promises"; import { existsSync } from "fs"; import path from "path"; const execAsync = promisify(exec); const CLAWHUB_BIN = "/usr/bin/clawhub"; const SKILLS_BASE = "/root/.openclaw/workspace/agents-skills"; export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url); const action = searchParams.get("action"); const q = searchParams.get("q") || ""; const slug = searchParams.get("slug") || ""; try { if (action === "search") { const { stdout } = await execAsync(`${CLAWHUB_BIN} search "${q}" 2>&1`); const lines = stdout.trim().split("\n"); const skills = lines .filter((line) => line.includes("(")) .map((line) => { const match = line.trim().match(/^(.+?)\s+(.+?)\s+\(([0-9.]+)\)$/); if (match) { return { slug: match[1], name: match[2], score: parseFloat(match[3]) }; } return null; }) .filter(Boolean); return NextResponse.json({ skills }); } if (action === "popular") { const { stdout } = await execAsync(`${CLAWHUB_BIN} search "productivity automation" 2>&1`); const lines = stdout.trim().split("\n"); const skills = lines .filter((line) => line.includes("(")) .map((line) => { const match = line.trim().match(/^(.+?)\s+(.+?)\s+\(([0-9.]+)\)$/); if (match) { return { slug: match[1], name: match[2], score: parseFloat(match[3]) }; } return null; }) .filter(Boolean) .sort((a: any, b: any) => b.score - a.score) .slice(0, 30); return NextResponse.json({ skills }); } if (action === "list") { // List installed skills per agent const agents = ["horus", "amun", "cleo"]; const installed: Record = {}; for (const agent of agents) { const agentPath = path.join(SKILLS_BASE, agent); if (existsSync(agentPath)) { try { const entries = await readdir(agentPath, { withFileTypes: true }); installed[agent] = entries .filter((d) => d.isDirectory()) .map((d) => d.name); } catch { installed[agent] = []; } } else { installed[agent] = []; } } return NextResponse.json({ installed }); } if (action === "info") { const { stdout } = await execAsync(`${CLAWHUB_BIN} inspect ${slug} 2>&1`); return NextResponse.json({ info: stdout }); } return NextResponse.json({ error: "Unknown action" }, { status: 400 }); } catch (e: unknown) { const error = e as Error & { stdout?: string; message?: string }; return NextResponse.json({ error: error.message || "Failed", details: error.stdout || "", }, { status: 500 }); } } export async function POST(req: NextRequest) { try { const body = await req.json(); const { action, slug, agents = ["horus"], name } = body; if (action === "install") { const results: string[] = []; for (const agent of agents) { const agentPath = path.join(SKILLS_BASE, agent, slug); const agentDir = path.join(SKILLS_BASE, agent); // Ensure agent dir exists if (!existsSync(agentDir)) { await mkdir(agentDir, { recursive: true }); } // Install to temp first const tempPath = `/tmp/clawhub-install-${slug}`; await execAsync(`rm -rf ${tempPath} 2>/dev/null; ${CLAWHUB_BIN} install ${slug} --dir ${tempPath} 2>&1`); // Move to agent folder if (existsSync(tempPath)) { await execAsync(`mv ${tempPath} ${agentPath}`); results.push(`✓ ${agent}: installed ${slug}`); } else { results.push(`✗ ${agent}: failed to install ${slug}`); } } return NextResponse.json({ output: results.join("\n") }); } if (action === "delete") { const results: string[] = []; for (const agent of agents) { const agentPath = path.join(SKILLS_BASE, agent, slug); if (existsSync(agentPath)) { await rm(agentPath, { recursive: true }); results.push(`✓ ${agent}: removed ${slug}`); } else { results.push(`- ${agent}: ${slug} not found`); } } return NextResponse.json({ output: results.join("\n") }); } return NextResponse.json({ error: "Unknown action" }, { status: 400 }); } catch (e: unknown) { const error = e as Error & { stdout?: string; stderr?: string; message?: string }; return NextResponse.json({ error: error.message || "Failed", details: error.stdout || error.stderr || "", }, { status: 500 }); } }