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>
84 lines
2.2 KiB
JavaScript
84 lines
2.2 KiB
JavaScript
#!/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}`);
|
||
});
|