summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-27 16:07:31 -0400
committerGitHub <[email protected]>2026-04-27 16:07:31 -0400
commit51fc10e407139f04285fd784b6511d40b07ecf42 (patch)
treec66e7de64a50b6c41fc27e921cd0728d78078528 /packages
parent7a1c8465f5f96b2f530da72839788b6097173063 (diff)
downloadopencode-51fc10e407139f04285fd784b6511d40b07ecf42.tar.gz
opencode-51fc10e407139f04285fd784b6511d40b07ecf42.zip
fix(httpapi): enforce instance route parity (#24660)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/pty.ts21
-rw-r--r--packages/opencode/src/server/routes/instance/index.ts1
-rw-r--r--packages/opencode/test/server/httpapi-bridge.test.ts23
-rw-r--r--packages/opencode/test/server/httpapi-pty.test.ts16
4 files changed, 61 insertions, 0 deletions
diff --git a/packages/opencode/src/server/routes/instance/httpapi/pty.ts b/packages/opencode/src/server/routes/instance/httpapi/pty.ts
index 930322ff3..4e46f30df 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/pty.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/pty.ts
@@ -1,6 +1,7 @@
import { EffectBridge } from "@/effect/bridge"
import { Pty } from "@/pty"
import { PtyID } from "@/pty/schema"
+import { Shell } from "@/shell/shell"
import { Effect, Layer, Schema } from "effect"
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
@@ -14,8 +15,14 @@ const Params = Schema.Struct({
const CursorQuery = Schema.Struct({
cursor: Schema.optional(Schema.String),
})
+const ShellItem = Schema.Struct({
+ path: Schema.String,
+ name: Schema.String,
+ acceptable: Schema.Boolean,
+})
export const PtyPaths = {
+ shells: `${root}/shells`,
list: root,
create: root,
get: `${root}/:ptyID`,
@@ -28,6 +35,15 @@ export const PtyApi = HttpApi.make("pty")
.add(
HttpApiGroup.make("pty")
.add(
+ HttpApiEndpoint.get("shells", PtyPaths.shells, {
+ success: Schema.Array(ShellItem),
+ }).annotateMerge(
+ OpenApi.annotations({
+ identifier: "pty.shells",
+ summary: "List available shells",
+ description: "Get a list of available shells on the system.",
+ }),
+ ),
HttpApiEndpoint.get("list", PtyPaths.list, {
success: Schema.Array(Pty.Info),
}).annotateMerge(
@@ -101,6 +117,10 @@ export const ptyHandlers = Layer.unwrap(
Effect.gen(function* () {
const pty = yield* Pty.Service
+ const shells = Effect.fn("PtyHttpApi.shells")(function* () {
+ return yield* Effect.promise(() => Shell.list())
+ })
+
const list = Effect.fn("PtyHttpApi.list")(function* () {
return yield* pty.list()
})
@@ -143,6 +163,7 @@ export const ptyHandlers = Layer.unwrap(
return HttpApiBuilder.group(PtyApi, "pty", (handlers) =>
handlers
+ .handle("shells", shells)
.handle("list", list)
.handle("create", create)
.handle("get", get)
diff --git a/packages/opencode/src/server/routes/instance/index.ts b/packages/opencode/src/server/routes/instance/index.ts
index 86a1db12b..68b508a9a 100644
--- a/packages/opencode/src/server/routes/instance/index.ts
+++ b/packages/opencode/src/server/routes/instance/index.ts
@@ -99,6 +99,7 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
app.post(SyncPaths.start, (c) => handler(c.req.raw, context))
app.post(SyncPaths.replay, (c) => handler(c.req.raw, context))
app.post(SyncPaths.history, (c) => handler(c.req.raw, context))
+ app.get(PtyPaths.shells, (c) => handler(c.req.raw, context))
app.get(PtyPaths.list, (c) => handler(c.req.raw, context))
app.post(PtyPaths.create, (c) => handler(c.req.raw, context))
app.get(PtyPaths.get, (c) => handler(c.req.raw, context))
diff --git a/packages/opencode/test/server/httpapi-bridge.test.ts b/packages/opencode/test/server/httpapi-bridge.test.ts
index 37f0a5ec1..dac23a654 100644
--- a/packages/opencode/test/server/httpapi-bridge.test.ts
+++ b/packages/opencode/test/server/httpapi-bridge.test.ts
@@ -3,6 +3,7 @@ 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 { FilePaths } from "../../src/server/routes/instance/httpapi/file"
import * as Log from "@opencode-ai/core/util/log"
import { resetDatabase } from "../fixture/db"
@@ -25,6 +26,10 @@ function app(input?: { password?: string; username?: string }) {
return InstanceRoutes(websocket)
}
+function routeKey(route: ReturnType<typeof InstanceRoutes>["routes"][number]) {
+ return `${route.method} ${route.path}`
+}
+
function authorization(username: string, password: string) {
return `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
}
@@ -46,6 +51,24 @@ afterEach(async () => {
})
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("allows requests when auth is disabled", async () => {
await using tmp = await tmpdir({ git: true })
await Bun.write(`${tmp.path}/hello.txt`, "hello")
diff --git a/packages/opencode/test/server/httpapi-pty.test.ts b/packages/opencode/test/server/httpapi-pty.test.ts
index 65a115a41..ffaea3b75 100644
--- a/packages/opencode/test/server/httpapi-pty.test.ts
+++ b/packages/opencode/test/server/httpapi-pty.test.ts
@@ -27,6 +27,22 @@ afterEach(async () => {
})
describe("pty HttpApi bridge", () => {
+ test("serves available shell list through experimental Effect routes", async () => {
+ await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
+ const response = await app().request(PtyPaths.shells, { headers: { "x-opencode-directory": tmp.path } })
+
+ expect(response.status).toBe(200)
+ expect(await response.json()).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ path: expect.any(String),
+ name: expect.any(String),
+ acceptable: expect.any(Boolean),
+ }),
+ ]),
+ )
+ })
+
testPty("serves PTY JSON routes through experimental Effect routes", async () => {
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
const headers = { "x-opencode-directory": tmp.path }