SSE clients (agents_dashboard) consider the stream connected only after
receiving the first byte of body. The previous implementation flushed
headers and then blocked waiting for status diffs (sse_status) or log
lines (sse_agents_logs) — which could be silent for minutes. UI sat
on "connecting" indefinitely.
Fix:
- After WriteHeader + Flush, emit ":ping\n\n" comment (SSE spec, valid
no-op) and flush. Unblocks client fgets immediately → state flips
to "connected" in < 1s.
- Add 15s ticker emitting ":ping\n\n" so idle streams stay alive
through Traefik / CDN proxies and clients detect dead servers.
- Same treatment for /sse/status and /sse/agents/{id}/logs (tail.go).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The logMiddleware wrapper (statusWriter) didn't forward Flush, so
`w.(http.Flusher)` in SSE handlers failed and returned the plain text
"streaming unsupported" with 500. SSE clients (agents_dashboard C++ app)
saw a closed connection with no events.
Add Flush() that delegates to the embedded ResponseWriter when it
implements Flusher. Required for /sse/status and /sse/agents/{id}/logs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handlers + poller llamaban Manager.StatusAll() siempre. En unified mode (launcher
arranca todos los agents como goroutines bajo 1 PID) no existen PID files
por agente, asi que StatusAll devolvia Running=false aunque los agents estaban
operativos.
Anade Server.statusAllAuto() que chequea IsUnifiedRunning() y delega a
StatusAllUnified() o StatusAll() segun corresponda. Reemplaza las 3 llamadas
directas en handlers.go (handleListAgents, handleGetAgent) y poller.go
(pollStatus seed + checkAndPublishDiffs).
Sin esto el frontend (0129) mostraria running=false universal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nuevo paquete internal/api con servidor HTTP stdlib (sin gin/echo):
- Auth Bearer via AGENTS_API_KEY con subtle.ConstantTimeCompare
- REST: GET /health (sin auth), GET/POST /agents, /agents/{id}, /{id}/{start,stop,restart,logs}
- SSE: /sse/status (broadcast diffs cada 2s) y /sse/agents/{id}/logs (tail -f)
- Pubsub in-memory (TODO: NATS cuando haya 2do cliente)
- Tail de logfiles: retroalimenta ultimos 50KB + poll 200ms para streaming
Integracion en cmd/launcher/main.go:
- Flag --api-port (0=desactivado, 8487 en produccion)
- Flag --api-key (override de AGENTS_API_KEY env var)
- Si apiPort>0 y sin clave, WARN y deshabilita en vez de fallar
Systemd unit en systemd/agents_and_robots.service:
- Restart=always (no on-failure — evita que exit limpio mate el service)
- EnvironmentFile para AGENTS_API_KEY y demas tokens
- WorkingDirectory=/home/ubuntu/CodeProyects/agents_and_robots
app.md v0.2.0:
- port: 8487, health_endpoint: /health (fix drift anterior donde era null)
- e2e_checks: build, tests, smoke_health, smoke_auth
- Documentacion Traefik+DNS pendiente humano post-merge
Tests: 12 tests unitarios en internal/api (auth, health, bus, agents, logs)
Smoke: /health 200, /agents sin auth 401, /agents con key 200 — verificado local
Co-Authored-By: fn-constructor (agent)