UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

577 lines (576 loc) 29.3 kB
import { n as resolveOpenClawPackageRootSync } from "./openclaw-root-CfYY-fyD.js"; import { c as isRecord } from "./record-coerce-DItp3I4t.js"; import { y as uniqueStrings } from "./string-normalization-DsCfAx8q.js"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js"; import { r as createLazyRuntimeModule } from "./lazy-runtime-CgCh8H_K.js"; import { n as getRuntimeConfig } from "./io.runtime-B9iJRs3w.js"; import { i as getGatewayPluginMetadataSnapshot } from "./current-plugin-metadata-state-B1UoAr4G.js"; import { s as resolveRuntimeServiceBuildId } from "./version-v1kuAkGj.js"; import { d as isLinkLocalIpAddress } from "./ip-BkT2Is0E.js"; import "./config-Cs0XXL3x.js"; import { r as runCommandWithTimeout } from "./exec-BIE-3oLG.js"; import { s as isLoopbackHost } from "./net-DbNPs6Xm.js"; import { i as loadOrCreateProcessDeviceIdentity } from "./device-identity-J83pn_rP.js"; import { t as resolveGatewayPublicOrigin } from "./gateway-public-origin-BcHLka2A.js"; import { a as getActiveSecretsRuntimeConfigSnapshot, o as getActiveSecretsRuntimeEnvState } from "./runtime-state-C4aJ8Hzz.js"; import { s as workerBundleArchiveRelativePath } from "./worker-bundle-hash-CxAJivBJ.js"; import { A as CLOUD_WORKER_PAIRING_SETUP_BOOTSTRAP_PROFILE, i as ensureDevicePairSetupBootstrapToken } from "./device-bootstrap-DRFMRCcr.js"; import { m as removePairedDeviceRole, n as getPairedDevice } from "./device-pairing-DFIQp3ZY.js"; import { t as DEVICE_WORKER_PROVIDER_ID } from "./device-provider-identity-v6nXqNq_.js"; import { r as WORKER_BOOTSTRAP_ARTIFACT_TRANSFER_PATH } from "./gateway-http-route-contracts-jBtizxKU.js"; import { a as resolvePairingSetupFromConfig, i as resolvePairingGatewayUrl, n as encodePairingSetupCode, r as resolveConfiguredPairingPublicUrl } from "./setup-code-Dv85EyDz.js"; import { n as bindDeviceWorkerReconciliation, r as createDeviceWorkerRuntime, t as bindDeviceWorkerAvailability } from "./device-provider-BjFBZG2n.js"; import { t as nodeWorkerGatewayNamespace } from "./node-worker-gateway-namespace-kRiA3LFq.js"; import { t as listRetainedWorkerBundleHashes } from "./worker-bundle-retention-BBmYnuI-.js"; import os from "node:os"; import { setTimeout } from "node:timers/promises"; //#region src/gateway/worker-environments/node-enrollment.ts const NODE_ENROLLMENT_TIMEOUT_MS = 6e5; const NODE_ENROLLMENT_POLL_MS = 250; function createWorkerNodeEnrollmentManager(options) { const now = options.now ?? Date.now; const controller = new AbortController(); const { signal } = controller; const active = /* @__PURE__ */ new Map(); const enrollmentClosers = /* @__PURE__ */ new WeakMap(); const commandRunner = async (argv, runOptions) => await runCommandWithTimeout(argv, { timeoutMs: runOptions.timeoutMs }); const prepare = async (record, enrollmentSignal) => { const preparationSignal = enrollmentSignal ?? signal; preparationSignal.throwIfAborted(); const config = options.getConfig(); const url = await resolvePairingGatewayUrl(config, { env: process.env, publicUrl: resolveConfiguredPairingPublicUrl(config) ?? resolveGatewayPublicOrigin(config), networkInterfaces: os.networkInterfaces, runCommandWithTimeout: commandRunner }); if (!url.url) throw new Error(url.error ?? "Cloud node bootstrap cannot resolve the Gateway address"); const host = new URL(url.url).hostname; if (isLoopbackHost(host) || isLinkLocalIpAddress(host) || host === "0.0.0.0" || host === "[::]") throw new Error(`Cloud node bootstrap resolved a Gateway address that a cloud worker cannot reach (${url.url}, from ${url.source ?? "unknown"}). Set gateway.publicOrigin (or plugins.entries.device-pair.config.publicUrl) to a URL reachable from the worker, such as a Tailscale Funnel or a reverse-proxied public origin with gateway.trustedProxies, then redispatch.`); preparationSignal.throwIfAborted(); const artifact = await options.prepareArtifact(record, preparationSignal); preparationSignal.throwIfAborted(); const tlsFingerprint = url.url.startsWith("wss://") ? url.source?.startsWith("gateway.bind=") ? options.getLocalTlsFingerprint?.() : url.source === "gateway.remote.url" ? config.gateway?.remote?.tlsFingerprint : void 0 : void 0; return { artifact, url: url.url, tlsFingerprint }; }; const reserve = (record, operationSignal) => { signal.throwIfAborted(); operationSignal?.throwIfAborted(); const admission = options.store.get(record.environmentId); if (admission?.state !== "provisioning" || admission.destroyRequestedAtMs !== null || admission.provisionOperationId !== record.provisionOperationId || admission.ownerEpoch !== record.ownerEpoch) throw new Error("Worker node enrollment is no longer provisioning"); active.get(record.environmentId)?.close(); const enrollmentAbort = new AbortController(); const enrollmentSignal = AbortSignal.any([ signal, enrollmentAbort.signal, ...operationSignal ? [operationSignal] : [] ]); const binding = { close: () => { if (active.get(record.environmentId) === binding) active.delete(record.environmentId); enrollmentAbort.abort(); } }; active.set(record.environmentId, binding); const current = () => { enrollmentSignal.throwIfAborted(); const live = options.store.get(record.environmentId); if (active.get(record.environmentId) !== binding || live?.state !== "provisioning" || live.destroyRequestedAtMs !== null || live.provisionOperationId !== record.provisionOperationId || live.ownerEpoch !== record.ownerEpoch) throw new Error("Worker node enrollment is no longer provisioning"); return live; }; return { binding, enrollmentSignal, current }; }; const grantArtifact = (prepared, artifact, enrollmentSignal, isAuthorized) => { const capability = options.transfer.prepare({ artifact, isAuthorized, signal: enrollmentSignal }); const url = new URL(prepared.url); url.protocol = url.protocol === "wss:" ? "https:" : "http:"; url.pathname = `${WORKER_BOOTSTRAP_ARTIFACT_TRANSFER_PATH}/artifacts/${artifact.tarballSha256}`; url.search = ""; url.hash = ""; return { url: url.toString(), token: capability.token, sha256: artifact.tarballSha256, bytes: artifact.tarballBytes, ...prepared.tlsFingerprint ? { tlsFingerprint: prepared.tlsFingerprint } : {} }; }; const grantRuntime = (prepared, enrollmentSignal, isAuthorized) => ({ nodeBootstrap: { ...grantArtifact(prepared, prepared.artifact, enrollmentSignal, isAuthorized), openclawVersion: prepared.artifact.openclawVersion, enabledPluginIds: prepared.artifact.enabledPluginIds }, signal: enrollmentSignal }); const prepareRuntime = async (record, bundle, operationSignal) => { const { binding, enrollmentSignal, current } = reserve(record, operationSignal); try { const prepared = await prepare(record, enrollmentSignal); const owner = current(); const isAuthorized = () => { const live = current(); return live.nodeSetupId === owner.nodeSetupId && live.nodeDeviceId === owner.nodeDeviceId; }; const runtime = { ...grantRuntime(prepared, enrollmentSignal, isAuthorized), workerBundle: { ...grantArtifact(prepared, bundle, enrollmentSignal, isAuthorized), packageRelativePath: workerBundleArchiveRelativePath(bundle.tarballSha256) } }; enrollmentClosers.set(runtime, binding.close); return runtime; } catch (error) { binding.close(); throw error; } }; const begin = async (record, operationSignal) => { const { binding, enrollmentSignal, current: requireCurrent } = reserve(record, operationSignal); try { const prepared = await prepare(record, enrollmentSignal); requireCurrent(); let current = options.store.ensureNodeEnrollment(record.environmentId); if (current.state !== "provisioning" || current.destroyRequestedAtMs !== null || current.provisionOperationId !== record.provisionOperationId || current.ownerEpoch !== record.ownerEpoch) throw new Error("Worker node enrollment is no longer provisioning"); let mode; let gatewayUrl = prepared.url; let tlsFingerprint = prepared.tlsFingerprint; if (current.nodeDeviceId) mode = { mode: "resume", deviceId: current.nodeDeviceId }; else { if (!current.nodeSetupId) throw new Error("Worker node enrollment setup identity was not persisted"); const issued = await ensureDevicePairSetupBootstrapToken({ setupId: current.nodeSetupId, profile: CLOUD_WORKER_PAIRING_SETUP_BOOTSTRAP_PROFILE }); requireCurrent(); if (issued.status === "completed") { current = options.store.ensureNodeEnrollment(record.environmentId); if (!current.nodeDeviceId || current.nodeDeviceId !== issued.deviceId) throw new Error("Worker node enrollment completion did not bind its environment"); mode = { mode: "resume", deviceId: current.nodeDeviceId }; } else { const config = options.getConfig(); const resolved = await resolvePairingSetupFromConfig(config, { env: process.env, publicUrl: resolveConfiguredPairingPublicUrl(config) ?? resolveGatewayPublicOrigin(config), bootstrapProfile: CLOUD_WORKER_PAIRING_SETUP_BOOTSTRAP_PROFILE, issuedBootstrap: issued, localTlsFingerprint: options.getLocalTlsFingerprint?.(), runCommandWithTimeout: commandRunner }); requireCurrent(); if (!resolved.ok) throw new Error(resolved.error); if (resolved.setupId !== current.nodeSetupId) throw new Error("Worker node enrollment setup identity changed during preparation"); gatewayUrl = resolved.payload.url; tlsFingerprint = resolved.payload.tlsFingerprint; mode = { mode: "connect", setupCode: encodePairingSetupCode(resolved.payload), setupId: current.nodeSetupId }; } } const owner = current; const isAuthorized = () => { const live = options.store.get(owner.environmentId); return active.get(owner.environmentId) === binding && !enrollmentSignal.aborted && live?.state === "provisioning" && live.destroyRequestedAtMs === null && live.provisionOperationId === record.provisionOperationId && live.ownerEpoch === record.ownerEpoch && live.nodeSetupId === owner.nodeSetupId && live.nodeDeviceId === owner.nodeDeviceId; }; const enrollment = { ...mode, ...grantRuntime({ ...prepared, url: gatewayUrl, tlsFingerprint }, enrollmentSignal, isAuthorized), openclawVersion: prepared.artifact.openclawVersion, displayName: truncateUtf16Safe(`Cloud worker ${owner.profileId}`, 64), signal: enrollmentSignal, waitForDeviceId: async () => { const deadline = now() + NODE_ENROLLMENT_TIMEOUT_MS; while (now() < deadline) { enrollmentSignal.throwIfAborted(); const live = options.store.ensureNodeEnrollment(owner.environmentId); if (live.destroyRequestedAtMs !== null || live.state !== "provisioning" || live.provisionOperationId !== owner.provisionOperationId || live.nodeSetupId !== owner.nodeSetupId || live.ownerEpoch !== owner.ownerEpoch || active.get(owner.environmentId) !== binding) throw new Error("Worker node enrollment is no longer current"); if (live.nodeDeviceId) { const availability = await options.resolveAvailability(live.nodeDeviceId); enrollmentSignal.throwIfAborted(); const latest = options.store.get(owner.environmentId); if (!latest || latest.state !== "provisioning" || latest.destroyRequestedAtMs !== null || latest.provisionOperationId !== owner.provisionOperationId || latest.ownerEpoch !== owner.ownerEpoch || latest.nodeSetupId !== owner.nodeSetupId || latest.nodeDeviceId !== live.nodeDeviceId || active.get(owner.environmentId) !== binding) throw new Error("Worker node enrollment is no longer current"); if (availability.available) return live.nodeDeviceId; } await setTimeout(NODE_ENROLLMENT_POLL_MS, void 0, { signal: enrollmentSignal }); } throw new Error("Worker node did not connect before the enrollment deadline"); } }; enrollmentClosers.set(enrollment, binding.close); return enrollment; } catch (error) { binding.close(); throw error; } }; const retire = async (record) => { active.get(record.environmentId)?.close(); const deviceId = record.nodeDeviceId; if (!deviceId) return; const sharedOwner = options.store.listForReconcile().find((candidate) => candidate.environmentId !== record.environmentId && candidate.nodeDeviceId === deviceId); if (sharedOwner) throw new Error(`Worker node ${deviceId} is still owned by environment ${sharedOwner.environmentId}`); await removePairedDeviceRole({ deviceId, role: "node" }); }; return { prepare: async (record, operationSignal) => { const preflight = new AbortController(); try { await prepare(record, AbortSignal.any([ signal, preflight.signal, ...operationSignal ? [operationSignal] : [] ])); } finally { preflight.abort(); } }, prepareRuntime, begin, retire, closeRuntime: (preparation) => enrollmentClosers.get(preparation)?.(), close: (enrollment) => enrollmentClosers.get(enrollment)?.(), stop: () => { controller.abort(); for (const binding of active.values()) binding.close(); options.transfer.closeAll(); } }; } //#endregion //#region src/gateway/server-worker-environment-startup.ts const loadWorkerEnvironmentRuntimeModule = createLazyRuntimeModule(() => import("./gateway/worker-environments/runtime.js")); const loadWorkerInferenceRuntimeModule = createLazyRuntimeModule(() => import("./inference-runtime-AYmOQFkZ.js")); const loadWorkerSessionToolExecutorModule = createLazyRuntimeModule(() => import("./worker-session-tool-executor-D5NwKDPo.js")); async function loadGatewayWorkerEnvironmentStartupState() { const [{ createWorkerEnvironmentStore }, { createWorkerSessionPlacementStore }] = await Promise.all([import("./store-DD3S0PkE.js"), import("./placement-store-B6cZxVDn.js")]); const store = createWorkerEnvironmentStore(); const placementStore = createWorkerSessionPlacementStore(); const records = store.list(); const durableProviderIds = uniqueStrings(records.flatMap((record) => record.state === "destroyed" || record.state === "failed" || record.state === "orphaned" ? [] : record.providerId === "device" ? [] : [record.providerId])); const listDurableProviderIds = () => uniqueStrings(store.listForReconcile().filter((record) => record.providerId !== DEVICE_WORKER_PROVIDER_ID).map((record) => record.providerId)); return { durableProviderIds, listDurableProviderIds, records, store, placementStore, hasNonlocalPlacementRecords: placementStore.listForReconcile().length > 0 }; } async function createGatewayWorkerEnvironmentRuntime(params) { const deviceRuntime = createDeviceWorkerRuntime({ getPairedDevice }); const [{ createWorkerEnvironmentService }, { createWorkerLiveEventReceiver }, { createWorkerSessionPlacementGate }, { createWorkerTranscriptCommitter }, { createWorkerTunnelManager }, { createNodeWorkerTunnelManager }, { createGatewayNodeWorkerBundleInstaller }, { createNodeWorkerBundleTransferService }, { createNodeWorkerBundleTransferHttpCallback }, { createNodeWorkspaceTransferService }, { createNodeWorkspaceTransferHttpCallback }, { createWorkerNodeDesktopCarrier }, { createWorkerNodePortalCarrier }, { createWorkerComputerService }, { resolveWorkerProvider }, { maintainConfiguredWorkerProviders }, { createWorkerBootstrapArtifactTransferService }, { createWorkerBootstrapArtifactTransferHttpCallback }] = await Promise.all([ import("./service-B8jqnQpd.js"), import("./live-events-Dk9K1-Jr.js"), import("./placement-worker-gate-BEjNEBI1.js"), import("./transcript-commit-C7SnsdTo.js"), import("./tunnel-Baan4-mH.js"), import("./node-worker-tunnel-B7ld1ZeV.js"), import("./node-worker-bundle-installer-madNSZf3.js"), import("./node-worker-bundle-transfer-service-BN0hbY4_.js"), import("./node-worker-bundle-transfer-http-DP4_oycC.js"), import("./node-workspace-transfer-service-CcIh7AMB.js"), import("./node-workspace-transfer-http-Cjmxs_Ba.js"), import("./node-desktop-carrier-CcPVPNpu.js"), import("./portal-node-carrier-FSunyjEw.js"), import("./computer-transport-DZTQViPz.js"), import("./worker-provider-registry-DMpZo5pl.js"), import("./worker-provider-maintenance-dr2vOHIf.js"), import("./worker-bootstrap-artifact-transfer-service-BjeFnbcN.js"), import("./worker-bootstrap-artifact-transfer-http-jF_OH8Il.js") ]); params.startup.placementStore.recoverWorkerSessionToolOperationsAfterRestart(); params.startup.placementStore.clearLocalTurnClaimsAfterRestart(); const placementGate = createWorkerSessionPlacementGate(params.startup.placementStore, { rejectExistingWorkerClaims: true }); const workerEnvironmentLog = params.log.child("worker-environments"); const listRetainedBundleHashes = () => listRetainedWorkerBundleHashes({ environments: params.startup.store.list(), placements: params.startup.placementStore.list() }); let workerBundleProducer; let workerNpmArtifact; const prepareInstallation = async (install) => { const [workerRuntime, { WORKER_PROTOCOL_FEATURES }] = await Promise.all([loadWorkerEnvironmentRuntimeModule(), import("./worker-admission-Bq_GENDb.js")]); const producer = workerBundleProducer ??= workerRuntime.createWorkerBundleProducer({ protocolFeatures: WORKER_PROTOCOL_FEATURES, cacheOwnership: "exclusive", onCacheCleanupError: (error) => { workerEnvironmentLog.warn(`Worker bundle cache cleanup failed: ${String(error)}`); } }); const bundle = await producer.prepare(); await producer.prune(listRetainedBundleHashes()); if (install === "bundle") return bundle; workerNpmArtifact ??= workerRuntime.resolveWorkerNpmInstallationArtifact({ bundle }).catch((error) => { workerNpmArtifact = void 0; throw error; }); return await workerNpmArtifact; }; const startupBindings = params.startup.records.flatMap((record) => record.state === "attached" && record.attachedSessionIds.length === 1 ? [{ environmentId: record.environmentId, runEpoch: record.ownerEpoch, sessionId: record.attachedSessionIds[0] }] : []); const workerLiveEvents = createWorkerLiveEventReceiver({ getConfig: getRuntimeConfig, startupBindings, startupOwners: new Map(startupBindings.map((binding) => [binding.environmentId, binding.runEpoch])) }); const workerTunnelManager = createWorkerTunnelManager({ desktopSessionRegistry: params.desktopSessionRegistry }); const notifyPortalChange = () => { const runtime = params.getPortalRuntime(); const service = runtime?.portalService; if (!service) return; runtime.broadcast("portal.changed", { portals: service.list().map(({ tokenQuery: _tokenQuery, url: _url, ...portal }) => portal) }, { dropIfSlow: true }); }; const workerNodeDesktopStreamBroker = params.nodeDesktopStreamBroker; const workerNodePortalCarrier = createWorkerNodePortalCarrier({ store: params.startup.store }); const workerNodeDesktopCarrier = workerNodeDesktopStreamBroker ? createWorkerNodeDesktopCarrier({ store: params.startup.store, desktopRegistry: params.desktopSessionRegistry }) : void 0; const nodeWorkerBundleTransfer = createNodeWorkerBundleTransferService(); const nodeBootstrapTransfer = createWorkerBootstrapArtifactTransferService(); const bootstrapProducers = /* @__PURE__ */ new Map(); const retiringBootstrapProducers = /* @__PURE__ */ new Set(); const retireBootstrapProducer = (producer) => { const retirement = producer.close().catch((error) => { workerEnvironmentLog.warn(`Cloud node artifact cleanup failed: ${String(error)}`); }).finally(() => retiringBootstrapProducers.delete(retirement)); retiringBootstrapProducers.add(retirement); }; const nodeWorkspaceTransfer = createNodeWorkspaceTransferService({ getOwner: (environmentId) => params.startup.store.getTransferOwner(environmentId) }); await nodeWorkspaceTransfer.initialize(); const gatewayDeviceId = loadOrCreateProcessDeviceIdentity().deviceId; const nodeWorkerGatewayNamespace$1 = nodeWorkerGatewayNamespace(gatewayDeviceId); const nodeWorkerTunnelManager = createNodeWorkerTunnelManager({ gatewayDeviceId, getEnvironment: (environmentId) => params.startup.store.get(environmentId), listEnvironments: () => params.startup.store.list(), getTransport: () => deviceRuntime.getNodeTransport(), launchNodeWorker: async (request) => await deviceRuntime.launchNodeWorker(request), validateWorkerTurn: (binding) => placementGate.validateWorkerTurn(binding), workspaceTransfer: nodeWorkspaceTransfer }); const ensureNodeWorkerBundle = createGatewayNodeWorkerBundleInstaller({ gatewayNamespace: nodeWorkerGatewayNamespace$1, getTransport: () => deviceRuntime.getNodeTransport(), transfer: nodeWorkerBundleTransfer }); const nodeEnrollment = createWorkerNodeEnrollmentManager({ store: params.startup.store, getConfig: getRuntimeConfig, getLocalTlsFingerprint: () => params.resolveGatewayContext()?.gatewayTlsFingerprint, resolveAvailability: deviceRuntime.resolveAvailability, transfer: nodeBootstrapTransfer, prepareArtifact: async (record, signal) => { const mode = record.profileSnapshot.executionMode === "remote-exec" ? "remote-exec" : "worker-turn"; let registry = params.getPluginRegistry(); let metadata = getGatewayPluginMetadataSnapshot(); let generation = bootstrapProducers.get(mode); if (!generation || generation.registry !== registry || generation.metadata !== metadata) { const [{ createNodeBootstrapArtifactProvider }, { resolveNodeBootstrapPlugins }] = await Promise.all([import("./node-bootstrap-artifact-nX0VsSJR.js"), import("./node-bootstrap-plugins-BE34eSqW.js")]); signal?.throwIfAborted(); registry = params.getPluginRegistry(); metadata = getGatewayPluginMetadataSnapshot(); generation = bootstrapProducers.get(mode); if (!generation || generation.registry !== registry || generation.metadata !== metadata) { const packageRoot = resolveOpenClawPackageRootSync({ moduleUrl: import.meta.url, argv1: process.argv[1], cwd: process.cwd() }); const runningBuildId = resolveRuntimeServiceBuildId(); if (!metadata || !packageRoot || !runningBuildId) throw new Error("Cloud node bootstrap requires the running build and plugin inventory; build OpenClaw and restart the Gateway"); const producer = createNodeBootstrapArtifactProvider({ packageRoot, runningBuildId, plugins: resolveNodeBootstrapPlugins({ registry, metadata, executionMode: mode }) }); if (generation) retireBootstrapProducer(generation.producer); generation = { registry, metadata, producer }; bootstrapProducers.set(mode, generation); } } return await generation.producer.prepare(signal); } }); let executeSessionTool = async () => { throw new Error("Worker session tools are unavailable"); }; let dispatchChild = async () => { throw new Error("Worker session dispatch is unavailable"); }; const computers = createWorkerComputerService({ store: params.startup.store, placements: params.startup.placementStore, resolveGatewayContext: params.resolveGatewayContext, getNodeTransport: () => deviceRuntime.getNodeTransport(), warn: (message) => workerEnvironmentLog.warn(message) }); const workerEnvironmentService = createWorkerEnvironmentService({ projectNamespace: nodeWorkerGatewayNamespace$1, prepareComputer: computers.prepare, executeComputer: computers.execute, closeComputers: computers.close, store: params.startup.store, getConfig: getRuntimeConfig, maintainProviders: (signal) => maintainConfiguredWorkerProviders({ getRegistry: params.getPluginRegistry, getConfig: getRuntimeConfig, signal, warn: (message) => workerEnvironmentLog.warn(message) }), resolveProvider: (providerId) => providerId === "device" ? deviceRuntime.provider : resolveWorkerProvider(params.getPluginRegistry(), providerId), prepareInstallation, ensureNodeWorkerBundle, prepareNodeBootstrap: nodeEnrollment.prepare, prepareNodeEnrollment: nodeEnrollment.begin, prepareNodeRuntime: nodeEnrollment.prepareRuntime, closeNodeRuntime: nodeEnrollment.closeRuntime, closeNodeEnrollment: nodeEnrollment.close, retireNodeEnrollment: nodeEnrollment.retire, stopNodeEnrollmentWaits: nodeEnrollment.stop, closeNodeBootstrapArtifacts: async () => { await Promise.all([...[...bootstrapProducers.values()].map(({ producer }) => producer.close()), ...retiringBootstrapProducers]); bootstrapProducers.clear(); }, tunnelManager: workerTunnelManager, nodeTunnelManager: nodeWorkerTunnelManager, nodeDesktopCarrier: workerNodeDesktopCarrier, nodePortalCarrier: workerNodePortalCarrier, closeWorkerPortals: async (environmentId, ownerEpoch) => { const service = params.getPortalRuntime()?.portalService; if (!service) return; await service.closeWorkerPortals(environmentId, ownerEpoch); notifyPortalChange(); }, stopNodeWorkerBundleTransfers: () => nodeWorkerBundleTransfer.closeAll(), applyTranscriptCommit: createWorkerTranscriptCommitter({ getConfig: getRuntimeConfig }).commit, executeInference: async (inferenceParams) => { return await (await loadWorkerInferenceRuntimeModule()).executeWorkerInference(inferenceParams); }, placementStore: placementGate, executeSessionTool: (request) => executeSessionTool(request), liveEvents: workerLiveEvents, resolveSshIdentity: async ({ provider, leaseId, profile, keyRef }) => { const workerRuntime = await loadWorkerEnvironmentRuntimeModule(); return await workerRuntime.resolveWorkerSshIdentity({ provider, leaseId, profile, keyRef, resolveGeneric: async (genericKeyRef) => ({ kind: "material", contents: await workerRuntime.resolveSecretRefString(genericKeyRef, { config: getActiveSecretsRuntimeConfigSnapshot()?.sourceConfig ?? getRuntimeConfig(), env: getActiveSecretsRuntimeEnvState() }) }) }); }, bootstrapWorker: async ({ operationId, sshEndpoint, installation, resolveIdentity, signal }) => { return await (await loadWorkerEnvironmentRuntimeModule()).bootstrapWorker({ operationId, ssh: sshEndpoint, artifact: installation, pinnedHostKey: sshEndpoint.hostKey }, { signal, resolveIdentity }); }, logger: workerEnvironmentLog }); bindDeviceWorkerAvailability(workerEnvironmentService, deviceRuntime.resolveAvailability); bindDeviceWorkerReconciliation(workerEnvironmentService, async (deviceId) => { const environmentIds = params.startup.store.listForReconcile().filter((record) => { const settings = record.profileSnapshot.settings; const profileDeviceId = isRecord(settings) ? settings.device : void 0; return record.providerId === "device" && typeof profileDeviceId === "string" && profileDeviceId.trim() === deviceId; }).map((record) => record.environmentId); for (const environmentId of environmentIds) params.startup.store.revokeEnvironmentCredential(environmentId); await Promise.all(environmentIds.map(async (environmentId) => { await workerEnvironmentService.reconcileEnvironment(environmentId).catch(() => { workerEnvironmentLog.warn(`Device worker reconcile failed (${deviceId}, ${environmentId}); periodic cleanup will retry`); }); })); return environmentIds; }); let workerSessionToolExecutor; executeSessionTool = async (request) => { return await (await (workerSessionToolExecutor ??= loadWorkerSessionToolExecutorModule().then(({ createWorkerSessionToolExecutor }) => createWorkerSessionToolExecutor({ resolveGatewayContext: params.resolveGatewayContext, placements: params.startup.placementStore, environments: workerEnvironmentService, dispatchChild: (...args) => dispatchChild(...args), portals: { getService: () => params.getPortalRuntime()?.portalService, carrier: workerNodePortalCarrier, onChanged: notifyPortalChange } }))))(request); }; const bindWorkerNodeDesktopControl = workerNodeDesktopCarrier && workerNodeDesktopStreamBroker ? (transport) => workerNodeDesktopCarrier.bindRuntime({ transport, streamBroker: workerNodeDesktopStreamBroker }) : void 0; return { workerEnvironmentService, workerLiveEvents, workerTunnelManager, nodeWorkerGatewayNamespace: nodeWorkerGatewayNamespace$1, bindWorkerSessionDispatch: (dispatch) => { dispatchChild = dispatch; }, bindDeviceNodeControl: (transport) => { deviceRuntime.bindNodeTransport(transport); if (workerNodeDesktopStreamBroker) workerNodePortalCarrier.bindRuntime({ transport, streamBroker: workerNodeDesktopStreamBroker }); }, ...bindWorkerNodeDesktopControl ? { bindWorkerNodeDesktopControl } : {}, bindNodeWorkspaceBindingResolver: (resolver) => nodeWorkerTunnelManager.bindWorkspaceBindingResolver(resolver), handleNodeWorkerBundleTransferRequest: createNodeWorkerBundleTransferHttpCallback(nodeWorkerBundleTransfer), handleWorkerBootstrapArtifactTransferRequest: createWorkerBootstrapArtifactTransferHttpCallback(nodeBootstrapTransfer), handleNodeWorkspaceTransferRequest: createNodeWorkspaceTransferHttpCallback(nodeWorkspaceTransfer) }; } //#endregion export { createGatewayWorkerEnvironmentRuntime, loadGatewayWorkerEnvironmentStartupState };