summaryrefslogtreecommitdiffhomepage
path: root/packages/api/src/index.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-04 21:21:20 +0900
committerAdam Malczewski <[email protected]>2026-06-04 21:21:20 +0900
commit394f1ed37ce860da6fdc385769bf29f9737105cd (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /packages/api/src/index.ts
parent81a9cdbadf8c9d940d4fe9a2a0de607dee1f5f1a (diff)
downloaddispatch-394f1ed37ce860da6fdc385769bf29f9737105cd.tar.gz
dispatch-394f1ed37ce860da6fdc385769bf29f9737105cd.zip
chore: genesis — remove all files to rebuild from scratch (arch rewrite)
Diffstat (limited to 'packages/api/src/index.ts')
-rw-r--r--packages/api/src/index.ts127
1 files changed, 0 insertions, 127 deletions
diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts
deleted file mode 100644
index 5615e08..0000000
--- a/packages/api/src/index.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import type { PermissionReply } from "@dispatch/core";
-import { createBunWebSocket } from "hono/bun";
-import { agentManager, app, permissionManager } from "./app.js";
-
-const { upgradeWebSocket, websocket } = createBunWebSocket();
-
-let clientIdCounter = 0;
-
-app.get(
- "/ws",
- upgradeWebSocket((_c) => {
- const clientId = String(++clientIdCounter);
-
- return {
- onOpen(_event, ws) {
- // Send current statuses immediately
- ws.send(JSON.stringify({ type: "statuses", statuses: agentManager.getAllStatuses() }));
-
- // Send any pending permission prompts
- const pending = permissionManager.getPending();
- if (pending.length > 0) {
- ws.send(JSON.stringify({ type: "permission-prompt", pending }));
- }
-
- const unsubscribe = agentManager.onEvent((event) => {
- ws.send(JSON.stringify(event));
- });
-
- permissionManager.registerClient(clientId, (data) => {
- ws.send(JSON.stringify(data));
- });
-
- // Store cleanup on the raw socket
- (ws as unknown as { _unsub?: () => void; _clientId?: string })._unsub = unsubscribe;
- (ws as unknown as { _unsub?: () => void; _clientId?: string })._clientId = clientId;
- },
- onMessage(event, _ws) {
- try {
- const message = JSON.parse(String(event.data)) as {
- type?: string;
- id?: string;
- reply?: string;
- };
- if (
- message.type === "permission-reply" &&
- typeof message.id === "string" &&
- typeof message.reply === "string"
- ) {
- const validReplies: PermissionReply[] = ["once", "always", "reject"];
- if (validReplies.includes(message.reply as PermissionReply)) {
- permissionManager.reply(message.id, message.reply as PermissionReply);
- }
- }
- } catch {
- // ignore malformed messages
- }
- },
- onClose(_event, ws) {
- const raw = ws as unknown as { _unsub?: () => void; _clientId?: string };
- if (raw._unsub) {
- raw._unsub();
- }
- if (raw._clientId) {
- permissionManager.unregisterClient(raw._clientId);
- }
- },
- };
- }),
-);
-
-export { app };
-
-// Starting port (overridable via PORT). When the port is already in use we
-// bump up one at a time (3000 → 3001 → 3002, …) until we find a free one, so
-// multiple dispatch instances (e.g. testing several features at once) can
-// coexist without manually juggling ports. The frontend defaults to :3000 —
-// point it at the chosen port via the in-app API-URL field / VITE_API_URL
-// when a bump happens.
-const START_PORT = Number(process.env.PORT) || 3000;
-
-/**
- * Bind the server to `START_PORT`, incrementing by one on EADDRINUSE until a
- * free port is found (up to the maximum valid TCP port, 65535). Bun's
- * `Bun.serve` throws synchronously when the port is taken, so we can catch and
- * retry. Returns the live server (whose `.port` reflects the port actually
- * bound).
- */
-function serveWithPortFallback() {
- let lastError: unknown;
- for (let port = START_PORT; port <= 65535; port++) {
- try {
- const server = Bun.serve({
- port,
- idleTimeout: 60,
- fetch: app.fetch,
- websocket,
- });
- if (port !== START_PORT) {
- console.warn(
- `dispatch: port ${START_PORT} in use — bound to ${port} instead. ` +
- `Set the frontend's API URL to http://localhost:${port}.`,
- );
- }
- console.log(`dispatch: API listening on http://localhost:${server.port}`);
- return server;
- } catch (err) {
- const code = (err as NodeJS.ErrnoException)?.code;
- if (code === "EADDRINUSE") {
- lastError = err;
- continue;
- }
- throw err;
- }
- }
- console.error(
- `dispatch: no free port at or above ${START_PORT}. ` +
- `Free one up or set PORT to an open port.`,
- );
- throw lastError ?? new Error(`No free port at or above ${START_PORT}`);
-}
-
-// Only start the server when run as the entry point — importing this module
-// (e.g. for `app`) must not bind a port. This preserves the prior
-// default-export behavior where Bun served only the entry file.
-if (import.meta.main) {
- serveWithPortFallback();
-}