English | 简体中文
Keep one managed terminal session available across desktop and mobile.
TermPilot is a local-first terminal session continuity tool for long-running work. It keeps the same managed session reachable from your phone without moving session content into the relay.
Tip
Documentation site: TermPilot Docs · Quick Start · Deployment Guide · Agent Operations · Troubleshooting · CLI Reference
Important
TermPilot does not import arbitrary Terminal or iTerm tabs. A session must be created or managed by TermPilot to be available on mobile.
TermPilot is built around one narrow path:
- one managed session already exists on your computer
- you leave your desk
- you still want that exact session on your phone
That session can be Claude Code, a deployment, a migration, or any other long-running terminal task. The product is designed around continuity, not remote desktop access.
Phone browser / PWA -- https / wss --> relay <-- ws / wss -- agent on your computer
|
+-- pairing, grant routing,
audit metadata, web UI
The system has three runtime pieces:
relay: HTTP + WebSocket entrypoint, web UI hosting, pairing, access control, encrypted message routingagent: daemon running on your computer, managing local sessions and keeping session content on-deviceapp: mobile web UI served by the relay
- Unified CLI package for relay, agent, and session commands
tmux-backed managed sessions with output replay served by the agent- Local-first session state: titles, cwd, status details, and terminal output stay on the agent host
- Device-scoped pairing, access grants, and encrypted browser-to-agent session messages
- Relay persistence limited to pairing, grant, and audit metadata, with SQLite as the default long-running store and optional PostgreSQL via
DATABASE_URL - Mobile web UI focused on viewing, light input, and shortcut controls on the same session
- Mobile terminal workspace tuned for phone use, with dedicated keyboard and command sections plus a focus mode for wider viewing
- Browser notifications for device offline, session exit, and suspected stale managed sessions while the page is in the background
- Adaptive output polling: active sessions stay snappy, while long-detached managed sessions are polled more conservatively
- Incremental replay and foreground recovery to keep long-running sessions responsive when the page returns from the background
- Managed command sessions include lightweight stale-session governance for long-detached, no-output leftovers
This is a deliberately narrow scope. TermPilot is built for session continuity, not for desktop remoting or generic server administration.
If you redeploy or migrate from an older binding without local keys, re-pair the device.
If you want the structured docs flow instead of reading the repository README top to bottom, use this path:
- Product overview: Why TermPilot
- First setup: Quick Start
- Relay deployment: Deployment Guide
- Agent lifecycle and session operations: Agent Operations
- Problem diagnosis: Troubleshooting
- Runtime design: Security Design, Architecture, Protocol
On both the server and your computer:
Node.js 22+@fengye404/termpilot
On your computer:
tmux
Install:
npm install -g @fengye404/termpilotRun on a server or another machine reachable from your phone:
termpilot relayUseful variants:
termpilot relay start
termpilot relay stop
termpilot relay runBy default, the relay starts in the background, listens on 0.0.0.0:8787, serves both the web UI and /ws, and persists relay metadata to ~/.termpilot/relay.db.
Run on your computer:
termpilot agentOn first run, the agent asks for the relay host and port, then:
- saves local config
- starts a background daemon
- prints a one-time pairing code
If you want the agent to stay up permanently, let a system process manager run:
termpilot agent --foreground --relay wss://your-domain.com/wsOpen the relay URL in your phone browser:
http://your-domain.com:8787- or
https://your-domain.combehind a reverse proxy
Enter the pairing code shown on your computer. After that, you land on the session list for that device.
For Claude Code:
termpilot claude codeFor any other managed command:
termpilot run -- opencodeIf you want to create a plain shell session first:
termpilot create --name my-task --cwd /path/to/projectThen attach it explicitly:
termpilot list
termpilot attach --sid <sid>If you only remember one rule:
termpilot run -- <command>means “start a managed session around this command”termpilot create+termpilot attachmeans “create a plain shell session, then re-enter it when needed”
termpilot relay
termpilot relay stop
termpilot relay run
termpilot agent
termpilot agent --pair
termpilot agent status
termpilot agent stop
termpilot pair
termpilot create --name my-task --cwd /path/to/project
termpilot list
termpilot attach --sid <sid>
termpilot kill --sid <sid>
termpilot grants
termpilot audit --limit 20
termpilot revoke --token <accessToken>
termpilot doctor
termpilot claude code
termpilot run -- <command>Default local state directory:
~/.termpilot
Common files:
config.json: saved relay configuration for the agentagent-runtime.json: background agent runtime staterelay-runtime.json: background relay runtime statestate.json: local managed session statedevice-key.json: local agent device keypairagent.log/relay.log: logs
Useful environment variables:
TERMPILOT_HOMETERMPILOT_RELAY_URLTERMPILOT_DEVICE_IDTERMPILOT_AGENT_TOKENTERMPILOT_RELAY_STORETERMPILOT_SQLITE_PATHTERMPILOT_ORPHAN_WARNING_MSTERMPILOT_MANAGED_SESSION_AUTOCLEANUP_MSHOSTPORTDATABASE_URLTERMPILOT_PAIRING_TTL_MINUTES
Managed command cleanup defaults:
TERMPILOT_ORPHAN_WARNING_MS: detached and idle warning threshold, default3600000(1 hour)TERMPILOT_MANAGED_SESSION_AUTOCLEANUP_MS: detached and idle auto-clean threshold, default43200000(12 hours)
Relay store defaults:
TERMPILOT_RELAY_STORE:sqliteby default, can be set tomemoryTERMPILOT_SQLITE_PATH: SQLite file path, default~/.termpilot/relay.db
Examples:
TERMPILOT_HOME=/data/termpilot termpilot agent
TERMPILOT_RELAY_URL=wss://your-domain.com/ws termpilot agent
HOST=0.0.0.0 PORT=8787 termpilot relayFor a quick private trial:
- run
termpilot relaydirectly on a reachable machine - open
http://your-ip:8787on mobile - point the agent to
ws://your-ip:8787/ws
For regular use:
- run
termpilot relayon a server - put a reverse proxy in front of it
- use
https://your-domain.comon mobile - use
wss://your-domain.com/wsfor the agent - keep relay metadata on SQLite or PostgreSQL instead of volatile memory
Minimal Caddy example:
your-domain.com {
reverse_proxy 127.0.0.1:8787
}For deployment and runtime details, continue with:
Use the published relay image directly:
docker pull fengye404/termpilot-relay:latestRun it with a persistent state volume:
docker run -d \
--name termpilot-relay \
-p 8787:8787 \
-e TERMPILOT_AGENT_TOKEN=change-me \
-v termpilot-relay-data:/var/lib/termpilot \
fengye404/termpilot-relay:latestInside the container, relay metadata persists to /var/lib/termpilot/relay.db by default. If you want reproducible rollout, pin a version tag such as fengye404/termpilot-relay:0.3.9.
TermPilot is intentionally not trying to be:
- a remote desktop
- a GUI control layer
- an importer for arbitrary existing terminal tabs
- a full terminal log archive system
- a general-purpose multi-tenant operations platform
If a task should remain visible and controllable from mobile, start it inside a TermPilot-managed session from the beginning.
This repository is a pnpm workspace monorepo:
src/cli.ts: top-level CLI entrypointagent/: desktop agent and local session managementrelay/: relay serverapp/: mobile web UIpackages/protocol/: shared protocol definitionsdocs/: VitePress documentation site
Run locally:
pnpm install
pnpm dev:relay
pnpm dev:app
pnpm dev:agentUseful checks:
pnpm typecheck
pnpm build
pnpm test:ui-smoke
pnpm check:stability
pnpm test:isolation