UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

192 lines (191 loc) • 9.67 kB
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs"; import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { r as createSqliteWorkerOperationAdmission } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs"; import { i as sessionChanges } from "./session-row-changes-Dj2yVWo-.mjs"; import { r as captureOpenClawStateWorkerContext } from "./openclaw-state-worker-context-BvvgY6-t.mjs"; import { o as runOpenClawStateWorkerOperation } from "./openclaw-state-worker-store-BxZXTKna.mjs"; import { n as reserveWorkerEnvironmentNativePublication } from "./store-native-publication-BM12vNea.mjs"; import { c as withDevicePairingLock, o as captureDevicePairingPublication, t as listDevicePairingStoreRecordsReadOnly } from "./device-pairing-store-readonly-BVo-mzyt.mjs"; //#region src/infra/device-pairing-card-renderer.ts let pairedCardRendererCache; function invalidatePairedCardRendererCache() { pairedCardRendererCache = void 0; } /** The pairing publication owner invalidates this projection after committed mutations. */ function readPairedCardRendererCache(stateDir, load) { if (pairedCardRendererCache?.stateDir !== stateDir) pairedCardRendererCache = { stateDir, value: load() }; return pairedCardRendererCache.value; } //#endregion //#region src/infra/device-pairing-worker.ts const DevicePairingAuthorityRefusedError = resolveGlobalSingleton(Symbol.for("openclaw.devicePairingAuthorityRefusedError"), () => class extends Error { constructor(message = "Device pairing authority changed") { super(message); } }); function admissionFacts(value) { if (!Array.isArray(value) || !value.every((entry) => isRecord(entry) && typeof entry.kind === "string")) throw new Error("Invalid pairing admission facts"); for (const entry of value) if (entry.kind === "bootstrap.cloudWorkerSetup" && (typeof entry.environmentId !== "string" || typeof entry.setupId !== "string" || typeof entry.credentialDigest !== "string" || typeof entry.provisionOperationId !== "string" || typeof entry.ownerEpoch !== "number") || (entry.kind === "bootstrap.consume" || entry.kind === "bootstrap.token") && typeof entry.expiresAtMs !== "number") throw new Error("Invalid pairing admission facts"); return value; } function commitReceipt(value) { if (!isRecord(value) || value.kind !== "devicePairing" || typeof value.beforeRevision !== "string" || typeof value.revision !== "string" || !Array.isArray(value.changed) || !value.changed.every((entry) => isRecord(entry) && typeof entry.deviceId === "string" && (entry.binding === null || isRecord(entry.binding) && typeof entry.binding.identity === "string" && (entry.binding.generation === void 0 || typeof entry.binding.generation === "string")))) throw new Error("Invalid pairing commit receipt"); let tokensReplaced; if (value.tokensReplaced !== void 0) { const replaced = value.tokensReplaced; if (!isRecord(replaced) || typeof replaced.deviceId !== "string" || !Array.isArray(replaced.roles) || !replaced.roles.every((role) => typeof role === "string")) throw new Error("Invalid pairing token replacement receipt"); tokensReplaced = { deviceId: replaced.deviceId, roles: replaced.roles }; } let workerEnvironment; if (value.workerEnvironment !== void 0) { const environment = value.workerEnvironment; if (!isRecord(environment) || typeof environment.environmentId !== "string" || typeof environment.nodeDeviceId !== "string" || typeof environment.updatedAtMs !== "number") throw new Error("Invalid pairing worker-environment receipt"); workerEnvironment = { environmentId: environment.environmentId, nodeDeviceId: environment.nodeDeviceId, updatedAtMs: environment.updatedAtMs }; } let nodeApproval; if (value.nodeApproval !== void 0) { const approved = value.nodeApproval; if (!isRecord(approved) || typeof approved.nodeId !== "string" || typeof approved.pairingIdentity !== "string" || approved.previousPairingGeneration !== void 0 && typeof approved.previousPairingGeneration !== "string" || typeof approved.nextPairingGeneration !== "string" || !isRecord(approved.surface) || !Array.isArray(approved.surface.caps) || !approved.surface.caps.every((cap) => typeof cap === "string") || !Array.isArray(approved.surface.commands) || !approved.surface.commands.every((command) => typeof command === "string")) throw new Error("Invalid node approval receipt"); let permissions; if (approved.surface.permissions !== void 0) { if (!isRecord(approved.surface.permissions)) throw new Error("Invalid node approval permissions receipt"); permissions = {}; for (const [key, allowed] of Object.entries(approved.surface.permissions)) { if (typeof allowed !== "boolean") throw new Error("Invalid node approval permissions receipt"); permissions[key] = allowed; } } nodeApproval = { nodeId: approved.nodeId, pairingIdentity: approved.pairingIdentity, previousPairingGeneration: approved.previousPairingGeneration, nextPairingGeneration: approved.nextPairingGeneration, surface: { caps: approved.surface.caps, commands: approved.surface.commands, permissions } }; } return { kind: "devicePairing", beforeRevision: value.beforeRevision, revision: value.revision, ...tokensReplaced ? { tokensReplaced } : {}, ...workerEnvironment ? { workerEnvironment } : {}, ...nodeApproval ? { nodeApproval } : {}, changed: value.changed.map((entry) => ({ deviceId: entry.deviceId, binding: entry.binding === null ? null : { identity: entry.binding.identity, ...entry.binding.generation === void 0 ? {} : { generation: entry.binding.generation } } })) }; } function executeDevicePairingMutation(command, options = {}) { const context = options.context ?? captureOpenClawStateWorkerContext(options.baseDir ? { env: { ...process.env, OPENCLAW_STATE_DIR: options.baseDir } } : {}); const captured = structuredClone(command); const operation = withDevicePairingLock(async () => { context.admission.assertCurrent(); options.assertCurrent?.(); const publication = captured.type === "devicePairing.registerJoinCode" || captured.type === "devicePairing.redeemJoinCode" || captured.type === "bootstrap.prune" ? void 0 : captureDevicePairingPublication(context.admission); const mutation = publication?.beginMutation(); let admission; let published = false; let publishEnvironment; const install = () => { if (!mutation) return; const committed = admission?.committed; if (committed && !published) { const receipt = commitReceipt(committed.facts); const environment = receipt.workerEnvironment; let environmentPublished = false; if (environment && publishEnvironment) { context.admission.assertCurrent(); environmentPublished = publishEnvironment(environment.environmentId, { nodeDeviceId: environment.nodeDeviceId, updatedAtMs: environment.updatedAtMs }); } published = true; mutation.publish(receipt); if (receipt.nodeApproval) options.onNodeApproved?.(receipt.nodeApproval); invalidatePairedCardRendererCache(); if (environmentPublished) sessionChanges.emit({ all: true, scope: "worker-environments" }); if (receipt.tokensReplaced) options.onTokensReplaced?.(receipt.tokensReplaced.deviceId, receipt.tokensReplaced.roles); } }; const removeService = publication?.servicePending(install); try { return await runOpenClawStateWorkerOperation(context, async (scope) => { try { const result = await scope.execute(captured); if (captured.type === "bootstrap.prune" && result === 0) { context.admission.assertCurrent(); options.assertCurrent?.(); } return result; } finally { install(); } }, { assertCurrent: options.assertCurrent, createAdmission: () => { let committed = false; admission = createSqliteWorkerOperationAdmission((request, grant) => { if (committed || request.stage !== "transaction" && request.stage !== "commit") throw new Error("Pairing admission requested out of order"); context.admission.assertCurrent(); options.assertCurrent?.(); for (const fact of admissionFacts(request.facts)) options.admit?.(fact); if (request.stage === "commit" && captured.type === "bootstrap.consume") publishEnvironment = reserveWorkerEnvironmentNativePublication(context.admission.identity); if (!grant()) throw new DevicePairingAuthorityRefusedError(); committed = request.stage === "commit"; }); return { admission, nativeLocations: [context.admission.databasePath] }; } }); } finally { try { install(); } finally { mutation?.finish(!admission || admission.settlement?.kind === "completed"); removeService?.(); } } }); const onAuthorityRefused = options.onAuthorityRefused; return onAuthorityRefused ? operation.catch((error) => { if (error instanceof DevicePairingAuthorityRefusedError) return onAuthorityRefused(); throw error; }) : operation; } /** Start the privileged effect in the same interval that publishes its pairing facts. */ async function withCurrentDevicePairingSnapshot(baseDir, prepare, preparePublication) { return (await withDevicePairingLock(async () => { const { paired } = await listDevicePairingStoreRecordsReadOnly(baseDir, true); if (preparePublication) await preparePublication(); return { value: prepare(paired)?.start() }; })).value; } //#endregion export { readPairedCardRendererCache as a, invalidatePairedCardRendererCache as i, executeDevicePairingMutation as n, withCurrentDevicePairingSnapshot as r, DevicePairingAuthorityRefusedError as t };