@fengye404/termpilot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,148 @@
1
+ # TermPilot 当前代码架构
2
+
3
+ ## 1. 当前产品形态
4
+
5
+ - 一个 npm 包:`termpilot`
6
+ - 一个服务器命令:`termpilot relay`
7
+ - 一个电脑命令:`termpilot agent`
8
+ - 手机端不安装,直接打开 relay 域名
9
+ - relay 同时负责 WebSocket 中继和 Web UI 托管
10
+
11
+ 对应拓扑:
12
+
13
+ ```text
14
+ 手机浏览器 --https/ws--> relay <--ws--> PC 端 agent
15
+ |
16
+ 配对、设备权限、消息路由、
17
+ 输出缓冲、审计日志、会话元数据、
18
+ 静态网页托管
19
+ ```
20
+
21
+ ## 2. 目录结构
22
+
23
+ ```text
24
+ src/
25
+ cli.ts
26
+ agent/
27
+ src/
28
+ app/
29
+ src/
30
+ components/
31
+ relay/
32
+ src/
33
+ packages/
34
+ protocol/
35
+ scripts/
36
+ docs/
37
+ ```
38
+
39
+ ### `src/cli.ts`
40
+
41
+ 统一 CLI 入口,负责把内部多模块收敛成外部单命令产品:
42
+
43
+ - `termpilot relay`
44
+ - `termpilot agent`
45
+ - `termpilot pair/create/list/attach/kill/...`
46
+
47
+ ### `agent/`
48
+
49
+ PC 端常驻进程和本地命令实现:
50
+
51
+ - `src/cli.ts`:agent 侧命令实现
52
+ - `src/index.ts`:开发态入口包装
53
+ - `src/daemon.ts`:常驻连接、轮询 `tmux`、同步输出与状态
54
+ - `src/tmux-backend.ts`:`tmux` 会话创建、输入、关闭、附着、抓屏
55
+ - `src/relay-admin.ts`:agent 调用 relay 管理接口的轻量客户端
56
+ - `src/state-store.ts`:本地 JSON 状态文件
57
+
58
+ ### `relay/`
59
+
60
+ 中继和网页托管层:
61
+
62
+ - `src/server.ts`:WebSocket 路由、HTTP 接口、消息转发、静态网页托管
63
+ - `src/index.ts`:开发态入口包装
64
+ - `src/session-store.ts`:会话元数据存储
65
+ - `src/auth-store.ts`:配对码与访问令牌存储
66
+ - `src/audit-store.ts`:审计事件存储
67
+ - `src/config.ts`:环境变量配置
68
+
69
+ ### `app/`
70
+
71
+ 手机端 PWA:
72
+
73
+ - `src/App.tsx`:状态、副作用、WebSocket 协调
74
+ - `src/components/ConnectionPanel.tsx`:连接与配对 UI
75
+ - `src/components/CreateSessionPanel.tsx`:创建会话 UI
76
+ - `src/components/SessionListPanel.tsx`:会话搜索、筛选、置顶、关闭
77
+ - `src/components/TerminalWorkspace.tsx`:终端区域、快捷键和粘贴发送
78
+ - `src/components/chrome.tsx`:通用面板与字段组件
79
+
80
+ ### `packages/protocol/`
81
+
82
+ 三端共享协议类型:
83
+
84
+ - 会话消息
85
+ - 配对与令牌数据结构
86
+ - 审计事件结构
87
+
88
+ ## 3. 运行时数据流
89
+
90
+ ### 启动链路
91
+
92
+ 1. 服务器执行 `termpilot relay`
93
+ 2. relay 监听 HTTP/WebSocket,并托管 `app/dist`
94
+ 3. 电脑执行 `termpilot agent --relay ...`
95
+ 4. 手机上直接打开 relay 域名
96
+
97
+ ### 会话创建
98
+
99
+ 1. 手机端或电脑端发起 `session.create`
100
+ 2. relay 把请求转发给对应 device 的 agent
101
+ 3. agent 在本地创建 `tmux` 会话
102
+ 4. agent 回推 `session.created`
103
+ 5. relay 广播给有权限的 client
104
+
105
+ ### 输出同步
106
+
107
+ 1. agent 周期性 `capture-pane`
108
+ 2. 如果缓冲变化,生成新的 `session.output`
109
+ 3. relay 缓冲最近一段输出帧
110
+ 4. app 渲染最新快照
111
+ 5. client 重连后可用 `session.replay` 补拉
112
+
113
+ ### 配对与访问控制
114
+
115
+ 1. 电脑端执行 `termpilot pair`
116
+ 2. relay 创建一次性配对码
117
+ 3. 手机端输入配对码,兑换设备访问令牌
118
+ 4. client WebSocket 以后携带设备令牌
119
+ 5. relay 只向该 client 暴露允许访问的设备和会话
120
+
121
+ ## 4. 当前实现边界
122
+
123
+ - 当前终端同步策略仍是“快照替换”,不是字节级增量流
124
+ - relay 输出缓冲只保留最近一段,不做长期日志归档
125
+ - 不接管 TermPilot 体系外的历史终端
126
+ - 手机端定位是“查看、轻输入、轻控制”,不追求桌面级重度编辑体验
127
+
128
+ ## 5. 当前代码取舍
129
+
130
+ - 对外形态已经统一成单一 CLI,但仓库内部仍保留 `agent/app/relay` 三个目录,方便独立开发
131
+ - `app` 的状态和副作用仍集中在 `App.tsx`,但渲染层已经拆到独立组件
132
+ - `relay` 已从单文件脚本式入口拆成 `server.ts + index.ts`
133
+ - `agent` 已从单文件 CLI 拆成 `cli.ts + index.ts + daemon.ts + tmux-backend.ts`
134
+
135
+ ## 6. 当前验证方式
136
+
137
+ 最有价值的检查命令:
138
+
139
+ - `pnpm typecheck`
140
+ - `pnpm build`
141
+ - `pnpm test:ui-smoke`
142
+ - `pnpm check:stability`
143
+
144
+ 它们覆盖的重点是:
145
+
146
+ - `termpilot relay` 和 `termpilot agent` 主链路
147
+ - 输出缓冲与重连一致性
148
+ - 手机端配对、切会话、关会话、清除绑定
@@ -0,0 +1,216 @@
1
+ # TermPilot 当前协议
2
+
3
+ ## 1. 连接入口
4
+
5
+ 手机端和 agent 都通过 relay 的同一个 WebSocket 入口连接:
6
+
7
+ ```text
8
+ ws(s)://<relay-host>/ws?role=<agent|client>&token=<token>&deviceId=<deviceId?>
9
+ ```
10
+
11
+ - `role=agent`:PC 端 agent
12
+ - `role=client`:手机端或浏览器端
13
+ - `token`:agent 固定令牌或 client 访问令牌
14
+ - `deviceId`:agent 连接时必须带;client 在业务消息里指定
15
+
16
+ ## 2. 当前消息类型
17
+
18
+ 系统消息:
19
+
20
+ - `auth.ok`
21
+ - `error`
22
+ - `relay.state`
23
+
24
+ 会话消息:
25
+
26
+ - `session.list`
27
+ - `session.list.result`
28
+ - `session.create`
29
+ - `session.created`
30
+ - `session.input`
31
+ - `session.resize`
32
+ - `session.kill`
33
+ - `session.replay`
34
+ - `session.output`
35
+ - `session.state`
36
+ - `session.exit`
37
+
38
+ ## 3. 会话模型
39
+
40
+ 当前会话对象字段:
41
+
42
+ - `sid`
43
+ - `deviceId`
44
+ - `name`
45
+ - `backend`
46
+ - `shell`
47
+ - `cwd`
48
+ - `status`
49
+ - `startedAt`
50
+ - `lastSeq`
51
+ - `lastActivityAt`
52
+ - `tmuxSessionName`
53
+
54
+ 当前 `backend` 固定为 `tmux`。
55
+
56
+ ## 4. 输入与快捷键
57
+
58
+ 当前支持的特殊按键:
59
+
60
+ - `enter`
61
+ - `tab`
62
+ - `ctrl_c`
63
+ - `ctrl_d`
64
+ - `escape`
65
+ - `arrow_up`
66
+ - `arrow_down`
67
+ - `arrow_left`
68
+ - `arrow_right`
69
+
70
+ 普通输入仍走:
71
+
72
+ ```json
73
+ {
74
+ "type": "session.input",
75
+ "deviceId": "pc-main",
76
+ "sid": "s_123",
77
+ "payload": {
78
+ "text": "echo hello\n"
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## 5. 输出同步策略
84
+
85
+ 当前不是字节级终端流,而是“快照替换”:
86
+
87
+ ```json
88
+ {
89
+ "type": "session.output",
90
+ "deviceId": "pc-main",
91
+ "sid": "s_123",
92
+ "seq": 12,
93
+ "payload": {
94
+ "data": "...当前 pane 快照...",
95
+ "mode": "replace"
96
+ }
97
+ }
98
+ ```
99
+
100
+ 对应实现:
101
+
102
+ - agent 轮询 `tmux capture-pane`
103
+ - 只有缓冲变化时才推新帧
104
+ - relay 保留最近一段输出帧
105
+ - client 重连后用 `session.replay` 补拉
106
+
107
+ ## 6. HTTP 接口
108
+
109
+ ### 创建一次性配对码
110
+
111
+ `POST /api/pairing-codes`
112
+
113
+ - 需要 `Authorization: Bearer <agent-token>`
114
+ - 请求体:`{ "deviceId": "pc-main" }`
115
+
116
+ ### 兑换配对码
117
+
118
+ `POST /api/pairings/redeem`
119
+
120
+ - 请求体:`{ "pairingCode": "ABC-234" }`
121
+
122
+ ### 查看当前设备已发出的访问令牌
123
+
124
+ `GET /api/devices/:deviceId/grants`
125
+
126
+ ### 撤销访问令牌
127
+
128
+ `DELETE /api/devices/:deviceId/grants/:accessToken`
129
+
130
+ ### 查看审计事件
131
+
132
+ `GET /api/devices/:deviceId/audit-events?limit=20`
133
+
134
+ ### 健康检查
135
+
136
+ `GET /health`
137
+
138
+ 返回:
139
+
140
+ - `ok`
141
+ - `storeMode`
142
+ - `agentsOnline`
143
+ - `clientsOnline`
144
+ - `webUiReady`
145
+
146
+ ## 7. 当前产品入口与协议的关系
147
+
148
+ - `termpilot relay` 对外暴露 HTTP + WebSocket
149
+ - 手机端直接访问 relay 域名,再走 `/ws`
150
+ - `termpilot agent` 始终只和 `/ws`、`/api/*` 交互
151
+ - 手机端网页不再要求单独部署
152
+
153
+ 当前审计动作包括:
154
+
155
+ - `pairing.code_created`
156
+ - `pairing.redeemed`
157
+ - `grant.revoked`
158
+ - `session.create_requested`
159
+ - `session.kill_requested`
160
+
161
+ ## 8. 当前协议边界
162
+
163
+ - client 侧仍直接处理较多状态拼装,没有事件聚合接口
164
+ - 输出补拉依赖最近缓冲,不是完整历史回放
165
+ - 审计目前只记录关键控制动作,不记录每一次普通输入
166
+
167
+ 字段说明:
168
+
169
+ - `seq`:输出序号,递增
170
+ - `data`:当前终端快照
171
+ - `mode=replace`:客户端收到后直接替换当前渲染内容
172
+
173
+ ## 9. 会话状态消息
174
+
175
+ 会话状态变化时发送:
176
+
177
+ ```json
178
+ {
179
+ "type": "session.state",
180
+ "deviceId": "pc-main",
181
+ "sid": "s_123",
182
+ "payload": {
183
+ "session": {}
184
+ }
185
+ }
186
+ ```
187
+
188
+ 会话退出时发送:
189
+
190
+ ```json
191
+ {
192
+ "type": "session.exit",
193
+ "deviceId": "pc-main",
194
+ "sid": "s_123",
195
+ "payload": {
196
+ "reason": "用户主动关闭会话",
197
+ "exitCode": null
198
+ }
199
+ }
200
+ ```
201
+
202
+ ## 10. 输出补拉
203
+
204
+ 手机端重连或重新进入会话时,可以请求补拉最近输出:
205
+
206
+ ```json
207
+ {
208
+ "type": "session.replay",
209
+ "reqId": "req_replay_001",
210
+ "deviceId": "pc-main",
211
+ "sid": "s_123",
212
+ "payload": {
213
+ "afterSeq": 8
214
+ }
215
+ }
216
+ ```
@@ -0,0 +1,77 @@
1
+ # TermPilot 全栈技术选型(2026 版)
2
+
3
+ ## 最终方案
4
+
5
+ 这次技术选型只保留已经拍板的方案,不再保留多余备选。
6
+
7
+ - 语言:TypeScript
8
+ - 运行时:Node.js 24 LTS
9
+ - 包管理:pnpm workspace
10
+
11
+ 手机端 `app/`:
12
+
13
+ - React
14
+ - Vite
15
+ - Tailwind CSS v4
16
+ - xterm.js
17
+ - PWA
18
+
19
+ 中继服务 `relay/`:
20
+
21
+ - Fastify
22
+ - WebSocket
23
+ - PostgreSQL
24
+
25
+ PC 端 `agent/`:
26
+
27
+ - Node.js
28
+ - tmux
29
+
30
+ ## 为什么是这套
31
+
32
+ 这套方案最重要的优点不是“技术多”,而是“统一且克制”:
33
+
34
+ - 三端尽量共享同一种语言和同一种工程习惯
35
+ - 前端走最主流的 React + Vite 路线,AI 最容易写对
36
+ - relay 不引入重框架,Fastify 足够轻也足够成熟
37
+ - agent 不引入多余框架,直接围绕 `tmux` 做就够了
38
+ - 仓库层面用 pnpm workspace,把多模块项目管清楚
39
+
40
+ ## 为什么不再加别的
41
+
42
+ 这次我刻意把一些名字收掉了,因为它们不是当前必须项:
43
+
44
+ - 不加 `Next.js`
45
+ - 不加 `React Native`
46
+ - 不加 `Flutter`
47
+ - 不加 `shadcn/ui`
48
+ - 不加 `Zustand`
49
+ - 不加 `Drizzle`
50
+ - 不加 `Zod`
51
+
52
+ 这些技术不是不好,而是对当前目标来说不是最小必要集合。
53
+
54
+ TermPilot 现在要的不是“技术看起来完整”,而是:
55
+
56
+ - AI 最容易持续参与开发
57
+ - 三端结构统一
58
+ - 尽量少配置、少概念、少框架
59
+
60
+ ## 当前代码与选型的对应关系
61
+
62
+ 当前仓库已经按这套方案开始收敛:
63
+
64
+ - `app/`:React + Vite + Tailwind CSS + xterm.js
65
+ - `relay/`:Fastify + WebSocket
66
+ - `agent/`:Node.js + tmux
67
+ - `packages/protocol/`:共享协议类型
68
+ - 根目录:pnpm workspace
69
+
70
+ 其中 PostgreSQL 采用的是“正式栈默认使用、开发时允许内存回退”的策略:
71
+
72
+ - 设置 `DATABASE_URL` 时使用 PostgreSQL
73
+ - 未设置时,为了本地开发效率,relay 会退回内存模式
74
+
75
+ ## 一句话总结
76
+
77
+ **TermPilot 当前最合理、最 AI 友好的全栈方案,就是 `TypeScript + Node.js 24 LTS + pnpm workspace`,在此基础上,手机端用 `React + Vite + Tailwind CSS + xterm.js`,中继服务用 `Fastify + WebSocket + PostgreSQL`,PC 端围绕 `tmux`。**
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@fengye404/termpilot",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "packageManager": "pnpm@10.31.0",
6
+ "description": "一个基于 tmux 的终端会话跨端查看与控制原型。",
7
+ "bin": {
8
+ "termpilot": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "app/dist",
13
+ "README.md",
14
+ "docs",
15
+ ".env.example"
16
+ ],
17
+ "engines": {
18
+ "node": ">=24"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "scripts": {
24
+ "build": "pnpm -r typecheck && tsc --noEmit -p tsconfig.json && pnpm --filter @termpilot/app build && tsup",
25
+ "build:web": "pnpm --filter @termpilot/app build",
26
+ "typecheck": "pnpm -r typecheck && tsc --noEmit -p tsconfig.json",
27
+ "dev:app": "pnpm --filter @termpilot/app run dev",
28
+ "dev:relay": "pnpm --filter @termpilot/relay run dev",
29
+ "dev:agent": "pnpm --filter @termpilot/agent run dev",
30
+ "cli": "node dist/cli.js",
31
+ "agent:create": "pnpm --filter @termpilot/agent run create",
32
+ "agent:list": "pnpm --filter @termpilot/agent run list",
33
+ "agent:kill": "pnpm --filter @termpilot/agent run kill",
34
+ "agent:attach": "pnpm --filter @termpilot/agent run attach",
35
+ "agent:pair": "pnpm --filter @termpilot/agent run pair",
36
+ "agent:grants": "pnpm --filter @termpilot/agent run grants",
37
+ "agent:audit": "pnpm --filter @termpilot/agent run audit",
38
+ "agent:revoke": "pnpm --filter @termpilot/agent run revoke",
39
+ "agent:doctor": "pnpm --filter @termpilot/agent run doctor",
40
+ "check:stability": "pnpm build && node scripts/check-relay-agent-stability.mjs",
41
+ "test:ui-smoke": "pnpm build && TERMPILOT_APP_URL=http://127.0.0.1:18787 TERMPILOT_RELAY_URL=ws://127.0.0.1:18787/ws python3 /Users/fengye/.agents/skills/webapp-testing/scripts/with_server.py --server \"PORT=18787 node dist/cli.js relay\" --port 18787 -- python3 scripts/ui_smoke.py",
42
+ "prepack": "pnpm build"
43
+ },
44
+ "dependencies": {
45
+ "@fastify/websocket": "^11.2.0",
46
+ "fastify": "^5.6.1",
47
+ "pg": "^8.16.3",
48
+ "ws": "^8.19.0"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^24.12.0",
52
+ "tsup": "^8.5.0",
53
+ "typescript": "^5.9.3"
54
+ }
55
+ }