refactor(server): unify instance httpapi middleware routing

Unify declared instance HTTP API endpoints under typed middleware routing, including event streaming and PTY WebSocket connect handling.\n\nPreserve PTY connect compatibility by checking missing PTYs before parsing optional cursor and ticket query fields, with regression coverage.
This commit is contained in:
Kit Langton
2026-05-27 08:45:11 -04:00
committed by GitHub
parent 9941e70d29
commit 76d814e747
18 changed files with 340 additions and 195 deletions

View File

@@ -9,6 +9,7 @@ import { WorkspaceID } from "../../src/control-plane/schema"
import type { WorkspaceAdapter } from "../../src/control-plane/types"
import { Workspace } from "../../src/control-plane/workspace"
import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace"
import { EventPaths } from "../../src/server/routes/instance/httpapi/groups/event"
import { Session } from "@/session/session"
import * as Log from "@opencode-ai/core/util/log"
import { Server } from "../../src/server/server"
@@ -344,6 +345,7 @@ describe("workspace HttpApi", () => {
proxied.push(request)
const url = new URL(request.url)
if (url.pathname === "/base/global/event") return eventStreamResponse()
if (url.pathname === "/base/event") return eventStreamResponse()
if (url.pathname === "/base/sync/history") return Response.json([])
return new Response(
JSON.stringify({
@@ -413,6 +415,18 @@ describe("workspace HttpApi", () => {
])
expect(forwarded[0]?.headers).not.toHaveProperty("x-opencode-directory")
expect(forwarded[0]?.headers).not.toHaveProperty("x-opencode-workspace")
const eventURL = new URL(`http://localhost${EventPaths.event}`)
eventURL.searchParams.set("workspace", workspace.id)
const eventResponse = yield* request(eventURL.toString(), dir)
expect(eventResponse.status).toBe(200)
expect(eventResponse.headers.get("content-type")).toContain("text/event-stream")
if (!eventResponse.body) throw new Error("missing proxied event response body")
const eventReader = eventResponse.body.getReader()
const event = yield* Effect.promise(() => eventReader.read())
yield* Effect.promise(() => eventReader.cancel())
expect(new TextDecoder().decode(event.value)).toContain("server.connected")
expect(proxied.some((item) => new URL(item.url).pathname === "/base/event")).toBe(true)
} finally {
void remote.stop(true)
yield* request(WorkspacePaths.remove.replace(":id", workspace.id), dir, { method: "DELETE" })