# Web Terminal ## Overview The web terminal provides an interactive shell session inside running tool instances directly from your browser. It uses xterm.js to render a full terminal emulator connected via WebSocket to a PTY-backed docker exec session. The terminal is designed to feel as close to a local terminal as possible, with features for network resilience, low-latency typing, and session continuity. ## How to Use ### Opening a Terminal 1. Navigate to a **project** and select a **repository** 2. Go to the repository **workspace** 3. Start or select a **tool instance** that supports the terminal interface 4. Click the **"Open Terminal"** button The terminal opens in full-page mode with a status bar at the top. ### Terminal Layout ``` ┌─────────────────────────────────────────────┐ │ ● Connected [Reconnect] [×] │ ├─────────────────────────────────────────────┤ │ │ │ user@container:~$ ls -la │ │ total 128 │ │ drwxr-xr-x 5 user user 4096 May 27 10:00 │ │ ... │ │ │ └─────────────────────────────────────────────┘ ``` **Status bar (top):** - **Connection dot** — color indicates connection health - **Status text** — shows current state and latency - **Reconnect button** — appears when disconnected - **Close button** — returns to the previous page ### Connection States | Indicator | Meaning | Action | |-----------|---------|--------| | 🟡 **Yellow dot** + "Connecting..." | Opening WebSocket | Wait or check network | | 🟢 **Green dot** + "Connected" | Healthy connection (<100ms) | Ready to use | | 🟡 **Yellow dot** + "Slow (150ms)" | Elevated latency | Connection usable but laggy | | 🟡 **Yellow dot** + "Reconnecting (2)" | Connection lost, retrying | Wait for auto-reconnect | | ⚪ **Gray dot** + "Disconnected" | Max retries exceeded | Click Reconnect or refresh | **Hover the status dot** to see the current round-trip latency in milliseconds. ### Typing Type normally as you would in a local terminal. The terminal supports: - **Printable characters** appear instantly (local echo) - **Special keys** (Tab, Enter, Ctrl+C, arrow keys) are sent to the server - **Password prompts** automatically suppress local echo - **Unicode** input and output ### Reconnecting The terminal **automatically reconnects** if the WebSocket drops: - Brief disconnects (WiFi hiccups, proxy timeouts) are recovered within 1–5 seconds - Up to **10 reconnection attempts** with exponential backoff - **Scrollback is preserved** across reconnects - A visual divider (`--- Reconnected ---`) separates old and new output **Manual reconnect:** - Click the **Reconnect** button in the status bar - Or press **Ctrl+Shift+R** anywhere in the terminal page ### Session Ended When the container process exits (e.g., you run `exit` or the container stops), the terminal shows an overlay: ``` ┌─────────────────────────┐ │ Session Ended │ │ The container process │ │ has exited. │ │ │ │ [Reconnect] [Go Back] │ └─────────────────────────┘ ``` - **Reconnect** — spawns a new shell session in the same container - **Go Back** — returns to the workspace page ## Keyboard Shortcuts | Shortcut | Action | |----------|--------| | `Ctrl+Shift+R` | Force reconnect (bypasses backoff) | | Standard terminal shortcuts | `Ctrl+C`, `Ctrl+D`, `Ctrl+L`, Tab completion, etc. | ## Technical Details ### WebSocket Protocol The terminal communicates over a binary WebSocket with mixed JSON control messages. **Connection:** ``` ws://api.example.com/ws/tool-instances/{instance_id}/terminal ``` **Binary frames** carry raw terminal I/O. **Text (JSON) frames** carry control messages: **Client → Server:** - `{"type":"ping","id":n}` — heartbeat ping - `{"type":"resize","cols":120,"rows":40}` — terminal resize - Raw bytes — keystroke input **Server → Client:** - `{"type":"pong","id":n}` — heartbeat response - `{"type":"status","status":"connected"}` — session ready - `{"type":"set_echo_state","enabled":false}` — disable local echo - `{"type":"session_ended","reason":"process_exit"}` — session ended - Raw bytes — terminal output ### Architecture ``` Browser Backend ┌──────────────────────┐ ┌─────────────────────────────┐ │ TerminalComponent │ │ terminal.py (WS endpoint) │ │ ├─ xterm.js │◄───────►│ ├─ auth + session mgmt │ │ ├─ FitAddon │ WS │ └─ echo state detection │ │ ├─ SerializeAddon │ │ │ │ └─ useTerminalConn. │ │ TerminalManager │ │ ├─ heartbeat │ │ ├─ read_loop (batching) │ │ ├─ reconnect │ │ ├─ write_loop │ │ ├─ local echo │ │ └─ heartbeat_loop │ │ └─ resize throttle│ │ │ │ │ │ TerminalSession │ │ sessionStorage │ │ ├─ PTY + docker exec │ │ (scrollback backup) │ │ └─ termios echo detection │ └──────────────────────┘ └─────────────────────────────┘ ``` ### Reconnect Behavior On disconnect: 1. The client serializes terminal scrollback to `sessionStorage` 2. Backoff timer starts (1s, 2s, 4s, 8s, 16s, then caps at 30s) 3. On reconnect, scrollback is restored + divider line 4. A new `docker exec` session is spawned transparently **Note:** The underlying docker exec PTY is not resumable. Reconnect creates a new shell, but scrollback continuity makes this transparent. ### Performance - **Local echo** makes printable characters appear in <1ms - **Message batching** on the backend reduces WebSocket frame overhead - **Resize debouncing** (200ms) + throttling (500ms) prevents server spam - **Heartbeat interval** is 15s to balance detection speed with server load ## Troubleshooting ### "Connecting..." stays yellow **Issue:** WebSocket cannot open **Check:** 1. Is the API server running? 2. Is the tool instance in "running" status? 3. Check browser console for connection errors 4. Verify the `VITE_API_BASE_URL` points to the correct API ### "Reconnecting" loops forever **Issue:** Max reconnection attempts exceeded **Check:** 1. Is the container still running? (`docker ps`) 2. Did the container crash or get stopped? 3. Check server logs for `Terminal session error` ### Typing feels slow **Issue:** High latency or no local echo **Check:** 1. Hover the status dot — latency >100ms is shown as "Slow" 2. Local echo only works for printable ASCII characters 3. Password prompts intentionally disable echo 4. Very high latency may indicate a congested network ### Terminal is blank after reconnect **Issue:** Scrollback not restored **Check:** 1. `sessionStorage` may have been cleared (new browser session) 2. The scrollback cap is 10,000 lines — very long sessions may truncate 3. Browser privacy settings may block `sessionStorage` ### "Session Ended" immediately **Issue:** Container process exits right away **Check:** 1. The container's default command may have finished 2. Check the tool type's Docker Compose template 3. Some tools (like one-off scripts) are not meant for persistent terminal sessions ## Configuration No additional configuration is required. The terminal adapts automatically to: - Browser window size (via ResizeObserver) - System light/dark theme preference - Network conditions (reconnect backoff) ## Related Features - [Workspace](workspace.md) — Open the terminal from the repository workspace - [Tool Types](tool-types.md) — Configure which tools expose a terminal interface - [SSH Keys](ssh-keys.md) — Manage SSH keys for repository access from within the terminal