UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

203 lines (202 loc) • 7.94 kB
import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs"; import { n as createAsyncLock } from "./json-files-C5mNTScg.mjs"; import { n as executeDevicePairingMutation, t as DevicePairingAuthorityRefusedError } from "./device-pairing-worker-BX2CH8m1.mjs"; import { n as loadBoundDeviceBootstrapContextReadOnly } from "./device-pairing-store-readonly-BVo-mzyt.mjs"; import { f as normalizeDeviceBootstrapHandoffProfile, o as PAIRING_SETUP_BOOTSTRAP_PROFILE, p as normalizeDeviceBootstrapProfile } from "./device-bootstrap-profile-DQKf7Vz1.mjs"; import "./pairing-files-CpoyV25r.mjs"; import { randomUUID } from "node:crypto"; //#region src/infra/device-bootstrap.ts const withLock = createAsyncLock(); const log = createSubsystemLogger("device-bootstrap"); function assertBootstrapTokenCurrent(facts) { if (facts.expiresAtMs < Date.now()) throw new DevicePairingAuthorityRefusedError(); } function warnIfIssuedBootstrapScopesWereStripped(params) { if (!params.input) return; const requestedProfile = normalizeDeviceBootstrapProfile(params.input); const requestedScopes = requestedProfile.scopes; if (requestedScopes.length === 0) return; const retainedScopeSet = new Set(params.profile.scopes); const strippedScopes = requestedScopes.filter((scope) => !retainedScopeSet.has(scope)); if (strippedScopes.length === 0) return; log.warn("bootstrap_token_scopes_stripped", { roles: requestedProfile.roles, requestedScopes, retainedScopes: params.profile.scopes, strippedScopes, consoleMessage: "bootstrap token scopes stripped to bootstrap handoff allowlist" }); } async function issueDeviceBootstrapTokenRecord(params) { const assertCurrent = params.assertCurrent; return await withLock(async () => { const input = params.profile || (params.roles || params.scopes ? { roles: params.roles, scopes: params.scopes } : void 0); const profile = input ? normalizeDeviceBootstrapHandoffProfile(input) : PAIRING_SETUP_BOOTSTRAP_PROFILE; warnIfIssuedBootstrapScopesWereStripped({ input, profile }); return await executeDevicePairingMutation({ type: "bootstrap.issue", input: { profile, setupId: params.setupId, nowMs: Date.now() } }, { baseDir: params.baseDir, assertCurrent }); }); } /** Issue a short-lived generic bootstrap token with a bounded role/scope handoff profile. */ async function issueDeviceBootstrapToken(params = {}) { return await issueDeviceBootstrapTokenRecord(params); } /** * Issue a setup bootstrap token plus an opaque correlation id. `setupId` is * minted here, beside the credential, so the presenting client can follow one * exact credential without ever handling the bearer token. Generic bootstrap * handoffs stay uncorrelated: only setup codes have a presenting client. */ async function issueDevicePairSetupBootstrapToken(params) { const setupId = randomUUID(); return { ...await issueDeviceBootstrapTokenRecord({ ...params, setupId }), setupId }; } function runBootstrapMutation(type, input, options) { return withLock(() => executeDevicePairingMutation({ type, input: input(Date.now()) }, options)); } /** Reuse one environment-owned setup credential across provider replay. */ async function ensureDevicePairSetupBootstrapToken(params) { const { baseDir, ...input } = params; return await runBootstrapMutation("bootstrap.ensure", (nowMs) => ({ ...input, nowMs }), { baseDir }); } /** Consume only while the bound device and its live handoff authority remain current. */ async function consumeDeviceBootstrapTokenWithSetupCompletion(params) { const { baseDir, pairedDeviceMatches, admitsCloudWorkerSetup, ...input } = params; return await runBootstrapMutation("bootstrap.consume", (nowMs) => ({ ...input, nowMs }), { baseDir, onAuthorityRefused: () => null, admit: (facts) => { if (facts.kind === "bootstrap.consume") { assertBootstrapTokenCurrent(facts); if (pairedDeviceMatches && !pairedDeviceMatches(facts.pairedDevice)) throw new DevicePairingAuthorityRefusedError(); } if (facts.kind === "bootstrap.cloudWorkerSetup" && admitsCloudWorkerSetup?.(facts) !== true) throw new DevicePairingAuthorityRefusedError(); } }); } /** Confirm that the pairing client received the credential-bearing handoff response. */ async function confirmDevicePairSetupCompletionDelivery(params) { const { baseDir, ...input } = params; return await runBootstrapMutation("bootstrap.confirm", (nowMs) => ({ ...input, nowMs }), { baseDir }); } /** Read the terminal outcome and prune expired completions under the settlement lock. */ async function readDevicePairSetupCompletion(params) { const { baseDir, ...input } = params; return await runBootstrapMutation("bootstrap.readCompletion", (nowMs) => ({ ...input, nowMs }), { baseDir }); } async function clearDeviceBootstrapTokens(params = {}) { const { baseDir, assertCurrent, ...input } = params; return await runBootstrapMutation("bootstrap.clear", (nowMs) => ({ ...input, nowMs }), { baseDir, assertCurrent }); } /** Revoke a bootstrap token unless its cloud-worker environment is already bound to the token's device. */ async function revokeDeviceBootstrapToken(params) { const { baseDir, ...input } = params; return await runBootstrapMutation("bootstrap.revoke", (nowMs) => ({ ...input, nowMs }), { baseDir }); } /** Restore an uncorrelated bootstrap bearer after an undelivered credential response. */ async function restoreGenericDeviceBootstrapToken(params) { const { baseDir, ...input } = params; return await runBootstrapMutation("bootstrap.restore", (nowMs) => ({ ...input, nowMs }), { baseDir }); } /** Record one role/scope leg of a multi-role bootstrap handoff. */ async function redeemDeviceBootstrapTokenProfile(params) { const { baseDir, ...input } = params; return await runBootstrapMutation("bootstrap.redeem", (nowMs) => ({ ...input, nowMs }), { baseDir, onAuthorityRefused: () => ({ recorded: false, fullyRedeemed: false }), admit: (facts) => { if (facts.kind === "bootstrap.token") assertBootstrapTokenCurrent(facts); } }); } /** Verify a bootstrap token, bind its first device identity, and stage requested scopes. */ async function verifyDeviceBootstrapToken(params) { const { baseDir, ...input } = params; return await runBootstrapMutation("bootstrap.verify", (nowMs) => ({ ...input, nowMs }), { baseDir, onAuthorityRefused: () => ({ ok: false, reason: "bootstrap_token_invalid" }), admit: (facts) => { if (facts.kind === "bootstrap.token") assertBootstrapTokenCurrent(facts); } }); } /** Remove retained setup outcomes independently of status requests or later pairings. */ async function pruneExpiredDevicePairSetupCompletions(params = {}) { return await withLock(() => executeDevicePairingMutation({ type: "bootstrap.prune", input: { nowMs: params.nowMs ?? Date.now() } }, { baseDir: params.baseDir })); } /** Read already-bound context only after verifying the same credential and identity. */ async function getBoundDeviceBootstrapContext(params) { const { baseDir, ...input } = params; return await withLock(() => loadBoundDeviceBootstrapContextReadOnly({ ...input, nowMs: Date.now() }, baseDir)); } async function getBoundDeviceBootstrapProfile(params) { return (await getBoundDeviceBootstrapContext(params))?.profile ?? null; } //#endregion export { getBoundDeviceBootstrapContext as a, issueDevicePairSetupBootstrapToken as c, redeemDeviceBootstrapTokenProfile as d, restoreGenericDeviceBootstrapToken as f, ensureDevicePairSetupBootstrapToken as i, pruneExpiredDevicePairSetupCompletions as l, verifyDeviceBootstrapToken as m, confirmDevicePairSetupCompletionDelivery as n, getBoundDeviceBootstrapProfile as o, revokeDeviceBootstrapToken as p, consumeDeviceBootstrapTokenWithSetupCompletion as r, issueDeviceBootstrapToken as s, clearDeviceBootstrapTokens as t, readDevicePairSetupCompletion as u };