Files
tomasz-syn-grzegorza 73e4fc005e Add stack Update, ComfyUI model manager, and slim ComfyUI stack.
Server UI gains Update on stack cards, ComfyUI Models tab with workflow
scan and downloads, and centralized comfyui_config. Model catalog and
download scripts move from stacks/comfyui to server-ui so ComfyUI stays a
minimal Docker wrapper for easier image updates.
2026-07-05 18:45:17 +00:00

141 lines
4.7 KiB
Markdown

# Server UI — CLI (terminal PTY)
**Data:** 2026-07-05
**Status:** wdrożone (bugfix FitAddon: 2026-07-05; pamięć sesji: 2026-07-05)
Zakładka **CLI** w panelu Server UI (`:8091`) — interaktywny shell bash w przeglądarce (PTY + xterm.js).
## UI
- Zakładka **CLI** (pierwsza, przed Pliki)
- Hash URL: `http://<host>:8091/#cli`
- Wymaga **API Key** w toolbarze (WebSocket nie obsługuje nagłówka `X-API-Key`)
## Architektura
```mermaid
sequenceDiagram
participant Browser
participant xterm as xterm_js
participant WS as WS_api_cli_ws
participant PTY as cli_pty_py
participant Shell as bash
Browser->>xterm: klawiatura
xterm->>WS: dane terminala
WS->>PTY: zapis master_fd
PTY->>Shell: stdin
Shell->>PTY: stdout/stderr
PTY->>WS: odczyt master_fd
WS->>xterm: output
```
## WebSocket
| Parametr | Wartość |
|----------|---------|
| Ścieżka | `/api/cli/ws` |
| Auth | `?api_key=<API_KEY>` (query string) |
| Reattach | `?session_id=<uuid>` (opcjonalnie, z `sessionStorage`) |
| Protokół I/O | surowe bajty terminala (text/binary frames) |
| Resize | JSON: `{"type":"resize","cols":120,"rows":40}` |
| Zamknij sesję | JSON: `{"type":"close"}` (klient → serwer) |
### Handshake sesji (serwer → klient)
| Payload | Znaczenie |
|---------|-----------|
| `{"type":"session","id":"<uuid>"}` | Nowa sesja — ID zapisane w `sessionStorage` (`server-ui-cli-session-id`) |
| `{"type":"replay","complete":true}` | Koniec replay bufora po reattach |
### Przykład (wscat)
```bash
API_KEY=$(sudo grep ^API_KEY= /opt/control-plane/.env | cut -d= -f2)
# wymaga: npm i -g wscat
wscat -c "ws://127.0.0.1:8091/api/cli/ws?api_key=${API_KEY}"
```
Bez klucza na bind LAN → połączenie zamknięte z kodem `1008`.
## Konfiguracja
W [`/opt/control-plane/.env`](/opt/control-plane/.env) (opcjonalnie):
| Zmienna | Domyślnie | Opis |
|---------|-----------|------|
| `CLI_ENABLED` | `1` | `0` = WebSocket odrzucany |
| `CLI_SHELL` | `/bin/bash` | Shell PTY (np. `/bin/bash -l`) |
| `CLI_DEFAULT_CWD` | `$HOME` usługi | Katalog startowy |
| `CLI_MAX_SESSIONS` | `5` | Limit równoległych sesji PTY (bash) |
| `CLI_SESSION_TTL` | `300` | Sekundy utrzymania odłączonej sesji przed kill |
## Pamięć sesji
- **Przełączanie zakładek** — WebSocket i bash pozostają aktywne (bez `disconnectCli`)
- **F5 / reload** — `session_id` z `sessionStorage` → reattach do tego samego bash (bufor ~256 KB replay)
- **Połącz ponownie** — wysyła `{"type":"close"}`, czyści `sessionStorage`, nowy bash
- **Zamknięcie karty** — serwer utrzymuje PTY przez `CLI_SESSION_TTL`, potem zwalnia slot
```mermaid
sequenceDiagram
participant Browser
participant SS as sessionStorage
participant WS as WebSocket
participant Mgr as CliSessionManager
participant Bash as bash
Browser->>WS: connect api_key
WS->>Mgr: create_session
Mgr->>Bash: spawn PTY
Mgr-->>Browser: session id
Browser->>SS: zapisz session_id
Note over Browser,Bash: przełączenie zakładki — WS żyje
Browser->>Browser: F5 reload
Browser->>SS: odczyt session_id
Browser->>WS: connect session_id
WS->>Mgr: attach_session
Mgr-->>Browser: replay + live output
```
## Uprawnienia POSIX
Server UI działa jako **`tomasz-syn-grzegorza`** (`server-ui.service`):
- `docker ps`, edycja repo, `/data/apps/` — OK
- `sudo` — interaktywnie (hasło użytkownika w terminalu)
- root-only pliki — odmowa jak w SSH
## Kod
| Plik | Rola |
|------|------|
| [`stacks/server-ui/cli_pty.py`](../stacks/server-ui/cli_pty.py) | `CliSessionManager` — PTY, detach/attach, bufor outputu |
| [`stacks/server-ui/app.py`](../stacks/server-ui/app.py) | WebSocket `/api/cli/ws`, mount `/static` |
| [`stacks/server-ui/static/index.html`](../stacks/server-ui/static/index.html) | Zakładka CLI |
| [`stacks/server-ui/static/vendor/xterm/`](../stacks/server-ui/static/vendor/xterm/) | xterm.js + FitAddon (vendored; UMD: `new FitAddon.FitAddon()`) |
## Bezpieczeństwo
- Pełny shell przez LAN = **wysokie ryzyko** — wymagany `API_KEY` przy `SERVER_UI_HOST=0.0.0.0`
- `CLI_ENABLED=0` jako kill switch
- `CLI_MAX_SESSIONS` ogranicza liczbę równoległych bashów (PTY, nie połączeń WS)
- Brak whitelisty komend (właściciel serwera)
- Jawne zamknięcie: przycisk **Połącz ponownie** lub `{"type":"close"}`
- Odłączenie WS (reload) → detach + TTL; reattach w tej samej karcie przeglądarki
## Deploy
```bash
sudo rsync -a --exclude '.venv' --exclude '__pycache__' \
stacks/server-ui/ /opt/server-ui/
sudo systemctl restart server-ui
```
Tutorial użytkownika: [`manual-tutorial/10-server-ui-cli.md`](../manual-tutorial/10-server-ui-cli.md)
## Znane problemy / bugfix
Pusty terminal przy działającym API Key: [`SERVER-UI-CLI-BUGFIX.md`](SERVER-UI-CLI-BUGFIX.md)