feat(sproutclaw): modularize webui, extensions, and agent config layout
Some checks failed
CI / build-check-test (push) Has been cancelled

Restructure local extensions into per-feature directories, split WebUI
into backend modules with slash commands and systemd support, and track
prompts/skills under .pi/agent for portable Gitea deployment.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
root
2026-06-10 16:57:08 +08:00
parent 11c3a3a399
commit cf5edd6394
132 changed files with 9288 additions and 1971 deletions

View File

@@ -0,0 +1,83 @@
#!/usr/bin/env node
/**
* pi-mono WebUI Server
*
* 被 webui 扩展启动,作为独立子进程运行。
* 内嵌 HTTP 服务 + pi RPC 子进程,提供浏览器聊天界面。
*
* 用法(由扩展自动调用):
* tsx backend/main.ts --port 19133
*
* 前端静态文件位于 frontend/dist/ 下Vite 构建产物)。
*/
import { existsSync, unlinkSync, writeFileSync } from "node:fs";
import { parsePort } from "./config/cli.ts";
import { createWebUiPaths } from "./config/paths.ts";
import { closeWebuiDatabase, initWebuiDatabase } from "./db/index.ts";
import { createWebUiServer } from "./http/server.ts";
import { createPiClient } from "./rpc/pi-client.ts";
import type { WebUiContext } from "./types/context.ts";
const paths = createWebUiPaths();
const port = parsePort();
function writePidFile(): void {
try {
writeFileSync(paths.pidFile, String(process.pid), "utf8");
} catch {
/* ignore */
}
}
function clearPidFile(): void {
try {
if (existsSync(paths.pidFile)) unlinkSync(paths.pidFile);
} catch {
/* ignore */
}
}
function shutdown(exitCode = 0): never {
closeWebuiDatabase();
clearPidFile();
process.exit(exitCode);
}
const webuiDb = initWebuiDatabase(paths.extensionRoot);
console.log(`[webui] 配置数据库: ${webuiDb.dbPath}`);
process.on("SIGTERM", () => shutdown(0));
process.on("SIGINT", () => shutdown(0));
writePidFile();
const piClient = createPiClient(paths, () => shutdown(1));
const ctx: WebUiContext = {
config: { paths, port },
rpc: {
sendCmd: piClient.sendCmd,
submitPrompt: piClient.submitPrompt,
getRunSnapshot: piClient.getRunSnapshot,
connectSseClient: piClient.connectSseClient,
removeSseClient: piClient.removeSseClient,
},
db: webuiDb,
};
const server = createWebUiServer(ctx);
server.on("error", (err: NodeJS.ErrnoException) => {
if (err.code === "EADDRINUSE") {
console.error(`[webui] 端口 ${port} 已被占用`);
} else {
console.error(`[webui] HTTP 服务启动失败: ${err.message}`);
}
process.exit(1);
});
server.listen(port, "0.0.0.0", () => {
console.log(`[webui] HTTP 服务已启动: http://localhost:${port}`);
console.log(`[webui] 局域网访问: http://smallmengya:${port}`);
});