#!/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 { arch, hostname, platform, release, type } from "node:os"; 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}`); console.log(`[webui] 运行环境: Node.js ${process.version} | ${type()} ${release()} (${platform()}/${arch()}) | 主机: ${hostname()}`); process.on("SIGTERM", () => shutdown(0)); process.on("SIGINT", () => shutdown(0)); writePidFile(); const piClient = createPiClient(paths, (code) => { if (code !== 0 && code !== null) { console.error(`[webui] pi RPC 进程异常退出 (code=${code}),WebUI 即将关闭`); setTimeout(() => shutdown(1), 500); return; } shutdown(1); }); const ctx: WebUiContext = { config: { paths, port }, rpc: { sendCmd: piClient.sendCmd, submitPrompt: piClient.submitPrompt, sendExtensionUiResponse: piClient.sendExtensionUiResponse, 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://${hostname()}:${port}`); });