import express from "express"; import { spawn } from "node:child_process"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const repoRoot = path.resolve(__dirname, ".."); const app = express(); app.use(express.static(path.join(__dirname, "public"))); function sseHeaders(res) { res.setHeader("Content-Type", "text/event-stream; charset=utf-8"); res.setHeader("Cache-Control", "no-cache, no-transform"); res.setHeader("Connection", "keep-alive"); // Nginx proxies may buffer unless disabled; harmless in dev. res.setHeader("X-Accel-Buffering", "no"); } function sseSend(res, event, data) { if (event) res.write(`event: ${event}\n`); const lines = String(data).split(/\r?\n/); for (const line of lines) res.write(`data: ${line}\n`); res.write("\n"); } app.get("/api/run", (req, res) => { const prompt = String(req.query.prompt || "").trim(); const mock = String(req.query.mock || "1") === "1"; const configPath = String(req.query.config || "./configs/config.yaml"); if (!prompt) { res.status(400).json({ error: "missing prompt" }); return; } sseHeaders(res); sseSend(res, "status", "starting"); // Unified in-container execution: Node spawns python directly. const py = process.env.PYTHON_BIN || "python"; const args = [ path.join(repoRoot, "main.py"), "--prompt", prompt, "--config", configPath, "--script-only", ]; if (mock) args.push("--mock"); const child = spawn(py, args, { cwd: repoRoot, env: process.env, stdio: ["ignore", "pipe", "pipe"], }); let buf = ""; child.stdout.setEncoding("utf8"); child.stdout.on("data", (chunk) => { buf += chunk; const parts = buf.split(/\r?\n/); buf = parts.pop() || ""; for (const line of parts) { if (!line) continue; // Forward raw lines. Frontend will parse SCENE_JSON. sseSend(res, "line", line); } }); child.stderr.setEncoding("utf8"); child.stderr.on("data", (chunk) => { sseSend(res, "stderr", chunk); }); req.on("close", () => { child.kill("SIGTERM"); }); child.on("exit", (code) => { if (buf.trim()) sseSend(res, "line", buf.trim()); sseSend(res, "done", String(code ?? 0)); res.end(); }); }); const port = Number(process.env.PORT || 3000); app.listen(port, () => { console.log(`[server] http://127.0.0.1:${port}`); });