# helmd — agent-helm v2-servern En **enskild statisk Go-binär** som äger kommunikationen med en tmux-session där en CLI-agent (agy, claude, …) kör, och exponerar ett **REST-API + SSE** som webb- och mobilklienterna pratar med. Designen bygger på insikten från Gemini-OAuth-döden: att integrera mot en specifik agents hook-mekanism är skört. `helmd` behandlar i stället **tmux som sanningskälla** — den läser skärmen, känner igen frågor och skickar tangenter. Fungerar därför med vilken TUI-agent som helst, och överlever att helmd själv startas om (sessioner adopteras tillbaka). ``` webb-PWA / Android ──(LAN eller SSH-tunnel)──► helmd :8788 ──tmux──► agy │ └──► ntfy.brasse-pc.eu (pushar till mobilen) ``` ## Säkerhetsmodell - **Ingen publik exponering.** Servern binder `127.0.0.1:8788` som default. På det privata nätet kan man binda `0.0.0.0:8788` (`helmd serve --listen 0.0.0.0:8788`). Utifrån: SSH-tunnel — `ssh -L 8788:127.0.0.1:8788 ` — eller Android-klientens inbyggda tunnel (steg 3 i planen). - **Bearer-token** på alla endpoints. Genereras vid första start, lagras `0600` i `~/.config/helmd/config.json`, visas med `helmd token`. Skickas som `Authorization: Bearer `, `X-Api-Token: ` eller `?token=` (för SSE/EventSource och ``-länkar). - Delad HTML serveras med `Content-Security-Policy: sandbox` så att ett delat dokument aldrig kan anropa API:et med klientens token. ## Kommandon ``` helmd serve [--listen host:port] [--config fil] starta servern helmd share [--note text] [--session namn] dela en fil till klienterna helmd token skriv ut API-token helmd version ``` `helmd share` är **agentens verktyg för att dela filer** (html, md, png, jpg, …) till klienterna: agenten kör kommandot i sin shell, filen laddas upp till servern och dyker upp i klientens delningsflik. Tillåtna filtyper styrs av `share_exts` i konfigen. ## API Alla svar är JSON om inget annat sägs. Auth krävs överallt. | Metod & väg | Gör | |---|---| | `GET /api/health` | `{ok, version, sessions}` | | `GET /api/sessions` | lista sessioner (namn, alive, seq, pending question) | | `POST /api/sessions` | `{name, cmd?, cwd?}` startar agent i ny tmux-session `helm-`; `{name, adopt:true, tmux?}` adopterar befintlig | | `GET /api/sessions/{n}` | en sessions tillstånd | | `DELETE /api/sessions/{n}` | döda sessionen | | `GET /api/sessions/{n}/screen` | pane-text; `?ansi=1` behåller färger, `?history=N` tar med N rader scrollback | | `POST /api/sessions/{n}/prompt` | `{text, submit?:bool}` klistrar in text (radbrytningar ok) och trycker Enter | | `GET /api/sessions/{n}/question` | `{question}` — detekterad väntande fråga eller `null` | | `POST /api/sessions/{n}/answer` | `{option:N}` väljer alternativ N (1-baserat). Numrerade listor väljs med siffertangent, onumrerade navigeras med pil+Enter | | `POST /api/sessions/{n}/keys` | `{keys:["Escape","Down","Enter"]}` råa tmux-tangenter | | `POST /api/sessions/{n}/mode` | `{action:"cycle"}` skickar Shift+Tab (lägesbyte i agy/claude) | | `GET /api/config` / `PUT /api/config` | läs/ändra agentkommando, args, workdir, poll-intervall, ntfy-inställningar (token ändras aldrig via API) | | `GET /api/shares` / `POST /api/shares` | lista / ladda upp (multipart, fält `file`, `note`, `session`) | | `GET /api/shares/{id}` / `…/raw` | metadata / själva filen | | `DELETE /api/shares/{id}` | ta bort delning | | `POST /api/notify` | `{topic?, title?, message, priority?}` → ntfy; topic måste finnas i `allowed_topics` (`agent-helm`, `claude`) | | `GET /api/events` | SSE-ström: `screen` (seq), `question`, `session`, `share` | ### Frågedetektering Pollern läser panen (default var 500:e ms), och `DetectQuestion` känner igen agy:s frågeformer: godkännande-listor (`1. Yes … 4. No`), trust-/val-listor med `>`-markör, och gamla Gemini-CLI:s inramade pickers. Varje fråga får en stabil hash — servern ntfy:ar **en gång per ny fråga** till topic `agent-helm` (kan stängas av med `notify_on_question:false`). Verifierat mot riktiga agy 1.1.9-sessioner (trust-prompt, kommandogodkännande) och mot mock i integrationstestet. ## Konfiguration `~/.config/helmd/config.json` (skapas vid första start; override med `HELMD_CONFIG` eller `--config`): ```json { "listen": "127.0.0.1:8788", "token": "", "agent_cmd": "agy", "agent_args": [], "workdir": "/home/brasse", "share_dir": "~/.local/share/helmd/shares", "share_exts": [".html", ".htm", ".md", ".txt", ".json", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".pdf"], "poll_ms": 500, "cols": 200, "rows": 50, "ntfy": { "server": "https://ntfy.brasse-pc.eu", "topic": "agent-helm", "allowed_topics": ["agent-helm", "claude"], "enabled": true, "notify_on_question": true } } ``` ## Webbklienten Inbäddad i binären (`web/` + `go:embed`) och serveras på `/` — surfa till servern (t.ex. `http://127.0.0.1:8788` genom en SSH-tunnel) och klistra in din token. Ramverksfri (ren ES-moduler): sessionslista, ANSI-färgad skärmvy (SSE + poll), promptfält, frågekort med knappar, tangent-verktygsrad, delningsflik (bilder, sandboxad HTML, markdown) och inställningar. Statiska filer kräver ingen auth (koden är publik); allt data går genom det token-skyddade API:et. ## Docker på Pi5 (LAN-only) `Dockerfile` bygger en arm64-image med tmux+bash via `.gitea/workflows/helmd-image.yaml` → `localhost:5000/helmd:latest`. Stack: `/srv/dockge-staks/helmd/` med host-port **8788** och volym `/srv/docker/helmd → /data`. **Ingen NPM-vhost** — nås bara på LAN (`http://192.168.0.19:8788`) eller via SSH-tunnel utifrån. Sessionerna kör i containern (bash tills en agent installeras där). ## Bygga & testa ```bash cd helmd go test ./... # enhetstester (frågedetektorn m.m.) CGO_ENABLED=0 go build -o build/helmd . bash scripts/run-integration.sh # 26 API-tester i isolerad Docker-container ``` Integrationstestet startar helmd i en `debian:stable-slim`-container med en mock-agent (`testdata/mock-agent.sh`) som härmar agy:s båda frågetyper, och kör hela API-ytan med curl. ## CI / release `.gitea/workflows/helmd-release.yaml` kör på push till master som rör `helmd/`: `go vet` + `go test`, bygger **linux x64 + arm64** statiskt och publicerar på den rullande releasen **`helmd-latest`** (`helmd-linux-x64`, `helmd-linux-arm64`, `checksums.txt`). ## Kända begränsningar (v1) - `mode cycle` skickar Shift+Tab (BTab) — verifierat tangentnamn i tmux men inte bekräftat att agy byter läge på det; justera via `/keys` om agy använder annan tangent. - Ingen WebSocket-terminal ännu — klienterna pollar `screen` + lyssnar på SSE. Räcker för kort-UI:t; rå terminal-vy kommer med webbklienten (steg 2). - En helmd-instans per maskin/användare; flera agenter = flera sessioner i samma helmd.