diff --git a/packages/opencode/src/permission/next.ts b/packages/opencode/src/permission/next.ts index 9ebcd170f7..ad5e1b1271 100644 --- a/packages/opencode/src/permission/next.ts +++ b/packages/opencode/src/permission/next.ts @@ -2,7 +2,6 @@ import { Bus } from "@/bus" import { BusEvent } from "@/bus/bus-event" import { Config } from "@/config/config" import { Identifier } from "@/id/id" -import { Plugin } from "@/plugin" import { Instance } from "@/project/instance" import { Storage } from "@/storage/storage" import { fn } from "@/util/fn" @@ -121,37 +120,57 @@ export namespace PermissionNext { async (input) => { const s = await state() const { ruleset, ...request } = input - for (const pattern of request.patterns ?? []) { + const ask = (request.patterns ?? []).reduce((ask, pattern) => { const rule = evaluate(request.permission, pattern, ruleset, s.approved) log.info("evaluated", { permission: request.permission, pattern, action: rule }) - if (rule.action === "deny") + if (rule.action === "deny") { throw new DeniedError(ruleset.filter((r) => Wildcard.match(request.permission, r.permission))) - if (rule.action === "ask") { - const id = input.id ?? Identifier.ascending("permission") - const info: Request = { - id, - ...request, - } - const pluginResult = await Plugin.trigger("permission.ask", info, { - status: "ask" as "ask" | "allow" | "deny", - }) - if (pluginResult.status === "deny") { - throw new RejectedError() - } - if (pluginResult.status === "allow") { - continue - } - return new Promise((resolve, reject) => { - s.pending[id] = { - info, - resolve, - reject, + } + return ask || rule.action === "ask" + }, false) + + if (!ask) return + + const id = input.id ?? Identifier.ascending("permission") + const info: Request = { + id, + ...request, + } + + return new Promise((resolve, reject) => { + s.pending[id] = { + info, + resolve, + reject, + } + + void import("@/plugin") + .then((plugin) => + plugin.Plugin.trigger("permission.ask", info, { + status: "ask" as "ask" | "allow" | "deny", + }), + ) + .then((result) => { + const existing = s.pending[id] + if (!existing) return + if (result.status === "deny") { + delete s.pending[id] + existing.reject(new RejectedError()) + return + } + if (result.status === "allow") { + delete s.pending[id] + existing.resolve() + return } Bus.publish(Event.Asked, info) }) - } - if (rule.action === "allow") continue - } + .catch((error) => { + log.error("permission.ask plugin failed", { error }) + if (!s.pending[id]) return + Bus.publish(Event.Asked, info) + }) + }) }, )