refactor(httpapi): fork server startup by flag (#24799)

This commit is contained in:
Kit Langton
2026-04-28 11:02:35 -04:00
committed by GitHub
parent 3fa78a8b01
commit 7739cc53b4
23 changed files with 190 additions and 371 deletions

View File

@@ -1,28 +1,11 @@
import { afterEach, describe, expect, test } from "bun:test"
import type { UpgradeWebSocket } from "hono/ws"
import { Flag } from "@opencode-ai/core/flag/flag"
import { Instance } from "../../src/project/instance"
import { InstanceRoutes } from "../../src/server/routes/instance"
import { WorkspaceRoutes } from "../../src/server/routes/control/workspace"
import { ConfigApi } from "../../src/server/routes/instance/httpapi/config"
import { EventPaths } from "../../src/server/routes/instance/httpapi/event"
import { ExperimentalApi } from "../../src/server/routes/instance/httpapi/experimental"
import { FileApi, FilePaths } from "../../src/server/routes/instance/httpapi/file"
import { InstanceApi } from "../../src/server/routes/instance/httpapi/instance"
import { McpApi } from "../../src/server/routes/instance/httpapi/mcp"
import { PermissionApi } from "../../src/server/routes/instance/httpapi/permission"
import { ProjectApi } from "../../src/server/routes/instance/httpapi/project"
import { ProviderApi } from "../../src/server/routes/instance/httpapi/provider"
import { PtyApi, PtyPaths } from "../../src/server/routes/instance/httpapi/pty"
import { QuestionApi } from "../../src/server/routes/instance/httpapi/question"
import { SessionApi } from "../../src/server/routes/instance/httpapi/session"
import { SyncApi } from "../../src/server/routes/instance/httpapi/sync"
import { TuiApi } from "../../src/server/routes/instance/httpapi/tui"
import { WorkspaceApi } from "../../src/server/routes/instance/httpapi/workspace"
import { PublicApi } from "../../src/server/routes/instance/httpapi/public"
import { Server } from "../../src/server/server"
import * as Log from "@opencode-ai/core/util/log"
import { HttpApi, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { OpenApi } from "effect/unstable/httpapi"
import { resetDatabase } from "../fixture/db"
import { tmpdir } from "../fixture/fixture"
@@ -34,48 +17,13 @@ const original = {
OPENCODE_SERVER_USERNAME: Flag.OPENCODE_SERVER_USERNAME,
}
const websocket = (() => () => new Response(null, { status: 501 })) as unknown as UpgradeWebSocket
const methods = ["get", "post", "put", "delete", "patch"] as const
function app(input?: { password?: string; username?: string }) {
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true
Flag.OPENCODE_SERVER_PASSWORD = input?.password
Flag.OPENCODE_SERVER_USERNAME = input?.username
return InstanceRoutes(websocket)
}
function routeKey(route: ReturnType<typeof InstanceRoutes>["routes"][number]) {
return `${route.method} ${route.path}`
}
function reflectedHttpApiRoutes() {
const routes = [`GET ${EventPaths.event}`, `GET ${PtyPaths.connect}`]
function addRoutes<Id extends string, Groups extends HttpApiGroup.Any>(api: HttpApi.HttpApi<Id, Groups>) {
HttpApi.reflect(api, {
onGroup() {},
onEndpoint({ endpoint }) {
routes.push(`${endpoint.method} ${endpoint.path}`)
},
})
}
addRoutes(ConfigApi)
addRoutes(ExperimentalApi)
addRoutes(FileApi)
addRoutes(InstanceApi)
addRoutes(McpApi)
addRoutes(PermissionApi)
addRoutes(ProjectApi)
addRoutes(ProviderApi)
addRoutes(PtyApi)
addRoutes(QuestionApi)
addRoutes(SessionApi)
addRoutes(SyncApi)
addRoutes(TuiApi)
addRoutes(WorkspaceApi)
return [...new Set(routes)]
return Server.Default().app
}
function openApiRouteKeys(spec: { paths: Record<string, Partial<Record<(typeof methods)[number], unknown>>> }) {
@@ -106,40 +54,7 @@ afterEach(async () => {
await resetDatabase()
})
describe("HttpApi Hono bridge", () => {
test("mounts experimental handlers for every legacy instance route", () => {
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = false
const legacy = InstanceRoutes(websocket)
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true
const experimental = InstanceRoutes(websocket)
const bridge = experimental.routes.slice(0, experimental.routes.length - legacy.routes.length)
const workspaceRoutes = WorkspaceRoutes().routes.map((route) => ({
...route,
path: `/experimental/workspace${route.path === "/" ? "" : route.path}`,
}))
const legacyRoutes = [...new Set([...legacy.routes, ...workspaceRoutes].map(routeKey))]
const bridgeRoutes = new Set(bridge.map(routeKey))
expect(legacyRoutes.filter((route) => !bridgeRoutes.has(route))).toEqual([])
expect([...bridgeRoutes].filter((route) => !legacyRoutes.includes(route)).sort()).toEqual([])
})
test("mounts every Effect HttpApi route through the Hono bridge", () => {
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = false
const legacy = InstanceRoutes(websocket)
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = true
const experimental = InstanceRoutes(websocket)
const bridgeRoutes = new Set(
experimental.routes.slice(0, experimental.routes.length - legacy.routes.length).map(routeKey),
)
const httpApiRoutes = reflectedHttpApiRoutes()
expect(httpApiRoutes.filter((route) => !bridgeRoutes.has(route))).toEqual([])
expect([...bridgeRoutes].filter((route) => !httpApiRoutes.includes(route)).sort()).toEqual([])
})
describe("HttpApi server", () => {
test("covers every generated OpenAPI route with Effect HttpApi contracts", async () => {
const honoRoutes = openApiRouteKeys(await Server.openapi())
const effectRoutes = openApiRouteKeys(OpenApi.fromApi(PublicApi))