Files
argument.es/server.ts
Theo Browne 25d84a994e docker
2026-02-20 03:51:29 -08:00

106 lines
3.5 KiB
TypeScript

import type { ServerWebSocket } from "bun";
import indexHtml from "./index.html";
import historyHtml from "./history.html";
import { getRounds } from "./db.ts";
import {
MODELS,
LOG_FILE,
log,
runGame,
type GameState,
} from "./game.ts";
// ── Game state ──────────────────────────────────────────────────────────────
const runsArg = process.argv.find((a) => a.startsWith("runs="));
const runsStr = runsArg ? runsArg.split("=")[1] : "infinite";
const runs = runsStr === "infinite" ? Infinity : parseInt(runsStr || "infinite", 10);
if (!process.env.OPENROUTER_API_KEY) {
console.error("Error: Set OPENROUTER_API_KEY environment variable");
process.exit(1);
}
const gameState: GameState = {
completed: [],
active: null,
scores: Object.fromEntries(MODELS.map((m) => [m.name, 0])),
done: false,
};
// ── WebSocket clients ───────────────────────────────────────────────────────
const clients = new Set<ServerWebSocket<unknown>>();
function broadcast() {
const msg = JSON.stringify({ type: "state", data: gameState, totalRounds: runs });
for (const ws of clients) {
ws.send(msg);
}
}
// ── Server ──────────────────────────────────────────────────────────────────
const port = parseInt(process.env.PORT ?? "5109", 10); // 5109 = SLOP
const server = Bun.serve({
port,
routes: {
"/": indexHtml,
"/history": historyHtml,
},
fetch(req, server) {
const url = new URL(req.url);
if (url.pathname.startsWith("/assets/")) {
const path = `./public${url.pathname}`;
const file = Bun.file(path);
return new Response(file);
}
if (url.pathname === "/api/history") {
const page = parseInt(url.searchParams.get("page") || "1", 10);
return new Response(JSON.stringify(getRounds(page)), {
headers: { "Content-Type": "application/json" }
});
}
if (url.pathname === "/ws") {
const upgraded = server.upgrade(req);
if (!upgraded) {
return new Response("WebSocket upgrade failed", { status: 400 });
}
return undefined;
}
return new Response("Not found", { status: 404 });
},
websocket: {
open(ws) {
clients.add(ws);
ws.send(JSON.stringify({ type: "state", data: gameState, totalRounds: runs }));
},
message(_ws, _message) {
// Spectator-only, no client messages handled
},
close(ws) {
clients.delete(ws);
},
},
development: process.env.NODE_ENV === "production" ? false : {
hmr: true,
console: true,
},
});
console.log(`\n🎮 Quipslop Web — http://localhost:${server.port}`);
console.log(`📡 WebSocket — ws://localhost:${server.port}/ws`);
console.log(`🎯 ${runs} rounds with ${MODELS.length} models\n`);
log("INFO", "server", `Web server started on port ${server.port}`, {
runs,
models: MODELS.map((m) => m.id),
});
// ── Start game ──────────────────────────────────────────────────────────────
runGame(runs, gameState, broadcast).then(() => {
console.log(`\n✅ Game complete! Log: ${LOG_FILE}`);
});