mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 02:59:35 +00:00
62 lines
2.7 KiB
TypeScript
62 lines
2.7 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
import { runNpx } from "@/lib/npx";
|
|
import { getAllowedFileRoots, isExistingFilePathAllowed } from "@/lib/file-access";
|
|
import { hasJsonContentType, isApiRequestAllowed } from "@/lib/request-security";
|
|
import { getProjectTrustStatus } from "@/lib/project-trust";
|
|
import { getSkillsCliEnvironment, isManagedRuntime } from "@/lib/app-runtime";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const ANSI_RE = /\x1B\[[0-9;]*m/g;
|
|
|
|
// POST /api/skills/install body: { package: string; scope: "global" | "project"; cwd?: string }
|
|
export async function POST(req: Request) {
|
|
if (!isApiRequestAllowed(req)) {
|
|
return NextResponse.json({ error: "Untrusted API request" }, { status: 403 });
|
|
}
|
|
if (!hasJsonContentType(req)) {
|
|
return NextResponse.json({ error: "Content-Type must be application/json" }, { status: 415 });
|
|
}
|
|
|
|
try {
|
|
const { package: pkg, scope, cwd } = await req.json() as { package?: string; scope?: string; cwd?: string };
|
|
if (!pkg?.trim()) return NextResponse.json({ error: "package required" }, { status: 400 });
|
|
|
|
const isGlobal = isManagedRuntime() || scope !== "project";
|
|
if (!isGlobal) {
|
|
if (!cwd) return NextResponse.json({ error: "cwd required for project install" }, { status: 400 });
|
|
const allowedRoots = await getAllowedFileRoots();
|
|
if (!isExistingFilePathAllowed(cwd, allowedRoots)) {
|
|
return NextResponse.json({ error: "Access denied" }, { status: 403 });
|
|
}
|
|
if (!getProjectTrustStatus(cwd, getAgentDir()).trusted) {
|
|
return NextResponse.json(
|
|
{ error: "Project resources must be trusted before installing project skills" },
|
|
{ status: 403 },
|
|
);
|
|
}
|
|
}
|
|
const args = ["skills", "add", pkg.trim(), "-y", "--agent", "pi"];
|
|
if (isGlobal) args.push("-g");
|
|
|
|
console.log(`[skills/install] running: npx ${args.join(" ")}`);
|
|
const { stdout, stderr } = await runNpx(args, {
|
|
timeout: 60000,
|
|
cwd: !isGlobal && cwd ? cwd : undefined,
|
|
env: getSkillsCliEnvironment(),
|
|
});
|
|
|
|
const output = (stdout + stderr).replace(ANSI_RE, "");
|
|
const success = /Installation complete|Installed \d+ skill/.test(output);
|
|
if (!success) {
|
|
return NextResponse.json({ error: output.slice(-300) || "Install failed" }, { status: 500 });
|
|
}
|
|
return NextResponse.json({ success: true, output });
|
|
} catch (e: unknown) {
|
|
const err = e as { stdout?: string; stderr?: string; message?: string };
|
|
const output = ((err.stdout ?? "") + (err.stderr ?? "")).replace(ANSI_RE, "");
|
|
return NextResponse.json({ error: output || (err.message ?? String(e)) }, { status: 500 });
|
|
}
|
|
}
|