summaryrefslogtreecommitdiffhomepage
path: root/js/src/server/server.ts
blob: ea4f5eab0fd50edeb4490d057487a147d5d8a279 (plain)
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
105
106
107
108
109
110
111
112
113
114
import { Log } from "../util/log";
import { Bus } from "../bus";
import { describeRoute, generateSpecs, openAPISpecs } from "hono-openapi";
import { Hono } from "hono";
import { streamSSE } from "hono/streaming";
import { Session } from "../session/session";
import { resolver, validator as zValidator } from "hono-openapi/zod";
import { z } from "zod";

export namespace Server {
  const log = Log.create({ service: "server" });
  const PORT = 16713;

  export type App = ReturnType<typeof app>;

  function app() {
    const app = new Hono();

    const result = app
      .get(
        "/openapi",
        openAPISpecs(app, {
          documentation: {
            info: {
              title: "opencode",
              version: "1.0.0",
              description: "opencode api",
            },
          },
        }),
      )
      .get("/event", async (c) => {
        log.info("event connected");
        return streamSSE(c, async (stream) => {
          stream.writeSSE({
            data: JSON.stringify({}),
          });
          const unsub = Bus.subscribeAll(async (event) => {
            await stream.writeSSE({
              data: JSON.stringify(event),
            });
          });
          await new Promise<void>((resolve) => {
            stream.onAbort(() => {
              unsub();
              resolve();
              log.info("event disconnected");
            });
          });
        });
      })
      .post(
        "/session_create",
        describeRoute({
          description: "Create a new session",
          responses: {
            200: {
              description: "Successfully created session",
              content: {
                "application/json": {
                  schema: resolver(Session.Info),
                },
              },
            },
          },
        }),
        async (c) => {
          const session = await Session.create();
          return c.json(session);
        },
      )
      .post(
        "/session_chat",
        zValidator(
          "json",
          z.object({
            sessionID: z.string(),
            parts: z.custom<Session.Message["parts"]>(),
          }),
        ),
        async (c) => {
          const body = c.req.valid("json");
          const msg = await Session.chat(body.sessionID, ...body.parts);
          return c.json(msg);
        },
      );

    return result;
  }

  export async function openapi() {
    const a = app();
    const result = await generateSpecs(a, {
      documentation: {
        info: {
          title: "opencode",
          version: "1.0.0",
          description: "opencode api",
        },
      },
    });
    return result;
  }

  export function listen() {
    const server = Bun.serve({
      port: PORT,
      hostname: "0.0.0.0",
      idleTimeout: 0,
      fetch: app().fetch,
    });
    return server;
  }
}