File size: 2,834 Bytes
a2d0320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { serve } from "bun";
import { readFile } from "fs/promises";
import { join } from "path";
import { GameState } from "./gameState";
import { AIService, MODEL } from "./aiService";

const PORT = parseInt(process.env.PORT || "7860");
const ROOT_DIR = import.meta.dir;
const gameState = new GameState(); // Single shared game state

const mimeTypes: Record<string, string> = {
  ".html": "text/html",
  ".js": "application/javascript",
  ".css": "text/css",
  ".map": "application/octet-stream",
  ".ico": "image/x-icon",
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".gif": "image/gif",
  ".svg": "image/svg+xml",
  ".webp": "image/webp",
  ".json": "application/json",
};

serve({
  port: PORT,
  async fetch(req) {
    const url = new URL(req.url);
    const pathname = url.pathname;

    // Simple config endpoint to expose runtime info to the UI
    if (pathname === "/config") {
      return new Response(JSON.stringify({ model: MODEL }), {
        headers: { "Content-Type": "application/json" },
      });
    }

    // Handle SSE connection
    if (pathname === "/game-stream") {
      const stream = new ReadableStream<string>({
        start(controller) {
          gameState.setController(controller);
          gameState.sendUpdate();

          // Initial state will be sent by gameState

          // Start AI agent loop
          const runAI = async () => {
            const state = gameState.getState();
            const action = await AIService.getNextAction(
              state.map,
              state.history
            );
            await gameState.handleAction(action);

            // Schedule next AI action
            setTimeout(runAI, 1000);
          };

          runAI();
        },
        cancel() {
          // No need to cleanup since we're using a shared state
        },
      });

      return new Response(stream, {
        headers: {
          "Content-Type": "text/event-stream",
          "Cache-Control": "no-cache",
          Connection: "keep-alive",
        },
      });
    }

    // Serve static files
    if (pathname === "/" || pathname === "/index.html") {
      const data = await readFile(join(ROOT_DIR, "index.html"));
      return new Response(data, {
        headers: { "Content-Type": "text/html" },
      });
    }

    const filePath = join(ROOT_DIR, pathname);
    try {
      const data = await readFile(filePath);
      const ext = filePath.slice(filePath.lastIndexOf("."));
      const mimeType = mimeTypes[ext] || "application/octet-stream";

      return new Response(data, {
        status: 200,
        headers: {
          "Content-Type": mimeType,
        },
      });
    } catch (error) {
      return new Response("Not Found", { status: 404 });
    }
  },
});

console.log(`Server is running at http://localhost:${PORT}`);