MIT · Plugin oficial · OpenCode + Codex

Controlá tus agentes
de IA
desde el teléfono.

Dashboard web con pestañas multi-proyecto, streaming en vivo vía SSE, notificaciones por Telegram y Web Push, túnel público opcional, y una PWA para seguir tus sesiones desde el celular. Funciona con OpenCode nativamente y con Codex CLI vía hook bridge.

413 tests · TypeScript strict
~20k LOC · Hexagonal-lite
2 CLIs soportados · más en camino
~/my-project — opencode
● live
// 01 · El problema

Tu agente de IA corre perfecto mientras estés frente a él.

OpenCode (y Codex) son agentes que viven en tu terminal. Funcionan perfecto mientras estés sentado frente a ellos — pero en cuanto te levantás, todo se frena. El agente pide permiso para ejecutar un comando, no hay nadie para aprobarlo. Querés seguir la ejecución desde el teléfono mientras hacés otra cosa, no podés. Abrís otro proyecto en otra terminal y perdés el hilo.

📋

Aprobar permisos sin estar frente a la terminal

Herramientas peligrosas necesitan aprobación, pero no estás ahí.

📱

Seguir sesiones largas desde el sofá

Desde el móvil, otro laptop o cualquier dispositivo en tu red.

🔄

Coordinar varios proyectos simultáneos

Cada uno con su tab. Cambiás de contexto sin perder sesiones.

🔔

Recibir notificaciones cuando termina

Web Push o Telegram — en tiempo real, sin estar en el dashboard.

👥

Compartir el dashboard con colegas

Pair programming asincrónico detrás de un túnel público.

🤖

Tener un solo dashboard para varios CLIs

OpenCode y Codex viven en pestañas distintas — acá conviven.

// 02 · Multi-CLI

Un dashboard. Dos integraciones. Sumás más en una carpeta.

La capa integrations/ implementa un puerto único AgentIntegration. Cada CLI vive en su propia carpeta y la composición root la conecta con una línea. Sumar un nuevo agente (Cursor, Aider, etc.) es un folder + una línea.

Native

OpenCode (SDK plugin)

Integración nativa vía hooks del SDK de OpenCode. El plugin se carga dentro del proceso TUI: ve eventos de sesión, permission.ask, tool.start/end, y emite cada uno al event bus interno.

integrations/opencode/
  ├── index.ts           # opencodeIntegration
  └── hooks/
      ├── event.ts
      ├── permission.ask.ts
      └── tool.ts
HTTP bridge

Codex CLI (hook bridge)

Codex no tiene un SDK de plugins — usa hooks por HTTP. El plugin expone POST /codex/hooks/:event y Codex postea ahí. Mismo bus de eventos, mismo dashboard, misma cola de permisos.

# ~/.codex/config.toml
[hooks]
endpoint = "http://127.0.0.1:4097/codex/hooks"
token = "${PILOT_HOOK_TOKEN}"
MethodPathDescripción
POST /codex/hooks/SessionStart Codex abrió una sesión — emite pilot.session.started
POST /codex/hooks/UserPromptSubmit El usuario envió un prompt — emite pilot.prompt.received
POST /codex/hooks/PreToolUse Una herramienta va a correr — emite pilot.tool.started
POST /codex/hooks/PostToolUse Tool finalizada — emite pilot.tool.completed
POST /codex/hooks/PermissionRequest Bloqueante: espera tu approve/deny en el dashboard
POST /codex/hooks/Stop Sesión cerrada — emite pilot.session.stopped
// 03 · Features

Todo lo que tu CLI hace, accesible desde el navegador.

Un servidor HTTP+SSE corre dentro del proceso del plugin. El dashboard es una SPA de módulos ES nativos servida desde el mismo origen — sin React, sin build, sin latencia.

Dashboard multi-proyecto

Pestañas para todos los proyectos que abriste con OpenCode o Codex. Cambiás de contexto sin perder sesiones. Cada tab recuerda sus propias sesiones, mensajes y estado.

Streaming en vivo vía SSE

Cada token aparece a medida que el agente lo genera. Sin recargar, sin polling, sin latencia. Efecto typewriter real.

Aprobación remota de permisos

Cuando el agente (OpenCode o Codex) pide permiso, aparece un banner en el dashboard — desde tu celular también. Aprobás o rechazás con un toque. Si hay cola, se muestra el contador 1/N.

Command palette + shortcuts

Ctrl/Cmd + K abre la paleta. Cambiás de agente, modelo, proveedor, creás sesión nueva, cambiás de proyecto — todo con teclado.

Settings UI

Configurás puerto, host, túnel, Telegram, VAPID keys, hookToken, timeouts y más desde una modal. Se guarda atómicamente en ~/.opencode-pilot/config.json.

📂

Browser de archivos con glob

Explorás el árbol del proyecto, buscás por patrón glob con debounce, y abrís archivos para ver su contenido (opt-in).

Web Push (VAPID)

Recibís notificaciones aunque el dashboard esté cerrado. Generás las claves VAPID desde Settings con un clic.

Bot de Telegram

Configurás token + chat ID (con hints en el dashboard). Recibís alertas de permisos, errores y fin de sesión en Telegram.

!

Browser notifications

Usa la Notifications API nativa cuando el permiso está concedido. Notificaciones inline sin perder la sesión.

PWA y soporte móvil

Interfaz responsive. Instalás el dashboard como app desde el menú del navegador. Service worker con cache versionado y detección de cambios de red.

QR pairing

Un QR para LAN, otro para túnel público. Empareja en segundos desde el celular.

🔗

Auto-focus de la carpeta activa

/remote levanta el dashboard y enfoca automáticamente la pestaña de la carpeta donde estás trabajando. Si no existe, la crea.

📝

Prompt desde el móvil

Envía prompts, aprueba permisos y cambia agentes como si estuvieras en el escritorio.

🌐

Túnel público opcional

PILOT_TUNNEL=cloudflared (o ngrok) y el plugin levanta un túnel automático, detecta la URL, te la muestra en el dashboard y genera un QR.

🪝

Codex hook bridge

POST /codex/hooks/:event recibe los 6 lifecycle hooks de Codex CLI. PermissionRequest es bloqueante hasta que apruebes desde el dashboard.

🔄

Recuperación de token automática

Si OpenCode reinició y tu token quedó stale, la pantalla de "token inválido" te deja pegar la URL fresca y extrae el nuevo token sola.

🔧

CLI con doctor y uninstall

bunx @lesquel/opencode-pilot doctor hace 6 checks de salud en <2 segundos. uninstall revierte toda la instalación (opcional --keep-config).

💡

Hints y validación inline

Cada campo de Settings tiene un hint con dónde conseguir el valor. Al perder foco, valida formato básico sin bloquear el save.

// 04 · Live dashboard

Una SPA vanilla que parece nativa.

Click en las sesiones para ver cómo cambia la transcripción. Todo corre desde el mismo origen que el proceso del CLI — cero latencia artificial.

https://localhost:4097/?token=4a9c…
Connected · SSE
agent · build refactor SSE reconnect $0.42 · 12.4k tok
U
SSE reconnect goes wild when I switch networks. Can you add exponential backoff and an explicit pong?
A
Got it. Adding exponential backoff (1s → 30s max) and a 15s heartbeat with timeout. Editing src/dashboard/sse.ts.
edit src/dashboard/sse.ts completed · 142ms
-  const delay = 1000;
+  const delay = Math.min(30_000, 1_000 * 2 ** attempts);
+  scheduleHeartbeat(15_000);
bun test sse.test.ts running…
[sse] 4 pass · 0 fail · 0 skip
[runtime] ✓ reconnects with jittered backoff
[runtime] ✓ heartbeat fires every 15s
⌘↵
// 05 · Cómo funciona

Screaming Architecture · 8 capas con un único punto de composición.

Desde v1.18.0, las carpetas anuncian su propósito, no su tecnología. La regla de dependencias es estricta: infra → core → (transport, integrations, notifications) → server. Sumar un nuevo CLI es una carpeta + una línea en el composition root.

┌─────────────────────┐      ┌─────────────────────────┐
│   OpenCode TUI      │      │   Codex CLI (hooks)     │
│   (SDK plugin)      │      │   POST /codex/hooks/*   │
└──────────┬──────────┘      └──────────┬──────────────┘
           │                            │
           ▼                            ▼
   ┌──────────────────────────────────────────┐
   │  integrations/  ports.ts                 │
   │  ├── opencode/  (native SDK)             │
   │  └── codex/     (HTTP bridge)            │
   └─────────────────┬────────────────────────┘
                     │
   ┌─────────────────▼────────────────────────┐
   │  core/                                   │  ← domain
   │  permissions · events · audit · settings │
   └─────────────────┬────────────────────────┘
                     │
   ┌─────────────────▼────────────────────────┐
   │  transport/http   ←→   notifications/    │
   │  Bun.serve + SSE       Telegram + Push   │
   └─────────────────┬────────────────────────┘
                     │
   ┌─────────────────▼────────────────────────┐
   │  dashboard/  (vanilla SPA, same origin)  │
   └──────────────────────────────────────────┘

core/

domain

Permisos, eventos, audit, settings, state, errores. Cero HTTP / Telegram / Codex.

transport/

how-exposed

Bun.serve + routes + handlers + middlewares. La forma en que el core se expone.

integrations/

adapters

OpenCode (SDK hooks) y Codex (HTTP hooks). Puerto único: AgentIntegration.

notifications/

fan-out

Telegram y Web Push. Puerto único: NotificationChannel. Pipeline orquesta el fan-out.

infra/

plumbing

Tunnel, QR, banner, logger, network, auth, paths, dotenv, circuit breaker.

dashboard/

browser-spa

SPA vanilla servida por transport. Sin React, sin build, módulos ES nativos.

tui/ + cli/

entry-points

Slash commands para OpenCode TUI + binario CLI (init, doctor, uninstall).

server/

composition

Façade: el único lugar que importa de todas las capas. Wires y devuelve el handle.

// 06 · Instalación

Un comando. Tres pasos. Cero configuración.

El instalador localiza tu config dir de OpenCode (XDG, APPDATA, etc.), registra el plugin en opencode.json y tui.json, y limpia wrappers viejos. Para Codex, agregás el endpoint en ~/.codex/config.toml.

Pre-requisitos

OpenCode (npm i -g opencode) o Codex CLI instalado. Bun (recomendado) o Node moderno.

✓ OpenCode ≥ 0.1.0 · ✓ Node 18.20+ o Bun · ✓ APPDATA/XDG_CONFIG_HOME accesibles

Ejecutá el init

Una sola línea. Detecta XDG_CONFIG_HOME / APPDATA automáticamente.

$bunx @lesquel/opencode-pilot init

Reabrí OpenCode

Cerrá todas las sesiones TUI y reabrí. Verás un toast: "Remote control plugin loaded".

$opencode

Abrí el dashboard

Tipeá /remote en el TUI. Se abre el navegador. El banner imprime URL, token y QR.

/remote
Desinstalar
$bunx @lesquel/opencode-pilot uninstall
Borra todo (config + state)
$bunx @lesquel/opencode-pilot uninstall --keep-config
Preserva ~/.opencode-pilot/config.json
Diagnóstico
$bunx @lesquel/opencode-pilot doctor
Reporta en <2s: plugin instalado, config edits presentes, OpenCode corriendo, pilot responsivo, state file válido.
// 07 · Casos de uso

Situaciones reales en las que cambia el juego.

Desarrollador solo que aprueba desde el celular

Tu agente está ejecutando un refactor largo. Te levantás a hacer café. En el celular, una notificación Web Push te avisa que pide permiso para ejecutar rm -rf node_modules. Aprobás con un toque.

Equipo que colabora revisando sesiones

Activás el túnel (PILOT_TUNNEL=cloudflared). El dashboard queda accesible con un link público. Un colega sigue la sesión en tiempo real para hacer pair programming asincrónico.

OpenCode + Codex en paralelo

Tenés OpenCode trabajando en una refactor y Codex generando tests en otra terminal. Los dos eventos llegan al mismo dashboard, en pestañas separadas, con permisos coordinados.

Quien trabaja en varios proyectos simultáneos

Tres terminales, tres proyectos, un dashboard. Las pestañas guardan estado entre cambios. Cero context switch tax.

Debug de sesiones remotas

Necesitás ver qué hizo el agente hace 2 horas en el servidor de CI. Te conectás al túnel, abrís la sesión, leés el histórico completo, descargás el diff.

Integración con notificaciones del equipo

Bot de Telegram conectado a un canal del equipo. Cada vez que un agente termina una tarea larga, el canal recibe el resumen. Todos ven el progreso sin abrir el dashboard.

// 08 · Mobile

Desde el sofá, la cama, o un hotel en Tokio.

Misma WiFi con PILOT_HOST=0.0.0.0, o expón un túnel público con Cloudflare o ngrok. El dashboard es mobile-first: drawer sidebar, bottom-sheets, 44×44 tap targets.

LAN
PILOT_HOST=0.0.0.0
Tunnel
PILOT_TUNNEL=cloudflared
Shortcut
c

🔒 La URL de túnel contiene el token. Tratala como una contraseña — rotala con /pilot-token.

Connect phone
Scan with camera app
192.168.1.14:4097/?t=4a9c…
Same WiFi · 44ms
// 09 · Keybinds & commands

Todo es un atajo.

Para quienes prefieren teclear antes que hacer click. Pulsá ? dentro del dashboard para la paleta completa.

Keyboard shortcuts global · sin input focused

Command palette · paleta + shortcuts
?
Command palette (modifier)
K
Nueva sesión
n
Toggle sidebar
s
Toggle multi-view
m
Toggle tema
t
Conectar móvil
c
Enfocar prompt
/
Enviar prompt
Toggle info panel
I
Cerrar modal/palette
Esc

Slash commands desde OpenCode TUI

/pilot · URL + token actual
/pilot
/pilot-token · rota el token, invalida dashboards
/pilot-token
/dashboard · alias de /pilot
/dashboard
/remote · info de conexión (host, port, tunnel)
/remote
/remote-control · status completo + hint de QR
/remote-control
Si los slash commands no aparecen, verificá que tui.json::plugin incluye el spec. OpenCode usa dos loaders — uno para el servidor, otro para la TUI — y necesitan registros separados.
// 10 · Config

Dos caminos: UI o .env.

Desde v1.12 editás todo desde el ícono de engranaje. Los valores viven en ~/.opencode-pilot/config.json. Si preferís archivos, un .env también funciona — y las env vars del shell siempre ganan.

Prioridad → 1. shell env · 2. Settings UI · 3. .env · 4. defaults
VariableDefaultDescripción
PILOT_PORT 4097 Puerto del servidor HTTP
PILOT_HOST 127.0.0.1 Dirección de bind. 0.0.0.0 para LAN.
PILOT_TUNNEL cloudflared o ngrok para acceso público
PILOT_PERMISSION_TIMEOUT 300000 Timeout de solicitud de permiso (ms)
PILOT_HOOK_TOKEN new Token bearer dedicado para /codex/hooks/*. Se acepta junto con el token principal.
PILOT_CODEX_PERMISSION_TIMEOUT_MS new 250000 Timeout para PermissionRequest de Codex (max 250000 — cap de Bun.serve idleTimeout)
PILOT_PROJECT_STATE new auto auto | always | off. Controla escrituras de pilot-state por proyecto
PILOT_DEV new false Re-lee el HTML del dashboard en cada request (DX)
PILOT_TELEGRAM_TOKEN Bot token de @BotFather para notificaciones Telegram
PILOT_TELEGRAM_CHAT_ID Chat ID al que enviar las alertas
PILOT_VAPID_PUBLIC_KEY Clave pública Web Push (genera con un click en Settings)
PILOT_VAPID_PRIVATE_KEY Clave privada Web Push
PILOT_ENABLE_GLOB_OPENER false Habilita /fs/glob y /fs/read para búsqueda glob desde el dashboard
PILOT_FETCH_TIMEOUT_MS 10000 Timeout para llamadas HTTP salientes (Telegram, push)
// 11 · Roadmap

Próximo paso: salir de la terminal y entrar al editor.

OpenCode Pilot empezó como un dashboard remoto. Hoy es el puente entre tu agente y cualquier dispositivo. El siguiente paso es claro: integrarlo donde más tiempo pasamos.

Hoy · v1.18.2

OpenCode (SDK) + Codex CLI (HTTP bridge)

Dos integraciones reales en producción. 6 endpoints de hooks Codex, 9+ event types compartidos, mismo dashboard, misma cola de permisos.

Próximo · Q2 2026

Visual Studio Code extension

Extensión nativa para VS Code. Webview embebido del dashboard, comandos en la palette, status bar con sesiones activas, notificaciones via VS Code Notifications API y handler de URI para abrir desde links externos. La meta: que abrir el dashboard sea un atajo en lugar de un /remote.

Explorando

Cloud relay v2 + más adapters

Servicio relay multi-tenant para que NAT y firewalls dejen de ser bloqueador (diseño en docs/CLOUD_RELAY_v2_DESIGN.md). Más adapters: Cursor, Aider, Continue.dev — el puerto AgentIntegration ya está listo, solo falta escribir cada bridge.

¿Querés contribuir o sugerir? Las issues abiertas en GitHub Issues son el lugar — especialmente la integración con VS Code.

// 12 · Docs

Documentación completa.

El docs/ del repo cubre desde la arquitectura de loaders hasta el diseño futuro del relay.

Tweaks theme + effects
dotted grid overlay
retro scanline effect
subtle title jitter