UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

118 lines (117 loc) 6.09 kB
import { c as isRecord } from "./record-coerce-DItp3I4t.js"; import { r as createPluginStateSyncKeyedStore } from "./plugin-state-store-C6hmUuuk.js"; import "./string-coerce-runtime-GQa0ehRA.js"; import "./plugin-state-store-runtime-Bqaaoi-g.js"; import { createHash } from "node:crypto"; //#region extensions/crabbox/src/crabbox-worker-warm-image-store.ts const WARM_IMAGE_MAX_ENTRIES = 128; const CAPTURE_WARNING_AGE_MS = 12e5; function crabboxLegacyWarmImageCaptureSelector(key, record) { return `legacy-${createHash("sha256").update(JSON.stringify({ key, record })).digest("hex")}`; } const openLegacyLeases = (env) => createPluginStateSyncKeyedStore("crabbox", { namespace: "warm-leases", maxEntries: 256, overflowPolicy: "evict-oldest", ...env ? { env } : {} }); const legacyLeaseSelector = (key, value) => `legacy-lease-${createHash("sha256").update(JSON.stringify({ key, value })).digest("hex")}`; function listCrabboxLegacyWarmLeases(env) { return openLegacyLeases(env).entries().map(({ key, value }) => ({ leaseId: key, machineClass: isRecord(value) && typeof value.machineClass === "string" ? value.machineClass : void 0, selector: legacyLeaseSelector(key, value) })); } function assertCrabboxWarmImageMigrationReady() { if (listCrabboxLegacyWarmLeases().length > 0) throw new Error("Crabbox has legacy worker allocations whose original image choices are unknown; run openclaw doctor --fix and follow its provider-cleanup recovery instructions before provisioning workers."); } function requireCanonicalProfile(record) { if (record && record.version !== 2) throw new Error("Crabbox warm-image state requires migration; run openclaw doctor --fix before provisioning workers."); return record; } function openCrabboxWarmImageStore(env) { const store = createPluginStateSyncKeyedStore("crabbox", { namespace: "warm-images", maxEntries: 128, overflowPolicy: "reject-new", ...env ? { env } : {} }); return { ...store, lookup(key) { return requireCanonicalProfile(store.lookup(key)); }, entries() { const entries = store.entries(); for (const entry of entries) requireCanonicalProfile(entry.value); return entries; }, update(key, update) { return store.update(key, (current) => update(requireCanonicalProfile(current))); } }; } function withoutCrabboxWarmImageOperation(record) { const profile = { ...record }; delete profile.operation; return profile; } function crabboxWarmImageCaptureStatus(_key, record) { const capture = record.operation?.type === "capture" ? record.operation : void 0; if (!capture) return; return { selector: capture.id, startedAtMs: capture.startedAtMs, ...capture.leaseId ? { leaseId: capture.leaseId } : {}, ...capture.provider ? { provider: capture.provider } : {}, phase: capture.phase, stale: Date.now() - capture.startedAtMs >= CAPTURE_WARNING_AGE_MS }; } function isCrabboxWarmImageCapturePaused(capture) { return capture.stale || capture.phase === "uncertain"; } function crabboxWarmImageRecoveryHint(selector) { return `Stop the owning Gateway and capture processes, confirm any worker being recovered is stopped, and resolve any untracked checkpoint in the Crabbox catalog before running: openclaw crabbox warm-images --recover ${selector} --acknowledge-provider-cleanup. Then restart the Gateway; the next eligible worker can capture again.`; } function listCrabboxWarmImages(env) { return openCrabboxWarmImageStore(env).entries().map(({ key, value }) => ({ profileKey: key, projectKey: value.projectKey, checkpointId: value.image?.checkpointId, state: value.image?.state ?? "no-image", createdAtMs: value.image?.createdAtMs, lastUsedAtMs: value.image?.lastUsedAtMs, baseCommit: value.image?.baseCommit, allocations: value.allocations, capture: crabboxWarmImageCaptureStatus(key, value), retirement: value.operation?.type === "retire" ? { checkpointId: value.operation.checkpointId } : void 0 })); } /** Recovery closes only the capture generation; allocation decisions remain authoritative. */ function clearCrabboxWarmImageCapture(key, selector) { const store = openCrabboxWarmImageStore(); const matches = (current) => current.operation?.type === "capture" && current.operation.id === selector; if (store.deleteIf(key, (current) => !current.image && Object.keys(current.allocations).length === 0 && matches(current))) return true; return store.update(key, (current) => current && matches(current) ? withoutCrabboxWarmImageOperation(current) : void 0); } function recoverCrabboxWarmImageCapture(selector, acknowledgeProviderCleanup) { if (!acknowledgeProviderCleanup) throw new Error("Recovery requires --acknowledge-provider-cleanup: confirm the original Gateway/capture processes and any worker being recovered are stopped, and any untracked provider artifact has been resolved. No state was changed."); if (selector.startsWith("legacy-lease-")) { const store = openLegacyLeases(); const entry = store.entries().find(({ key, value }) => legacyLeaseSelector(key, value) === selector); if (!entry || !store.deleteIf(entry.key, (value) => legacyLeaseSelector(entry.key, value) === selector)) throw new Error("Legacy allocation selector is absent or changed; rerun openclaw crabbox warm-images --json. No state was changed."); return; } const entry = openCrabboxWarmImageStore().entries().find(({ key, value }) => crabboxWarmImageCaptureStatus(key, value)?.selector === selector); if (!entry || !clearCrabboxWarmImageCapture(entry.key, selector)) throw new Error("Capture selector is absent or changed; rerun openclaw crabbox warm-images --json. No state was changed."); } //#endregion export { crabboxWarmImageCaptureStatus as a, listCrabboxLegacyWarmLeases as c, recoverCrabboxWarmImageCapture as d, withoutCrabboxWarmImageOperation as f, crabboxLegacyWarmImageCaptureSelector as i, listCrabboxWarmImages as l, assertCrabboxWarmImageMigrationReady as n, crabboxWarmImageRecoveryHint as o, clearCrabboxWarmImageCapture as r, isCrabboxWarmImageCapturePaused as s, WARM_IMAGE_MAX_ENTRIES as t, openCrabboxWarmImageStore as u };