codexpanel 0.1.0 → 0.1.1
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 +77 -7
- package/README.zh.md +126 -0
- package/bin/codexpanel.cjs +415 -141
- package/docs/desktop-npx-install-flow.md +694 -0
- package/package.json +11 -3
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
# CodexPanel 电脑端 npx 安装流程说明
|
|
2
|
+
|
|
3
|
+
本文档专门解释电脑端安装命令 `npx -y codexpanel` 的工作方式。你可以把它理解成一条很短的安装入口,背后会做几件顺序明确的事:先准备资源,再请你登录,登录后完成绑定,最后启动本机 agent 和本地状态面板。
|
|
4
|
+
|
|
5
|
+
## 这条命令做什么
|
|
6
|
+
|
|
7
|
+
`npx -y codexpanel` 会从 npm 拉起 CodexPanel 的电脑端安装器。它的目标不是“直接装一个静默程序”,而是完成一次可确认、可回退、可记录的设备接入。
|
|
8
|
+
|
|
9
|
+
这意味着它会先和正式生产域 `https://codexpanel.com` 通信,除非你显式指定 `--server test`、`--server local` 或自部署 URL。
|
|
10
|
+
|
|
11
|
+
## 为什么要先下载资源,再按 Enter 打开浏览器
|
|
12
|
+
|
|
13
|
+
安装流程被分成两段。
|
|
14
|
+
|
|
15
|
+
第一段是资源准备:CLI 会先读取安装清单,再下载并校验电脑端 agent、app-server 连接器和安装脚本探针。这样做的好处是,用户按下 Enter 的时候,系统已经知道安装所需资源是可用的,不会出现“浏览器已经打开了,但安装器还在等下载”的割裂感。
|
|
16
|
+
|
|
17
|
+
第二段才是登录绑定:终端会提示 `资源已准备好。按 Enter 拉起浏览器登录并绑定这台电脑。`,用户确认后,浏览器才会打开登录页。这样更像一个正式安装流程,而不是悄悄弹出一个网页。
|
|
18
|
+
|
|
19
|
+
## 浏览器登录是怎么工作的
|
|
20
|
+
|
|
21
|
+
浏览器会打开 `/desktop/setup?flowId=...`。这个页面会先让你登录或注册,然后展示这台电脑的名称、系统用户、deviceId、目标账号和设备额度。
|
|
22
|
+
|
|
23
|
+
登录成功后,页面会检查这台设备是否已经绑定过其他账号。如果已经绑定,它不会自动抢绑,而是让你选择:
|
|
24
|
+
|
|
25
|
+
- 保持原绑定并退出
|
|
26
|
+
- 切换绑定到当前账号
|
|
27
|
+
|
|
28
|
+
如果你选择切换,页面会再次确认,并检查当前账号的设备额度。
|
|
29
|
+
|
|
30
|
+
页面完成绑定后会提示:`绑定成功,可以关闭浏览器,或回到安装界面查看进度。`
|
|
31
|
+
|
|
32
|
+
## 终端登录方式
|
|
33
|
+
|
|
34
|
+
除了浏览器登录,这个安装器也支持两种终端模式。
|
|
35
|
+
|
|
36
|
+
### 用户名和密码
|
|
37
|
+
|
|
38
|
+
运行:
|
|
39
|
+
|
|
40
|
+
```powershell
|
|
41
|
+
npx -y codexpanel login --terminal-login
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
然后按提示输入用户名和密码。它会像普通登录一样走 session,成功后继续完成设备绑定。
|
|
45
|
+
|
|
46
|
+
### 一次性 token
|
|
47
|
+
|
|
48
|
+
如果你想在浏览器里登录,但让终端继续完成绑定,可以先打开安装器打印的 `/desktop/setup?flowId=...` 页面,登录后点击“生成终端一次性 token”。然后运行:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
npx -y codexpanel login --token-login
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
然后把页面显示的 token 粘贴进终端。这个 token 只对当前安装流有效,默认 5 分钟过期,使用成功后即失效。它不是长期凭证,也不是 agent 后续使用的 device token。
|
|
55
|
+
|
|
56
|
+
## `--server` 怎么选
|
|
57
|
+
|
|
58
|
+
默认:
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
npx -y codexpanel
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
会连接正式生产域 `https://codexpanel.com`。
|
|
65
|
+
|
|
66
|
+
测试环境:
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
npx -y codexpanel --server test
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
等价于 `https://jd.6a.gs`。
|
|
73
|
+
|
|
74
|
+
本地环境:
|
|
75
|
+
|
|
76
|
+
```powershell
|
|
77
|
+
npx -y codexpanel --server local
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
会动态寻找一个可用的本地端口,不再固定写死到某个值。这样你本机已经有服务占着端口时,安装器也能自动避开冲突。
|
|
81
|
+
|
|
82
|
+
自部署:
|
|
83
|
+
|
|
84
|
+
```powershell
|
|
85
|
+
npx -y codexpanel --server https://example.com
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
会直接连到你自己的 relay。
|
|
89
|
+
|
|
90
|
+
## 为什么不再使用访问码
|
|
91
|
+
|
|
92
|
+
以前为了兼容安装,会把访问码当成一种入口凭证。现在这条路已经取消了。
|
|
93
|
+
|
|
94
|
+
原因很简单:访问码更像临时通行证,不适合作为电脑设备的长期绑定身份。现在的流程改成“浏览器 session + 设备 token”,这样更清楚,也更容易审计。
|
|
95
|
+
|
|
96
|
+
## 安装完成后会发生什么
|
|
97
|
+
|
|
98
|
+
绑定成功后,安装器会启动本机 agent,并在本地打开一个状态面板。这个面板只监听 `127.0.0.1`,端口也是动态选择的,不会固定撞端口。
|
|
99
|
+
|
|
100
|
+
面板里会看到:
|
|
101
|
+
|
|
102
|
+
- 当前 npm 包版本
|
|
103
|
+
- agent 版本
|
|
104
|
+
- 服务端最新版本
|
|
105
|
+
- 是否有更新
|
|
106
|
+
- 当前账号和 deviceId
|
|
107
|
+
- 绑定状态
|
|
108
|
+
- relay 地址
|
|
109
|
+
- 最近心跳
|
|
110
|
+
- agent 运行状态
|
|
111
|
+
- Codex app-server 状态
|
|
112
|
+
- 是否能打开或重启 agent
|
|
113
|
+
|
|
114
|
+
这块面板的目的,是让不熟悉命令行的人也能看懂“现在装到哪一步了”。
|
|
115
|
+
|
|
116
|
+
## 技术工作原理:安装时的交互过程
|
|
117
|
+
|
|
118
|
+
下面按真实链路解释安装器、浏览器、relay、Windows bootstrap 和本机 agent 分别做什么。
|
|
119
|
+
|
|
120
|
+
### 1. CLI 解析环境和目标 server
|
|
121
|
+
|
|
122
|
+
用户运行 `npx -y codexpanel` 后,npm 会临时下载并执行 `codexpanel` 包里的 `bin/codexpanel.cjs`。
|
|
123
|
+
|
|
124
|
+
CLI 首先解析参数:
|
|
125
|
+
|
|
126
|
+
- 不传 `--server`:连接 `https://codexpanel.com`。
|
|
127
|
+
- `--server test` 或 `--server jd`:连接 `https://jd.6a.gs`。
|
|
128
|
+
- `--server local`:在本机动态找一个可用端口,生成 `http://127.0.0.1:<port>`,不会固定使用 `4871`。
|
|
129
|
+
- `--server https://example.com`:连接自部署 relay。
|
|
130
|
+
|
|
131
|
+
CLI 同时生成本机 profile,包括:
|
|
132
|
+
|
|
133
|
+
- `cliVersion`
|
|
134
|
+
- `serverLabel`
|
|
135
|
+
- `platform`
|
|
136
|
+
- `arch`
|
|
137
|
+
- `host`
|
|
138
|
+
- `computerUser`
|
|
139
|
+
- `userDomain`
|
|
140
|
+
- `deviceId`
|
|
141
|
+
- `deviceName`
|
|
142
|
+
- `workspace`
|
|
143
|
+
- `autoStart`
|
|
144
|
+
- `tunnelEnabled`
|
|
145
|
+
- `panelPortHint`
|
|
146
|
+
|
|
147
|
+
`deviceId` 来自本机 hostname、Windows domain、系统用户名和 user profile 路径的稳定 hash。它不是随机数,所以同一台电脑同一系统用户重复安装时会得到同一个 deviceId,方便识别换绑和 token 轮换。
|
|
148
|
+
|
|
149
|
+
### 2. 先下载和校验资源
|
|
150
|
+
|
|
151
|
+
CLI 会先请求:
|
|
152
|
+
|
|
153
|
+
```text
|
|
154
|
+
GET /agent/version
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
relay 返回 agent release manifest,里面包含:
|
|
158
|
+
|
|
159
|
+
- `agentVersion`
|
|
160
|
+
- `desktopAgent.path`
|
|
161
|
+
- `desktopAgent.sha256`
|
|
162
|
+
- `connector.path`
|
|
163
|
+
- `connector.sha256`
|
|
164
|
+
- `bootstrap.path`
|
|
165
|
+
- `bootstrap.sha256`
|
|
166
|
+
|
|
167
|
+
CLI 随后下载 manifest 指向的资源:
|
|
168
|
+
|
|
169
|
+
```text
|
|
170
|
+
GET /agent/desktop-agent.js
|
|
171
|
+
GET /agent/app-server-connector.js
|
|
172
|
+
GET /agent/bootstrap.ps1
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
每个资源下载后都会按 manifest 中的 `sha256` 校验。只有这些资源全部可下载、hash 正确,安装器才进入登录阶段。这样做的意义是:用户按 Enter 打开浏览器前,安装器已经确认服务端资源完整,后续失败更容易定位到登录、绑定或本机启动,而不是混在下载阶段。
|
|
176
|
+
|
|
177
|
+
### 3. 创建 setup flow
|
|
178
|
+
|
|
179
|
+
资源准备好后,CLI 调用:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
POST /api/desktop/setup/start
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
请求体里包含前面生成的本机 profile。relay 会创建一个短时 setup flow,并返回:
|
|
186
|
+
|
|
187
|
+
- `flowId`:公开流程 ID,用于浏览器页面和 CLI 轮询。
|
|
188
|
+
- `flowSecret`:只给 CLI 保存的流程 secret,用于安装器证明自己是这个 flow 的发起端。
|
|
189
|
+
- `oneTimeCode`:浏览器页面 URL 里的短码,用于防止别人只拿到 flowId 就审批。
|
|
190
|
+
- `loginUrl`:形如 `/desktop/setup?flowId=...&code=...`。
|
|
191
|
+
- `expiresAt`:flow 过期时间。
|
|
192
|
+
|
|
193
|
+
CLI 此时打印:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
资源已准备好。按 Enter 拉起浏览器登录并绑定这台电脑。
|
|
197
|
+
Resources are ready. Press Enter to open your browser and sign in.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
用户按 Enter 后,CLI 才打开浏览器。如果用户传了 `--no-browser`,CLI 不打开浏览器,只显示登录 URL 和一次性确认码,方便用户手动打开。
|
|
201
|
+
|
|
202
|
+
### 4. 浏览器登录和绑定审批
|
|
203
|
+
|
|
204
|
+
浏览器打开:
|
|
205
|
+
|
|
206
|
+
```text
|
|
207
|
+
GET /desktop/setup?flowId=...&code=...
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
页面会先调用:
|
|
211
|
+
|
|
212
|
+
```text
|
|
213
|
+
GET /api/auth/status
|
|
214
|
+
POST /api/desktop/setup/poll
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
如果用户未登录,页面显示登录/注册表单。登录使用:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
POST /api/auth/login
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
注册使用:
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
POST /api/auth/register
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
成功后 relay 设置 `codexpanel_session` cookie。浏览器后续审批靠 session cookie 鉴权。
|
|
230
|
+
|
|
231
|
+
页面展示的数据来自 setup flow 和当前登录用户:
|
|
232
|
+
|
|
233
|
+
- 电脑名
|
|
234
|
+
- 系统用户名
|
|
235
|
+
- `deviceId`
|
|
236
|
+
- 工作目录
|
|
237
|
+
- 当前账号
|
|
238
|
+
- 当前账号角色
|
|
239
|
+
- 设备额度
|
|
240
|
+
- 该 deviceId 是否已绑定
|
|
241
|
+
- 如果已绑定,当前绑定用户是谁
|
|
242
|
+
|
|
243
|
+
用户点击“绑定这台电脑”时,页面调用:
|
|
244
|
+
|
|
245
|
+
```text
|
|
246
|
+
POST /api/desktop/setup/approve
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
提交:
|
|
250
|
+
|
|
251
|
+
- `flowId`
|
|
252
|
+
- URL 中的 `code`
|
|
253
|
+
- `confirmRebind`
|
|
254
|
+
|
|
255
|
+
relay 会校验:
|
|
256
|
+
|
|
257
|
+
- 用户必须已登录。
|
|
258
|
+
- viewer 角色不能绑定设备。
|
|
259
|
+
- 禁用用户不能绑定设备。
|
|
260
|
+
- 当前账号设备额度必须足够。
|
|
261
|
+
- 同一 deviceId 如果已绑定到其他用户,且 `confirmRebind` 不是 `true`,返回 `device_rebind_required`,不自动抢绑。
|
|
262
|
+
|
|
263
|
+
审批通过后,relay 在内存中的 setup flow 写入:
|
|
264
|
+
|
|
265
|
+
- `setupToken`:给 Windows bootstrap 使用的一次性安装 token。
|
|
266
|
+
- `deviceToken`:给安装后的 agent 使用的长期设备 token 明文,只在这个 flow 内短暂存在。
|
|
267
|
+
- `deviceToken.hash`:后续会写入设备记录,服务端长期保存 hash,不保存明文。
|
|
268
|
+
- `approvedByUserId`
|
|
269
|
+
- `approvedByRole`
|
|
270
|
+
- `userId`
|
|
271
|
+
|
|
272
|
+
浏览器显示:
|
|
273
|
+
|
|
274
|
+
```text
|
|
275
|
+
绑定成功,可以关闭浏览器,或回到安装界面查看进度。
|
|
276
|
+
Binding succeeded. You can close this browser or return to the installer.
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### 5. 终端登录和一次性 token 登录
|
|
280
|
+
|
|
281
|
+
如果用户选择终端用户名密码:
|
|
282
|
+
|
|
283
|
+
```powershell
|
|
284
|
+
npx -y codexpanel login --terminal-login
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
CLI 会在终端询问用户名和密码,然后调用:
|
|
288
|
+
|
|
289
|
+
```text
|
|
290
|
+
POST /api/desktop/setup/login-token
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
请求体包含:
|
|
294
|
+
|
|
295
|
+
- `flowId`
|
|
296
|
+
- `flowSecret`
|
|
297
|
+
- `username`
|
|
298
|
+
- `password`
|
|
299
|
+
- 可选 `confirmRebind`
|
|
300
|
+
|
|
301
|
+
relay 使用和 Web 登录相同的用户体系校验密码、登录失败限流、用户状态和角色,然后复用 setup approve 逻辑完成审批。
|
|
302
|
+
|
|
303
|
+
如果用户选择一次性 token 登录:
|
|
304
|
+
|
|
305
|
+
1. 先打开 `/desktop/setup?flowId=...` 页面。
|
|
306
|
+
2. 在浏览器登录。
|
|
307
|
+
3. 点击“生成终端一次性 token”。
|
|
308
|
+
4. 页面调用:
|
|
309
|
+
|
|
310
|
+
```text
|
|
311
|
+
POST /api/desktop/setup/terminal-token
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
relay 校验当前浏览器 session、flowId 和 code,然后给这个 setup flow 写入短时 token hash。页面只显示 token 明文一次。
|
|
315
|
+
|
|
316
|
+
CLI 再运行:
|
|
317
|
+
|
|
318
|
+
```powershell
|
|
319
|
+
npx -y codexpanel login --token-login
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
并调用:
|
|
323
|
+
|
|
324
|
+
```text
|
|
325
|
+
POST /api/desktop/setup/login-token
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
请求体包含:
|
|
329
|
+
|
|
330
|
+
- `flowId`
|
|
331
|
+
- `flowSecret`
|
|
332
|
+
- `token`
|
|
333
|
+
- 可选 `confirmRebind`
|
|
334
|
+
|
|
335
|
+
relay 只在同一个 flow 内验证该 token,默认 5 分钟过期。审批成功后 token 立即失效。这个 token 不等于 device token,也不是长期登录凭据。
|
|
336
|
+
|
|
337
|
+
## 技术工作原理:bootstrap 安装阶段
|
|
338
|
+
|
|
339
|
+
CLI 轮询到 flow 被 approved 后,会请求:
|
|
340
|
+
|
|
341
|
+
```text
|
|
342
|
+
GET /agent/bootstrap.ps1?setupToken=...
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Windows bootstrap 用 `setupToken` 向 relay 换取当前 flow 的安装上下文。脚本里会嵌入本次安装需要的值:
|
|
346
|
+
|
|
347
|
+
- `RelayUrl`
|
|
348
|
+
- `UserId`
|
|
349
|
+
- `SetupToken`
|
|
350
|
+
- `DeviceToken`
|
|
351
|
+
- `PanelToken`
|
|
352
|
+
- `PanelPortHint`
|
|
353
|
+
- `DeviceId`
|
|
354
|
+
- `DeviceName`
|
|
355
|
+
- `Workspace`
|
|
356
|
+
- `AgentVersion`
|
|
357
|
+
- `TunnelEnabled`
|
|
358
|
+
- `TunnelLocalHost`
|
|
359
|
+
- `TunnelLocalPort`
|
|
360
|
+
|
|
361
|
+
bootstrap 在 Windows 本机做这些事:
|
|
362
|
+
|
|
363
|
+
1. 创建 `%LOCALAPPDATA%\CodexPanelAgent`。
|
|
364
|
+
2. 下载 `desktop-agent.js` 和 `app-server-connector.js` 到本机。
|
|
365
|
+
3. 查找 Node 和 Codex 可执行文件。
|
|
366
|
+
4. 动态选择本地状态面板端口。
|
|
367
|
+
5. 生成 `agent-runtime.json`。
|
|
368
|
+
6. 生成 start/stop/status/login/version/autostart/open-codex 等 PowerShell 辅助脚本。
|
|
369
|
+
7. 调用:
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
POST /api/desktop/setup/complete
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
8. 写入本地配置、启动 agent,并打开本地状态面板。
|
|
376
|
+
|
|
377
|
+
`agent-runtime.json` 是本机 agent 的主要数据来源,包含:
|
|
378
|
+
|
|
379
|
+
- relay 地址
|
|
380
|
+
- userId
|
|
381
|
+
- deviceId
|
|
382
|
+
- deviceName
|
|
383
|
+
- deviceToken 明文
|
|
384
|
+
- workspace
|
|
385
|
+
- Codex 路径
|
|
386
|
+
- app-server listen 地址
|
|
387
|
+
- 本地 panel host/port/token
|
|
388
|
+
- tunnel 配置
|
|
389
|
+
- helper 脚本路径
|
|
390
|
+
- 日志路径
|
|
391
|
+
|
|
392
|
+
这里的 deviceToken 只保存在用户本机配置文件中。服务端只保存 token hash。
|
|
393
|
+
|
|
394
|
+
## 技术工作原理:安装完成后的鉴权
|
|
395
|
+
|
|
396
|
+
安装完成后,agent 访问 relay 时使用:
|
|
397
|
+
|
|
398
|
+
```http
|
|
399
|
+
Authorization: Bearer <deviceToken>
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
relay 收到请求后会:
|
|
403
|
+
|
|
404
|
+
1. 从 Authorization header 中取出 bearer token。
|
|
405
|
+
2. 计算 sha256 hash。
|
|
406
|
+
3. 遍历设备记录,找到匹配的 `deviceTokenHash`。
|
|
407
|
+
4. 确认设备所属用户仍存在且状态 active。
|
|
408
|
+
5. 将请求视为该 device 的设备身份。
|
|
409
|
+
|
|
410
|
+
因此:
|
|
411
|
+
|
|
412
|
+
- 浏览器用户操作用 session cookie 鉴权。
|
|
413
|
+
- 安装过程用 flowSecret、code、setupToken 做短时握手。
|
|
414
|
+
- agent 长期运行用 deviceToken 鉴权。
|
|
415
|
+
- 本地状态面板用 panelToken 鉴权,不暴露 deviceToken。
|
|
416
|
+
|
|
417
|
+
## 技术工作原理:agent 启动后做什么
|
|
418
|
+
|
|
419
|
+
agent 启动入口是 `desktop-agent.js`。它先读取 `agent-runtime.json`,然后初始化几个循环和服务。
|
|
420
|
+
|
|
421
|
+
启动时立即执行:
|
|
422
|
+
|
|
423
|
+
1. `announceOnline("boot")`
|
|
424
|
+
2. `checkForAgentUpdate("boot")`
|
|
425
|
+
3. `startLocalPanel()`
|
|
426
|
+
4. 如果开启 SSE,执行 `connectSse()`
|
|
427
|
+
5. `startTunnelLoop()`
|
|
428
|
+
6. `startPollLoop()`
|
|
429
|
+
|
|
430
|
+
### 上线注册
|
|
431
|
+
|
|
432
|
+
agent 调用:
|
|
433
|
+
|
|
434
|
+
```text
|
|
435
|
+
POST /api/device/register
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
请求体来自 `onlinePayload()`,包含:
|
|
439
|
+
|
|
440
|
+
- `agentId`
|
|
441
|
+
- `userId`
|
|
442
|
+
- `deviceId`
|
|
443
|
+
- `deviceName`
|
|
444
|
+
- host、系统用户、domain、platform
|
|
445
|
+
- agentVersion
|
|
446
|
+
- desktopStatus
|
|
447
|
+
- workspace
|
|
448
|
+
- codexPath
|
|
449
|
+
- sandbox 和 approvalPolicy
|
|
450
|
+
- mode
|
|
451
|
+
- appServerListen
|
|
452
|
+
- tunnel 状态
|
|
453
|
+
- panelUrl 和 panelPort
|
|
454
|
+
- npmPackageVersion
|
|
455
|
+
- capabilities
|
|
456
|
+
|
|
457
|
+
capabilities 会告诉 relay 这台设备能做什么,例如:
|
|
458
|
+
|
|
459
|
+
- `codex-cli`
|
|
460
|
+
- `app-server-proxy`
|
|
461
|
+
- `codex-desktop`
|
|
462
|
+
- `threads`
|
|
463
|
+
- `turns`
|
|
464
|
+
- `approvals`
|
|
465
|
+
- `cloudflare-tunnel-client`
|
|
466
|
+
- `wss-direct-rpc`
|
|
467
|
+
|
|
468
|
+
注册成功后,agent 还会发一个 `agent.online` event 到:
|
|
469
|
+
|
|
470
|
+
```text
|
|
471
|
+
POST /api/events
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### 控制事件轮询和心跳
|
|
475
|
+
|
|
476
|
+
agent 的主循环是 `startPollLoop()`。它每 10 秒执行一次 `pollOnce()`。
|
|
477
|
+
|
|
478
|
+
每次 poll 调用:
|
|
479
|
+
|
|
480
|
+
```text
|
|
481
|
+
POST /api/agent/pull-control
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
请求体包含:
|
|
485
|
+
|
|
486
|
+
- `sinceId`:上次处理到的事件 ID。
|
|
487
|
+
- `payload`:当前 `onlinePayload()`。
|
|
488
|
+
|
|
489
|
+
relay 返回:
|
|
490
|
+
|
|
491
|
+
- 当前设备状态。
|
|
492
|
+
- `events`:发给 agent 的控制事件。
|
|
493
|
+
- `nextEventId`:下一次轮询游标。
|
|
494
|
+
- `agentRelease`:服务端最新 agent release 信息。
|
|
495
|
+
- agent client 数等运行信息。
|
|
496
|
+
|
|
497
|
+
agent 收到后会:
|
|
498
|
+
|
|
499
|
+
1. 用 `agentRelease` 检查是否需要自更新。
|
|
500
|
+
2. 对每个 event 调用 `handleBridgeEvent()`。
|
|
501
|
+
3. 更新 `lastPolledEventId`。
|
|
502
|
+
4. 持久化本地 agent state。
|
|
503
|
+
5. 更新本地状态面板里的 `lastHeartbeatAt`。
|
|
504
|
+
|
|
505
|
+
如果请求失败,agent 会记录 `lastHeartbeatError`,10 秒后继续下一轮。失败不会让 agent 退出,这样断网、服务端重启、DNS 抖动后都能自动恢复。
|
|
506
|
+
|
|
507
|
+
### 事件如何被处理
|
|
508
|
+
|
|
509
|
+
`handleBridgeEvent()` 会先去重:
|
|
510
|
+
|
|
511
|
+
- 已处理过的 event id 不重复执行。
|
|
512
|
+
- 只处理 target 为 `agent` 的事件。
|
|
513
|
+
- 如果事件指定了 `deviceId`,必须匹配本机 `DEVICE_ID` 或 `AGENT_ID`。
|
|
514
|
+
|
|
515
|
+
当前主要事件类型:
|
|
516
|
+
|
|
517
|
+
- `agent.app.request`:代理到 Codex app-server,例如 status、start、connect、listThreads、readThread、startThread、resumeThread、startTurn、interrupt、resolveServerRequest。
|
|
518
|
+
- `task.start`:启动 Codex CLI task,并先发 approval request。
|
|
519
|
+
- `approval.resolve`:用户批准或拒绝后继续或取消任务。
|
|
520
|
+
- `run.cancel`:取消当前运行中的 Codex CLI。
|
|
521
|
+
|
|
522
|
+
CLI task 启动后,agent 会把 stdout/stderr 和 Codex JSON event 转成 relay event:
|
|
523
|
+
|
|
524
|
+
- `codex.run.started`
|
|
525
|
+
- `codex.event`
|
|
526
|
+
- `terminal.chunk`
|
|
527
|
+
- `run.completed`
|
|
528
|
+
- `agent.error`
|
|
529
|
+
|
|
530
|
+
### SSE 辅助通道
|
|
531
|
+
|
|
532
|
+
如果启用 SSE,agent 还会连接:
|
|
533
|
+
|
|
534
|
+
```text
|
|
535
|
+
GET /events?role=agent&client=<deviceId>
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
SSE 连接成功后,relay 可以直接推事件给 agent。SSE 失败或断开时:
|
|
539
|
+
|
|
540
|
+
- 非 200 状态:2 秒后重连。
|
|
541
|
+
- 连接 end:1.5 秒后重连。
|
|
542
|
+
- 网络 error:1.5 秒后重连。
|
|
543
|
+
|
|
544
|
+
即使 SSE 不可用,10 秒一次的 `/api/agent/pull-control` 轮询仍然是兜底控制通道。
|
|
545
|
+
|
|
546
|
+
### WSS/直连 tunnel 循环
|
|
547
|
+
|
|
548
|
+
如果 `tunnelEnabled` 为 true,agent 会运行 `startTunnelLoop()`。
|
|
549
|
+
|
|
550
|
+
循环逻辑:
|
|
551
|
+
|
|
552
|
+
1. 确保本机 tunnel local server 已启动。
|
|
553
|
+
2. 如果没有有效 lease 或 cloudflared 不在运行,调用:
|
|
554
|
+
|
|
555
|
+
```text
|
|
556
|
+
POST /api/domain-lease/claim
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
3. 如果已有 lease,调用:
|
|
560
|
+
|
|
561
|
+
```text
|
|
562
|
+
POST /api/domain-lease/heartbeat
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
4. relay 返回更新后的 lease,agent 更新本地 tunnel 状态。
|
|
566
|
+
|
|
567
|
+
lease 中包含 entry URL、data host、WSS URL、cloudflared token 等直连所需信息。agent 会启动 cloudflared,把外部 WSS 连接导到本机 tunnel local service。
|
|
568
|
+
|
|
569
|
+
如果 heartbeat 失败,并且错误类似:
|
|
570
|
+
|
|
571
|
+
- reclaim required
|
|
572
|
+
- unknown domain lease
|
|
573
|
+
- domain lease is released
|
|
574
|
+
- hostname is outdated
|
|
575
|
+
|
|
576
|
+
agent 会停止 cloudflared、清空 lease,并在 1 秒后重新 claim。其他失败则按 `TUNNEL_HEARTBEAT_MS` 继续重试。
|
|
577
|
+
|
|
578
|
+
### 自更新循环
|
|
579
|
+
|
|
580
|
+
agent 在启动和每次 control poll 时检查服务端 release。
|
|
581
|
+
|
|
582
|
+
数据来源:
|
|
583
|
+
|
|
584
|
+
```text
|
|
585
|
+
GET /agent/version
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
或 `/api/agent/pull-control` 返回的 `agentRelease`。
|
|
589
|
+
|
|
590
|
+
如果 `agentVersion` 与本地不同,agent 会下载:
|
|
591
|
+
|
|
592
|
+
```text
|
|
593
|
+
GET /agent/desktop-agent.js
|
|
594
|
+
GET /agent/app-server-connector.js
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
下载后校验 sha256,替换本地文件,发送 `agent.update.started`,停止 cloudflared,启动新 agent 进程,再退出旧进程。如果更新失败,发送 `agent.update.failed`,保留旧 agent 继续运行。
|
|
598
|
+
|
|
599
|
+
### 本地状态面板
|
|
600
|
+
|
|
601
|
+
本地状态面板只监听:
|
|
602
|
+
|
|
603
|
+
```text
|
|
604
|
+
http://127.0.0.1:<panelPort>/
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
访问时需要 URL query 里的 `panelToken`,或 header `x-codexpanel-panel-token`。这个 token 是本机随机生成的 local panel token,不是 deviceToken。
|
|
608
|
+
|
|
609
|
+
面板接口:
|
|
610
|
+
|
|
611
|
+
```text
|
|
612
|
+
GET /api/status
|
|
613
|
+
POST /api/action
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
`/api/status` 返回:
|
|
617
|
+
|
|
618
|
+
- npm 包版本
|
|
619
|
+
- agent 版本
|
|
620
|
+
- 服务端最新 agent 版本
|
|
621
|
+
- 是否有更新和更新命令
|
|
622
|
+
- relay URL
|
|
623
|
+
- userId、deviceId、deviceName
|
|
624
|
+
- 绑定状态
|
|
625
|
+
- agent PID
|
|
626
|
+
- 安装目录
|
|
627
|
+
- workspace
|
|
628
|
+
- 最近心跳和最近错误
|
|
629
|
+
- Codex app-server 状态
|
|
630
|
+
- WSS/tunnel 状态
|
|
631
|
+
- helper 脚本路径
|
|
632
|
+
- 下一步建议
|
|
633
|
+
|
|
634
|
+
`/api/action` 支持:
|
|
635
|
+
|
|
636
|
+
- `start`
|
|
637
|
+
- `stop`
|
|
638
|
+
- `restart`
|
|
639
|
+
- `open-codex`
|
|
640
|
+
- `login`
|
|
641
|
+
|
|
642
|
+
这些动作不会直接执行任意命令,只调用 bootstrap 生成的固定 helper 脚本。
|
|
643
|
+
|
|
644
|
+
## 数据从哪里来
|
|
645
|
+
|
|
646
|
+
安装和运行过程里的主要数据来源如下:
|
|
647
|
+
|
|
648
|
+
| 数据 | 来源 | 用途 |
|
|
649
|
+
| --- | --- | --- |
|
|
650
|
+
| npm 包版本 | `package.json` 和 CLI 内置 `VERSION` | 显示安装器版本、面板更新提示 |
|
|
651
|
+
| server 地址 | CLI 参数 `--server` 或默认生产域 | 决定所有 relay API 请求目标 |
|
|
652
|
+
| deviceId | 本机 hostname/domain/user/profile hash | 识别同一台电脑和换绑 |
|
|
653
|
+
| deviceName | CLI 参数或 hostname + 系统用户名 | 展示给用户确认 |
|
|
654
|
+
| flowId/flowSecret/code | `/api/desktop/setup/start` | 连接 CLI、浏览器和 relay 的短时安装流 |
|
|
655
|
+
| session cookie | `/api/auth/login` 或 `/api/auth/register` | 浏览器用户身份 |
|
|
656
|
+
| setupToken | `/api/desktop/setup/approve` 成功后生成 | Windows bootstrap 下载安装上下文 |
|
|
657
|
+
| deviceToken | setup flow 审批后生成 | agent 长期鉴权,明文只在本机保存 |
|
|
658
|
+
| deviceTokenHash | relay 计算并保存 | 服务端验证 deviceToken |
|
|
659
|
+
| panelToken | bootstrap 本机生成 | 本地状态面板鉴权 |
|
|
660
|
+
| agentRelease | `/agent/version` 或 pull-control 返回 | agent 自更新 |
|
|
661
|
+
| heartbeat 状态 | agent poll 和 tunnel heartbeat | 面板展示、服务端在线状态 |
|
|
662
|
+
|
|
663
|
+
## 重试和失败提示
|
|
664
|
+
|
|
665
|
+
安装器和 agent 的重试策略分层处理:
|
|
666
|
+
|
|
667
|
+
- 资源下载失败:CLI 直接显示下载 URL、HTTP 状态或 hash mismatch,让用户知道是网络、证书还是服务端资源问题。
|
|
668
|
+
- 浏览器未打开:CLI 打印 login URL,用户可手动打开。
|
|
669
|
+
- setup flow 过期:CLI 提示重新运行 `npx -y codexpanel`。
|
|
670
|
+
- 设备已绑定其他用户:relay 返回 `device_rebind_required`,浏览器或终端要求用户明确确认,不自动换绑。
|
|
671
|
+
- PowerShell bootstrap 失败:终端显示安装目录、日志路径和失败原因。
|
|
672
|
+
- agent 心跳失败:agent 记录 `lastHeartbeatError`,面板展示错误和建议,下一轮继续重试。
|
|
673
|
+
- SSE 断开:1.5 到 2 秒后重连。
|
|
674
|
+
- tunnel lease 失效:停止 cloudflared 并重新 claim。
|
|
675
|
+
- 自更新失败:记录事件并继续运行旧版本。
|
|
676
|
+
|
|
677
|
+
## 版本和更新怎么看
|
|
678
|
+
|
|
679
|
+
面板会显示当前 npm 包版本,也会显示服务端最新版本和是否有更新。
|
|
680
|
+
|
|
681
|
+
如果有更新,面板会直接告诉你状态。安装器和面板都不会故弄玄虚,只会告诉你当前版本、目标版本和下一步动作。
|
|
682
|
+
|
|
683
|
+
## 常见失败原因
|
|
684
|
+
|
|
685
|
+
- 浏览器没弹出来:通常是系统默认浏览器没有响应,手动打开终端里打印的登录链接即可。
|
|
686
|
+
- 登录失败:检查用户名、密码,或者重新申请一次性 token。
|
|
687
|
+
- 设备额度不足:说明当前账号已经达到可绑定设备上限,需要换绑、释放设备或联系管理员。
|
|
688
|
+
- 同一 deviceId 已被其他账号绑定:页面会让你自己决定是否换绑,不会自动抢绑。
|
|
689
|
+
- 本地面板打不开:通常是端口冲突,安装器会自动换端口;如果仍失败,终端会提示具体原因。
|
|
690
|
+
- 安装资源下载失败:终端会显示 URL 和失败原因,方便你判断是网络、证书还是服务端问题。
|
|
691
|
+
|
|
692
|
+
## 一句话记住它
|
|
693
|
+
|
|
694
|
+
`npx -y codexpanel` 不是“把一个命令偷偷塞进电脑”,而是“先备好资源,再请你登录,确认这台电脑归谁,再把本机 agent 和状态面板装起来”。
|