You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
6.7 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# cmux-remote
Browser-based remote terminal UI for [cmux](https://github.com/manaflow-ai/cmux). Access and control your cmux terminal sessions from any device with a web browser.
![Version](https://img.shields.io/badge/version-0.1.0-blue)
## Features
- **Real-time terminal streaming** — Adaptive polling with differential updates (patches, not full redraws)
- **Claude Code mode** — Auto-detects Claude Code sessions and renders with structured blocks (tool use, code, diffs, tables, selections)
- **Remote access via ngrok** — Secure tunnel with QR code for quick mobile access
- **Mobile-first UI** — Virtual keyboard, touch gestures, iOS PWA support
- **Multi-workspace** — Sidebar for switching between workspaces, panes, and surfaces
- **Browser surface support** — Screenshots for non-terminal surfaces with pinch-zoom
- **Scrollback history** — Auto-loads scrollback when scrolling up
- **Authentication** — Password-based auth with bcrypt + JWT, rate-limited login
- **Themes** — Dark/light mode with persistent preference
## Installation
### Global CLI
```bash
npm install -g cmux-remote
```
### From source
```bash
git clone https://github.com/user/cmux-remote.git
cd cmux-remote
npm install
npm run build
```
## Prerequisites
- **cmux** must be installed and running (`cmux` binary in PATH, or set `CMUX_PATH`)
- **Node.js** >= 18
- **ngrok** account (optional, for remote tunnel) — set `NGROK_AUTHTOKEN` env var
## Usage
```bash
# Start with defaults (port 9870, no tunnel — front it with a reverse proxy, no auth)
cmux-remote
# Set a password
cmux-remote -P mypassword
# Opt back into the ngrok tunnel
cmux-remote --tunnel
# Override the external URL shown in the banner / QR code
cmux-remote --public-url https://cmux-remote.example.com
# Custom port with verbose logging
cmux-remote -p 3000 -v
# All options
cmux-remote --help
```
### Options
| Option | Default | Description |
|--------|---------|-------------|
| `-p, --port <port>` | `9870` | Local server port |
| `-P, --password <pass>` | none | Access password (or `CMUX_REMOTE_PASSWORD` env) |
| `--tunnel` | off | Enable ngrok tunnel (off by default — use a reverse proxy instead) |
| `--tunnel-domain <domain>` | none | Fixed ngrok domain (or `NGROK_DOMAIN` env) |
| `--public-url <url>` | `https://cmux-remote.yirugi.synology.me` | External URL for banner/QR (or `CMUX_PUBLIC_URL` env) |
| `--poll-rate <ms>` | `200` | Screen poll interval |
| `--theme <theme>` | `dark` | Default theme (`dark` or `light`) |
| `-v, --verbose` | off | Verbose server logging |
### Configuration file
Create `~/.cmux-remote/config` for persistent settings:
```ini
CMUX_REMOTE_PASSWORD=mypassword
CMUX_PUBLIC_URL=https://cmux-remote.yirugi.synology.me
# ngrok is off by default; only needed if you start with --tunnel
NGROK_AUTHTOKEN=your_token
NGROK_DOMAIN=my-domain.ngrok-free.app
CMUX_PATH=/usr/local/bin/cmux
```
Environment variables set in the config file won't override existing env vars.
## Architecture
```
Browser ←── WebSocket ──→ Node.js Server ←── CLI/Socket ──→ cmux daemon
│ │
├─ terminal-view.js ├─ screen-poller.ts (adaptive polling)
├─ claude-parser.js ├─ input-handler.ts (key translation)
├─ claude-renderer.js ├─ cmux-client.ts (cmux CLI wrapper)
└─ app.js (orchestrator) └─ session-manager.ts
```
### Server (`src/`)
| Module | Description |
|--------|-------------|
| `index.ts` | CLI entry point (Commander.js), bootstraps all components |
| `server/app.ts` | Express 5 HTTP server, REST API, static files, CSP headers |
| `server/websocket.ts` | WebSocket server, client sessions, message routing |
| `server/auth.ts` | bcrypt password hashing, JWT tokens, rate limiting |
| `server/tunnel.ts` | ngrok tunnel management |
| `bridge/cmux-client.ts` | cmux CLI wrapper with concurrency limiting and caching |
| `bridge/screen-poller.ts` | Adaptive screen polling with diff-based updates |
| `bridge/input-handler.ts` | Translates browser keys to cmux protocol |
| `bridge/session-manager.ts` | Client auth state and surface subscriptions |
| `protocol/messages.ts` | Type-safe WebSocket message definitions |
| `utils/config.ts` | Configuration interface and defaults |
| `utils/text-differ.ts` | Line-based diff algorithm for incremental updates |
### Client (`public/`)
| File | Description |
|------|-------------|
| `js/app.js` | Main orchestrator — routing, WS lifecycle, surface switching |
| `js/terminal-view.js` | ANSI-to-HTML renderer with 256-color and 24-bit RGB support |
| `js/claude-parser.js` | Parses Claude Code output into structured blocks |
| `js/claude-renderer.js` | Renders Claude blocks (prompts, tools, code, diffs, tables) |
| `js/claude-keyboard.js` | Claude-specific keyboard shortcuts (Yes/No/Esc) |
| `js/websocket-client.js` | WebSocket client with auto-reconnection |
| `js/virtual-keyboard.js` | Mobile virtual keyboard with modifiers (Ctrl, Alt, Shift) |
| `js/sidebar.js` | Workspace/surface tree navigation |
| `js/gestures.js` | Touch swipe and gesture handling |
| `js/theme.js` | Dark/light theme persistence |
| `js/auth.js` | Login flow and JWT management |
### Adaptive Polling
The screen poller adjusts its rate based on activity:
- **Fast** (200ms) — Active terminal with recent changes
- **Idle** (1-2s) — No changes for 3+ poll cycles
- **Deep idle** (2s) — 10+ seconds inactive
Resets to fast mode on any user input. Sends full screen snapshots every 30 seconds; incremental diff patches otherwise.
### Claude Code Mode
When a surface runs Claude Code, the client automatically switches to a structured renderer:
- **Prompts** with `` marker
- **Tool use** blocks with icon, name, and arguments
- **Tool results** in bordered boxes
- **Code blocks** with syntax-aware display
- **Diff blocks** with +/- coloring
- **Tables** parsed from box-drawing characters
- **Selection menus** with clickable options
- **Status bar** with mode pill (Plan/Code/Bypass) and git branch
## Protocol
Communication uses JSON over WebSocket.
**Client → Server:**
```
auth { token }
subscribe { workspace, surface }
unsubscribe { surface }
send-text { workspace, surface, text }
send-key { workspace, surface, key }
list-workspaces
scroll-request { workspace, surface, lines }
```
**Server → Client:**
```
screen { surface, content, lines, scrollback? }
screen-diff { surface, patches[] }
workspaces { workspaces[] }
browser-screenshot { surface, imageData, mime }
auth-ok / auth-fail
error { message }
```
## Development
```bash
# Development with auto-reload (no tunnel)
npm run dev:local
# Development with tunnel
npm run dev
# Run tests
npm test
# Build for production
npm run build
# Start production server
npm start
```
## License
MIT