import ClawdbotKit import ClawdbotProtocol import Foundation import OSLog @MainActor final class ExecApprovalsGatewayPrompter { static let shared = ExecApprovalsGatewayPrompter() private let logger = Logger(subsystem: "com.clawdbot", category: "exec-approvals.gateway") private var task: Task? struct GatewayApprovalRequest: Codable, Sendable { var id: String var request: ExecApprovalPromptRequest var createdAtMs: Int var expiresAtMs: Int } func start() { guard self.task == nil else { return } self.task = Task { [weak self] in await self?.run() } } func stop() { self.task?.cancel() self.task = nil } private func run() async { let stream = await GatewayConnection.shared.subscribe(bufferingNewest: 200) for await push in stream { if Task.isCancelled { return } await self.handle(push: push) } } private func handle(push: GatewayPush) async { guard case let .event(evt) = push else { return } guard evt.event == "exec.approval.requested" else { return } guard let payload = evt.payload else { return } do { let data = try JSONEncoder().encode(payload) let request = try JSONDecoder().decode(GatewayApprovalRequest.self, from: data) let decision = ExecApprovalsPromptPresenter.prompt(request.request) try await GatewayConnection.shared.requestVoid( method: .execApprovalResolve, params: [ "id": AnyCodable(request.id), "decision": AnyCodable(decision.rawValue), ], timeoutMs: 10000) } catch { self.logger.error("exec approval handling failed \(error.localizedDescription, privacy: .public)") } } }