d9982d853d
Daemon de larga vida que mantiene una TUI claude interactiva viva y responde prompts en ~2.7s, embebible como subproceso via NDJSON por stdin/stdout. Arranca mitmproxy (addon tee_anthropic_sse) + claude TUI en PTY (creack/pty directo, persistente) una vez. Cada prompt se teclea en la TUI viva; la respuesta se lee del SSE de la red (exacta, corta en message_stop). El cold start (~7s) se paga una vez; los siguientes mensajes ~2.7s, con memoria entre turnos. Protocolo: send/restart/shutdown -> ready/text_delta/result/restarted. Validado: 2.7s por mensaje en caliente (vs 15s parseando TUI, vs 9s one-shot), restart relanza la conversacion. Reusa tee_anthropic_sse_py_cybersecurity + vt_render_go_tui.
155 lines
5.7 KiB
Markdown
155 lines
5.7 KiB
Markdown
---
|
||
name: claude_session
|
||
lang: go
|
||
domain: infra
|
||
version: 0.1.0
|
||
description: "Daemon de larga vida que mantiene una sesion claude (TUI interactiva) caliente y responde prompts rapido, pensado para embeber en una app como subproceso via NDJSON por stdin/stdout. Arranca mitmproxy + claude TUI una vez; cada prompt se teclea en la TUI viva y la respuesta se lee del SSE de la red (exacta, corta en message_stop). Primer mensaje paga el arranque (~7s); los siguientes ~2-3s, con memoria entre turnos. Comando restart para reiniciar la conversacion."
|
||
tags: [cli, claude, daemon, session, ndjson, mitmproxy, web-proxy, streaming]
|
||
uses_functions:
|
||
- tee_anthropic_sse_py_cybersecurity
|
||
- vt_render_go_tui
|
||
uses_types: []
|
||
framework: ""
|
||
entry_point: "main.go"
|
||
dir_path: "apps/claude_session"
|
||
icon:
|
||
phosphor: "lightning"
|
||
accent: "#f59e0b"
|
||
e2e_checks:
|
||
- id: build
|
||
cmd: "go build -o claude_session ."
|
||
timeout_s: 60
|
||
- id: bad_cmd
|
||
cmd: "echo '{\"cmd\":\"bogus\"}' | timeout 30 ./claude_session 2>/dev/null | grep -q 'unknown cmd' && echo ok || true"
|
||
expect_stdout_contains: "ok"
|
||
timeout_s: 40
|
||
---
|
||
|
||
# claude_session
|
||
|
||
## Que hace
|
||
|
||
Daemon que mantiene una sesion `claude` (TUI interactiva real, **nunca `claude -p`**) **caliente**
|
||
y responde prompts en ~2-3s. Pensado para embeberse en una app como subproceso de larga vida,
|
||
controlado por NDJSON por stdin/stdout.
|
||
|
||
Arranca **una vez** un mitmproxy (addon `tee_anthropic_sse`) y la TUI de claude en un PTY, y los
|
||
mantiene vivos. Cada prompt se teclea en la TUI viva; la respuesta se lee **del SSE de la red**
|
||
(exacta, token a token, cortada por `message_stop`). El cold start (~7s) se paga una sola vez al
|
||
arrancar; los mensajes siguientes solo pagan la generacion, y la conversacion **mantiene contexto
|
||
entre turnos**.
|
||
|
||
## Latencia (medida)
|
||
|
||
| Camino | por mensaje |
|
||
|---|---|
|
||
| `claude_pipe` (parsear render TUI) | ~15s |
|
||
| `claude_wire` (interceptar red, one-shot) | ~9s |
|
||
| **`claude_session` (daemon caliente)** | **~2.7s** (+ ~7s de arranque, una vez) |
|
||
|
||
## Protocolo NDJSON
|
||
|
||
Un objeto JSON por linea.
|
||
|
||
**stdin (comandos):**
|
||
|
||
```json
|
||
{"cmd":"send","prompt":"hola"}
|
||
{"cmd":"restart"}
|
||
{"cmd":"shutdown"}
|
||
```
|
||
|
||
**stdout (eventos):**
|
||
|
||
```json
|
||
{"type":"ready"}
|
||
{"type":"text_delta","text":"H"}
|
||
{"type":"text_delta","text":"ola"}
|
||
{"type":"result","result":"Hola"}
|
||
{"type":"restarted"}
|
||
{"type":"error","message":"..."}
|
||
```
|
||
|
||
- `send`: teclea el prompt en la TUI viva, emite `text_delta` segun llegan del SSE, luego `result`
|
||
y un `ready` cuando la TUI vuelve al input box.
|
||
- `restart`: mata y relanza la TUI (conversacion **nueva, sin memoria**), mantiene el mitmproxy.
|
||
Emite `restarted` + `ready`.
|
||
- `shutdown`: mata todo y termina.
|
||
|
||
## Arquitectura
|
||
|
||
```
|
||
mitmdump + tee_anthropic_sse (persistente — capta el SSE de /v1/messages)
|
||
claude TUI en PTY (creack/pty) (persistente — NO se mata entre prompts)
|
||
loop NDJSON (teclea prompts, lee el wire, multiplexa por message_stop)
|
||
```
|
||
|
||
Reusa `tee_anthropic_sse_py_cybersecurity` (addon SSE) y `vt_render_go_tui` (render del PTY para
|
||
detectar readiness del input box).
|
||
|
||
## Embeber en una app
|
||
|
||
La app arranca el binario como subproceso de larga vida y habla por sus stdin/stdout:
|
||
|
||
```python
|
||
import subprocess, json
|
||
p = subprocess.Popen(["claude_session", "--cwd", "/home/enmanuel/fn_registry"],
|
||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True, bufsize=1)
|
||
# esperar {"type":"ready"}
|
||
def send(prompt):
|
||
p.stdin.write(json.dumps({"cmd": "send", "prompt": prompt}) + "\n"); p.stdin.flush()
|
||
for line in p.stdout:
|
||
ev = json.loads(line)
|
||
if ev["type"] == "result":
|
||
return ev["result"]
|
||
```
|
||
|
||
## Ejemplo (NDJSON por stdin)
|
||
|
||
```bash
|
||
cd apps/claude_session && go build -o claude_session .
|
||
|
||
printf '%s\n' \
|
||
'{"cmd":"send","prompt":"di solo OK"}' \
|
||
'{"cmd":"send","prompt":"di solo DOS"}' \
|
||
'{"cmd":"shutdown"}' \
|
||
| ./claude_session --cwd /home/enmanuel/fn_registry
|
||
# {"type":"ready"}
|
||
# {"type":"text_delta","text":"O"} ... {"type":"result","result":"OK"} {"type":"ready"}
|
||
# {"type":"text_delta","text":"D"} ... {"type":"result","result":"DOS"} {"type":"ready"}
|
||
```
|
||
|
||
## Flags
|
||
|
||
| Flag | Default | Que hace |
|
||
|---|---|---|
|
||
| `--cwd` | `~/fn_registry` | Directorio de la sesion claude (MCP aprobados). |
|
||
| `--port` | `8901` | Puerto del mitmproxy. |
|
||
| `--root` | `~/fn_registry` | Raiz del registry (para localizar el addon). |
|
||
| `--addon` | `<root>/python/functions/cybersecurity/tee_anthropic_sse.py` | Addon mitmproxy. |
|
||
| `--ca` | `~/.mitmproxy/mitmproxy-ca-cert.pem` | CA de mitmproxy. |
|
||
| `--bin` | `claude` | Binario claude. |
|
||
| `--warmup` | `12s` | Espera maxima a que la TUI este lista. |
|
||
|
||
## Prerrequisitos
|
||
|
||
- `mitmproxy` (`mitmdump` en PATH) + CA generada y confiada via `NODE_EXTRA_CA_CERTS`.
|
||
- `claude` en el PATH.
|
||
|
||
## Gotchas
|
||
|
||
- **Readiness post-restart prematura**: tras `restart`, el `ready` puede emitirse antes de que la
|
||
TUI termine de cargar (detecta el input box `❯` pronto). El primer `send` tras un restart puede
|
||
tardar mas (~5-6s en vez de ~2.7s) porque el input se teclea mientras claude aun arranca (se
|
||
bufferea, no se pierde). Refinamiento pendiente: readiness mas estricta (esperar que cese el
|
||
trafico de arranque en el proxy).
|
||
- **Sesion secuencial**: un prompt a la vez. No mandes un `send` mientras otro genera.
|
||
- **Memoria entre turnos**: la TUI viva acumula la conversacion (es una feature). Usa `restart`
|
||
para empezar de cero.
|
||
- **Requiere mitmproxy + CA** (`NODE_EXTRA_CA_CERTS`); depende de que claude no haga TLS pinning
|
||
(hoy no lo hace).
|
||
- **Una interaccion dispara varias /v1/messages**; el addon filtra por `has_tools` para seguir la
|
||
respuesta principal.
|
||
- **Linux/Unix** (PTY POSIX).
|
||
- **Es trafico de tu propia cuenta y maquina** — observabilidad local.
|