Skip to content

Commit 1508196

Browse files
authored
feat: bridge question routes from Hono to Effect HttpApi (#22718)
1 parent f624360 commit 1508196

4 files changed

Lines changed: 49 additions & 27 deletions

File tree

packages/opencode/src/flag/flag.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export namespace Flag {
8484
export const OPENCODE_STRICT_CONFIG_DEPS = truthy("OPENCODE_STRICT_CONFIG_DEPS")
8585

8686
export const OPENCODE_WORKSPACE_ID = process.env["OPENCODE_WORKSPACE_ID"]
87+
export const OPENCODE_EXPERIMENTAL_HTTPAPI = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_HTTPAPI")
8788
export const OPENCODE_EXPERIMENTAL_WORKSPACES = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_WORKSPACES")
8889

8990
function number(key: string) {

packages/opencode/src/server/instance/httpapi/question.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { QuestionID } from "@/question/schema"
33
import { Effect, Layer, Schema } from "effect"
44
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
55

6-
const root = "/experimental/httpapi/question"
6+
const root = "/question"
77

88
export const QuestionApi = HttpApi.make("question")
99
.add(
@@ -29,19 +29,29 @@ export const QuestionApi = HttpApi.make("question")
2929
description: "Provide answers to a question request from the AI assistant.",
3030
}),
3131
),
32+
HttpApiEndpoint.post("reject", `${root}/:requestID/reject`, {
33+
params: { requestID: QuestionID },
34+
success: Schema.Boolean,
35+
}).annotateMerge(
36+
OpenApi.annotations({
37+
identifier: "question.reject",
38+
summary: "Reject question request",
39+
description: "Reject a question request from the AI assistant.",
40+
}),
41+
),
3242
)
3343
.annotateMerge(
3444
OpenApi.annotations({
3545
title: "question",
36-
description: "Experimental HttpApi question routes.",
46+
description: "Question routes.",
3747
}),
3848
),
3949
)
4050
.annotateMerge(
4151
OpenApi.annotations({
42-
title: "opencode experimental HttpApi",
52+
title: "opencode HttpApi",
4353
version: "0.0.1",
44-
description: "Experimental HttpApi surface for selected instance routes.",
54+
description: "Effect HttpApi surface for instance routes.",
4555
}),
4656
)
4757

@@ -64,8 +74,13 @@ export const QuestionLive = Layer.unwrap(
6474
return true
6575
})
6676

77+
const reject = Effect.fn("QuestionHttpApi.reject")(function* (ctx: { params: { requestID: QuestionID } }) {
78+
yield* svc.reject(ctx.params.requestID)
79+
return true
80+
})
81+
6782
return HttpApiBuilder.group(QuestionApi, "question", (handlers) =>
68-
handlers.handle("list", list).handle("reply", reply),
83+
handlers.handle("list", list).handle("reply", reply).handle("reject", reject),
6984
)
7085
}),
7186
).pipe(Layer.provide(Question.defaultLayer))

packages/opencode/src/server/instance/httpapi/server.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
import { NodeHttpServer } from "@effect/platform-node"
21
import { Effect, Layer, Redacted, Schema } from "effect"
32
import { HttpApiBuilder, HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi"
4-
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
5-
import { createServer } from "node:http"
3+
import { HttpRouter, HttpServer, HttpServerRequest } from "effect/unstable/http"
64
import { AppRuntime } from "@/effect/app-runtime"
75
import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref"
6+
import { Observability } from "@/effect/observability"
7+
import { memoMap } from "@/effect/run-service"
88
import { Flag } from "@/flag/flag"
99
import { InstanceBootstrap } from "@/project/bootstrap"
1010
import { Instance } from "@/project/instance"
11+
import { lazy } from "@/util/lazy"
1112
import { Filesystem } from "@/util/filesystem"
12-
import { Permission } from "@/permission"
13-
import { ProviderAuth } from "@/provider/auth"
14-
import { Question } from "@/question"
1513
import { PermissionApi, PermissionLive } from "./permission"
1614
import { ProviderApi, ProviderLive } from "./provider"
1715
import { QuestionApi, QuestionLive } from "./question"
@@ -113,26 +111,24 @@ export namespace ExperimentalHttpApiServer {
113111
const ProviderSecured = ProviderApi.middleware(Authorization)
114112

115113
export const routes = Layer.mergeAll(
116-
HttpApiBuilder.layer(QuestionSecured, { openapiPath: "/experimental/httpapi/question/doc" }).pipe(
117-
Layer.provide(QuestionLive),
118-
),
114+
HttpApiBuilder.layer(QuestionSecured).pipe(Layer.provide(QuestionLive)),
119115
HttpApiBuilder.layer(PermissionSecured, { openapiPath: "/experimental/httpapi/permission/doc" }).pipe(
120116
Layer.provide(PermissionLive),
121117
),
122118
HttpApiBuilder.layer(ProviderSecured, { openapiPath: "/experimental/httpapi/provider/doc" }).pipe(
123119
Layer.provide(ProviderLive),
124120
),
125-
).pipe(Layer.provide(auth), Layer.provide(normalize), Layer.provide(instance))
126-
127-
export const layer = (opts: { hostname: string; port: number }) =>
128-
HttpRouter.serve(routes, { disableListenLog: true, disableLogger: true }).pipe(
129-
Layer.provideMerge(NodeHttpServer.layer(createServer, { port: opts.port, host: opts.hostname })),
130-
)
121+
).pipe(
122+
Layer.provide(auth),
123+
Layer.provide(normalize),
124+
Layer.provide(instance),
125+
Layer.provide(HttpServer.layerServices),
126+
Layer.provideMerge(Observability.layer),
127+
)
131128

132-
export const layerTest = HttpRouter.serve(routes, { disableListenLog: true, disableLogger: true }).pipe(
133-
Layer.provideMerge(NodeHttpServer.layerTest),
134-
Layer.provideMerge(Question.defaultLayer),
135-
Layer.provideMerge(Permission.defaultLayer),
136-
Layer.provideMerge(ProviderAuth.defaultLayer),
129+
export const webHandler = lazy(() =>
130+
HttpRouter.toWebHandler(routes, {
131+
memoMap,
132+
}),
137133
)
138134
}

packages/opencode/src/server/instance/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { LSP } from "../../lsp"
1414
import { Command } from "../../command"
1515
import { QuestionRoutes } from "./question"
1616
import { PermissionRoutes } from "./permission"
17+
import { Flag } from "@/flag/flag"
18+
import { ExperimentalHttpApiServer } from "./httpapi/server"
1719
import { ProjectRoutes } from "./project"
1820
import { SessionRoutes } from "./session"
1921
import { PtyRoutes } from "./pty"
@@ -27,15 +29,22 @@ import { SyncRoutes } from "./sync"
2729
import { WorkspaceRouterMiddleware } from "./middleware"
2830
import { AppRuntime } from "@/effect/app-runtime"
2931

30-
export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono =>
31-
new Hono()
32+
export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
33+
const app = new Hono()
3234
.use(WorkspaceRouterMiddleware(upgrade))
3335
.route("/project", ProjectRoutes())
3436
.route("/pty", PtyRoutes(upgrade))
3537
.route("/config", ConfigRoutes())
3638
.route("/experimental", ExperimentalRoutes())
3739
.route("/session", SessionRoutes())
3840
.route("/permission", PermissionRoutes())
41+
42+
if (Flag.OPENCODE_EXPERIMENTAL_HTTPAPI) {
43+
const handler = ExperimentalHttpApiServer.webHandler().handler
44+
app.all("/question", (c) => handler(c.req.raw)).all("/question/*", (c) => handler(c.req.raw))
45+
}
46+
47+
return app
3948
.route("/question", QuestionRoutes())
4049
.route("/provider", ProviderRoutes())
4150
.route("/sync", SyncRoutes())
@@ -283,3 +292,4 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono =>
283292
return c.json(await AppRuntime.runPromise(Format.Service.use((svc) => svc.status())))
284293
},
285294
)
295+
}

0 commit comments

Comments
 (0)