Files
agents_and_robots/dev/issues/0038-element-widgets-dashboard.md
T
egutierrez 52d5632d89 docs: crear issues 0036-0041 — nuevas features del sistema
Issues planificados:
- 0036: Claude Code streaming de progreso en Matrix
- 0037: Agente que crea otros agentes/bots via Matrix
- 0038: Webapps y dashboards embebidos en Element via widgets
- 0039: Recordatorios dinámicos y crons que invocan agentes
- 0040: Soporte para mensajes de voz (audio → STT)
- 0041: Videollamadas con agentes via LiveKit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:19:09 +00:00

297 lines
15 KiB
Markdown

# 0038 — Webapps y dashboards embebidos en Element via widgets
**Estado:** pendiente
## Objetivo
Incorporar un servidor HTTP embebido en el launcher que sirva dashboards y mini-apps de los agentes, integrables en rooms de Element como Matrix widgets. Los usuarios podran ver estado en tiempo real, metricas e interfaces interactivas de sus agentes directamente desde sus rooms Matrix, sin salir del cliente.
## Contexto
- El launcher ya arranca multiples agentes en paralelo (`cmd/launcher/main.go`) y tiene un logger centralizado con JSONL rotado por dia.
- `shell/logger/query.go` ya expone `ReadLogs()` y `ReadDayLogs()` para consultar logs JSONL por agente y fecha — reutilizable para las API de metricas.
- `internal/config/schema.go` define `AgentConfig` con todas las secciones; falta una seccion `WebCfg` para el servidor HTTP.
- Matrix soporta widgets via state events `im.vector.modular.widgets` (Element Web y Desktop). El agente puede enviar estos state events usando mautrix-go.
- Actualmente no existe `shell/web/` ni ningun endpoint HTTP en el proyecto.
- El issue 0035 (audit trail + `!metrics`) agrega metricas del dia actual; este issue va mas alla con visualizacion web persistente y en tiempo real.
## Arquitectura
### Pure core / impure shell
- **`pkg/`** — no se modifica. No hay logica pura nueva; la transformacion de datos de logs a metricas se puede hacer con funciones helper dentro de `shell/web/handlers.go` (son inherentemente I/O-bound: leen archivos).
- **`shell/web/`** — NEW, 100% impuro: servidor HTTP, handlers API, SSE streaming, widget registration via Matrix.
- **`internal/config/schema.go`** — MOD: agregar `WebCfg` al schema de configuracion.
- **`cmd/launcher/main.go`** — MOD: arrancar servidor web junto con los agentes.
### Fase 1 — Servidor HTTP embebido
```
shell/web/ NEW — package del servidor web
shell/web/server.go NEW — setup del servidor HTTP + routes
shell/web/handlers.go NEW — handlers de los endpoints API
shell/web/static/ NEW — archivos estaticos del dashboard (embed.FS)
```
Endpoints:
- `GET /api/agents` — lista de agentes en ejecucion con estado (running/stopped/error)
- `GET /api/agents/{id}` — detalle del agente (config filtrada, uptime, ultima actividad)
- `GET /api/agents/{id}/metrics` — metricas agregadas del dia (reutiliza `shell/logger/query.go`)
- `GET /api/agents/{id}/logs` — SSE stream de logs en tiempo real
- `GET /dashboard` — SPA del dashboard (HTML/JS/CSS embebido via `embed.FS`)
- `GET /dashboard/{id}` — vista filtrada por agente (util para widgets)
### Fase 2 — Integracion Matrix widget
```
shell/web/widget.go NEW — helper para registrar widgets en rooms Matrix
```
- Cuando un agente se une a un room, opcionalmente registra un widget via state event `im.vector.modular.widgets`.
- Widget URL apunta al dashboard embebido filtrado para ese agente: `{base_url}/dashboard/{agent-id}?room={room_id}`.
- Usa mautrix-go `client.SendStateEvent()` para enviar el state event.
- Config: `matrix.widgets.enabled`, `matrix.widgets.base_url`, `matrix.widgets.auto_register`.
### Fase 3 — Dashboard UI
```
shell/web/static/index.html NEW — SPA entry point
shell/web/static/app.js NEW — logica JS del dashboard
shell/web/static/style.css NEW — estilos
```
- SPA con vanilla JS (o Preact si crece), embebido en el binario Go via `embed.FS`.
- Vistas:
- **Lista de agentes**: estado (running/stopped/error), tipo (agent/robot), uptime.
- **Detalle de agente**: resumen de config, mensajes recientes, uso de tools.
- **Log viewer en vivo**: via SSE (`EventSource` en JS), muestra logs en tiempo real.
- **Graficas de metricas**: mensajes/hora, tool calls, errores, latencia LLM.
## Archivos afectados
| Archivo | Cambio | Descripcion |
|---------|--------|-------------|
| `shell/web/` | NEW | Package completo del servidor web |
| `shell/web/server.go` | NEW | Setup HTTP server, router, middleware |
| `shell/web/handlers.go` | NEW | Handlers API: agents, metrics, logs SSE |
| `shell/web/widget.go` | NEW | Helper para registrar widgets Matrix en rooms |
| `shell/web/static/` | NEW | Dashboard SPA (HTML/JS/CSS embebido) |
| `internal/config/schema.go` | MOD | Agregar `WebCfg` con Enabled, Port, Host, BasePath, Auth |
| `cmd/launcher/main.go` | MOD | Arrancar servidor web junto con los agentes |
| `shell/matrix/client.go` | MOD | Agregar metodo para enviar state events de widget (si no existe) |
## Tareas
### Fase 1 — Servidor HTTP embebido
- [ ] **1.1** Agregar `WebCfg` a `internal/config/schema.go`:
```go
type WebCfg struct {
Enabled bool `yaml:"enabled"` // habilitar servidor web (default false)
Host string `yaml:"host"` // bind address (default "127.0.0.1")
Port int `yaml:"port"` // puerto HTTP (default 8080)
BasePath string `yaml:"base_path"` // prefijo de rutas (default "/")
Auth WebAuthCfg `yaml:"auth"` // autenticacion
}
type WebAuthCfg struct {
Enabled bool `yaml:"enabled"` // requerir autenticacion
TokenEnv string `yaml:"token_env"` // env var con el token de acceso
}
```
Agregar campo `Web WebCfg yaml:"web"` a `AgentConfig` (o a un nuevo `LauncherConfig` si se decide no atar a cada agente).
- [ ] **1.2** Crear `shell/web/server.go`:
- Struct `Server` con `http.Server`, referencia a la lista de agentes en ejecucion, config, logger.
- Constructor `New(cfg WebCfg, agents []AgentInfo, logDir string, logger *slog.Logger) *Server`.
- Metodo `Start(ctx context.Context) error` — arranca el servidor HTTP en goroutine, se detiene con ctx.
- Router usando `http.ServeMux` de la stdlib (Go 1.22+ soporta `{id}` patterns).
- Middleware basico: logging, CORS (necesario para iframe de widgets), auth opcional.
- [ ] **1.3** Crear `shell/web/handlers.go` — handler `GET /api/agents`:
- Devuelve JSON array con: `id`, `name`, `type`, `status`, `uptime`, `description`.
- La info de agentes se obtiene de un registry que el launcher puebla al arrancar.
- [ ] **1.4** Handler `GET /api/agents/{id}`:
- Config del agente (filtrada: sin tokens, passwords, API keys).
- Uptime, ultima actividad, cantidad de mensajes procesados.
- Error si el `{id}` no existe.
- [ ] **1.5** Handler `GET /api/agents/{id}/metrics`:
- Reutilizar `shell/logger/ReadDayLogs()` para obtener logs del dia actual.
- Calcular: mensajes recibidos, comandos ejecutados, llamadas LLM (count + tokens + latencia media), tool calls (count + errores), errores totales.
- Devuelve JSON con los agregados.
- [ ] **1.6** Handler `GET /api/agents/{id}/logs` (SSE):
- Server-Sent Events stream con los ultimos N logs y nuevos logs en tiempo real.
- `Content-Type: text/event-stream`.
- Tail del archivo JSONL actual con polling o fsnotify.
- [ ] **1.7** Integrar arranque del servidor en `cmd/launcher/main.go`:
- Leer config web (puede ser una seccion nueva en un `launcher.yaml` o reutilizar env vars).
- Si `web.enabled`, crear `web.Server` y arrancarlo en el mismo `WaitGroup`.
- Pasar la lista de agentes al servidor para que los pueda consultar.
- [ ] **1.8** Tests: handlers con `httptest`:
- Test de `/api/agents` con lista de agentes mock.
- Test de `/api/agents/{id}` con agente existente y no existente.
- Test de `/api/agents/{id}/metrics` con logs JSONL de ejemplo en tmpdir.
- Test del middleware de auth (token valido, invalido, deshabilitado).
### Fase 2 — Integracion Matrix widget
- [ ] **2.1** Investigar formato del state event `im.vector.modular.widgets`:
- Campos requeridos: `type`, `url`, `name`, `id`, `creatorUserId`.
- Verificar compatibilidad con Element Web 1.x actual.
- [ ] **2.2** Crear `shell/web/widget.go`:
- Funcion `RegisterWidget(ctx context.Context, client *mautrix.Client, roomID, widgetID, widgetName, baseURL, agentID string) error`.
- Construye el state event content con la URL del dashboard filtrado.
- Envia via `client.SendStateEvent(roomID, "im.vector.modular.widgets", widgetID, content)`.
- Funcion `UnregisterWidget(...)` para limpiar al salir.
- [ ] **2.3** Agregar seccion `matrix.widgets.*` al config:
```yaml
matrix:
widgets:
enabled: false # habilitar registro automatico de widgets
base_url: "" # URL publica del servidor web (requerido si enabled)
auto_register: true # registrar widget al unirse a room
widget_name: "Dashboard" # nombre visible del widget
```
- [ ] **2.4** Integrar auto-registro en el runtime:
- En `devagents/runtime.go` o `devagents/handler.go`, despues de join a room, si `widgets.enabled` y `base_url` configurado, llamar a `RegisterWidget`.
- Manejar error gracefully (log warning, no romper el agente).
- [ ] **2.5** Tests:
- Test del formato del state event generado (campos requeridos presentes).
- Test de `RegisterWidget` con mock de mautrix client.
- Test de la URL generada (incluye agent ID y room ID como query params).
### Fase 3 — Dashboard UI
- [ ] **3.1** Crear `shell/web/static/index.html`:
- HTML minimo con viewport meta, link a CSS, script tag.
- Routing basico client-side (hash-based: `#/`, `#/agent/{id}`).
- [ ] **3.2** Crear `shell/web/static/app.js`:
- Fetch `/api/agents` y renderizar lista de agentes con indicadores de estado.
- Colores por status: verde (running), rojo (error), gris (stopped).
- Click en agente → navega a vista detalle.
- [ ] **3.3** Vista detalle de agente:
- Fetch `/api/agents/{id}` y `/api/agents/{id}/metrics`.
- Mostrar: nombre, tipo, uptime, descripcion, metricas del dia en tabla.
- Seccion de metricas con numeros grandes y colores.
- [ ] **3.4** Log viewer en vivo:
- Conectar a `/api/agents/{id}/logs` via `EventSource`.
- Mostrar logs en panel scrollable con auto-scroll.
- Colores por nivel: DEBUG (gris), INFO (blanco), WARN (amarillo), ERROR (rojo).
- [ ] **3.5** Graficas de metricas (simple):
- Canvas o SVG basico (sin librerias externas) para mensajes/hora y tool calls.
- Alternativa: ASCII-art charts si se quiere mantener minimalismo extremo.
- [ ] **3.6** Embed estaticos en Go:
```go
//go:embed static/*
var staticFS embed.FS
```
- Servir con `http.FileServer(http.FS(staticFS))` en el router.
- Fallback a `index.html` para SPA routing.
- [ ] **3.7** Tests del dashboard:
- Test de que `embed.FS` contiene los archivos esperados.
- Test de que `/dashboard` sirve HTML valido.
- Test de que las rutas SPA redirigen a `index.html`.
### Fase 4 — Tests de integracion y cleanup
- [ ] **4.1** Test de integracion end-to-end: arrancar servidor, verificar que todos los endpoints responden correctamente con agentes mock.
- [ ] **4.2** Documentar configuracion en el config.yaml template de `agents/_template/`.
- [ ] **4.3** Agregar seccion en `CLAUDE.md` sobre el servidor web y widgets.
## Ejemplo de uso
### Configuracion basica
```yaml
# En la config del launcher o en un agent config
web:
enabled: true
host: "0.0.0.0"
port: 8080
auth:
enabled: true
token_env: "WEB_DASHBOARD_TOKEN"
```
### Dashboard standalone
1. Habilitar en config: `web.enabled: true`, `web.port: 8080`
2. Arrancar launcher: `./dev-scripts/server/start.sh`
3. Navegar a `http://localhost:8080/dashboard`
4. Ver lista de agentes con estado, click en uno para ver metricas y logs en vivo
### Widget en Element
1. Configurar adicionalmente:
```yaml
matrix:
widgets:
enabled: true
base_url: "https://bots.example.com"
auto_register: true
```
2. Agente se une a un room → auto-registra widget
3. En Element Web aparece un panel con el dashboard filtrado para ese agente
4. El usuario ve metricas y logs sin salir del room
### Acceso directo a API
```bash
# Lista de agentes
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/agents
# Metricas de un agente
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/agents/asistente-2/metrics
# Stream de logs en vivo
curl -H "Authorization: Bearer $TOKEN" -N http://localhost:8080/api/agents/asistente-2/logs
```
## Decisiones de diseno
1. **`net/http` sin frameworks**: consistente con el estilo del proyecto (stdlib, sin dependencias externas para HTTP). Go 1.22+ tiene routing con path params nativo en `http.ServeMux`.
2. **`embed.FS` para estaticos**: deployment de un solo binario. No se necesitan archivos externos ni pasos de build frontend separados. El dashboard es lo suficientemente simple para vanilla JS.
3. **SSE en vez de WebSocket para logs en vivo**: SSE es mas simple, funciona a traves de proxies HTTP, reconexion automatica en el browser, y es suficiente para un flujo unidireccional (servidor → cliente). WebSocket seria overkill para este caso.
4. **Config `WebCfg` a nivel launcher, no por agente**: el servidor web es uno solo para todos los agentes (lo sirve el launcher). Evita N puertos por N agentes. La info por agente se filtra en los endpoints.
5. **Widget registration opcional**: el dashboard funciona standalone sin Matrix widgets. Los widgets son un bonus para integracion en Element. Si el usuario no configura `widgets.base_url`, simplemente no se registran widgets.
6. **Auth por token simple**: para la primera iteracion, un bearer token en env var es suficiente. Integracion con Matrix OIDC o session cookies se puede agregar despues si es necesario.
7. **Filtrar secrets del API**: el endpoint `/api/agents/{id}` nunca expone tokens, API keys, passwords ni recovery keys. Se filtran los campos `*_env`, `access_token_env`, etc. antes de serializar.
## Prerequisitos
- **Issue 0035 (audit trail + !metrics)**: no es bloqueante pero si esta implementado, el endpoint de metricas puede reutilizar la logica de agregacion. Sin el, se implementa directamente leyendo JSONL.
- `shell/logger/query.go` — ya existe y funciona.
- Go 1.22+ — necesario para `http.ServeMux` con path params (el proyecto usa Go 1.23.5, OK).
## Riesgos
| Riesgo | Mitigacion |
|--------|------------|
| Element widget support varia entre clientes (Web vs mobile vs Desktop) | Testear con Element Web primero (el cliente principal del proyecto). Mobile puede no soportar widgets custom. Documentar limitaciones. |
| CORS necesario para iframe de widgets | Agregar headers CORS configurables en el middleware del servidor. Restringir origenes al homeserver. |
| HTTPS obligatorio para widgets en produccion | Element requiere HTTPS para widgets. Documentar que en produccion se necesita reverse proxy (nginx/caddy) con TLS. En desarrollo localhost funciona sin HTTPS. |
| Dashboard crece en complejidad → SPA inmanejable con vanilla JS | Empezar simple. Si crece, migrar a Preact (~3KB) que se puede embeber sin build system. No usar React/Vue/frameworks pesados. |
| Servidor web expuesto → superficie de ataque | Auth por defecto deshabilitada → solo escucha en 127.0.0.1. En produccion, auth habilitada + HTTPS + reverse proxy. Nunca exponer secretos en la API. |
| SSE streaming consume memoria si hay muchos clientes | Limitar a N conexiones SSE simultaneas (configurable). Desconectar clientes idle. Buffer limitado de logs en memoria. |
| `embed.FS` aumenta tamano del binario | Los archivos estaticos son HTML/JS/CSS minimo (estimado <100KB). Impacto negligible vs las dependencias Go existentes. |