import { Router } from "express"; import type { Db } from "@paperclipai/db"; import { assertBoard, assertCompanyAccess } from "./authz.js"; import { puterProxyService } from "../services/puter-proxy.js"; import { badRequest } from "../errors.js"; export function puterProxyRoutes(db: Db): Router { const router = Router(); // POST /puter-proxy/token — store or rotate Puter auth token router.post("/puter-proxy/token", async (req, res) => { assertBoard(req); const { companyId, token } = req.body; if (!companyId || typeof companyId !== "string") { throw badRequest("companyId is required"); } if (!token || typeof token !== "string") { throw badRequest("token is required"); } assertCompanyAccess(req, companyId); await puterProxyService(db).storeToken(companyId, token); res.json({ ok: true }); }); // POST /puter-proxy/chat — stream AI chat via Puter proxy router.post("/puter-proxy/chat", async (req, res) => { assertBoard(req); const { companyId, agentId, messages, model } = req.body; if (!companyId || typeof companyId !== "string") { res.status(400).json({ error: "companyId is required" }); return; } if (!Array.isArray(messages) || messages.length === 0) { res.status(400).json({ error: "messages must be a non-empty array" }); return; } assertCompanyAccess(req, companyId); // Set SSE headers and flush immediately (same pattern as chat.ts) res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.setHeader("X-Accel-Buffering", "no"); res.flushHeaders(); res.write(":ok\n\n"); const abort = new AbortController(); req.on("close", () => abort.abort()); try { const svc = puterProxyService(db); // agentId is optional — pass through as-is (string or undefined) for await (const chunk of svc.chatStream( companyId, agentId as string | undefined, messages, model as string | undefined, abort.signal, )) { if (!res.writable) break; res.write(`data: ${JSON.stringify({ token: chunk })}\n\n`); } if (res.writable && !abort.signal.aborted) { res.write(`data: ${JSON.stringify({ done: true })}\n\n`); } } catch (err) { if (res.writable) { res.write(`data: ${JSON.stringify({ error: "Puter stream error" })}\n\n`); } } finally { res.end(); } }); return router; }