Skip to content

Núcleo del gateway

源码版本v2026.6.11

Responsabilidad

startGatewayServer es la entrada del proceso residente de OpenClaw: enlaza el puerto, carga plugins, registra métodos RPC y hace broadcast de los eventos del agent. Todos los mensajes de canales, las peticiones ACP y los clientes web se conectan a este gateway, que reenvía las peticiones al bucle principal del agent y propaga los eventos que el agent produce de vuelta a cada cliente.

El gateway en sí no «piensa» — solo hace tres cosas: recibir, reenviar, propagar: recibe peticiones JSON-RPC, las reenvía al handler correspondiente (muchos terminan en el agent) y propaga los eventos del agent a las conexiones WebSocket suscritas a esa sesión.

Motivación de diseño

¿Por qué separar el gateway como un proceso residente? Porque OpenClaw debe estar conectado simultáneamente a 22 canales, y la mayoría requieren conexiones de larga duración (WebSocket/polling). Si por cada mensaje se levantara un proceso efímero, el coste de handshake y la complejidad de restauración de estado colapsarían el diseño. Un gateway residente permite que todas las conexiones de canal, todas las sesiones de agent y todo el estado de plugins se compartan dentro de un único proceso: la coordinación entre capas (por ejemplo, un mensaje del canal A dispara al agent, que llama a una herramienta, y el resultado debe entregarse al canal B) es solo una llamada de función dentro del proceso.

Archivos clave

Flujo de datos

La secuencia de arranque central de startGatewayServer (server.impl.ts:551):

typescript
export async function startGatewayServer(
  port = 18789,
  opts: GatewayServerOptions = {},
): Promise<GatewayServer> {
  normalizeStateDirEnv(process.env);
  // Limpia la generación de plugins vieja que pudo quedar entre dos reinicios,
  // para garantizar estado limpio entre reinicios in-process
  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();

Nótese el puerto por defecto 18789 y que el primer paso del arranque es limpiar la generación npm de plugins vieja — está pensado para reinicios in-process: al reiniciar, el gateway primero reclama los recursos de plugins dejados por la ronda anterior para evitar fugas.

El despacho de peticiones (handleGatewayRequest:638) va por JSON-RPC:

typescript
/** 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;
  // Prefiere usar el registry adjunto por el llamador cuando tiene el método;
  // si no, lo reconstruye desde el registry live de plugins,
  // para que los métodos RPC registrados tras el snapshot inicial sigan siendo
  // alcanzables (#94127)

El registry de handlers se reconstruye por petición (createRequestGatewayMethodRegistry:598): fusiona los handlers núcleo, los handlers del plugin activo en ese momento y los handlers extra del llamador. Este diseño permite a los plugins registrar métodos RPC en caliente sin reiniciar el gateway.

El broadcast de eventos del agent (createAgentEventHandler:299) inyecta tres primitivas:

typescript
export function createAgentEventHandler({
  broadcast,              // broadcast a todas las conexiones suscritas a esta sesión
  broadcastToConnIds,    // broadcast dirigido a un conjunto de conexiones
  nodeSendToSession,     // entrega entre nodos a una sesión
  agentRunSeq,
  chatRunState,
  resolveSessionKeyForRun,
  ...

broadcast y broadcastToConnIds los inyecta createGatewayNodeSessionRuntime en server.impl.ts:971 y atraviesan todo el ciclo de vida del gateway.

Límites y fallos

  • Reinicio in-process: al reiniciar, el gateway primero limpia la generación npm de plugins vieja (src/gateway/server.impl.ts:560); si la limpieza falla, solo emite un warn y no bloquea el arranque — poder levantar el servicio importa más que la limpieza impecable.
  • Alcance de los handlers hot-registrados: handleGatewayRequest cae al registry live para reconstruir cuando el snapshot del llamador no tiene el método, lo que arregla #94127 — de lo contrario, los RPC de plugin registrados tras el snapshot inicial quedarían « invisibles».
  • Directorio de estado: el arranque primero llama normalizeStateDirEnv(process.env) para asegurar que $OPENCLAW_STATE_DIR se resuelve correctamente; en caso contrario, las rutas de configuración y workspace quedarían desalineadas.
  • Runtime de red: bootstrapGatewayNetworkRuntime() se invoca antes de cargar la capa de red; si se invierte el orden, los canales WebSocket no estarán listos.

Resumen

El gateway solo hace tres cosas — recibir, reenviar y propagar —, pero cada una está diseñada para «residente + multicanal + hot-swappable»: limpieza previa al arranque, registry de handlers reconstruido por petición y primitivas de broadcast inyectadas. Lo que de verdad «piensa» es el bucle principal del agent; cómo se autorizan y despachan las peticiones se ve en tabla de métodos RPC; cómo los eventos llegan al cliente se ve en broadcast de chat.

Referencias oficiales: Documentación del gateway · README.