import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { GatewayServerLiveState } from "./server-live-state.js"; import type { GatewayRequestContext, GatewayClient } from "./server-methods/types.js"; import { disconnectAllSharedGatewayAuthClients } from "./server-shared-auth-generation.js"; type GatewayRequestContextClient = GatewayClient & { socket: { close: (code: number, reason: string) => void }; usesSharedGatewayAuth?: boolean; }; type GatewayRequestContextParams = { deps: GatewayRequestContext["deps"]; runtimeState: Pick; getRuntimeConfig: GatewayRequestContext["getRuntimeConfig"]; execApprovalManager: GatewayRequestContext["execApprovalManager"]; pluginApprovalManager: GatewayRequestContext["pluginApprovalManager"]; loadGatewayModelCatalog: GatewayRequestContext["loadGatewayModelCatalog"]; getHealthCache: GatewayRequestContext["getHealthCache"]; refreshHealthSnapshot: GatewayRequestContext["refreshHealthSnapshot"]; logHealth: GatewayRequestContext["logHealth"]; logGateway: GatewayRequestContext["logGateway"]; incrementPresenceVersion: GatewayRequestContext["incrementPresenceVersion"]; getHealthVersion: GatewayRequestContext["getHealthVersion"]; broadcast: GatewayRequestContext["broadcast"]; broadcastToConnIds: GatewayRequestContext["broadcastToConnIds"]; nodeSendToSession: GatewayRequestContext["nodeSendToSession"]; nodeSendToAllSubscribed: GatewayRequestContext["nodeSendToAllSubscribed"]; nodeSubscribe: GatewayRequestContext["nodeSubscribe"]; nodeUnsubscribe: GatewayRequestContext["nodeUnsubscribe"]; nodeUnsubscribeAll: GatewayRequestContext["nodeUnsubscribeAll"]; hasConnectedTalkNode: GatewayRequestContext["hasConnectedTalkNode"]; clients: Set; enforceSharedGatewayAuthGenerationForConfigWrite: (nextConfig: OpenClawConfig) => void; nodeRegistry: GatewayRequestContext["nodeRegistry"]; agentRunSeq: GatewayRequestContext["agentRunSeq"]; chatAbortControllers: GatewayRequestContext["chatAbortControllers"]; chatAbortedRuns: GatewayRequestContext["chatAbortedRuns"]; chatRunBuffers: GatewayRequestContext["chatRunBuffers"]; chatDeltaSentAt: GatewayRequestContext["chatDeltaSentAt"]; chatDeltaLastBroadcastLen: GatewayRequestContext["chatDeltaLastBroadcastLen"]; addChatRun: GatewayRequestContext["addChatRun"]; removeChatRun: GatewayRequestContext["removeChatRun"]; subscribeSessionEvents: GatewayRequestContext["subscribeSessionEvents"]; unsubscribeSessionEvents: GatewayRequestContext["unsubscribeSessionEvents"]; subscribeSessionMessageEvents: GatewayRequestContext["subscribeSessionMessageEvents"]; unsubscribeSessionMessageEvents: GatewayRequestContext["unsubscribeSessionMessageEvents"]; unsubscribeAllSessionEvents: GatewayRequestContext["unsubscribeAllSessionEvents"]; getSessionEventSubscriberConnIds: GatewayRequestContext["getSessionEventSubscriberConnIds"]; registerToolEventRecipient: GatewayRequestContext["registerToolEventRecipient"]; dedupe: GatewayRequestContext["dedupe"]; wizardSessions: GatewayRequestContext["wizardSessions"]; findRunningWizard: GatewayRequestContext["findRunningWizard"]; purgeWizardSession: GatewayRequestContext["purgeWizardSession"]; getRuntimeSnapshot: GatewayRequestContext["getRuntimeSnapshot"]; getEventLoopHealth?: GatewayRequestContext["getEventLoopHealth"]; startChannel: GatewayRequestContext["startChannel"]; stopChannel: GatewayRequestContext["stopChannel"]; markChannelLoggedOut: GatewayRequestContext["markChannelLoggedOut"]; wizardRunner: GatewayRequestContext["wizardRunner"]; broadcastVoiceWakeChanged: GatewayRequestContext["broadcastVoiceWakeChanged"]; broadcastVoiceWakeRoutingChanged: GatewayRequestContext["broadcastVoiceWakeRoutingChanged"]; unavailableGatewayMethods: ReadonlySet; }; export function createGatewayRequestContext( params: GatewayRequestContextParams, ): GatewayRequestContext { return { deps: params.deps, // Keep cron reads live so config hot reload can swap cron/store state without rebuilding // every handler closure that already holds this request context. get cron() { return params.runtimeState.cronState.cron; }, get cronStorePath() { return params.runtimeState.cronState.storePath; }, getRuntimeConfig: params.getRuntimeConfig, execApprovalManager: params.execApprovalManager, pluginApprovalManager: params.pluginApprovalManager, loadGatewayModelCatalog: params.loadGatewayModelCatalog, getHealthCache: params.getHealthCache, refreshHealthSnapshot: params.refreshHealthSnapshot, logHealth: params.logHealth, logGateway: params.logGateway, incrementPresenceVersion: params.incrementPresenceVersion, getHealthVersion: params.getHealthVersion, broadcast: params.broadcast, broadcastToConnIds: params.broadcastToConnIds, nodeSendToSession: params.nodeSendToSession, nodeSendToAllSubscribed: params.nodeSendToAllSubscribed, nodeSubscribe: params.nodeSubscribe, nodeUnsubscribe: params.nodeUnsubscribe, nodeUnsubscribeAll: params.nodeUnsubscribeAll, hasConnectedTalkNode: params.hasConnectedTalkNode, hasExecApprovalClients: (excludeConnId?: string) => { for (const gatewayClient of params.clients) { if (excludeConnId && gatewayClient.connId === excludeConnId) { continue; } const scopes = Array.isArray(gatewayClient.connect.scopes) ? gatewayClient.connect.scopes : []; if (scopes.includes("operator.admin") || scopes.includes("operator.approvals")) { return true; } } return false; }, disconnectClientsForDevice: (deviceId: string, opts?: { role?: string }) => { for (const gatewayClient of params.clients) { if (gatewayClient.connect.device?.id !== deviceId) { continue; } if (opts?.role && gatewayClient.connect.role !== opts.role) { continue; } try { gatewayClient.socket.close(4001, "device removed"); } catch { /* ignore */ } } }, disconnectClientsUsingSharedGatewayAuth: () => { disconnectAllSharedGatewayAuthClients(params.clients); }, enforceSharedGatewayAuthGenerationForConfigWrite: params.enforceSharedGatewayAuthGenerationForConfigWrite, nodeRegistry: params.nodeRegistry, agentRunSeq: params.agentRunSeq, chatAbortControllers: params.chatAbortControllers, chatAbortedRuns: params.chatAbortedRuns, chatRunBuffers: params.chatRunBuffers, chatDeltaSentAt: params.chatDeltaSentAt, chatDeltaLastBroadcastLen: params.chatDeltaLastBroadcastLen, addChatRun: params.addChatRun, removeChatRun: params.removeChatRun, subscribeSessionEvents: params.subscribeSessionEvents, unsubscribeSessionEvents: params.unsubscribeSessionEvents, subscribeSessionMessageEvents: params.subscribeSessionMessageEvents, unsubscribeSessionMessageEvents: params.unsubscribeSessionMessageEvents, unsubscribeAllSessionEvents: params.unsubscribeAllSessionEvents, getSessionEventSubscriberConnIds: params.getSessionEventSubscriberConnIds, registerToolEventRecipient: params.registerToolEventRecipient, dedupe: params.dedupe, wizardSessions: params.wizardSessions, findRunningWizard: params.findRunningWizard, purgeWizardSession: params.purgeWizardSession, getRuntimeSnapshot: params.getRuntimeSnapshot, getEventLoopHealth: params.getEventLoopHealth, startChannel: params.startChannel, stopChannel: params.stopChannel, markChannelLoggedOut: params.markChannelLoggedOut, wizardRunner: params.wizardRunner, broadcastVoiceWakeChanged: params.broadcastVoiceWakeChanged, broadcastVoiceWakeRoutingChanged: params.broadcastVoiceWakeRoutingChanged, unavailableGatewayMethods: params.unavailableGatewayMethods, }; }