mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-13 23:52:06 +00:00
feat(server): wire Server.listen() through native HttpApi listener (kill-switch)
When the effect-httpapi backend is selected, Server.listen() now delegates to HttpApiListener.listen() — a native Bun.serve listener with inline WebSocket upgrade handling — instead of routing through the Hono runtime adapter (Hono.fetch + createBunWebSocket). The Hono backend path is unchanged, and a kill-switch env var (OPENCODE_HTTPAPI_LEGACY_LISTENER) forces the effect-httpapi backend back through the Hono adapter as an escape hatch if the native listener regresses for a user. This unblocks the Hono deletion arc by giving the native listener real production traffic on dev/beta/local channels (where OPENCODE_EXPERIMENTAL_HTTPAPI defaults on) while leaving prod/latest channels on the Hono path.
This commit is contained in:
@@ -94,6 +94,12 @@ export const Flag = {
|
||||
OPENCODE_EXPERIMENTAL_HTTPAPI:
|
||||
truthy("OPENCODE_EXPERIMENTAL_HTTPAPI") ||
|
||||
(!falsy("OPENCODE_EXPERIMENTAL_HTTPAPI") && HTTPAPI_DEFAULT_ON_CHANNELS.has(InstallationChannel)),
|
||||
// Kill-switch that forces the effect-httpapi backend back through the legacy
|
||||
// hono runtime adapter (Hono.fetch + createBunWebSocket) instead of the
|
||||
// native Bun.serve listener. Defaults to false; set to "true"/"1" to revert
|
||||
// if the native listener regresses for a user. Has no effect when the hono
|
||||
// backend is selected.
|
||||
OPENCODE_HTTPAPI_LEGACY_LISTENER: truthy("OPENCODE_HTTPAPI_LEGACY_LISTENER"),
|
||||
OPENCODE_EXPERIMENTAL_WORKSPACES: OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_WORKSPACES"),
|
||||
OPENCODE_EXPERIMENTAL_EVENT_SYSTEM: OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_EVENT_SYSTEM"),
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { InstanceMiddleware } from "./routes/instance/middleware"
|
||||
import { WorkspaceRoutes } from "./routes/control/workspace"
|
||||
import { ExperimentalHttpApiServer } from "./routes/instance/httpapi/server"
|
||||
import { PublicApi } from "./routes/instance/httpapi/public"
|
||||
import { HttpApiListener } from "./httpapi-listener"
|
||||
import * as ServerBackend from "./backend"
|
||||
import type { CorsOptions } from "./cors"
|
||||
|
||||
@@ -182,35 +183,53 @@ export async function openapiHono() {
|
||||
export let url: URL
|
||||
|
||||
export async function listen(opts: ListenOptions): Promise<Listener> {
|
||||
const built = create(opts)
|
||||
const server = await built.runtime.listen(opts)
|
||||
const selected = select()
|
||||
const native = selected.backend === "effect-httpapi" && !Flag.OPENCODE_HTTPAPI_LEGACY_LISTENER
|
||||
let inner: Listener
|
||||
if (native) {
|
||||
log.info("server backend selected", {
|
||||
...ServerBackend.attributes(selected),
|
||||
"opencode.server.listener": "bun-native",
|
||||
})
|
||||
inner = await HttpApiListener.listen(opts)
|
||||
} else {
|
||||
const built = create(opts)
|
||||
const server = await built.runtime.listen(opts)
|
||||
const innerUrl = new URL("http://localhost")
|
||||
innerUrl.hostname = opts.hostname
|
||||
innerUrl.port = String(server.port)
|
||||
inner = {
|
||||
hostname: opts.hostname,
|
||||
port: server.port,
|
||||
url: innerUrl,
|
||||
stop: (close?: boolean) => server.stop(close),
|
||||
}
|
||||
}
|
||||
|
||||
const next = new URL("http://localhost")
|
||||
next.hostname = opts.hostname
|
||||
next.port = String(server.port)
|
||||
const next = new URL(inner.url)
|
||||
url = next
|
||||
|
||||
const mdns =
|
||||
opts.mdns &&
|
||||
server.port &&
|
||||
inner.port &&
|
||||
opts.hostname !== "127.0.0.1" &&
|
||||
opts.hostname !== "localhost" &&
|
||||
opts.hostname !== "::1"
|
||||
if (mdns) {
|
||||
MDNS.publish(server.port, opts.mdnsDomain)
|
||||
MDNS.publish(inner.port, opts.mdnsDomain)
|
||||
} else if (opts.mdns) {
|
||||
log.warn("mDNS enabled but hostname is loopback; skipping mDNS publish")
|
||||
}
|
||||
|
||||
let closing: Promise<void> | undefined
|
||||
return {
|
||||
hostname: opts.hostname,
|
||||
port: server.port,
|
||||
hostname: inner.hostname,
|
||||
port: inner.port,
|
||||
url: next,
|
||||
stop(close?: boolean) {
|
||||
closing ??= (async () => {
|
||||
if (mdns) MDNS.unpublish()
|
||||
await server.stop(close)
|
||||
await inner.stop(close)
|
||||
})()
|
||||
return closing
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user