Cœur de passerelle
Responsabilités
startGatewayServer est l'entrée du processus persistant d'OpenClaw: binder le port, charger les plugins, enregistrer les méthodes RPC, diffuser les événements de l'agent. Tous les messages des canaux, requêtes ACP, et clients Web se connectent à cette passerelle, qui les forward à la boucle principale de l'agent et diffuse les événements de l'agent à tous les clients.
La passerelle elle-même ne « pense » pas — elle ne fait que recevoir, forwarder, diffuser: recevoir une requête JSON-RPC, la forwarder au handler correspondant (beaucoup atterrissent in fine sur l'agent), et diffuser les événements de l'agent aux WebSocket qui ont souscrit à la session.
Motivation de conception
Pourquoi ériger la passerelle en processus persistant distinct? Parce qu'OpenClaw doit être branché simultanément sur 22 canaux, dont la plupart nécessitent des connexions longues (WebSocket/polling). Si chaque message déclenchait un processus court, le coût de handshake et la complexité de restauration d'état écraseraient la conception. Une passerelle persistante permet à toutes les connexions de canaux, toutes les sessions d'agent, et tout l'état des plugins de partager un seul processus; la coordination inter-couche (par exemple: un message du canal A déclenche un agent qui appelle un outil, dont le résultat doit être livré au canal B) n'est plus qu'un appel de fonction intra-processus.
Fichiers clés
openclaw.mjs— Entrée racine, vérifie la version Node puis forward versdist/entry.js.entry.ts main:79-131—main()→runMainOrRootHelprépartit.run-main hot path passerelle:159-225—gateway rundynamic import direct.run-command .action:64-71— Parse les options puis appellerunGatewayCommand.server.ts startGatewayServer:31-40— Wrapper fin, lazy load server.impl.implémentation startGatewayServer:551-580— Vrai démarrage: nettoyage de l'ancienne génération de plugins, bootstrap du runtime réseau.coreGatewayHandlers:269— Registre central des handlers RPC.createRequestGatewayMethodRegistry:598-630— Fusionne core+plugin+extra handler à chaque requête.handleGatewayRequest:638-660— Autorise et répartit une requête JSON-RPC.createAgentEventHandler:299-320— Diffuse les événements de l'agent aux clients WebSocket.
Flux de données
La séquence de démarrage centrale de startGatewayServer (server.impl.ts:551):
export async function startGatewayServer(
port = 18789,
opts: GatewayServerOptions = {},
): Promise<GatewayServer> {
normalizeStateDirEnv(process.env);
// Nettoie l'ancienne génération de plugins laissée par le redémarrage précédent, pour un état propre entre deux runs
const installRecords = loadInstalledPluginIndexInstallRecordsSync();
const removedGenerations = await cleanupRetiredManagedNpmInstallGenerations({
activeInstallPaths: Object.values(installRecords).flatMap((record) =>
record.installPath ? [record.installPath] : [],
),
onError: (error, projectRoot) =>
log.warn(`failed to clean retained npm generation ${projectRoot}: ${String(error)}`),
});
const { bootstrapGatewayNetworkRuntime } = await import("./server-network-runtime.js");
bootstrapGatewayNetworkRuntime();Notez le port par défaut 18789 et le fait que la première chose exécutée est le nettoyage de l'ancienne génération de plugins npm — c'est pour les redémarrages in-process: la passerelle doit d'abord récupérer les ressources de la génération précédente pour éviter les fuites.
La répartition des requêtes (handleGatewayRequest:638) en JSON-RPC:
/** Authorizes and dispatches one gateway JSON-RPC-style request. */
export async function handleGatewayRequest(
opts: GatewayRequestOptions & { extraHandlers?: GatewayRequestHandlers },
): Promise<void> {
const { req, respond, client, isWebchatConnect, context } = opts;
// Privilégier le registry attaché par l'appelant (s'il possède la méthode),
// sinon reconstruire depuis le live plugin registry pour que les méthodes RPC
// des plugins enregistrés après le snapshot de démarrage restent atteignables (#94127)Le handler registry est reconstruit à chaque requête (createRequestGatewayMethodRegistry:598): on fusionne core handlers, plugin handlers actifs, et extra handlers fournis par l'appelant. Ce design permet aux plugins d'enregistrer chaud (hot register) des méthodes RPC sans redémarrer la passerelle.
La diffusion des événements agent (createAgentEventHandler:299) reçoit trois primitives injectées:
export function createAgentEventHandler({
broadcast, // Diffuser à toutes les connexions abonnées à cette session
broadcastToConnIds, // Diffusion ciblée à des connexions précises
nodeSendToSession, // Livraison inter-nœud vers une session
agentRunSeq,
chatRunState,
resolveSessionKeyForRun,
...broadcast et broadcastToConnIds sont injectés par createGatewayNodeSessionRuntime dans server.impl.ts:971, pendant toute la vie de la passerelle.
Limites et modes d'échec
- Redémarrage in-process: au redémarrage de la passerelle, on nettoie d'abord l'ancienne génération de plugins npm (
src/gateway/server.impl.ts:560); si le nettoyage échoue, on log un warn sans bloquer le démarrage — pouvoir lever le service passe avant un nettoyage complet. - Accessibilité des handlers en hot register:
handleGatewayRequestrepli sur le live registry si le snapshot de l'appelant ne possède pas la méthode, fix de #94127 — sinon les RPC de plugins enregistrés après le snapshot de démarrage seraient « invisibles ». - Répertoire d'état: le démarrage appelle
normalizeStateDirEnv(process.env)pour s'assurer que$OPENCLAW_STATE_DIRest résolu correctement, sinon les chemins de config et de workspace se désynchronisent. - Runtime réseau:
bootstrapGatewayNetworkRuntime()est appelé avant de charger la couche réseau; un ordre inverse laisserait les canaux WebSocket non prêts.
Résumé
La passerelle ne fait que recevoir, forwarder, diffuser, mais chaque tâche est conçue pour « persistant + multi-canaux + hot-swappable »: nettoyage au démarrage, handler registry recréé par requête, primitives de diffusion injectées. Ce qui « pense » vraiment est la Boucle principale de l'agent; pour l'autorisation et la répartition des requêtes, voir Table de méthodes RPC; pour la livraison des événements aux clients, voir Diffusion du chat.
Pour comparer avec la documentation officielle: Gateway docs · README.