- tmux som sanningskälla (agentoberoende, överlever omstart via adoption) - API: sessions, prompt, question/answer, keys, mode, config, shares, notify, events - frågedetektor testad mot riktiga agy 1.1.9-dumpar + mock - integrationstest: 26 tester i isolerad debian-container (scripts/run-integration.sh) - e2e-verifierad mot riktig agy (trust-fråga -> svar -> prompt) - .gitea/workflows/helmd-release.yaml -> rullande helmd-latest (x64+arm64) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
140 lines
6.0 KiB
Markdown
140 lines
6.0 KiB
Markdown
# 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 <host>` — 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 <t>`,
|
|
`X-Api-Token: <t>` eller `?token=<t>` (för SSE/EventSource och
|
|
`<img>`-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] <fil> 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>`; `{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": "<genereras>",
|
|
"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
|
|
}
|
|
}
|
|
```
|
|
|
|
## 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.
|