From ce695ae270824bf97d0f50614a2d265b20c1ff63 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sun, 31 May 2026 22:37:23 +0800 Subject: [PATCH] refactor(server): effectify permission reply route --- .../src/server/instance/permission.ts | 15 +++-- .../test/server/permission-routes.test.ts | 57 +++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 packages/opencode/test/server/permission-routes.test.ts diff --git a/packages/opencode/src/server/instance/permission.ts b/packages/opencode/src/server/instance/permission.ts index b43284928..ccf2a56fa 100644 --- a/packages/opencode/src/server/instance/permission.ts +++ b/packages/opencode/src/server/instance/permission.ts @@ -79,11 +79,16 @@ export const PermissionRoutes = lazy(() => async (c) => { const params = c.req.valid("param") const json = c.req.valid("json") - await Permission.reply({ - requestID: params.requestID, - reply: json.reply, - message: json.message, - }) + await AppRuntime.runPromise( + Effect.gen(function* () { + const permission = yield* Permission.Service + yield* permission.reply({ + requestID: params.requestID, + reply: json.reply, + message: json.message, + }) + }), + ) return c.json(true) }, ) diff --git a/packages/opencode/test/server/permission-routes.test.ts b/packages/opencode/test/server/permission-routes.test.ts new file mode 100644 index 000000000..8cf961838 --- /dev/null +++ b/packages/opencode/test/server/permission-routes.test.ts @@ -0,0 +1,57 @@ +import { afterEach, describe, expect, test } from "bun:test" +import { Deferred } from "effect" +import { Hono } from "hono" +import { AppRuntime } from "../../src/effect/app-runtime" +import { Permission } from "../../src/permission" +import { PermissionID } from "../../src/permission/schema" +import { Instance } from "../../src/project/instance" +import { PermissionRoutes } from "../../src/server/instance/permission" +import { SessionID } from "../../src/session/schema" +import { tmpdir } from "../fixture/fixture" + +afterEach(async () => { + await Instance.disposeAll() +}) + +describe("permission routes", () => { + function app() { + return new Hono().route("/permission", PermissionRoutes()) + } + + test("replies to a pending permission through the route runtime", async () => { + await using tmp = await tmpdir({ git: true }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const requestID = PermissionID.ascending() + const pending = await AppRuntime.runPromise(Deferred.make()) + const asked = AppRuntime.runPromise( + Permission.Service.use((permission) => + permission.ask({ + id: requestID, + sessionID: SessionID.descending(), + permission: "bash", + patterns: ["echo ok"], + metadata: {}, + always: ["echo ok"], + ruleset: [{ permission: "bash", pattern: "echo ok", action: "ask" }], + onPending: () => Deferred.succeed(pending, undefined), + }), + ), + ) + + await AppRuntime.runPromise(Deferred.await(pending)) + + const response = await app().request(`/permission/${requestID}/reply`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ reply: "once" }), + }) + + expect(response.status).toBe(200) + expect(await response.json()).toBe(true) + await expect(asked).resolves.toBeUndefined() + }, + }) + }) +})