Files
headquarter/openspec/changes/tool-terminal/design.md
T
Fusion e344e961d6 feat: implement web terminal for tool instances
- Add TerminalSession backend service for docker exec subprocess management
- Add TerminalManager for WebSocket session lifecycle management
- Create WebSocket endpoint at /ws/tool-instances/{id}/terminal
- Add session cookie authentication and instance ownership verification
- Install xterm.js with fit and web-links addons
- Create TerminalComponent with xterm.js integration
- Create TerminalPage with full-screen terminal view
- Add terminal route at /instances/:id/terminal
- Add terminal button to InstanceList for running instances
- Add terminal and arrow-left icons to icon registry
- Add comprehensive terminal CSS styles (dark theme, responsive)

Quality gates: typecheck ✓, lint ✓, build ✓, Python syntax ✓
2026-05-19 21:11:29 +02:00

3.9 KiB

Tool Terminal - Design

Architecture

Browser                    Backend                    Container
  │                          │                           │
  │  WebSocket connect       │                           │
  │─────────────────────────>│                           │
  │                          │  docker exec -it bash     │
  │                          │───────────────────────────>│
  │                          │                           │
  │  stdin (keystrokes)      │  stdin                    │
  │─────────────────────────>│───────────────────────────>│
  │                          │                           │
  │  stdout/stderr           │  stdout/stderr            │
  │<─────────────────────────│<───────────────────────────│
  │                          │                           │
  │  resize (cols, rows)     │  pty resize               │
  │─────────────────────────>│───────────────────────────>│
  │                          │                           │

Component Design

Backend

TerminalManager:

  • Manages active terminal sessions
  • Maps WebSocket connections to container processes
  • Handles session lifecycle (create, resize, cleanup)

WebSocket Endpoint:

  • GET /ws/tool-instances/{instance_id}/terminal
  • Authenticates user via session cookie
  • Establishes bidirectional WebSocket
  • Spawns docker exec -it with pseudo-TTY

Docker PTY:

  • Uses docker exec with TTY allocation
  • Streams stdin/stdout/stderr via subprocess
  • Handles resize via stty or docker API

Frontend

TerminalComponent:

  • Wraps xterm.js terminal
  • Manages WebSocket connection
  • Handles terminal resize
  • Fits container to parent element

TerminalPage:

  • Full-page terminal view
  • Shows instance name in header
  • Connection status indicator
  • Reconnect on disconnect

Data Flow

  1. User clicks "Terminal" on running instance
  2. Frontend opens WebSocket connection
  3. Backend verifies ownership and spawns shell
  4. Bidirectional streaming begins
  5. User types → WebSocket → docker exec stdin
  6. Container output → docker exec stdout → WebSocket → xterm.js
  7. Resize events forwarded to adjust PTY dimensions

Session Lifecycle

Connect
  │
  ▼
Authenticate ──> Reject (403)
  │
  ▼
Spawn Shell
  │
  ▼
Stream I/O ◄───> Resize
  │
  ▼
Disconnect
  │
  ▼
Cleanup Process

Access Control

  • WebSocket handshake validates session cookie
  • Backend verifies user owns the instance
  • Reject connection with 403 if unauthorized
  • Close connection if instance stops running

Technical Details

Backend Libraries:

  • asyncio for WebSocket handling
  • subprocess with docker exec -it
  • fcntl for PTY resize (Linux)

Frontend Libraries:

  • xterm - Terminal emulator
  • xterm-addon-fit - Auto-fit to container
  • xterm-addon-web-links - Clickable URLs

Docker Commands:

# Spawn shell
docker exec -it {container_id} /bin/bash

# Alternative with explicit TTY
docker exec -i {container_id} sh -c 'exec bash'

Error Handling

  • Connection refused → Show error message
  • Container not running → Disable terminal button
  • Shell spawn failed → Show error and close
  • Network disconnect → Attempt reconnect

CSS Integration

.terminal-container {
  width: 100%;
  height: 100%;
  min-height: 400px;
  background: #1e1e1e;
  border-radius: 8px;
  overflow: hidden;
}

.terminal-container .xterm {
  padding: 8px;
}