UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

286 lines (285 loc) • 10.9 kB
import { r as normalizeAgentIdStrict } from "./agent-id-D1-Q6vR4.mjs"; import "./session-key-DY3MOX4N.mjs"; import { i as deferSqliteWorkerCommitReceipt, s as requestSqliteWorkerOperationAdmission } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs"; import { c as generateToken, h as resolveExecApprovalsSocketPath, l as mergeExecApprovalsSocketDefaults, n as assertNoPendingLegacyExecApprovals, p as resolveExecApprovalsDisplayPath, u as normalizeExecApprovalsInternal } from "./exec-approvals-migration-gate-BHpUkl4x.mjs"; import { t as resolveExecApprovalsFromFileInternal } from "./exec-approvals-resolver-C1cTECqF.mjs"; import { t as applyExecAuthorizationCommit } from "./exec-approvals-authorization.kernel-BkCHCtjQ.mjs"; import { n as maxAsk, r as minSecurity, t as assertExecApprovalsHostPolicyUnchanged } from "./exec-approvals-policy-BUCGxklR.mjs"; import { c as snapshotFromExecApprovalsDatabase, d as writeExecApprovalsConfigRow, l as snapshotFromExecApprovalsRow, n as assertExecApprovalsMutationAllowed, r as deleteExecApprovalsConfigRow, s as serializeExecApprovals, t as ExecApprovalsMutationFencedError } from "./exec-approvals-sqlite-CbRxT0B7.mjs"; import { n as assertOpenClawStateLeaseWorkerOwnedInTransaction } from "./openclaw-state-lease-worker-Ddwq3QcA.mjs"; import { n as assertAgentDeletionWorkerPredicate } from "./agent-deletion.worker-DuP8KOrz.mjs"; import { isDeepStrictEqual } from "node:util"; //#region src/infra/exec-approvals-mutation.kernel.ts /** Apply the requested edit to the transaction's current policy, never a host callback. */ function applyExecApprovalsUpdate(file, update) { if (update.kind === "replace") return update.preserveSocket ? mergeExecApprovalsSocketDefaults({ normalized: update.file, current: file }) : update.file; if (update.kind === "ensure-agent") return file.agents?.[update.agentId] ? null : { ...file, agents: { ...file.agents, [update.agentId]: update.policy } }; const originalDefaults = resolveExecApprovalsFromFileInternal({ file: update.original }).defaults; const currentDefaults = resolveExecApprovalsFromFileInternal({ file }).defaults; const next = structuredClone(file); let changed = false; for (const field of [ "security", "ask", "askFallback" ]) { const currentValue = file.defaults?.[field]; const originalValue = update.original.defaults?.[field]; const doesNotLoosen = field === "ask" ? maxAsk(originalDefaults.ask, currentDefaults.ask) === originalDefaults.ask : minSecurity(originalDefaults[field], currentDefaults[field]) === originalDefaults[field]; if (update.policy[field] !== void 0 && currentValue === update.written.defaults?.[field] && currentValue !== originalValue && doesNotLoosen) { next.defaults = { ...next.defaults, [field]: originalValue }; changed = true; } } return changed ? next : null; } //#endregion //#region src/infra/exec-approvals-mutation.worker.ts function mutate(context, label, operation) { const options = context.stateOptions(); assertNoPendingLegacyExecApprovals({ env: options.env }); return context.write(({ db }) => { requestSqliteWorkerOperationAdmission({ stage: "transaction", facts: void 0 }); const current = snapshotFromExecApprovalsDatabase(db, resolveExecApprovalsDisplayPath(options.env)); const edit = operation(current, db); let snapshot = current; if (edit.next !== null) { const raw = edit.raw ?? serializeExecApprovals(edit.next); if (edit.remove || !current.exists || current.raw !== raw) { if (edit.remove) { deleteExecApprovalsConfigRow(db); snapshot = snapshotFromExecApprovalsRow({ path: current.path }); } else { const persisted = writeExecApprovalsConfigRow({ db, file: edit.next, raw: edit.raw }); snapshot = snapshotFromExecApprovalsRow({ path: current.path, row: { raw_json: persisted } }); } } } const changed = snapshot.raw !== current.raw; const facts = changed ? { kind: "exec-policy-publication", file: snapshot.file } : void 0; requestSqliteWorkerOperationAdmission({ stage: "commit", facts }); if (changed) deferSqliteWorkerCommitReceipt(db, facts); return edit.result(snapshot); }, { operationLabel: label }); } const execPolicyMutationOperations = { "execApprovals.update": (input, context) => mutate(context, "exec-approvals.update", (current, db) => { if (input.baseHash !== void 0 && current.hash !== input.baseHash) return { next: null, result: () => null }; const next = applyExecApprovalsUpdate(current.file, input.update); if (next) assertExecApprovalsMutationAllowed({ db, current: current.file, next }); return { next, result: (snapshot) => snapshot }; }), "execApprovals.restoreSnapshot": (input, context) => mutate(context, "exec-approvals.restore-cas", (current, db) => { if (current.hash !== input.baseHash) return { next: null, result: () => false }; assertExecApprovalsMutationAllowed({ db, current: current.file, next: input.snapshot.file }); return { next: input.snapshot.file, raw: input.snapshot.raw ?? serializeExecApprovals(input.snapshot.file), remove: !input.snapshot.exists, result: () => true }; }), "execApprovals.ensureSnapshot": (_input, context) => { const options = context.stateOptions(); assertNoPendingLegacyExecApprovals({ env: options.env }); const snapshot = snapshotFromExecApprovalsDatabase(context.open().db, resolveExecApprovalsDisplayPath(options.env)); const socketPath = resolveExecApprovalsSocketPath(options.env); const ensureSocket = (file) => { const next = normalizeExecApprovalsInternal(file); return { ...next, socket: { path: next.socket?.path?.trim() || socketPath, token: next.socket?.token?.trim() || generateToken() } }; }; if (snapshot.file.socket?.path?.trim() && snapshot.file.socket.token?.trim() && snapshot.raw === serializeExecApprovals(ensureSocket(snapshot.file))) return snapshot; return mutate(context, "exec-approvals.ensure", (current, db) => { const next = ensureSocket(current.file); assertExecApprovalsMutationAllowed({ db, current: current.file, next }); return { next, result: (result) => result }; }); } }; //#endregion //#region src/infra/exec-approvals-retirement.worker.ts function retireAgentPolicies(input, context) { assertNoPendingLegacyExecApprovals({ env: context.stateOptions().env }); const { guard } = input; const agentId = guard.predicate.agentId; if (guard.lease.scope !== "core:agent-deletion" || guard.lease.key !== agentId) throw new ExecApprovalsMutationFencedError(); return context.write((database) => { assertOpenClawStateLeaseWorkerOwnedInTransaction(database.db, guard.lease); assertAgentDeletionWorkerPredicate(database, guard.predicate); const current = snapshotFromExecApprovalsDatabase(database.db, resolveExecApprovalsDisplayPath(context.stateOptions().env)); const matches = (key) => { const normalized = normalizeAgentIdStrict(key); return normalized.ok && normalized.value === agentId; }; const entries = input.action === "remove" ? Object.entries(current.file.agents ?? {}).filter(([key]) => matches(key)) : input.entries; const agents = { ...current.file.agents }; for (const [key, policy] of entries) { if (!matches(key) || input.action === "restore" && agents[key] !== void 0 && !isDeepStrictEqual(agents[key], policy)) throw new ExecApprovalsMutationFencedError(); if (input.action === "remove") delete agents[key]; else agents[key] = policy; } const next = { ...current.file, agents }; const raw = serializeExecApprovals(next); const changed = entries.length > 0 && raw !== current.raw; if (changed) writeExecApprovalsConfigRow({ db: database.db, file: next, raw }); assertOpenClawStateLeaseWorkerOwnedInTransaction(database.db, guard.lease, "write", "commit", { kind: "exec-approvals-retirement", nonce: input.nonce, agentId, operationId: guard.predicate.operationId, action: input.action, raw: changed ? raw : null }); deferSqliteWorkerCommitReceipt(database.db, { kind: "exec-approvals-retirement", nonce: input.nonce, action: input.action }); return entries; }, { operationLabel: `exec-approvals.retirement-${input.action}` }); } const execApprovalRetirementOperations = { "execApprovals.retireAgent": retireAgentPolicies }; //#endregion //#region src/infra/exec-approvals-authorization.worker.ts function applyAuthorizationBatch(db, initial, items) { let current = initial; const outcomes = items.map((item) => { let next; try { next = applyExecAuthorizationCommit(structuredClone(current.file), item); } catch (error) { return { ok: false, message: error instanceof Error ? error.message : String(error) }; } if (next !== null) { assertExecApprovalsHostPolicyUnchanged(current.file, next); try { assertExecApprovalsMutationAllowed({ db, current: current.file, next }); } catch (error) { if (!(error instanceof ExecApprovalsMutationFencedError)) throw error; return { ok: false, message: error.message }; } const raw = serializeExecApprovals(next); if (!current.exists || current.raw !== raw) current = snapshotFromExecApprovalsRow({ path: current.path, row: { raw_json: raw } }); } return { ok: true, snapshot: current }; }); return { snapshot: current, outcomes }; } function commitExecAuthorizationsInWorker(input, context) { const options = context.stateOptions(); assertNoPendingLegacyExecApprovals({ env: options.env }); const displayPath = resolveExecApprovalsDisplayPath(options.env); if (!input.items.some((item) => item.matches.length > 0 || item.allowAlwaysDecision !== void 0 && item.allowAlwaysDecision.kind !== "one-shot")) { const { db } = context.open(); return applyAuthorizationBatch(db, snapshotFromExecApprovalsDatabase(db, displayPath), input.items).outcomes; } return context.write(({ db }) => { requestSqliteWorkerOperationAdmission({ stage: "transaction", facts: void 0 }); const current = snapshotFromExecApprovalsDatabase(db, displayPath); const committed = applyAuthorizationBatch(db, current, input.items); if (committed.snapshot.raw !== current.raw) writeExecApprovalsConfigRow({ db, file: committed.snapshot.file, raw: committed.snapshot.raw ?? void 0 }); requestSqliteWorkerOperationAdmission({ stage: "commit", facts: void 0 }); return committed.outcomes; }, { operationLabel: "exec-approvals.commit-authorizations" }); } const execAuthorizationOperations = { ...execApprovalRetirementOperations, ...execPolicyMutationOperations, "execApprovals.commitAuthorizations": commitExecAuthorizationsInWorker }; //#endregion export { execAuthorizationOperations };