3.2 KiB
3.2 KiB
Explore: Responsive Web Terminal
Problem Statement
The current web terminal feels sluggish and fragile compared to a local terminal session. Key pain points:
- No reconnection — A brief network hiccup kills the terminal. Users must navigate away and back.
- No heartbeat — Half-open connections stall silently. No way to know if the terminal is alive.
- High input latency — Every keystroke round-trips to the server before appearing on screen. No local echo.
- Inefficient I/O path — Backend
selectpolling with 0.1s timeout, 4096-byte reads, busy-wait sleep(0.01). Frontend receives Blob and converts to ArrayBuffer asynchronously. - No scrollback persistence — Reconnect starts with a blank terminal. Session history is lost.
- Rudimentary resize — Fires on every window resize event with no debouncing.
- No connection quality feedback — Binary status (connected/disconnected). No latency or health indicator.
- No graceful container exit handling — Process death closes WebSocket with a generic error.
Current Architecture
Frontend
apps/web/src/components/terminal.tsx— xterm.js v5.3.0 with FitAddon and WebLinksAddon- WebSocket to
/ws/tool-instances/{instance_id}/terminal - Receives Blob (binary) and string (JSON control) messages
- Sends raw bytes for input, JSON for resize
- Basic status: connecting | connected | disconnected | error
Backend
apps/api/src/api/terminal.py— FastAPI WebSocket endpoint, auth, session lifecycleapps/api/src/services/terminal_manager.py— Manages TerminalSession, read/write loopsapps/api/src/services/terminal_session.py— PTY-baseddocker execwithselectI/O- Protocol: raw bytes for terminal I/O, JSON for resize control messages
Gaps vs. Local Terminal Feel
| Aspect | Local Terminal | Current Web Terminal |
|---|---|---|
| Keystroke feedback | Immediate (kernel TTY) | Round-trip (~50-200ms) |
| Network resilience | N/A (local) | Dies on any disconnect |
| Scrollback | Persistent | Lost on reconnect |
| Resize | Instant | Undebounced, may spam |
| Health visibility | Always local | Binary connected/disconnected |
| Large output | Buffered by kernel | Select polling, 4KB chunks |
Opportunities
- WebSocket reconnection with exponential backoff and session token for continuity
- Heartbeat/ping-pong to detect half-open connections within seconds
- Local echo optimization for printable characters (with server-side authoritative sync)
- Message batching on backend to reduce WebSocket frame overhead
- Scrollback serialization via xterm-addon-serialize to restore on reconnect
- Resize debouncing to avoid flooding the server
- Connection quality indicator (latency, jitter) in the terminal chrome
- Graceful handling of container exit with clear user messaging
Risks
- Adding heartbeat may increase server load with many concurrent terminals
- Local echo requires careful handling of password prompts and special modes
- Reconnecting to a docker exec PTY is not natively resumable — new
docker execon reconnect - xterm-addon-serialize may be large for very long sessions
- Changes touch both frontend and backend — cross-stack coordination needed