fix(server): provide fresh ConfigProvider per HttpApi listener (#25726)
This commit is contained in:
@@ -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)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user