@fengye404/termpilot 0.1.7 → 0.1.9
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.
- package/README.md +49 -200
- package/dist/cli.js +45 -29
- package/docs/.vitepress/config.mts +64 -0
- package/docs/.vitepress/dist/404.html +23 -0
- package/docs/.vitepress/dist/README.html +26 -0
- package/docs/.vitepress/dist/architecture.html +42 -0
- package/docs/.vitepress/dist/assets/README.md.B4-OJVNQ.js +1 -0
- package/docs/.vitepress/dist/assets/README.md.B4-OJVNQ.lean.js +1 -0
- package/docs/.vitepress/dist/assets/app.BG4pRgiG.js +1 -0
- package/docs/.vitepress/dist/assets/architecture.md.JnC1zyYV.js +17 -0
- package/docs/.vitepress/dist/assets/architecture.md.JnC1zyYV.lean.js +1 -0
- package/docs/.vitepress/dist/assets/chunks/@localSearchIndexroot.l5vdUGaZ.js +1 -0
- package/docs/.vitepress/dist/assets/chunks/VPLocalSearchBox.DMeTzGam.js +9 -0
- package/docs/.vitepress/dist/assets/chunks/framework.BZohXCq9.js +19 -0
- package/docs/.vitepress/dist/assets/chunks/theme.D0_6rd9F.js +2 -0
- package/docs/.vitepress/dist/assets/development.md.iwUVjeO6.js +7 -0
- package/docs/.vitepress/dist/assets/development.md.iwUVjeO6.lean.js +1 -0
- package/docs/.vitepress/dist/assets/getting-started.md.Cirp1CHi.js +1 -0
- package/docs/.vitepress/dist/assets/getting-started.md.Cirp1CHi.lean.js +1 -0
- package/docs/.vitepress/dist/assets/index.md.D9XElNRh.js +1 -0
- package/docs/.vitepress/dist/assets/index.md.D9XElNRh.lean.js +1 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/operations-guide.md.DfNiIg5F.js +18 -0
- package/docs/.vitepress/dist/assets/operations-guide.md.DfNiIg5F.lean.js +1 -0
- package/docs/.vitepress/dist/assets/protocol.md.CCXFJUPR.js +40 -0
- package/docs/.vitepress/dist/assets/protocol.md.CCXFJUPR.lean.js +1 -0
- package/docs/.vitepress/dist/assets/style.B0lvUXq1.css +1 -0
- package/docs/.vitepress/dist/assets/tech-selection-2026.md.Dk_ymWTx.js +1 -0
- package/docs/.vitepress/dist/assets/tech-selection-2026.md.Dk_ymWTx.lean.js +1 -0
- package/docs/.vitepress/dist/development.html +32 -0
- package/docs/.vitepress/dist/favicon.svg +6 -0
- package/docs/.vitepress/dist/getting-started.html +26 -0
- package/docs/.vitepress/dist/hashmap.json +1 -0
- package/docs/.vitepress/dist/index.html +26 -0
- package/docs/.vitepress/dist/operations-guide.html +43 -0
- package/docs/.vitepress/dist/protocol.html +65 -0
- package/docs/.vitepress/dist/tech-selection-2026.html +26 -0
- package/docs/.vitepress/dist/vp-icons.css +1 -0
- package/docs/.vitepress/theme/custom.css +42 -0
- package/docs/.vitepress/theme/index.ts +10 -0
- package/docs/README.md +10 -7
- package/docs/getting-started.md +136 -0
- package/docs/index.md +57 -0
- package/docs/operations-guide.md +6 -6
- package/docs/public/favicon.svg +6 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,99 +1,60 @@
|
|
|
1
1
|
# TermPilot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@fengye404/termpilot)
|
|
4
|
+
[](https://www.npmjs.com/package/@fengye404/termpilot)
|
|
5
|
+
[](https://github.com/fengye404/TermPilot/actions)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
TermPilot 是一个终端优先的远程控制工具。它把电脑上的 `tmux` 会话暴露给手机浏览器,让你在电脑和手机之间无缝切换,同步查看和控制同一批任务。
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## 产品形态
|
|
9
|
+
## 为什么是它
|
|
10
10
|
|
|
11
11
|
- 一个 npm 包:`@fengye404/termpilot`
|
|
12
12
|
- 一个服务器命令:`termpilot relay`
|
|
13
13
|
- 一个电脑命令:`termpilot agent`
|
|
14
14
|
- 手机端不安装,直接打开 relay 域名
|
|
15
|
-
- relay
|
|
16
|
-
|
|
17
|
-
## 5 分钟快速上手
|
|
18
|
-
|
|
19
|
-
### 1. 启动 relay
|
|
15
|
+
- relay 同时负责 Web UI 托管和 WebSocket 中继
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
## 30 秒理解工作流
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
1. 在服务器执行 `termpilot relay`
|
|
20
|
+
2. 在电脑执行 `termpilot agent`
|
|
21
|
+
3. 手机上打开 relay 域名并输入配对码
|
|
22
|
+
4. 在电脑直接执行 `termpilot claude code`
|
|
23
|
+
5. 手机和电脑同时看到同一个会话输出
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
## 快速开始
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
### 1. 启动 relay
|
|
31
28
|
|
|
32
29
|
```bash
|
|
30
|
+
npm install -g @fengye404/termpilot
|
|
33
31
|
termpilot relay
|
|
34
|
-
termpilot relay stop
|
|
35
|
-
termpilot relay run
|
|
36
32
|
```
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
- `termpilot relay stop`:停止后台 relay
|
|
40
|
-
- `termpilot relay run`:前台运行,适合看日志和排查问题
|
|
41
|
-
|
|
42
|
-
如果你只是先本地体验,也可以直接在自己电脑上跑 relay,然后让手机走局域网访问。
|
|
43
|
-
|
|
44
|
-
### 2. 启动电脑 agent
|
|
45
|
-
|
|
46
|
-
在你的电脑上执行:
|
|
34
|
+
### 2. 启动 agent
|
|
47
35
|
|
|
48
36
|
```bash
|
|
49
37
|
npm install -g @fengye404/termpilot
|
|
50
38
|
termpilot agent
|
|
51
39
|
```
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
第一次运行时,`termpilot agent` 会交互式询问:
|
|
54
42
|
|
|
55
|
-
1.
|
|
56
|
-
2.
|
|
57
|
-
3. 自动保存本机配置
|
|
58
|
-
4. 后台启动 agent
|
|
59
|
-
5. 输出一次性配对码
|
|
43
|
+
1. relay 域名或 IP
|
|
44
|
+
2. 端口,默认 `8787`
|
|
60
45
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
termpilot agent
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
这条命令会根据当前状态自动处理:
|
|
68
|
-
|
|
69
|
-
- 没有后台 agent:按本机已保存配置启动
|
|
70
|
-
- 已经有后台 agent:直接显示当前状态
|
|
71
|
-
- 想重新给手机配对:执行 `termpilot agent --pair`
|
|
72
|
-
|
|
73
|
-
常用管理命令:
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
termpilot agent status
|
|
77
|
-
termpilot agent stop
|
|
78
|
-
termpilot agent --pair
|
|
79
|
-
```
|
|
46
|
+
然后它会自动保存配置、后台启动 agent,并打印一次性配对码。
|
|
80
47
|
|
|
81
48
|
### 3. 手机完成配对
|
|
82
49
|
|
|
83
|
-
|
|
50
|
+
手机浏览器打开:
|
|
84
51
|
|
|
85
52
|
- `http://your-domain.com:8787`
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
然后:
|
|
89
|
-
|
|
90
|
-
1. 输入电脑端刚打印出来的配对码
|
|
91
|
-
2. 点“配对”
|
|
92
|
-
3. 成功后直接进入会话列表
|
|
53
|
+
- 或 `https://your-domain.com`
|
|
93
54
|
|
|
94
|
-
|
|
55
|
+
输入配对码后,直接进入会话列表。
|
|
95
56
|
|
|
96
|
-
|
|
57
|
+
### 4. 直接启动任务
|
|
97
58
|
|
|
98
59
|
```bash
|
|
99
60
|
termpilot claude code
|
|
@@ -105,163 +66,51 @@ termpilot claude code
|
|
|
105
66
|
termpilot open code
|
|
106
67
|
```
|
|
107
68
|
|
|
108
|
-
|
|
69
|
+
这会直接创建一个受 TermPilot 管理的 `tmux` 会话并 attach 到当前终端,手机端会同步看到同一个会话。
|
|
109
70
|
|
|
110
|
-
|
|
111
|
-
- 把命令写进这个会话
|
|
112
|
-
- 当前终端自动 attach 进去
|
|
113
|
-
- 手机端同步看到同一个会话
|
|
114
|
-
|
|
115
|
-
### 5. 你现在应该能做到什么
|
|
116
|
-
|
|
117
|
-
此时你可以:
|
|
118
|
-
|
|
119
|
-
- 在电脑上看 `claude code` / `open code` 的流式输出
|
|
120
|
-
- 在手机上看同一份输出
|
|
121
|
-
- 在手机上补一条命令、发快捷键、关闭会话
|
|
122
|
-
- 随时在电脑和手机之间切换
|
|
123
|
-
|
|
124
|
-
### 服务器
|
|
125
|
-
|
|
126
|
-
发布后:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
npm install -g @fengye404/termpilot
|
|
130
|
-
termpilot relay
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
当前仓库内本地验证:
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
pnpm install
|
|
137
|
-
pnpm build
|
|
138
|
-
npm install -g .
|
|
139
|
-
termpilot relay
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
常用参数:
|
|
71
|
+
## 常用命令
|
|
143
72
|
|
|
144
73
|
```bash
|
|
145
74
|
termpilot relay
|
|
146
|
-
termpilot relay run
|
|
147
75
|
termpilot relay stop
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### 电脑
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
npm install -g @fengye404/termpilot
|
|
76
|
+
termpilot relay run
|
|
155
77
|
termpilot agent
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
如果你只是想看调试日志,可以显式前台运行:
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
termpilot agent --foreground
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
查看后台状态:
|
|
165
|
-
|
|
166
|
-
```bash
|
|
78
|
+
termpilot agent --pair
|
|
167
79
|
termpilot agent status
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
停止后台 agent:
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
80
|
termpilot agent stop
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
本地测试:
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
termpilot agent
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### 手机
|
|
183
|
-
|
|
184
|
-
直接打开 relay 域名:
|
|
185
|
-
|
|
186
|
-
- `https://your-domain.com`
|
|
187
|
-
|
|
188
|
-
首次使用时,直接执行上面的 `termpilot agent` 就会进入配置引导并拿到配对码;如果你已经跑着后台 agent、只是想重新给手机配对,用 `termpilot agent --pair`。
|
|
189
|
-
|
|
190
|
-
配对成功后:
|
|
191
|
-
|
|
192
|
-
- 访问令牌会自动写回页面
|
|
193
|
-
- 手机端默认先显示会话列表
|
|
194
|
-
- 点进一个会话后才进入终端详情页
|
|
195
|
-
- 连接信息和设备设置都在页面底部折叠区
|
|
196
|
-
|
|
197
|
-
## 日常使用
|
|
198
|
-
|
|
199
|
-
### 直接把命令交给 TermPilot
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
termpilot agent
|
|
203
81
|
termpilot claude code
|
|
204
82
|
termpilot open code
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
如果你想跑别的命令,也可以直接:
|
|
208
|
-
|
|
209
|
-
```bash
|
|
210
|
-
termpilot npm run dev
|
|
211
|
-
termpilot python worker.py
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### 手动管理会话
|
|
215
|
-
|
|
216
|
-
创建会话并进入:
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
termpilot create --name claude-main --cwd /path/to/project
|
|
83
|
+
termpilot create --name my-task --cwd /path/to/project
|
|
220
84
|
termpilot list
|
|
221
85
|
termpilot attach --sid <sid>
|
|
86
|
+
termpilot kill --sid <sid>
|
|
222
87
|
```
|
|
223
88
|
|
|
224
|
-
|
|
89
|
+
## 文档
|
|
225
90
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
91
|
+
- [文档首页](./docs/index.md)
|
|
92
|
+
- [快速开始](./docs/getting-started.md)
|
|
93
|
+
- [部署与运维指南](./docs/operations-guide.md)
|
|
94
|
+
- [当前架构](./docs/architecture.md)
|
|
95
|
+
- [当前协议](./docs/protocol.md)
|
|
96
|
+
- [开发文档](./docs/development.md)
|
|
231
97
|
|
|
232
|
-
|
|
98
|
+
## 最佳实践
|
|
233
99
|
|
|
234
|
-
|
|
100
|
+
1. 需要跨端同步的任务,一开始就用 `termpilot claude code`、`termpilot open code` 或 `termpilot create` 启动,不要先在普通终端里跑再想着接管。
|
|
101
|
+
2. `termpilot agent` 适合作为长期后台入口。第一次配置完之后,日常只需要记住这一条命令。
|
|
102
|
+
3. relay 长期使用时,优先挂到 HTTPS/WSS 域名后面;本地演示再用裸 IP 和 `8787`。
|
|
103
|
+
4. 手机更适合看输出、发短命令和轻控制;电脑前的重输入仍然建议在本地终端完成。
|
|
104
|
+
|
|
105
|
+
## 本地开发
|
|
235
106
|
|
|
236
107
|
```bash
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
termpilot agent status
|
|
243
|
-
termpilot agent stop
|
|
244
|
-
termpilot create --name claude-main
|
|
245
|
-
termpilot list
|
|
246
|
-
termpilot attach --sid <sid>
|
|
247
|
-
termpilot kill --sid <sid>
|
|
248
|
-
termpilot grants
|
|
249
|
-
termpilot audit --limit 30
|
|
250
|
-
termpilot revoke --token <accessToken>
|
|
251
|
-
termpilot doctor
|
|
108
|
+
pnpm install
|
|
109
|
+
pnpm build
|
|
110
|
+
pnpm docs:dev
|
|
111
|
+
pnpm test:ui-smoke
|
|
112
|
+
pnpm check:stability
|
|
252
113
|
```
|
|
253
|
-
|
|
254
|
-
## 最佳实践
|
|
255
|
-
|
|
256
|
-
1. 需要跨端同步的任务,一开始就用 `termpilot create` 创建,不要先在普通终端里跑再想着接管。
|
|
257
|
-
2. 第一次先跑一次 `termpilot agent` 完成本机配置,之后日常就只需要记住这一条命令。
|
|
258
|
-
3. 如果只是想“开一个会话然后立刻跑起来”,优先用 `termpilot claude code` 这类直达命令,不必手动 `create + attach`。
|
|
259
|
-
4. 一个长期任务用一个独立会话,名称直接写任务语义,比如 `claude-main`、`deploy-watch`、`batch-fix`。
|
|
260
|
-
5. 电脑前重操作优先 `termpilot attach`;手机更适合看进度、发短命令、补快捷键和关闭会话。
|
|
261
|
-
6. 普通 iTerm / Terminal 标签页不是 TermPilot 管理对象,不要指望后面“无缝接管”进来。
|
|
262
|
-
7. 手机优先走一次性配对码,不要长期传播访问令牌。
|
|
263
|
-
8. 要长期使用 relay,优先放到 HTTPS/WSS 域名后面,并接 PostgreSQL;本地演示可以先用内存模式。
|
|
264
|
-
9. 换手机或访问权变更时,先 `termpilot grants`,再 `termpilot revoke --token ...`。
|
|
265
114
|
10. 想排查控制历史时先看 `termpilot audit --limit 30`。
|
|
266
115
|
11. 服务器上日常用 `termpilot relay` 后台运行;只有排查问题时才用 `termpilot relay run`。
|
|
267
116
|
|
package/dist/cli.js
CHANGED
|
@@ -849,11 +849,7 @@ function isLocalRelayHost(hostname) {
|
|
|
849
849
|
}
|
|
850
850
|
function normalizeRelayUrl(rawHost, rawPort) {
|
|
851
851
|
const hostInput = rawHost.trim();
|
|
852
|
-
const portInput = rawPort.trim()
|
|
853
|
-
const normalizedPort = Number(portInput);
|
|
854
|
-
if (!Number.isFinite(normalizedPort) || normalizedPort <= 0 || normalizedPort > 65535) {
|
|
855
|
-
throw new Error("\u7AEF\u53E3\u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1 \u5230 65535 \u4E4B\u95F4\u7684\u6570\u5B57\u3002");
|
|
856
|
-
}
|
|
852
|
+
const portInput = rawPort.trim();
|
|
857
853
|
if (hostInput.includes("://")) {
|
|
858
854
|
const parsed = new URL(hostInput);
|
|
859
855
|
if (parsed.protocol === "http:") {
|
|
@@ -861,8 +857,14 @@ function normalizeRelayUrl(rawHost, rawPort) {
|
|
|
861
857
|
} else if (parsed.protocol === "https:") {
|
|
862
858
|
parsed.protocol = "wss:";
|
|
863
859
|
}
|
|
864
|
-
if (
|
|
865
|
-
|
|
860
|
+
if (portInput) {
|
|
861
|
+
const normalizedPort2 = Number(portInput);
|
|
862
|
+
if (!Number.isFinite(normalizedPort2) || normalizedPort2 <= 0 || normalizedPort2 > 65535) {
|
|
863
|
+
throw new Error("\u7AEF\u53E3\u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1 \u5230 65535 \u4E4B\u95F4\u7684\u6570\u5B57\u3002");
|
|
864
|
+
}
|
|
865
|
+
parsed.port = String(normalizedPort2);
|
|
866
|
+
} else if (!parsed.port && parsed.protocol === "ws:" && isLocalRelayHost(parsed.hostname)) {
|
|
867
|
+
parsed.port = "8787";
|
|
866
868
|
}
|
|
867
869
|
if (!parsed.pathname || parsed.pathname === "/") {
|
|
868
870
|
parsed.pathname = "/ws";
|
|
@@ -872,6 +874,16 @@ function normalizeRelayUrl(rawHost, rawPort) {
|
|
|
872
874
|
return parsed.toString();
|
|
873
875
|
}
|
|
874
876
|
const protocol = isLocalRelayHost(hostInput) ? "ws:" : "wss:";
|
|
877
|
+
if (!portInput) {
|
|
878
|
+
if (protocol === "ws:") {
|
|
879
|
+
return `${protocol}//${hostInput}:8787/ws`;
|
|
880
|
+
}
|
|
881
|
+
return `${protocol}//${hostInput}/ws`;
|
|
882
|
+
}
|
|
883
|
+
const normalizedPort = Number(portInput);
|
|
884
|
+
if (!Number.isFinite(normalizedPort) || normalizedPort <= 0 || normalizedPort > 65535) {
|
|
885
|
+
throw new Error("\u7AEF\u53E3\u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1 \u5230 65535 \u4E4B\u95F4\u7684\u6570\u5B57\u3002");
|
|
886
|
+
}
|
|
875
887
|
return `${protocol}//${hostInput}:${normalizedPort}/ws`;
|
|
876
888
|
}
|
|
877
889
|
async function promptForAgentConfig(deviceId) {
|
|
@@ -885,7 +897,7 @@ async function promptForAgentConfig(deviceId) {
|
|
|
885
897
|
if (!host) {
|
|
886
898
|
throw new Error("\u672A\u8F93\u5165 relay \u57DF\u540D\u6216 IP\uFF0C\u5DF2\u53D6\u6D88\u3002");
|
|
887
899
|
}
|
|
888
|
-
const port = await rl.question("\u8BF7\u8F93\u5165 relay \u7AEF\u53E3\uFF08\u76F4\u63A5\u56DE\u8F66\u9ED8\u8BA4 8787\uFF09: ");
|
|
900
|
+
const port = await rl.question("\u8BF7\u8F93\u5165 relay \u7AEF\u53E3\uFF08\u53CD\u4EE3\u57DF\u540D\u53EF\u76F4\u63A5\u56DE\u8F66\uFF1B\u672C\u5730 IP \u9ED8\u8BA4 8787\uFF09: ");
|
|
889
901
|
const relayUrl = normalizeRelayUrl(host, port);
|
|
890
902
|
console.log(`\u5C06\u4F7F\u7528 relay: ${relayUrl}`);
|
|
891
903
|
return { relayUrl, deviceId };
|
|
@@ -1324,7 +1336,8 @@ function loadRelayRuntime() {
|
|
|
1324
1336
|
pid: parsed.pid,
|
|
1325
1337
|
host: parsed.host,
|
|
1326
1338
|
port: parsed.port,
|
|
1327
|
-
startedAt: parsed.startedAt
|
|
1339
|
+
startedAt: parsed.startedAt,
|
|
1340
|
+
cliPath: typeof parsed.cliPath === "string" ? parsed.cliPath : void 0
|
|
1328
1341
|
};
|
|
1329
1342
|
} catch {
|
|
1330
1343
|
return null;
|
|
@@ -1842,7 +1855,18 @@ function createStaticPath(webDir, urlPath) {
|
|
|
1842
1855
|
return resolvedPath;
|
|
1843
1856
|
}
|
|
1844
1857
|
function resolveDefaultWebDir(moduleUrl = import.meta.url) {
|
|
1845
|
-
|
|
1858
|
+
const candidates = [
|
|
1859
|
+
"../../app/dist",
|
|
1860
|
+
"../app/dist",
|
|
1861
|
+
"./app/dist"
|
|
1862
|
+
];
|
|
1863
|
+
for (const candidate of candidates) {
|
|
1864
|
+
const resolvedDir = fileURLToPath(new URL(candidate, moduleUrl));
|
|
1865
|
+
if (existsSync(path3.join(resolvedDir, "index.html"))) {
|
|
1866
|
+
return resolvedDir;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return fileURLToPath(new URL("../app/dist", moduleUrl));
|
|
1846
1870
|
}
|
|
1847
1871
|
async function startRelayServer(options = {}) {
|
|
1848
1872
|
const config = {
|
|
@@ -2333,26 +2357,14 @@ function readRuntimeStatus2() {
|
|
|
2333
2357
|
}
|
|
2334
2358
|
return { runtime, alive };
|
|
2335
2359
|
}
|
|
2336
|
-
function printRuntime(runtime = readRuntimeStatus2().runtime) {
|
|
2337
|
-
if (!runtime) {
|
|
2338
|
-
console.log("\u540E\u53F0 relay \u5F53\u524D\u672A\u8FD0\u884C\u3002");
|
|
2339
|
-
console.log(`\u8FD0\u884C\u65F6\u6587\u4EF6: ${getRelayRuntimeFilePath()}`);
|
|
2340
|
-
console.log(`\u65E5\u5FD7: ${getRelayLogFilePath()}`);
|
|
2341
|
-
return;
|
|
2342
|
-
}
|
|
2343
|
-
console.log("\u540E\u53F0 relay \u6B63\u5728\u8FD0\u884C\u3002");
|
|
2344
|
-
console.log(`PID: ${runtime.pid}`);
|
|
2345
|
-
console.log(`\u76D1\u542C: http://${runtime.host}:${runtime.port}`);
|
|
2346
|
-
console.log(`\u542F\u52A8\u65F6\u95F4: ${runtime.startedAt}`);
|
|
2347
|
-
console.log(`\u65E5\u5FD7: ${getRelayLogFilePath()}`);
|
|
2348
|
-
}
|
|
2349
2360
|
async function runForeground() {
|
|
2350
2361
|
const config = loadConfig();
|
|
2351
2362
|
saveRelayRuntime({
|
|
2352
2363
|
pid: process.pid,
|
|
2353
2364
|
host: config.host,
|
|
2354
2365
|
port: config.port,
|
|
2355
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2366
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2367
|
+
cliPath: process.argv[1]
|
|
2356
2368
|
});
|
|
2357
2369
|
process.on("exit", () => {
|
|
2358
2370
|
clearRelayRuntime(process.pid);
|
|
@@ -2366,11 +2378,14 @@ async function runStart2() {
|
|
|
2366
2378
|
const existing = readRuntimeStatus2();
|
|
2367
2379
|
if (existing.runtime && existing.alive) {
|
|
2368
2380
|
const sameConfig = existing.runtime.host === config.host && existing.runtime.port === config.port;
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2381
|
+
const sameCliPath = existing.runtime.cliPath === process.argv[1];
|
|
2382
|
+
if (sameConfig && sameCliPath) {
|
|
2383
|
+
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u5DF2\u5728\u8FD0\u884C\uFF0C\u6B63\u5728\u91CD\u542F\u5230\u5F53\u524D\u914D\u7F6E\u3002");
|
|
2384
|
+
} else if (!sameCliPath) {
|
|
2385
|
+
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u6B63\u5728\u8FD0\u884C\uFF0C\u4F46\u5B89\u88C5\u7248\u672C\u6216\u5165\u53E3\u5DF2\u53D8\u5316\uFF0C\u6B63\u5728\u91CD\u542F\u5230\u5F53\u524D\u7248\u672C\u3002");
|
|
2386
|
+
} else {
|
|
2387
|
+
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u5DF2\u5728\u8FD0\u884C\uFF0C\u4F46\u76D1\u542C\u914D\u7F6E\u548C\u5F53\u524D\u547D\u4EE4\u4E0D\u4E00\u81F4\uFF0C\u6B63\u5728\u91CD\u542F\u3002");
|
|
2372
2388
|
}
|
|
2373
|
-
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u5DF2\u5728\u8FD0\u884C\uFF0C\u4F46\u76D1\u542C\u914D\u7F6E\u548C\u5F53\u524D\u547D\u4EE4\u4E0D\u4E00\u81F4\uFF0C\u6B63\u5728\u91CD\u542F\u3002");
|
|
2374
2389
|
await runStop2();
|
|
2375
2390
|
}
|
|
2376
2391
|
clearRelayRuntime();
|
|
@@ -2389,7 +2404,8 @@ async function runStart2() {
|
|
|
2389
2404
|
pid: child.pid,
|
|
2390
2405
|
host: config.host,
|
|
2391
2406
|
port: config.port,
|
|
2392
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2407
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2408
|
+
cliPath: process.argv[1]
|
|
2393
2409
|
});
|
|
2394
2410
|
console.log(`\u540E\u53F0 relay \u5DF2\u542F\u52A8\uFF0CPID: ${child.pid}`);
|
|
2395
2411
|
console.log(`\u76D1\u542C: http://${config.host}:${config.port}`);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { defineConfig } from "vitepress";
|
|
2
|
+
|
|
3
|
+
const repo = process.env.GITHUB_REPOSITORY?.split("/")[1] ?? "TermPilot";
|
|
4
|
+
const base = process.env.GITHUB_ACTIONS ? `/${repo}/` : "/";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
lang: "zh-CN",
|
|
8
|
+
title: "TermPilot",
|
|
9
|
+
description: "手机和电脑共享同一个 tmux 会话的终端远程控制工具。",
|
|
10
|
+
base,
|
|
11
|
+
lastUpdated: true,
|
|
12
|
+
cleanUrls: true,
|
|
13
|
+
head: [
|
|
14
|
+
["meta", { name: "theme-color", content: "#1f7a53" }],
|
|
15
|
+
],
|
|
16
|
+
themeConfig: {
|
|
17
|
+
siteTitle: "TermPilot",
|
|
18
|
+
logo: "/favicon.svg",
|
|
19
|
+
nav: [
|
|
20
|
+
{ text: "首页", link: "/" },
|
|
21
|
+
{ text: "快速开始", link: "/getting-started" },
|
|
22
|
+
{ text: "部署与运维", link: "/operations-guide" },
|
|
23
|
+
{ text: "架构", link: "/architecture" },
|
|
24
|
+
{ text: "协议", link: "/protocol" },
|
|
25
|
+
],
|
|
26
|
+
sidebar: [
|
|
27
|
+
{
|
|
28
|
+
text: "开始",
|
|
29
|
+
items: [
|
|
30
|
+
{ text: "文档首页", link: "/" },
|
|
31
|
+
{ text: "快速开始", link: "/getting-started" },
|
|
32
|
+
{ text: "部署与运维指南", link: "/operations-guide" },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
text: "参考",
|
|
37
|
+
items: [
|
|
38
|
+
{ text: "代码架构", link: "/architecture" },
|
|
39
|
+
{ text: "协议说明", link: "/protocol" },
|
|
40
|
+
{ text: "开发文档", link: "/development" },
|
|
41
|
+
{ text: "技术选型", link: "/tech-selection-2026" },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
search: {
|
|
46
|
+
provider: "local",
|
|
47
|
+
},
|
|
48
|
+
socialLinks: [
|
|
49
|
+
{ icon: "github", link: "https://github.com/fengye404/TermPilot" },
|
|
50
|
+
],
|
|
51
|
+
editLink: {
|
|
52
|
+
pattern: "https://github.com/fengye404/TermPilot/edit/main/docs/:path",
|
|
53
|
+
text: "在 GitHub 上编辑此页",
|
|
54
|
+
},
|
|
55
|
+
outline: {
|
|
56
|
+
level: [2, 3],
|
|
57
|
+
label: "本页目录",
|
|
58
|
+
},
|
|
59
|
+
footer: {
|
|
60
|
+
message: "为长期任务和跨端终端控制而构建。",
|
|
61
|
+
copyright: "Copyright © 2026 Fengye",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN" dir="ltr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>404 | TermPilot</title>
|
|
7
|
+
<meta name="description" content="Not Found">
|
|
8
|
+
<meta name="generator" content="VitePress v1.6.4">
|
|
9
|
+
<link rel="preload stylesheet" href="/assets/style.B0lvUXq1.css" as="style">
|
|
10
|
+
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
|
|
11
|
+
|
|
12
|
+
<script type="module" src="/assets/app.BG4pRgiG.js"></script>
|
|
13
|
+
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
|
|
14
|
+
<meta name="theme-color" content="#1f7a53">
|
|
15
|
+
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
|
|
16
|
+
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<div id="app"></div>
|
|
20
|
+
<script>window.__VP_HASH_MAP__=JSON.parse("{\"architecture.md\":\"JnC1zyYV\",\"development.md\":\"iwUVjeO6\",\"getting-started.md\":\"Cirp1CHi\",\"index.md\":\"D9XElNRh\",\"operations-guide.md\":\"DfNiIg5F\",\"protocol.md\":\"CCXFJUPR\",\"readme.md\":\"B4-OJVNQ\",\"tech-selection-2026.md\":\"Dk_ymWTx\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"zh-CN\",\"dir\":\"ltr\",\"title\":\"TermPilot\",\"description\":\"手机和电脑共享同一个 tmux 会话的终端远程控制工具。\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"siteTitle\":\"TermPilot\",\"logo\":\"/favicon.svg\",\"nav\":[{\"text\":\"首页\",\"link\":\"/\"},{\"text\":\"快速开始\",\"link\":\"/getting-started\"},{\"text\":\"部署与运维\",\"link\":\"/operations-guide\"},{\"text\":\"架构\",\"link\":\"/architecture\"},{\"text\":\"协议\",\"link\":\"/protocol\"}],\"sidebar\":[{\"text\":\"开始\",\"items\":[{\"text\":\"文档首页\",\"link\":\"/\"},{\"text\":\"快速开始\",\"link\":\"/getting-started\"},{\"text\":\"部署与运维指南\",\"link\":\"/operations-guide\"}]},{\"text\":\"参考\",\"items\":[{\"text\":\"代码架构\",\"link\":\"/architecture\"},{\"text\":\"协议说明\",\"link\":\"/protocol\"},{\"text\":\"开发文档\",\"link\":\"/development\"},{\"text\":\"技术选型\",\"link\":\"/tech-selection-2026\"}]}],\"search\":{\"provider\":\"local\"},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/fengye404/TermPilot\"}],\"editLink\":{\"pattern\":\"https://github.com/fengye404/TermPilot/edit/main/docs/:path\",\"text\":\"在 GitHub 上编辑此页\"},\"outline\":{\"level\":[2,3],\"label\":\"本页目录\"},\"footer\":{\"message\":\"为长期任务和跨端终端控制而构建。\",\"copyright\":\"Copyright © 2026 Fengye\"}},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":true}");</script>
|
|
21
|
+
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN" dir="ltr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>docs 目录 | TermPilot</title>
|
|
7
|
+
<meta name="description" content="手机和电脑共享同一个 tmux 会话的终端远程控制工具。">
|
|
8
|
+
<meta name="generator" content="VitePress v1.6.4">
|
|
9
|
+
<link rel="preload stylesheet" href="/assets/style.B0lvUXq1.css" as="style">
|
|
10
|
+
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
|
|
11
|
+
|
|
12
|
+
<script type="module" src="/assets/app.BG4pRgiG.js"></script>
|
|
13
|
+
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
|
|
14
|
+
<link rel="modulepreload" href="/assets/chunks/theme.D0_6rd9F.js">
|
|
15
|
+
<link rel="modulepreload" href="/assets/chunks/framework.BZohXCq9.js">
|
|
16
|
+
<link rel="modulepreload" href="/assets/README.md.B4-OJVNQ.lean.js">
|
|
17
|
+
<meta name="theme-color" content="#1f7a53">
|
|
18
|
+
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
|
|
19
|
+
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<div id="app"><div class="Layout" data-v-1bdd7537><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0a4b5d46></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0a4b5d46>Skip to content</a><!--]--><!----><header class="VPNav" data-v-1bdd7537 data-v-07e0ee65><div class="VPNavBar" data-v-07e0ee65 data-v-bfcfe748><div class="wrapper" data-v-bfcfe748><div class="container" data-v-bfcfe748><div class="title" data-v-bfcfe748><div class="VPNavBarTitle has-sidebar" data-v-bfcfe748 data-v-6ce24249><a class="title" href="/" data-v-6ce24249><!--[--><!--]--><!--[--><img class="VPImage logo" src="/favicon.svg" alt data-v-105fdaa6><!--]--><span data-v-6ce24249>TermPilot</span><!--[--><!--]--></a></div></div><div class="content" data-v-bfcfe748><div class="content-body" data-v-bfcfe748><!--[--><!--]--><div class="VPNavBarSearch search" data-v-bfcfe748><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-bfcfe748 data-v-d1dc8fec><span id="main-nav-aria-label" class="visually-hidden" data-v-d1dc8fec> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-d1dc8fec data-v-82ee0671><!--[--><span data-v-82ee0671>首页</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/getting-started" tabindex="0" data-v-d1dc8fec data-v-82ee0671><!--[--><span data-v-82ee0671>快速开始</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/operations-guide" tabindex="0" data-v-d1dc8fec data-v-82ee0671><!--[--><span data-v-82ee0671>部署与运维</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/architecture" tabindex="0" data-v-d1dc8fec data-v-82ee0671><!--[--><span data-v-82ee0671>架构</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/protocol" tabindex="0" data-v-d1dc8fec data-v-82ee0671><!--[--><span data-v-82ee0671>协议</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-bfcfe748 data-v-d994237e><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-d994237e data-v-9a85cf30 data-v-b1949eaf><span class="check" data-v-b1949eaf><span class="icon" data-v-b1949eaf><!--[--><span class="vpi-sun sun" data-v-9a85cf30></span><span class="vpi-moon moon" data-v-9a85cf30></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-bfcfe748 data-v-a61e22dd data-v-f3c77898><!--[--><a class="VPSocialLink no-icon" href="https://github.com/fengye404/TermPilot" aria-label="github" target="_blank" rel="noopener" data-v-f3c77898 data-v-8a10446f><span class="vpi-social-github"></span></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-bfcfe748 data-v-6d16081b data-v-35350ec6><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-35350ec6><span class="vpi-more-horizontal icon" data-v-35350ec6></span></button><div class="menu" data-v-35350ec6><div class="VPMenu" data-v-35350ec6 data-v-52b08d90><!----><!--[--><!--[--><!----><div class="group" data-v-6d16081b><div class="item appearance" data-v-6d16081b><p class="label" data-v-6d16081b>Appearance</p><div class="appearance-action" data-v-6d16081b><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-6d16081b data-v-9a85cf30 data-v-b1949eaf><span class="check" data-v-b1949eaf><span class="icon" data-v-b1949eaf><!--[--><span class="vpi-sun sun" data-v-9a85cf30></span><span class="vpi-moon moon" data-v-9a85cf30></span><!--]--></span></span></button></div></div></div><div class="group" data-v-6d16081b><div class="item social-links" data-v-6d16081b><div class="VPSocialLinks social-links-list" data-v-6d16081b data-v-f3c77898><!--[--><a class="VPSocialLink no-icon" href="https://github.com/fengye404/TermPilot" aria-label="github" target="_blank" rel="noopener" data-v-f3c77898 data-v-8a10446f><span class="vpi-social-github"></span></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-bfcfe748 data-v-1e510fee><span class="container" data-v-1e510fee><span class="top" data-v-1e510fee></span><span class="middle" data-v-1e510fee></span><span class="bottom" data-v-1e510fee></span></span></button></div></div></div></div><div class="divider" data-v-bfcfe748><div class="divider-line" data-v-bfcfe748></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-1bdd7537 data-v-18894e30><div class="container" data-v-18894e30><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-18894e30><span class="vpi-align-left menu-icon" data-v-18894e30></span><span class="menu-text" data-v-18894e30>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-18894e30 data-v-bdfacdf2><button data-v-bdfacdf2>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-1bdd7537 data-v-1d1d7027><div class="curtain" data-v-1d1d7027></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-1d1d7027><span class="visually-hidden" id="sidebar-aria-label" data-v-1d1d7027> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-594fed9e><section class="VPSidebarItem level-0" data-v-594fed9e data-v-cd30f15d><div class="item" role="button" tabindex="0" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><h2 class="text" data-v-cd30f15d>开始</h2><!----></div><div class="items" data-v-cd30f15d><!--[--><div class="VPSidebarItem level-1 is-link" data-v-cd30f15d data-v-cd30f15d><div class="item" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><a class="VPLink link link" href="/" data-v-cd30f15d><!--[--><p class="text" data-v-cd30f15d>文档首页</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-cd30f15d data-v-cd30f15d><div class="item" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><a class="VPLink link link" href="/getting-started" data-v-cd30f15d><!--[--><p class="text" data-v-cd30f15d>快速开始</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-cd30f15d data-v-cd30f15d><div class="item" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><a class="VPLink link link" href="/operations-guide" data-v-cd30f15d><!--[--><p class="text" data-v-cd30f15d>部署与运维指南</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-594fed9e><section class="VPSidebarItem level-0" data-v-594fed9e data-v-cd30f15d><div class="item" role="button" tabindex="0" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><h2 class="text" data-v-cd30f15d>参考</h2><!----></div><div class="items" data-v-cd30f15d><!--[--><div class="VPSidebarItem level-1 is-link" data-v-cd30f15d data-v-cd30f15d><div class="item" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><a class="VPLink link link" href="/architecture" data-v-cd30f15d><!--[--><p class="text" data-v-cd30f15d>代码架构</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-cd30f15d data-v-cd30f15d><div class="item" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><a class="VPLink link link" href="/protocol" data-v-cd30f15d><!--[--><p class="text" data-v-cd30f15d>协议说明</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-cd30f15d data-v-cd30f15d><div class="item" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><a class="VPLink link link" href="/development" data-v-cd30f15d><!--[--><p class="text" data-v-cd30f15d>开发文档</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-cd30f15d data-v-cd30f15d><div class="item" data-v-cd30f15d><div class="indicator" data-v-cd30f15d></div><a class="VPLink link link" href="/tech-selection-2026" data-v-cd30f15d><!--[--><p class="text" data-v-cd30f15d>技术选型</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-1bdd7537 data-v-f89b4fb6><div class="VPDoc has-sidebar has-aside" data-v-f89b4fb6 data-v-1870bdbd><!--[--><!--]--><div class="container" data-v-1870bdbd><div class="aside" data-v-1870bdbd><div class="aside-curtain" data-v-1870bdbd></div><div class="aside-container" data-v-1870bdbd><div class="aside-content" data-v-1870bdbd><div class="VPDocAside" data-v-1870bdbd data-v-2bdf3a15><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-2bdf3a15 data-v-3d03f0c2><div class="content" data-v-3d03f0c2><div class="outline-marker" data-v-3d03f0c2></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-3d03f0c2>本页目录</div><ul class="VPDocOutlineItem root" data-v-3d03f0c2 data-v-87a812ef><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-2bdf3a15></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-1870bdbd><div class="content-container" data-v-1870bdbd><!--[--><!--]--><main class="main" data-v-1870bdbd><div style="position:relative;" class="vp-doc _README" data-v-1870bdbd><div><h1 id="docs-目录" tabindex="-1">docs 目录 <a class="header-anchor" href="#docs-目录" aria-label="Permalink to "docs 目录""></a></h1><p>如果你在 GitHub 仓库里浏览源码,推荐从下面几个入口开始:</p><ul><li><a href="./">文档首页</a></li><li><a href="./getting-started">快速开始</a></li><li><a href="./operations-guide">部署与运维指南</a></li><li><a href="./architecture">代码架构</a></li><li><a href="./development">开发文档</a></li></ul><p>如果 GitHub Pages 已经启用,优先看站点版本,它会带导航、目录和搜索。</p></div></div></main><footer class="VPDocFooter" data-v-1870bdbd data-v-b170fa4f><!--[--><!--]--><div class="edit-info" data-v-b170fa4f><div class="edit-link" data-v-b170fa4f><a class="VPLink link vp-external-link-icon no-icon edit-link-button" href="https://github.com/fengye404/TermPilot/edit/main/docs/README.md" target="_blank" rel="noreferrer" data-v-b170fa4f><!--[--><span class="vpi-square-pen edit-link-icon" data-v-b170fa4f></span> 在 GitHub 上编辑此页<!--]--></a></div><div class="last-updated" data-v-b170fa4f><p class="VPLastUpdated" data-v-b170fa4f data-v-33a4642e>Last updated: <time datetime="2026-03-11T17:35:45.000Z" data-v-33a4642e></time></p></div></div><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-b170fa4f><span class="visually-hidden" id="doc-footer-aria-label" data-v-b170fa4f>Pager</span><div class="pager" data-v-b170fa4f><!----></div><div class="pager" data-v-b170fa4f><a class="VPLink link pager-link next" href="/" data-v-b170fa4f><!--[--><span class="desc" data-v-b170fa4f>Next page</span><span class="title" data-v-b170fa4f>文档首页</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><footer class="VPFooter has-sidebar" data-v-1bdd7537 data-v-9a5ea44b><div class="container" data-v-9a5ea44b><p class="message" data-v-9a5ea44b>为长期任务和跨端终端控制而构建。</p><p class="copyright" data-v-9a5ea44b>Copyright © 2026 Fengye</p></div></footer><!--[--><!--]--></div></div>
|
|
23
|
+
<script>window.__VP_HASH_MAP__=JSON.parse("{\"architecture.md\":\"JnC1zyYV\",\"development.md\":\"iwUVjeO6\",\"getting-started.md\":\"Cirp1CHi\",\"index.md\":\"D9XElNRh\",\"operations-guide.md\":\"DfNiIg5F\",\"protocol.md\":\"CCXFJUPR\",\"readme.md\":\"B4-OJVNQ\",\"tech-selection-2026.md\":\"Dk_ymWTx\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"zh-CN\",\"dir\":\"ltr\",\"title\":\"TermPilot\",\"description\":\"手机和电脑共享同一个 tmux 会话的终端远程控制工具。\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"siteTitle\":\"TermPilot\",\"logo\":\"/favicon.svg\",\"nav\":[{\"text\":\"首页\",\"link\":\"/\"},{\"text\":\"快速开始\",\"link\":\"/getting-started\"},{\"text\":\"部署与运维\",\"link\":\"/operations-guide\"},{\"text\":\"架构\",\"link\":\"/architecture\"},{\"text\":\"协议\",\"link\":\"/protocol\"}],\"sidebar\":[{\"text\":\"开始\",\"items\":[{\"text\":\"文档首页\",\"link\":\"/\"},{\"text\":\"快速开始\",\"link\":\"/getting-started\"},{\"text\":\"部署与运维指南\",\"link\":\"/operations-guide\"}]},{\"text\":\"参考\",\"items\":[{\"text\":\"代码架构\",\"link\":\"/architecture\"},{\"text\":\"协议说明\",\"link\":\"/protocol\"},{\"text\":\"开发文档\",\"link\":\"/development\"},{\"text\":\"技术选型\",\"link\":\"/tech-selection-2026\"}]}],\"search\":{\"provider\":\"local\"},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/fengye404/TermPilot\"}],\"editLink\":{\"pattern\":\"https://github.com/fengye404/TermPilot/edit/main/docs/:path\",\"text\":\"在 GitHub 上编辑此页\"},\"outline\":{\"level\":[2,3],\"label\":\"本页目录\"},\"footer\":{\"message\":\"为长期任务和跨端终端控制而构建。\",\"copyright\":\"Copyright © 2026 Fengye\"}},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":true}");</script>
|
|
24
|
+
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|