server.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import { Log } from "../util/log";
  2. import { Bus } from "../bus";
  3. import { describeRoute, generateSpecs, openAPISpecs } from "hono-openapi";
  4. import { Hono } from "hono";
  5. import { streamSSE } from "hono/streaming";
  6. import { Session } from "../session/session";
  7. import { resolver, validator as zValidator } from "hono-openapi/zod";
  8. import { z } from "zod";
  9. export namespace Server {
  10. const log = Log.create({ service: "server" });
  11. const PORT = 16713;
  12. export type App = ReturnType<typeof app>;
  13. function app() {
  14. const app = new Hono();
  15. const result = app
  16. .get(
  17. "/openapi",
  18. openAPISpecs(app, {
  19. documentation: {
  20. info: {
  21. title: "opencode",
  22. version: "1.0.0",
  23. description: "opencode api",
  24. },
  25. },
  26. }),
  27. )
  28. .get("/event", async (c) => {
  29. log.info("event connected");
  30. return streamSSE(c, async (stream) => {
  31. stream.writeSSE({
  32. data: JSON.stringify({}),
  33. });
  34. const unsub = Bus.subscribeAll(async (event) => {
  35. await stream.writeSSE({
  36. data: JSON.stringify(event),
  37. });
  38. });
  39. await new Promise<void>((resolve) => {
  40. stream.onAbort(() => {
  41. unsub();
  42. resolve();
  43. log.info("event disconnected");
  44. });
  45. });
  46. });
  47. })
  48. .post(
  49. "/session_create",
  50. describeRoute({
  51. description: "Create a new session",
  52. responses: {
  53. 200: {
  54. description: "Successfully created session",
  55. content: {
  56. "application/json": {
  57. schema: resolver(Session.Info),
  58. },
  59. },
  60. },
  61. },
  62. }),
  63. async (c) => {
  64. const session = await Session.create();
  65. return c.json(session);
  66. },
  67. )
  68. .post(
  69. "/session_chat",
  70. zValidator(
  71. "json",
  72. z.object({
  73. sessionID: z.string(),
  74. parts: z.custom<Session.Message["parts"]>(),
  75. }),
  76. ),
  77. async (c) => {
  78. const body = c.req.valid("json");
  79. const msg = await Session.chat(body.sessionID, ...body.parts);
  80. return c.json(msg);
  81. },
  82. );
  83. return result;
  84. }
  85. export async function openapi() {
  86. const a = app();
  87. const result = await generateSpecs(a, {
  88. documentation: {
  89. info: {
  90. title: "opencode",
  91. version: "1.0.0",
  92. description: "opencode api",
  93. },
  94. },
  95. });
  96. return result;
  97. }
  98. export function listen() {
  99. const server = Bun.serve({
  100. port: PORT,
  101. hostname: "0.0.0.0",
  102. idleTimeout: 0,
  103. fetch: app().fetch,
  104. });
  105. return server;
  106. }
  107. }