openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
109 lines (108 loc) • 5.71 kB
JavaScript
import { a as formatNodeRunnerUpdateRequired } from "./node-runner-inventory-CsPbvshW.js";
import { t as WorkerProviderError } from "./capability-provider.types-cizOzEy5.js";
import { i as hasEffectivePairedDeviceRole } from "./device-pairing-DFIQp3ZY.js";
import { t as DEVICE_WORKER_PROVIDER_ID } from "./device-provider-identity-v6nXqNq_.js";
import { t as createNodeWorkerLaunchAdapter } from "./node-launch-adapter-CYClo3Jk.js";
import { createHash } from "node:crypto";
//#region src/gateway/worker-environments/device-provider.ts
const DEVICE_WORKER_DORMANCY_MS = 12096e5;
const DEVICE_WORKER_AVAILABILITY = /* @__PURE__ */ new WeakMap();
const DEVICE_WORKER_RECONCILIATION = /* @__PURE__ */ new WeakMap();
function bindDeviceWorkerAvailability(service, resolveAvailability) {
DEVICE_WORKER_AVAILABILITY.set(service, resolveAvailability);
}
async function resolveDeviceWorkerAvailability(service, deviceId) {
const resolveAvailability = service ? DEVICE_WORKER_AVAILABILITY.get(service) : void 0;
return resolveAvailability ? await resolveAvailability(deviceId) : { available: false };
}
function deviceUnavailableText(deviceId, availability) {
if (availability.issue) return formatNodeRunnerUpdateRequired(deviceId, availability.issue);
switch (availability.unavailableReason) {
case "unpaired": return `device worker is not a paired node host: ${deviceId}`;
case "disconnected": return `device worker node is not connected: ${deviceId}; reconnect it before retrying`;
case "hosting-unavailable": return `device node ${deviceId} is connected but cannot host sessions; enable session hosting (nodeHost.workerRuns.enabled), update the node if needed, then reconnect it`;
case "at-capacity": return `device worker is at capacity (all worker slots in use): ${deviceId}; stop an existing worker environment or retry when a slot is free`;
default: return `device worker availability is unknown: ${deviceId}; verify the node host is paired and connected, then retry`;
}
}
function bindDeviceWorkerReconciliation(service, reconcile) {
DEVICE_WORKER_RECONCILIATION.set(service, reconcile);
}
async function reconcileDeviceWorker(service, deviceId) {
const reconcile = service ? DEVICE_WORKER_RECONCILIATION.get(service) : void 0;
return reconcile ? await reconcile(deviceId) : [];
}
function requireDeviceId(profile) {
const deviceId = profile.device;
if (typeof deviceId !== "string" || !deviceId.trim()) throw new WorkerProviderError("device worker profile requires a device setting");
return deviceId.trim();
}
function hasPairedNodeRole(device) {
return Boolean(device && hasEffectivePairedDeviceRole(device, "node"));
}
function isWithinDeviceDormancy(device, nowMs) {
const disconnectedAtMs = device.nodeSurface?.lastDisconnectedAtMs;
return disconnectedAtMs === void 0 || nowMs - disconnectedAtMs < DEVICE_WORKER_DORMANCY_MS;
}
function deviceLeaseId(deviceId, operationId) {
return `device:${createHash("sha256").update(deviceId).digest("hex")}:${createHash("sha256").update(operationId).digest("hex").slice(0, 32)}`;
}
/** Core runtime for already-paired node hosts; pairing remains the durable trust owner. */
function createDeviceWorkerRuntime(options) {
const now = options.now ?? Date.now;
let nodeTransport;
const launchAdapter = createNodeWorkerLaunchAdapter({ getTransport: () => nodeTransport });
const findConnectedNode = async (deviceId) => (await nodeTransport?.listCurrentNodes())?.find((node) => node.nodeId === deviceId);
const resolveAvailability = async (deviceId) => {
const [paired, connected] = await Promise.all([options.getPairedDevice(deviceId), findConnectedNode(deviceId)]);
const current = connected && nodeTransport?.isCurrent(connected) ? connected : void 0;
const unavailableReason = !hasPairedNodeRole(paired) ? "unpaired" : !current ? nodeTransport?.isConnected?.(deviceId) ? "hosting-unavailable" : "disconnected" : void 0;
const issue = nodeTransport?.getIssue?.(deviceId);
return {
available: unavailableReason === void 0,
...unavailableReason === void 0 && current ? { node: current } : {},
...issue ? { issue } : {},
...unavailableReason ? { unavailableReason } : {}
};
};
const provider = {
id: DEVICE_WORKER_PROVIDER_ID,
supportedExecutionModes: ["worker-turn", "remote-exec"],
provisionBeforeInstallation: true,
resolveAllocation: async (profile, operationId) => ({
leaseId: deviceLeaseId(requireDeviceId(profile), operationId),
sharedHost: true
}),
provision: async (profile, operationId) => {
const deviceId = requireDeviceId(profile);
const availability = await resolveAvailability(deviceId);
if (!availability.available) throw new WorkerProviderError(deviceUnavailableText(deviceId, availability));
return {
...await provider.resolveAllocation(profile, operationId),
node: { deviceId }
};
},
inspect: async ({ profile }) => {
const deviceId = requireDeviceId(profile);
const paired = await options.getPairedDevice(deviceId);
if (!hasPairedNodeRole(paired)) return { status: "unknown" };
if (await findConnectedNode(deviceId)) return {
status: "active",
sharedHost: true
};
return isWithinDeviceDormancy(paired, now()) ? { status: "dormant" } : { status: "unknown" };
},
destroy: async () => {}
};
return {
provider,
resolveAvailability,
launchNodeWorker: launchAdapter.launch,
getNodeTransport: () => nodeTransport,
bindNodeTransport: (transport) => {
nodeTransport = transport;
}
};
}
//#endregion
export { reconcileDeviceWorker as a, deviceUnavailableText as i, bindDeviceWorkerReconciliation as n, resolveDeviceWorkerAvailability as o, createDeviceWorkerRuntime as r, bindDeviceWorkerAvailability as t };