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.
Send button: - submitText() sends text without trailing "\n", then a discrete Enter key event after 100ms. cmux delivers send_text as a bracketed paste, so a trailing newline became a literal newline in the Claude/Codex input box instead of submitting — this required a second (empty) press to actually send. Now one press = the full type-then-Enter sequence. Fixes Codex always needing two presses and Claude 4+ line messages not sending / rendering mid-state. - data-text shortcut keys now type then send Enter separately - mousedown.preventDefault keeps soft keyboard up so first tap submits - Remove IME composition Enter handler (textarea Enter = newline now) Codex view: - New codex-parser.js / codex-renderer.js for OpenAI Codex CLI surfaces - /api/codex-surfaces endpoint; generalize surface-ref lookup into _getSurfaceRefsForProcess() (claude + codex) - app.js updateCodexMode(), terminal.setCodexMode(), codex-mode body class (mutually exclusive with claude-mode) Claude mode: - Auto-load scrollback when content fits the viewport - Trim claude-keyboard.js scaffolding; parser/renderer/CSS tweaks Bump virtual-keyboard.js?v=9 cache-bust. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
3 months ago | |
|---|---|---|
| bin | 5 months ago | |
| public | 3 months ago | |
| src | 3 months ago | |
| test | 5 months ago | |
| .gitignore | 5 months ago | |
| README.md | 5 months ago | |
| package-lock.json | 5 months ago | |
| package.json | 5 months ago | |
| tsconfig.json | 5 months ago | |
README.md
cmux-remote
Browser-based remote terminal UI for cmux. Access and control your cmux terminal sessions from any device with a web browser.
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
npm install -g cmux-remote
From source
git clone https://github.com/user/cmux-remote.git
cd cmux-remote
npm install
npm run build
Prerequisites
- cmux must be installed and running (
cmuxbinary in PATH, or setCMUX_PATH) - Node.js >= 18
- ngrok account (optional, for remote tunnel) — set
NGROK_AUTHTOKENenv var
Usage
# Start with defaults (port 9870, ngrok tunnel enabled, no auth)
cmux-remote
# Set a password
cmux-remote -P mypassword
# Local only (no tunnel)
cmux-remote --no-tunnel
# 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) |
--no-tunnel |
tunnel on | Disable ngrok tunnel |
--tunnel-domain <domain> |
none | Fixed ngrok domain (or NGROK_DOMAIN 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:
CMUX_REMOTE_PASSWORD=mypassword
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
# 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