// Stands in for this backend's /ws/edge/v1 and records every frame a real // gateway sends, one JSON object per line. // // What it is for: refreshing tests/fixtures/gateway_frames.json. Those fixtures // are deliberately real captures rather than hand-written examples - a // hand-written example agrees with whoever wrote it, which is how the two ends // of this protocol drifted into incompatible shapes without anything failing. // So when the protocol changes, re-capture rather than editing the fixture. // // It needs no database and no auth, which is the point: it isolates the // gateway's side of the wire from everything else. For the full path through a // real database and real printers, use e2e-edge.sh instead. // // Usage: // bun run scripts/capture-gateway-frames.js > frames.jsonl // # then, in continuum-proxy, with its CONTINUUM_* env set: // CONTINUUM_UPLINK_URL=ws://localhost:59999/ws/edge/v1 cargo run // // PORT=59999 where to listen // FAN_TEST_PRINTER=p1p send one set_fan_speed to this printer, to capture a // command_result. Leave unset to stay read-only. // // SAFETY: with FAN_TEST_PRINTER set this writes to a real printer. Only ever a // fan speed, and only to the printer you name - but name an idle one. const PORT = Number(process.env.PORT ?? 59999); const FAN_TEST_PRINTER = process.env.FAN_TEST_PRINTER; let commandSent = false; Bun.serve({ port: PORT, fetch(req, server) { if (server.upgrade(req)) return; return new Response("this endpoint only speaks WebSocket\n", { status: 400 }); }, websocket: { open() { console.error(`[capture] gateway connected`); }, message(ws, raw) { // stdout is the capture, stderr is the commentary, so the two can be // redirected separately. console.log(typeof raw === "string" ? raw : raw.toString()); let msg; try { msg = JSON.parse(typeof raw === "string" ? raw : raw.toString()); } catch (err) { console.error(`[capture] not JSON, recorded anyway: ${err}`); return; } switch (msg.type) { case "hello": // No token check: this is not the backend, it is a recorder. console.error(`[capture] hello from ${msg.gateway_id} (farm ${msg.farm_id})`); ws.send(JSON.stringify({ type: "hello_ack", session_id: "capture" })); break; case "heartbeat": ws.send(JSON.stringify({ type: "heartbeat_ack", seq: msg.seq })); break; case "telemetry": console.error( `[capture] telemetry: ${msg.printers .map((p) => `${p.printer_id}=${p.state ?? "?"}`) .join(" ")}`, ); if (FAN_TEST_PRINTER && !commandSent) { commandSent = true; const reported = msg.printers.find((p) => p.printer_id === FAN_TEST_PRINTER); // Refuse to touch a printer that is not idle, and refuse to guess // at one the gateway has not reported at all. if (!reported) { console.error(`[capture] '${FAN_TEST_PRINTER}' not in this batch, not sending`); } else if (reported.state !== "IDLE" && reported.state !== "FINISH") { console.error(`[capture] '${FAN_TEST_PRINTER}' is ${reported.state}, not sending`); } else { console.error(`[capture] -> set_fan_speed 0 to ${FAN_TEST_PRINTER}`); ws.send(JSON.stringify({ type: "command", command_id: "capture-1", printer_id: FAN_TEST_PRINTER, command: { action: "set_fan_speed", percent: 0 }, })); } } break; case "command_result": console.error(`[capture] command_result ${msg.command_id}: ok=${msg.ok}`); break; default: console.error(`[capture] ${msg.type}`); } }, close() { console.error("[capture] gateway disconnected"); }, }, }); console.error(`[capture] listening on :${PORT} - frames to stdout, commentary to stderr`); if (!FAN_TEST_PRINTER) { console.error("[capture] read-only (set FAN_TEST_PRINTER to also capture a command_result)"); }