From 751d42d6a6c92e06be6fe8eaff330df4fc158256 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 1 Apr 2026 12:35:55 +0800 Subject: [PATCH] cleanup --- packages/opencode/src/server/instance.ts | 4 +- packages/opencode/src/server/router.ts | 4 +- packages/opencode/src/server/routes/pty.ts | 120 ++++++++++----------- packages/opencode/src/server/server.ts | 8 +- 4 files changed, 66 insertions(+), 70 deletions(-) diff --git a/packages/opencode/src/server/instance.ts b/packages/opencode/src/server/instance.ts index 12994d77e0..3845ba7f8e 100644 --- a/packages/opencode/src/server/instance.ts +++ b/packages/opencode/src/server/instance.ts @@ -41,8 +41,8 @@ const DEFAULT_CSP = const csp = (hash = "") => `default-src 'self'; script-src 'self' 'wasm-unsafe-eval'${hash ? ` 'sha256-${hash}'` : ""}; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:` -export const InstanceRoutes = (app?: Hono, upgrade?: UpgradeWebSocket) => - (app ?? new Hono()) +export const InstanceRoutes = (upgrade: UpgradeWebSocket, app: Hono = new Hono()) => + app .onError(errorHandler(log)) .route("/project", ProjectRoutes()) .route("/pty", PtyRoutes(upgrade)) diff --git a/packages/opencode/src/server/router.ts b/packages/opencode/src/server/router.ts index 606e169c41..b239c62728 100644 --- a/packages/opencode/src/server/router.ts +++ b/packages/opencode/src/server/router.ts @@ -25,8 +25,8 @@ function local(method: string, path: string) { return false } -export function WorkspaceRouterMiddleware(upgrade?: UpgradeWebSocket): MiddlewareHandler { - const routes = lazy(() => InstanceRoutes(undefined, upgrade)) +export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): MiddlewareHandler { + const routes = lazy(() => InstanceRoutes(upgrade)) return async (c) => { const raw = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd() diff --git a/packages/opencode/src/server/routes/pty.ts b/packages/opencode/src/server/routes/pty.ts index 0b783dbdc2..c333f4dd69 100644 --- a/packages/opencode/src/server/routes/pty.ts +++ b/packages/opencode/src/server/routes/pty.ts @@ -7,67 +7,7 @@ import { PtyID } from "@/pty/schema" import { NotFoundError } from "../../storage/db" import { errors } from "../error" -export function PtyRoutes(upgradeWebSocket?: UpgradeWebSocket) { - const connect: MiddlewareHandler = upgradeWebSocket - ? upgradeWebSocket(async (c) => { - const id = PtyID.zod.parse(c.req.param("ptyID")) - const cursor = (() => { - const value = c.req.query("cursor") - if (!value) return - const parsed = Number(value) - if (!Number.isSafeInteger(parsed) || parsed < -1) return - return parsed - })() - let handler: Awaited> - if (!(await Pty.get(id))) throw new Error("Session not found") - - type Socket = { - readyState: number - send: (data: string | Uint8Array | ArrayBuffer) => void - close: (code?: number, reason?: string) => void - } - - const isSocket = (value: unknown): value is Socket => { - if (!value || typeof value !== "object") return false - if (!("readyState" in value)) return false - if (!("send" in value) || typeof (value as { send?: unknown }).send !== "function") return false - if (!("close" in value) || typeof (value as { close?: unknown }).close !== "function") return false - return typeof (value as { readyState?: unknown }).readyState === "number" - } - - const pending: string[] = [] - let ready = false - - return { - async onOpen(_event, ws) { - const socket = ws.raw - if (!isSocket(socket)) { - ws.close() - return - } - handler = await Pty.connect(id, socket, cursor) - ready = true - for (const msg of pending) handler?.onMessage(msg) - pending.length = 0 - }, - onMessage(event) { - if (typeof event.data !== "string") return - if (!ready) { - pending.push(event.data) - return - } - handler?.onMessage(event.data) - }, - onClose() { - handler?.onClose() - }, - onError() { - handler?.onClose() - }, - } - }) - : async (c) => c.json({ error: "WebSocket upgrade unavailable" }, 501) - +export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) { return new Hono() .get( "/", @@ -209,6 +149,62 @@ export function PtyRoutes(upgradeWebSocket?: UpgradeWebSocket) { }, }), validator("param", z.object({ ptyID: PtyID.zod })), - connect, + upgradeWebSocket(async (c) => { + const id = PtyID.zod.parse(c.req.param("ptyID")) + const cursor = (() => { + const value = c.req.query("cursor") + if (!value) return + const parsed = Number(value) + if (!Number.isSafeInteger(parsed) || parsed < -1) return + return parsed + })() + let handler: Awaited> + if (!(await Pty.get(id))) throw new Error("Session not found") + + type Socket = { + readyState: number + send: (data: string | Uint8Array | ArrayBuffer) => void + close: (code?: number, reason?: string) => void + } + + const isSocket = (value: unknown): value is Socket => { + if (!value || typeof value !== "object") return false + if (!("readyState" in value)) return false + if (!("send" in value) || typeof (value as { send?: unknown }).send !== "function") return false + if (!("close" in value) || typeof (value as { close?: unknown }).close !== "function") return false + return typeof (value as { readyState?: unknown }).readyState === "number" + } + + const pending: string[] = [] + let ready = false + + return { + async onOpen(_event, ws) { + const socket = ws.raw + if (!isSocket(socket)) { + ws.close() + return + } + handler = await Pty.connect(id, socket, cursor) + ready = true + for (const msg of pending) handler?.onMessage(msg) + pending.length = 0 + }, + onMessage(event) { + if (typeof event.data !== "string") return + if (!ready) { + pending.push(event.data) + return + } + handler?.onMessage(event.data) + }, + onClose() { + handler?.onClose() + }, + onError() { + handler?.onClose() + }, + } + }), ) } diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index ea33882286..3822da71eb 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -44,7 +44,7 @@ export namespace Server { export const Default = lazy(() => create({}).app) - export function ControlPlaneRoutes(app = new Hono(), opts?: { cors?: string[] }, upgrade?: UpgradeWebSocket): Hono { + export function ControlPlaneRoutes(upgrade: UpgradeWebSocket, app = new Hono(), opts?: { cors?: string[] }): Hono { return app .onError(errorHandler(log)) .use((c, next) => { @@ -239,7 +239,7 @@ export namespace Server { const app = new Hono() const ws = createNodeWebSocket({ app }) return { - app: ControlPlaneRoutes(app, opts, ws.upgradeWebSocket), + app: ControlPlaneRoutes(ws.upgradeWebSocket, app, opts), ws, } } @@ -253,8 +253,8 @@ export namespace Server { // hono-openapi can see describeRoute metadata (`.route()` wraps // handlers when the sub-app has a custom errorHandler, which // strips the metadata symbol). - const app = ControlPlaneRoutes(new Hono()) - InstanceRoutes(app) + const { app, ws } = create({}) + InstanceRoutes(ws.upgradeWebSocket, app) const result = await generateSpecs(app, { documentation: { info: {