Objetivo
Probar el spawn-plane en local: una sesión Claude Code spawnea otra a través del broker + runner daemon. Esto requiere que el Mac tenga un runner corriendo (GATEWAY_MODE=runner) que acepte spawn_orders del broker y ejecute cliLauncher.launch().
Para que UC4 funcione, el Mac necesita un runner daemon corriendo:
cd /path/to/mks-agentics
GATEWAY_MODE=runner \
RUNNER_ID=mac \
BROKER_URL=https://broker.gateway.mks2508.systems \
BROKER_TOKEN=<JWT> \
GATEWAY_PORT=4105 \
bun run apps/gateway-server/src/runner.ts
El runner se conecta al broker vía WS (/ws/runner/mac) y espera spawn_order.
Cuando recibe uno, llama a cliLauncher.launch() y responde peer_registered.
Estado actual (2026-06-20): el runner daemon NO está configurado como servicio permanente en Mac. Hay que arrancarlo manualmente para UC4. Esto es parte del trabajo pendiente de spawn-plane (0.18.D+).
F1 — Arrancar runner daemon en Mac
Configurar y arrancar el runner
cd apps/gateway-server && bun run src/runner.tscon env vars → arranca El runner inicia y se conecta al broker- Log muestra
runner online: id=macConexión WS al broker establecida curl -s https://broker.gateway.mks2508.systems/api/runners -H 'Authorization: Bearer $JWT'→ incluye 'mac' El runner está registrado en el brokercurl -s http://localhost:4105/health→ 200 OK El runner expone health check local
Variables de entorno para el runner
| Variable | Valor | Nota |
|---|---|---|
| GATEWAY_MODE | runner | Activa modo runner (sin REST API pública) |
| RUNNER_ID | mac | Identificador único del runner |
| BROKER_URL | https://broker.gateway.mks2508.systems | WebSocket del broker |
| BROKER_TOKEN | (JWT válido) | Auth del WS (?token=<JWT>) |
| GATEWAY_PORT | 4105 | Puerto para --sdk-url de las sesiones spawneadas |
| GATEWAY_HOST | 127.0.0.1 | Solo localhost (F-OPS-1) |
| CLAUDE_BINARY_PATH | claude | Binary de Claude Code (default) |
# En una terminal dedicada (TMUX pane o similar): $ cd /Volumes/KODAK1TB/REPOS\ y\ PROYECTOS/nodejs-bun/mks-agentics # Generar JWT para el runner: $ source .env.brk-extract.local $ JWT=$(curl -s -X POST "$OIDC_TOKEN_ENDPOINT" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&client_id=$MCP_STANDALONE_CLIENT_ID&client_secret=$MCP_STANDALONE_CLIENT_SECRET&scope=broker" \ | jq -r '.access_token') # Arrancar runner: $ GATEWAY_MODE=runner \ RUNNER_ID=mac \ BROKER_URL=https://broker.gateway.mks2508.systems \ BROKER_TOKEN="$JWT" \ GATEWAY_PORT=4105 \ GATEWAY_HOST=127.0.0.1 \ bun run apps/gateway-server/src/runner.ts # Output esperado: → Runner started: id=mac broker=https://broker.gateway.mks2508.systems localPort=4105 → runner online: id=mac
F2 — Arrancar sesión A con channels {#f2-sessionA}
Sesión existente que spawnea
- En otra terminal:
cl minimax --proxy --channelsSesión A con channels activos ListPeers→ muestra peer A y posiblemente el runner Peer discovery funcionaListPeersNO muestra peer B todavía Aún no se ha spawneado nada
F3 — Spawn peer B
Ejecutar SpawnPeer desde sesión A
- En sesión A:
SpawnPeerconhost='mac',provider='minimax',name='spawned-peer-B'MCP tool SpawnPeer → POST /api/spawn del broker - El broker enruta el spawn_order al runner 'mac' vía WS runner.ts recibe
spawn_ordery llama a cliLauncher.launch() - La respuesta de SpawnPeer incluye
spawnIdystatus: 'pending'El spawn fue aceptado por el broker - En unos segundos,
ListPeersmuestra 3+ peers (A, B, runner) El peer B se registró tras spawn exitoso GetPeer <peerId-B>→ status 'running' La sesión B está viva
SpawnPeer — comandos
# Dentro de Claude Code sesión A: > SpawnPeer name="spawned-peer-B" provider="minimax" host="mac" cwd="/tmp" → { → "spawnId": "spawn_abc123...", → "status": "pending", → "host": "mac" → } # Esperar unos segundos y verificar: > ListPeers → [ → {"id": "a1b2c3d4-...", "kind": "operator", "name": "Claude Code — A", "status": "running"}, → {"id": "e5f6g7h8-...", "kind": "operator", "name": "spawned-peer-B", "status": "running"}, → {"id": "mac", "kind": "runner", "name": "mac", "status": "online"} → ] > GetPeer e5f6g7h8-... → (card completa del peer B — spawneado exitosamente)
F4 — Comunicación post-spawn
Verificar mensajería entre A y B
SendPeerMessage to=<peerId-B> content='hola desde A, spawneada'Mensaje de A a la sesión spawneada B- En la terminal del runner, el log muestra
peer_registered: spawnId=... peerId=... pid=...Runner confirma el spawn exitoso - El mensaje llega a B vía channel-push (si channels=true en el spawn) La sesión spawneada tiene channels porque el runner hereda la config
CheckInbox <peerId-B>→ el mensaje está en el inbox (fallback) Verificación alternativa si channel-push falla
F5 — Stop peer B
Detener la sesión spawneada
StopPeer peerId=<peerId-B>→ 200 OK MCP tool StopPeer → broker → runner → stop_order WS- Runner log muestra
peer_stopped: spawnId=...El runner detuvo el proceso Claude CLI ListPeers→ peer B ya no aparece (o status 'stopped') Peer desregistrado correctamente
- Runner se conecta al broker y aparece en /api/runners Runner registry funciona
- SpawnPeer → spawn_order → peer_registered Spawn-plane local funciona
- ListPeers muestra el peer spawneado Peer discovery post-spawn
- StopPeer detiene la sesión spawneada Lifecycle completo: spawn → communicate → stop
- El runner no arranca por falta de BROKER_TOKEN Necesita JWT válido de OIDC client_credentials
- SpawnPeer falla porque el host no coincide con ningún runner Verificar RUNNER_ID y el parámetro host en SpawnPeer
- El spawn nunca completa (queda en pending) Runner offline, broker no enruta, o cliLauncher falla
¿Está el runner daemon corriendo en Mac? prerequisite
Verificar con curl http://localhost:4105/health
¿Aparece el peer spawneado en ListPeers? verificación
Después de SpawnPeer, esperar 5-10s y ejecutar ListPeers
Código relevante
- runner.tssource
- spawn.routes.tssource
- broker.service.tssource
- runner-registry.tssource