fix(server): provide fresh ConfigProvider per HttpApi listener (#25726)

This commit is contained in:
Kit Langton
2026-05-04 13:06:29 -04:00
committed by GitHub
parent 25dc6f09bc
commit fb07c2070c
2 changed files with 25 additions and 17 deletions
+7 -1
View File
@@ -5,7 +5,7 @@ import { lazy } from "@/util/lazy"
import * as Log from "@opencode-ai/core/util/log" import * as Log from "@opencode-ai/core/util/log"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
import { WorkspaceID } from "@/control-plane/schema" import { WorkspaceID } from "@/control-plane/schema"
import { Context, Effect, Exit, Layer, Scope } from "effect" import { ConfigProvider, Context, Effect, Exit, Layer, Scope } from "effect"
import { HttpRouter, HttpServer } from "effect/unstable/http" import { HttpRouter, HttpServer } from "effect/unstable/http"
import { OpenApi } from "effect/unstable/httpapi" import { OpenApi } from "effect/unstable/httpapi"
import * as HttpApiServer from "#httpapi-server" import * as HttpApiServer from "#httpapi-server"
@@ -259,6 +259,12 @@ async function listenHttpApi(opts: ListenOptions, selection: ServerBackend.Selec
}).pipe( }).pipe(
Layer.provideMerge(WebSocketTracker.layer), Layer.provideMerge(WebSocketTracker.layer),
Layer.provideMerge(HttpApiServer.layer({ port, hostname: opts.hostname })), Layer.provideMerge(HttpApiServer.layer({ port, hostname: opts.hostname })),
// Install a fresh `ConfigProvider` per listener so `Config.string(...)`
// reads reflect the current `process.env`. Effect's default
// `ConfigProvider` snapshots `process.env` on first read and caches the
// result on a module-singleton Reference; without overriding it here,
// every later `Server.listen()` keeps observing that initial snapshot.
Layer.provide(ConfigProvider.layer(ConfigProvider.fromEnv())),
) )
const start = async (port: number) => { const start = async (port: number) => {
@@ -40,8 +40,8 @@ async function startListener(backend: "effect-httpapi" | "hono" = "effect-httpap
return Server.listen({ hostname: "127.0.0.1", port: 0 }) return Server.listen({ hostname: "127.0.0.1", port: 0 })
} }
async function startNoAuthListener() { async function startNoAuthListener(backend: "effect-httpapi" | "hono" = "effect-httpapi") {
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = false Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = backend === "effect-httpapi"
Flag.OPENCODE_SERVER_PASSWORD = undefined Flag.OPENCODE_SERVER_PASSWORD = undefined
Flag.OPENCODE_SERVER_USERNAME = auth.username Flag.OPENCODE_SERVER_USERNAME = auth.username
delete process.env.OPENCODE_SERVER_PASSWORD delete process.env.OPENCODE_SERVER_PASSWORD
@@ -300,18 +300,20 @@ describe("HttpApi Server.listen", () => {
} }
}) })
testPty("keeps PTY websocket tickets optional when server auth is disabled", async () => { for (const backend of ["effect-httpapi", "hono"] as const) {
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } }) testPty(`keeps PTY websocket tickets optional when server auth is disabled (${backend})`, async () => {
const listener = await startNoAuthListener() await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
try { const listener = await startNoAuthListener(backend)
const info = await createCat(listener, tmp.path) try {
const ws = await openSocket(socketURL(listener, info.id, tmp.path)) const info = await createCat(listener, tmp.path)
const message = waitForMessage(ws, (message) => message.includes("ping-no-auth")) const ws = await openSocket(socketURL(listener, info.id, tmp.path))
ws.send("ping-no-auth\n") const message = waitForMessage(ws, (message) => message.includes(`ping-no-auth-${backend}`))
expect(await message).toContain("ping-no-auth") ws.send(`ping-no-auth-${backend}\n`)
ws.close(1000) expect(await message).toContain(`ping-no-auth-${backend}`)
} finally { ws.close(1000)
await stop(listener, "timed out cleaning up no-auth listener").catch(() => undefined) } finally {
} await stop(listener, "timed out cleaning up no-auth listener").catch(() => undefined)
}) }
})
}
}) })