@fengye404/termpilot
v0.6.10
Published
一个基于 tmux 的终端会话跨端查看与控制原型。
Downloads
4,121
Readme
TermPilot
English | 简体中文
让同一条受管理终端会话在电脑和手机之间持续可见、可控制。
TermPilot 是一个本地优先的终端会话共享工具,方便你从手机继续访问电脑上已经运行中的受管理会话,同时把会话内容保留在本机。
[!TIP] 文档站: TermPilot Docs · 快速开始 · 部署指南 · Agent 运维 · 故障排查 · CLI 参考
[!IMPORTANT] TermPilot 不会自动导入任意 Terminal 或 iTerm 标签页。只有由 TermPilot 创建或管理的会话,才能在手机端继续访问。
它是什么
TermPilot 围绕一条很明确的主路径设计:
- 电脑上已经有一条受管理会话在运行
- 你离开了工位
- 你仍然想在手机上继续接入这条原会话
这条会话可以是 Claude Code、部署流程、迁移任务,或者任何长期运行的终端任务。整个产品围绕同一条会话在多端之间持续可达来设计。
架构
手机浏览器 / PWA -- https / wss --> relay <-- ws / wss -- 电脑上的 agent
|
+-- 配对、授权路由、
审计元数据、Web UI运行时由三部分组成:
relay: HTTP + WebSocket 入口,负责 Web UI 托管、配对、权限控制和加密信封转发agent: 跑在电脑上的守护进程,负责管理本地会话,并把敏感会话数据留在端侧app: 由 relay 提供的移动端 Web UI
当前模型
- 一个统一 CLI,同时提供 relay、agent 和会话管理命令
- 基于
tmux的受管理会话,输出回放由 agent 提供 - 本地优先的会话状态模型,会话标题、cwd、状态细节和终端输出保留在 agent 所在电脑
- 设备级配对与授权模型,用于浏览器与 agent 之间的加密通信
- relay 持久化默认只保留配对、grant 与审计元数据,长期运行默认使用 SQLite,也可通过
DATABASE_URL切换到 PostgreSQL - 一个面向移动端的 Web UI,聚焦查看、轻输入和快捷控制
- 页面在后台时,浏览器可对设备离线、会话退出和疑似残留会话发出提醒
- 输出同步采用自适应轮询:活跃会话保持更快刷新,长期无人附着的托管命令会话会自动降频
- 托管命令会话带有轻量残留治理,会自动回收长期无人附着且无输出的会话
这是一条刻意收窄的产品边界,重点放在会话连续性和多端接入体验上。
如果你是从旧版本升级、且本地绑定里还没有本地密钥,需要重新配对一次。
快速开始
如果你更希望按文档站的分层路径阅读,而不是直接从仓库首页一路往下看,可以从这里进入:
环境要求
服务器和电脑都需要:
Node.js 22+@fengye404/termpilot
电脑还需要:
tmux
安装:
npm install -g @fengye404/termpilot1. 启动 relay
在服务器或一台手机可访问的机器上执行:
termpilot relay常用变体:
termpilot relay start
termpilot relay stop
termpilot relay run默认情况下,relay 会后台启动,监听 0.0.0.0:8787,同时提供 Web UI 和 /ws,并把 relay 元数据持久化到 ~/.termpilot/relay.db。
2. 启动 agent
在你的电脑上执行:
termpilot agent首次运行时,agent 会询问 relay 主机和端口,然后:
- 保存本地配置
- 启动后台守护进程
- 打印一次性配对码
如果你希望 agent 长期常驻,更推荐交给系统进程管理器托管:
termpilot agent --foreground --relay wss://your-domain.com/ws3. 手机完成配对
在手机浏览器打开:
http://your-domain.com:8787- 或反向代理后的
https://your-domain.com
输入电脑端打印出来的配对码,随后进入该设备的会话列表。
4. 启动受管理会话
如果你主要跑 Claude Code:
termpilot claude code如果你要跑其他托管命令:
termpilot run -- opencode如果你想先创建一条普通 shell 会话:
termpilot create --name my-task --cwd /path/to/project然后再显式接入它:
termpilot list
termpilot attach --sid <sid>如果你只记一条规则,记这个就够了:
termpilot run -- <command>表示“围绕这个命令启动一条受管理会话”termpilot create+termpilot attach表示“先建一条普通 shell 会话,再按需回到它”
本地开发测试流程
如果你是在当前仓库里迭代开发,最好不要把本地开发运行和全局 npm 安装的 termpilot、以及默认的 ~/.termpilot 状态目录混在一起。
先把下面这段加到 ~/.zshrc:
tpdev() {
local root="/Users/fengye/workspace/TermPilot"
local home="/tmp/termpilot-local"
local cmd="${1:-help}"
if [ "$#" -gt 0 ]; then
shift
fi
case "$cmd" in
help|-h|--help)
cat <<'EOF'
tpdev build
tpdev reset
tpdev fresh
tpdev refresh
tpdev relay ...
tpdev agent ...
tpdev claude code
tpdev run -- <command>
EOF
;;
build)
(cd "$root" && pnpm build)
;;
reset)
(cd "$root" && TERMPILOT_HOME="$home" pnpm local:reset)
;;
fresh|refresh)
(cd "$root" && pnpm build && TERMPILOT_HOME="$home" pnpm local:reset)
;;
*)
(cd "$root" && TERMPILOT_HOME="$home" node dist/cli.js "$cmd" "$@")
;;
esac
}然后重新加载 shell:
source ~/.zshrc之后就按这套本地专用链路跑:
tpdev fresh
tpdev relay run
tpdev agent --relay ws://127.0.0.1:8787/ws --pair
tpdev claude code如果你改了代码,推荐先跑 tpdev fresh,因为 tpdev relay ... 和 tpdev agent ... 走的仍然是已经构建好的 dist/cli.js。
tpdev refresh 也可以用,它是 tpdev fresh 的别名。
如果本地调试过程中 relay、agent、tmux 会话或者状态目录已经变乱了,可以直接一键重置:
tpdev reset这条命令会停止 /tmp/termpilot-local 这套本地开发环境里的 agent 和 relay,清掉该状态目录里记录的 tmux 会话,并删除整个目录本身。
这套方式的好处是:
- 使用的是当前仓库构建出来的 CLI,而不是全局 npm 安装版本
- 本地状态目录固定隔离在
/tmp/termpilot-local - 可以直接对着
http://127.0.0.1:8787做浏览器测试,不用先发 npm 包
如果你要启动别的托管命令,也可以直接:
tpdev run -- <command>CLI 参考
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>配置
默认本地状态目录:
~/.termpilot常见文件:
config.json: agent 保存的 relay 配置agent-runtime.json: 后台 agent 运行状态relay-runtime.json: 后台 relay 运行状态state.json: 本地受管理会话状态device-key.json: agent 本地设备密钥agent.log/relay.log: 日志
常用环境变量:
TERMPILOT_HOMETERMPILOT_RELAY_URLTERMPILOT_DEVICE_IDTERMPILOT_AGENT_TOKENTERMPILOT_RELAY_STORETERMPILOT_SQLITE_PATHTERMPILOT_ORPHAN_WARNING_MSTERMPILOT_MANAGED_SESSION_AUTOCLEANUP_MSHOSTPORTDATABASE_URLTERMPILOT_PAIRING_TTL_MINUTES
托管命令自动治理默认值:
TERMPILOT_ORPHAN_WARNING_MS: detached 且无输出的预警阈值,默认3600000(1 小时)TERMPILOT_MANAGED_SESSION_AUTOCLEANUP_MS: detached 且无输出的自动清理阈值,默认43200000(12 小时)
relay 存储默认值:
TERMPILOT_RELAY_STORE: 默认sqlite,可显式设成memoryTERMPILOT_SQLITE_PATH: SQLite 文件路径,默认~/.termpilot/relay.db
示例:
TERMPILOT_HOME=/data/termpilot termpilot agent
TERMPILOT_RELAY_URL=wss://your-domain.com/ws termpilot agent
HOST=0.0.0.0 PORT=8787 termpilot relay部署说明
如果只是快速试通:
- 在一台可访问机器上直接运行
termpilot relay - 手机上打开
http://your-ip:8787 - 让 agent 连接
ws://your-ip:8787/ws
如果准备长期使用:
- 在服务器上运行
termpilot relay - 前面放一个反向代理
- 手机上使用
https://your-domain.com - agent 使用
wss://your-domain.com/ws - relay 元数据尽量放在 SQLite 或 PostgreSQL 上,而不是易失的内存存储
最小 Caddy 示例:
your-domain.com {
reverse_proxy 127.0.0.1:8787
}如果你想继续看更细的部署、运维和排障文档,可以直接进入:
relay Docker 镜像
直接使用已经发布好的 relay 镜像:
docker pull fengye404/termpilot-relay:latest使用持久化卷启动:
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:latest容器内默认把 relay 元数据持久化到 /var/lib/termpilot/relay.db。如果你希望固定版本,可以改成 fengye404/termpilot-relay:0.3.9 这样的 tag。
非目标
TermPilot 明确不打算变成:
- 远程桌面
- 图形界面控制层
- 任意现有终端标签页导入器
- 完整终端日志归档系统
- 通用型多租户运维平台
如果一个任务希望在手机端持续可见、可控制,就应该从一开始运行在 TermPilot 管理的会话里。
仓库结构
这个仓库是一个 pnpm workspace monorepo:
src/cli.ts: 顶层 CLI 入口agent/: 桌面端 agent 与本地会话管理relay/: relay 服务端app/: 移动端 Web UIpackages/protocol/: 共享协议定义docs/: VitePress 文档站
开发
本地运行:
pnpm install
pnpm dev:relay
pnpm dev:app
pnpm dev:agent常用检查:
pnpm typecheck
pnpm build
pnpm test:ui-smoke
pnpm check:stability
pnpm test:isolation