refactor(server): replace Bun serve with Hono node adapters (#18335)

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
Co-authored-by: Brendan Allan <git@brendonovich.dev>
This commit is contained in:
Dax
2026-04-06 13:24:55 -04:00
committed by GitHub
parent 4394e42615
commit 535343bf56
18 changed files with 364 additions and 152 deletions

View File

@@ -3,7 +3,7 @@ import { Bus } from "@/bus"
import { InstanceState } from "@/effect/instance-state"
import { makeRuntime } from "@/effect/run-service"
import { Instance } from "@/project/instance"
import { type IPty } from "bun-pty"
import type { Proc } from "#pty"
import z from "zod"
import { Log } from "../util/log"
import { lazy } from "@opencode-ai/util/lazy"
@@ -26,9 +26,11 @@ export namespace Pty {
close: (code?: number, reason?: string) => void
}
const sock = (ws: Socket) => (ws.data && typeof ws.data === "object" ? ws.data : ws)
type Active = {
info: Info
process: IPty
process: Proc
buffer: string
bufferCursor: number
cursor: number
@@ -50,10 +52,7 @@ export namespace Pty {
return out
}
const pty = lazy(async () => {
const { spawn } = await import("bun-pty")
return spawn
})
const pty = lazy(() => import("#pty"))
export const Info = z
.object({
@@ -124,9 +123,9 @@ export namespace Pty {
try {
session.process.kill()
} catch {}
for (const [key, ws] of session.subscribers.entries()) {
for (const [sub, ws] of session.subscribers.entries()) {
try {
if (ws.data === key) ws.close()
if (sock(ws) === sub) ws.close()
} catch {}
}
session.subscribers.clear()
@@ -198,7 +197,7 @@ export namespace Pty {
}
log.info("creating session", { id, cmd: command, args, cwd })
const spawn = yield* Effect.promise(() => pty())
const { spawn } = yield* Effect.promise(() => pty())
const proc = yield* Effect.sync(() =>
spawn(command, args, {
name: "xterm-256color",
@@ -234,7 +233,7 @@ export namespace Pty {
session.subscribers.delete(key)
continue
}
if (ws.data !== key) {
if (sock(ws) !== key) {
session.subscribers.delete(key)
continue
}
@@ -304,15 +303,12 @@ export namespace Pty {
}
log.info("client connected to session", { id })
// Use ws.data as the unique key for this connection lifecycle.
// If ws.data is undefined, fallback to ws object.
const key = ws.data && typeof ws.data === "object" ? ws.data : ws
// Optionally cleanup if the key somehow exists
session.subscribers.delete(key)
session.subscribers.set(key, ws)
const sub = sock(ws)
session.subscribers.delete(sub)
session.subscribers.set(sub, ws)
const cleanup = () => {
session.subscribers.delete(key)
session.subscribers.delete(sub)
}
const start = session.bufferCursor

View File

@@ -0,0 +1,26 @@
import { spawn as create } from "bun-pty"
import type { Opts, Proc } from "./pty"
export type { Disp, Exit, Opts, Proc } from "./pty"
export function spawn(file: string, args: string[], opts: Opts): Proc {
const pty = create(file, args, opts)
return {
pid: pty.pid,
onData(listener) {
return pty.onData(listener)
},
onExit(listener) {
return pty.onExit(listener)
},
write(data) {
pty.write(data)
},
resize(cols, rows) {
pty.resize(cols, rows)
},
kill(signal) {
pty.kill(signal)
},
}
}

View File

@@ -0,0 +1,27 @@
/** @ts-expect-error */
import * as pty from "@lydell/node-pty"
import type { Opts, Proc } from "./pty"
export type { Disp, Exit, Opts, Proc } from "./pty"
export function spawn(file: string, args: string[], opts: Opts): Proc {
const proc = pty.spawn(file, args, opts)
return {
pid: proc.pid,
onData(listener) {
return proc.onData(listener)
},
onExit(listener) {
return proc.onExit(listener)
},
write(data) {
proc.write(data)
},
resize(cols, rows) {
proc.resize(cols, rows)
},
kill(signal) {
proc.kill(signal)
},
}
}

View File

@@ -0,0 +1,25 @@
export type Disp = {
dispose(): void
}
export type Exit = {
exitCode: number
signal?: number | string
}
export type Opts = {
name: string
cols?: number
rows?: number
cwd?: string
env?: Record<string, string>
}
export type Proc = {
pid: number
onData(listener: (data: string) => void): Disp
onExit(listener: (event: Exit) => void): Disp
write(data: string): void
resize(cols: number, rows: number): void
kill(signal?: string): void
}