Files
clawdbot/src/gateway/exec-approval-manager.ts
2026-01-19 10:07:56 +00:00

75 lines
2.1 KiB
TypeScript

import { randomUUID } from "node:crypto";
import type { ExecApprovalDecision } from "../infra/exec-approvals.js";
export type ExecApprovalRequestPayload = {
command: string;
cwd?: string | null;
host?: string | null;
security?: string | null;
ask?: string | null;
agentId?: string | null;
resolvedPath?: string | null;
sessionKey?: string | null;
};
export type ExecApprovalRecord = {
id: string;
request: ExecApprovalRequestPayload;
createdAtMs: number;
expiresAtMs: number;
resolvedAtMs?: number;
decision?: ExecApprovalDecision;
resolvedBy?: string | null;
};
type PendingEntry = {
record: ExecApprovalRecord;
resolve: (decision: ExecApprovalDecision) => void;
reject: (err: Error) => void;
timer: ReturnType<typeof setTimeout>;
};
export class ExecApprovalManager {
private pending = new Map<string, PendingEntry>();
create(request: ExecApprovalRequestPayload, timeoutMs: number): ExecApprovalRecord {
const now = Date.now();
const id = randomUUID();
const record: ExecApprovalRecord = {
id,
request,
createdAtMs: now,
expiresAtMs: now + timeoutMs,
};
return record;
}
async waitForDecision(record: ExecApprovalRecord, timeoutMs: number): Promise<ExecApprovalDecision> {
return await new Promise<ExecApprovalDecision>((resolve, reject) => {
const timer = setTimeout(() => {
this.pending.delete(record.id);
resolve("deny");
}, timeoutMs);
this.pending.set(record.id, { record, resolve, reject, timer });
});
}
resolve(recordId: string, decision: ExecApprovalDecision, resolvedBy?: string | null): boolean {
const pending = this.pending.get(recordId);
if (!pending) return false;
clearTimeout(pending.timer);
pending.record.resolvedAtMs = Date.now();
pending.record.decision = decision;
pending.record.resolvedBy = resolvedBy ?? null;
this.pending.delete(recordId);
pending.resolve(decision);
return true;
}
getSnapshot(recordId: string): ExecApprovalRecord | null {
const entry = this.pending.get(recordId);
return entry?.record ?? null;
}
}