codexpanel 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 +24 -11
- package/README.zh.md +22 -9
- package/bin/codexpanel.cjs +16 -40
- package/docs/desktop-npx-install-flow.md +82 -200
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# codexpanel
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
[涓枃鐗堟湰](README.zh.md)
|
|
4
4
|
|
|
5
5
|
codexpanel is a mobile control plane for managing local Codex work from a phone. It provides a Next.js web console, relay service, desktop agent, PostgreSQL-backed durable storage, shared protocol package, and reusable UI package in one monorepo.
|
|
6
6
|
|
|
@@ -10,10 +10,10 @@ codexpanel is a mobile control plane for managing local Codex work from a phone.
|
|
|
10
10
|
- `apps/relay`: HTTP/SSE/WSS relay service, PostgreSQL/SQLite storage, and device onboarding scripts
|
|
11
11
|
- `apps/agent`: desktop-side agent entrypoint
|
|
12
12
|
- `packages/protocol`: shared schemas and transport helpers
|
|
13
|
-
- `packages/codex-app-server`: Codex app-server
|
|
13
|
+
- `packages/codex-app-server`: Codex app-server schema and method snapshots
|
|
14
14
|
- `packages/ui`: shared React UI primitives
|
|
15
15
|
|
|
16
|
-
Human reference files, local databases, deployment bundles, server handoff notes, and
|
|
16
|
+
Human reference files, local databases, deployment bundles, server handoff notes, and retired prototype artifacts live outside the production tree in `files_for_human/` and are ignored by Git.
|
|
17
17
|
|
|
18
18
|
## Commands
|
|
19
19
|
|
|
@@ -44,7 +44,7 @@ npx -y codexpanel --server local
|
|
|
44
44
|
npx -y codexpanel --server https://example.com
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
`--server test` uses `https://jd.6a.gs`. `--server local` dynamically finds a free local relay port instead of assuming `4871`. A full URL connects to a self-hosted relay. Useful options include `--device-name`, `--workspace`, `--
|
|
47
|
+
`--server test` uses `https://jd.6a.gs`. `--server local` dynamically finds a free local relay port instead of assuming `4871`. A full URL connects to a self-hosted relay. Useful options include `--device-name`, `--workspace`, `--token-login`, `--no-browser`, `--no-autostart`, `--dry-run`, and `--print-command`. The retired `--legacy-tunnel` flag is rejected because direct tunnel access is not part of the final control path.
|
|
48
48
|
|
|
49
49
|
For terminal token login, open the printed `/desktop/setup?flowId=...` link, sign in, click the one-time terminal token button, then run `npx -y codexpanel login --token-login` and paste the token. The token is scoped to the current setup flow and expires quickly.
|
|
50
50
|
|
|
@@ -58,23 +58,36 @@ Web/admin login uses a session cookie. Configure at least one of these before ex
|
|
|
58
58
|
|
|
59
59
|
```env
|
|
60
60
|
CODEXPANEL_ADMIN_USERNAME=admin
|
|
61
|
-
|
|
61
|
+
CODEXPANEL_ADMIN_USER_ID=admin
|
|
62
|
+
CODEXPANEL_ADMIN_PASSWORD=Admin778899
|
|
62
63
|
CODEXPANEL_SESSION_SECRET=long_random_secret
|
|
63
64
|
CODEXPANEL_LOGIN_MAX_ATTEMPTS=8
|
|
65
|
+
CODEXPANEL_LOGIN_IP_MAX_ATTEMPTS=24
|
|
64
66
|
CODEXPANEL_LOGIN_WINDOW_MS=900000
|
|
65
67
|
CODEXPANEL_LOGIN_LOCKOUT_MS=900000
|
|
66
|
-
|
|
68
|
+
CODEXPANEL_LOGIN_MAX_BUCKETS=5000
|
|
69
|
+
CODEXPANEL_EMAIL_CODE_IP_MAX_ATTEMPTS=10
|
|
70
|
+
CODEXPANEL_EMAIL_CODE_IP_WINDOW_MS=900000
|
|
71
|
+
CODEXPANEL_EMAIL_CODE_MAX_VERIFY_ATTEMPTS=8
|
|
72
|
+
CODEXPANEL_EMAIL_CODE_MAX_CODES=5000
|
|
73
|
+
CODEXPANEL_DESKTOP_SETUP_MAX_PENDING=1000
|
|
74
|
+
CODEXPANEL_DESKTOP_SETUP_START_WINDOW_MS=900000
|
|
75
|
+
CODEXPANEL_DESKTOP_SETUP_START_MAX_ATTEMPTS=20
|
|
76
|
+
CODEXPANEL_DESKTOP_SETUP_START_MAX_IP_BUCKETS=5000
|
|
77
|
+
CODEXPANEL_REGISTRATION_ENABLED=false
|
|
67
78
|
```
|
|
68
79
|
|
|
69
|
-
Desktop setup uses browser login binding or terminal
|
|
80
|
+
Desktop setup uses browser login binding or a one-time terminal token. `CODEXPANEL_ADMIN_*` only bootstraps the first admin; additional users must be created through registration or admin operations.
|
|
70
81
|
|
|
71
|
-
`CODEXPANEL_REGISTRATION_ENABLED=true` opens self-service web registration
|
|
82
|
+
`CODEXPANEL_REGISTRATION_ENABLED=true` opens self-service web registration. If the variable is omitted, development defaults to open and production defaults to closed; production onboarding must opt in explicitly. New users are stored in the existing relay users table with PBKDF2 password hashes. Permissions are assigned through IAM permission groups, not account roles.
|
|
83
|
+
|
|
84
|
+
Desktop setup start requests are also rate-limited by source IP and capped by pending flow count. Tune `CODEXPANEL_DESKTOP_SETUP_*` only if installer traffic legitimately exceeds the defaults.
|
|
72
85
|
|
|
73
86
|
## First-batch user operations
|
|
74
87
|
|
|
75
|
-
Use `/admin
|
|
88
|
+
Use `/admin/` with an admin session for first-batch testing:
|
|
76
89
|
|
|
77
|
-
- Enable/disable users,
|
|
90
|
+
- Enable/disable users, assign permission groups, and reset passwords. These actions invalidate existing local-user sessions.
|
|
78
91
|
- Recharge, gift, or deduct balance through the wallet adjustment action. Every change requires an idempotency key and is recorded in the user wallet ledger; do not edit `balance` directly.
|
|
79
92
|
- Grant, renew, or revoke monthly cards. Device quota is derived server-side from the user entitlement.
|
|
80
93
|
- Bind devices to users or disable devices from the admin device table. Ordinary users cannot operate another user's device, and disabled devices are blocked for non-admin operation.
|
|
@@ -87,7 +100,7 @@ Rollback for a test user is done by disabling the user, revoking the monthly car
|
|
|
87
100
|
- `/console/`: mobile control console and PWA start URL.
|
|
88
101
|
- `/desktop/setup`: browser sign-in and desktop device binding flow.
|
|
89
102
|
- `/device.html`: migration page that points users to `npx -y codexpanel`.
|
|
90
|
-
- `/admin
|
|
103
|
+
- `/admin/` and `/project.html`: admin and product documentation pages.
|
|
91
104
|
|
|
92
105
|
## Storage and deployment
|
|
93
106
|
|
package/README.zh.md
CHANGED
|
@@ -10,10 +10,10 @@ codexpanel 是一个面向 Codex 本地工作的移动控制平面。它把手
|
|
|
10
10
|
- `apps/relay`:HTTP/SSE/WSS relay 服务、PostgreSQL/SQLite 存储、设备接入脚本。
|
|
11
11
|
- `apps/agent`:电脑端 agent 入口。
|
|
12
12
|
- `packages/protocol`:共享 schema 和传输 helper。
|
|
13
|
-
- `packages/codex-app-server`:Codex app-server
|
|
13
|
+
- `packages/codex-app-server`:Codex app-server schema 与 method 快照。
|
|
14
14
|
- `packages/ui`:共享 React UI 基础组件。
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
人工参考文件、本地数据库、部署包、服务器交接说明和已退役原型产物放在 `files_for_human/`,该目录不属于生产树并被 Git 忽略。
|
|
17
17
|
|
|
18
18
|
## 常用命令
|
|
19
19
|
|
|
@@ -44,7 +44,7 @@ npx -y codexpanel --server local
|
|
|
44
44
|
npx -y codexpanel --server https://example.com
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
`--server test` 使用 `https://jd.6a.gs`。`--server local` 会动态寻找可用本地端口,不再假定 `4871`。完整 URL 会连接自部署 relay。常用参数包括 `--device-name`、`--workspace`、`--
|
|
47
|
+
`--server test` 使用 `https://jd.6a.gs`。`--server local` 会动态寻找可用本地端口,不再假定 `4871`。完整 URL 会连接自部署 relay。常用参数包括 `--device-name`、`--workspace`、`--token-login`、`--no-browser`、`--no-autostart`、`--dry-run` 和 `--print-command`。已退役的 `--legacy-tunnel` 会被拒绝,因为 direct tunnel 不属于最终控制路径。
|
|
48
48
|
|
|
49
49
|
终端 token 登录的使用方式是:打开安装器打印的 `/desktop/setup?flowId=...` 链接,在浏览器登录后点击一次性终端 token 按钮,再运行 `npx -y codexpanel login --token-login` 并粘贴 token。该 token 只绑定当前安装流,过期很快。
|
|
50
50
|
|
|
@@ -58,21 +58,34 @@ Web/admin 登录使用 session cookie。公网暴露 relay 前至少配置以下
|
|
|
58
58
|
|
|
59
59
|
```env
|
|
60
60
|
CODEXPANEL_ADMIN_USERNAME=admin
|
|
61
|
-
|
|
61
|
+
CODEXPANEL_ADMIN_USER_ID=admin
|
|
62
|
+
CODEXPANEL_ADMIN_PASSWORD=Admin778899
|
|
62
63
|
CODEXPANEL_SESSION_SECRET=long_random_secret
|
|
63
64
|
CODEXPANEL_LOGIN_MAX_ATTEMPTS=8
|
|
65
|
+
CODEXPANEL_LOGIN_IP_MAX_ATTEMPTS=24
|
|
64
66
|
CODEXPANEL_LOGIN_WINDOW_MS=900000
|
|
65
67
|
CODEXPANEL_LOGIN_LOCKOUT_MS=900000
|
|
66
|
-
|
|
68
|
+
CODEXPANEL_LOGIN_MAX_BUCKETS=5000
|
|
69
|
+
CODEXPANEL_EMAIL_CODE_IP_MAX_ATTEMPTS=10
|
|
70
|
+
CODEXPANEL_EMAIL_CODE_IP_WINDOW_MS=900000
|
|
71
|
+
CODEXPANEL_EMAIL_CODE_MAX_VERIFY_ATTEMPTS=8
|
|
72
|
+
CODEXPANEL_EMAIL_CODE_MAX_CODES=5000
|
|
73
|
+
CODEXPANEL_DESKTOP_SETUP_MAX_PENDING=1000
|
|
74
|
+
CODEXPANEL_DESKTOP_SETUP_START_WINDOW_MS=900000
|
|
75
|
+
CODEXPANEL_DESKTOP_SETUP_START_MAX_ATTEMPTS=20
|
|
76
|
+
CODEXPANEL_DESKTOP_SETUP_START_MAX_IP_BUCKETS=5000
|
|
77
|
+
CODEXPANEL_REGISTRATION_ENABLED=false
|
|
67
78
|
```
|
|
68
79
|
|
|
69
|
-
电脑端 setup
|
|
80
|
+
电脑端 setup 支持浏览器登录绑定和终端一次性 token 登录。`CODEXPANEL_ADMIN_*` 只用于启动首个管理员;其他用户必须通过注册或后台运营创建。
|
|
70
81
|
|
|
71
|
-
`CODEXPANEL_REGISTRATION_ENABLED=true`
|
|
82
|
+
`CODEXPANEL_REGISTRATION_ENABLED=true` 会打开自助注册。未配置该变量时,开发环境默认开放、生产环境默认关闭;生产环境如需开放注册必须显式设置为 `true`。新用户写入 relay 的 users 表,密码使用 PBKDF2 hash;后台权限只通过 IAM 权限组和 permission scopes 分配,不再使用账号角色。
|
|
83
|
+
|
|
84
|
+
电脑端 setup 启动请求会按来源 IP 限流,并受 pending flow 总量限制。只有安装流量确实超过默认值时,才需要调整 `CODEXPANEL_DESKTOP_SETUP_*`。
|
|
72
85
|
|
|
73
86
|
## 第一批用户运营
|
|
74
87
|
|
|
75
|
-
使用 admin session 打开 `/admin
|
|
88
|
+
使用 admin session 打开 `/admin/` 进行第一批测试:
|
|
76
89
|
|
|
77
90
|
- 启用/禁用用户、调整角色、重置密码。这些操作会让对应本地用户旧 session 失效。
|
|
78
91
|
- 通过钱包调整操作充值、赠送或扣减余额。每次变更都需要幂等键,并写入钱包流水;不要直接改 `balance`。
|
|
@@ -87,7 +100,7 @@ CODEXPANEL_REGISTRATION_ENABLED=true
|
|
|
87
100
|
- `/console/`:手机控制台和 PWA start URL。
|
|
88
101
|
- `/desktop/setup`:浏览器登录和电脑设备绑定流程。
|
|
89
102
|
- `/device.html`:迁移页,引导用户改用 `npx -y codexpanel`。
|
|
90
|
-
- `/admin
|
|
103
|
+
- `/admin/` 和 `/project.html`:管理后台和产品文档页。
|
|
91
104
|
|
|
92
105
|
## 存储与部署
|
|
93
106
|
|
package/bin/codexpanel.cjs
CHANGED
|
@@ -11,7 +11,7 @@ const crypto = require("crypto");
|
|
|
11
11
|
const readline = require("readline");
|
|
12
12
|
const { spawn, spawnSync } = require("child_process");
|
|
13
13
|
|
|
14
|
-
const VERSION = "0.1.
|
|
14
|
+
const VERSION = "0.1.9";
|
|
15
15
|
const PROD_URL = "https://codexpanel.com";
|
|
16
16
|
const TEST_URL = "https://jd.6a.gs";
|
|
17
17
|
const LOCAL_HOST = "127.0.0.1";
|
|
@@ -31,11 +31,9 @@ Options:
|
|
|
31
31
|
--server <name|url> production/prod, test/jd, local, or a relay URL. Defaults to production
|
|
32
32
|
--workspace, --cwd <path> Default workspace for Codex Desktop/app-server
|
|
33
33
|
--device-name, --name <name> Device display name shown in CodexPanel
|
|
34
|
-
--terminal-login Ask for username and password in this terminal
|
|
35
34
|
--token-login Ask for a one-time terminal token from the setup page
|
|
36
35
|
--no-browser Do not open a browser; print the login URL instead
|
|
37
36
|
--no-autostart Do not enable Windows login autostart
|
|
38
|
-
--no-tunnel Disable Cloudflare WSS direct tunnel
|
|
39
37
|
--dry-run Print resolved installer details without installing
|
|
40
38
|
--print-command Print the equivalent PowerShell bootstrap command after login approval
|
|
41
39
|
-h, --help Show help
|
|
@@ -56,11 +54,9 @@ function parseArgs(argv) {
|
|
|
56
54
|
server: process.env.CODEXPANEL_SERVER || "production",
|
|
57
55
|
workspace: process.env.CODEXPANEL_WORKSPACE || "",
|
|
58
56
|
deviceName: process.env.CODEXPANEL_DEVICE_NAME || "",
|
|
59
|
-
terminalLogin: false,
|
|
60
57
|
tokenLogin: false,
|
|
61
58
|
browser: true,
|
|
62
59
|
autoStart: true,
|
|
63
|
-
tunnelEnabled: true,
|
|
64
60
|
dryRun: false,
|
|
65
61
|
printCommand: false,
|
|
66
62
|
};
|
|
@@ -80,17 +76,17 @@ function parseArgs(argv) {
|
|
|
80
76
|
else if (arg === "--server") options.server = readValue(arg);
|
|
81
77
|
else if (arg === "--workspace" || arg === "--cwd") options.workspace = readValue(arg);
|
|
82
78
|
else if (arg === "--device-name" || arg === "--name") options.deviceName = readValue(arg);
|
|
83
|
-
else if (arg === "--terminal-login")
|
|
79
|
+
else if (arg === "--terminal-login") throw new Error("--terminal-login has been retired. Use browser sign-in or --token-login.");
|
|
84
80
|
else if (arg === "--token-login") options.tokenLogin = true;
|
|
85
81
|
else if (arg === "--no-browser") options.browser = false;
|
|
86
82
|
else if (arg === "--no-autostart") options.autoStart = false;
|
|
87
|
-
else if (arg === "--
|
|
83
|
+
else if (arg === "--legacy-tunnel") throw new Error("--legacy-tunnel has been retired. Device traffic uses the server WSS gateway.");
|
|
84
|
+
else if (arg === "--no-tunnel") throw new Error("--no-tunnel has been retired. Device traffic uses the server WSS gateway.");
|
|
88
85
|
else if (arg === "--dry-run") options.dryRun = true;
|
|
89
86
|
else if (arg === "--print-command") options.printCommand = true; else {
|
|
90
87
|
throw new Error(`Unknown option: ${arg}`);
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
|
-
if (options.terminalLogin && options.tokenLogin) throw new Error("Choose only one of --terminal-login or --token-login.");
|
|
94
90
|
return options;
|
|
95
91
|
}
|
|
96
92
|
|
|
@@ -164,7 +160,6 @@ function computerProfile(options, resolvedServer) {
|
|
|
164
160
|
deviceName: options.deviceName || `${os.hostname()} / ${defaultUsername()} Codex Desktop Agent`,
|
|
165
161
|
workspace: options.workspace || process.env.USERPROFILE || process.env.HOME || process.cwd(),
|
|
166
162
|
autoStart: options.autoStart,
|
|
167
|
-
tunnelEnabled: options.tunnelEnabled,
|
|
168
163
|
panelPortHint: DEFAULT_PANEL_PORT_START,
|
|
169
164
|
};
|
|
170
165
|
}
|
|
@@ -369,7 +364,12 @@ async function preloadResources(relayUrl) {
|
|
|
369
364
|
let manifest = {};
|
|
370
365
|
try { manifest = JSON.parse(manifestText); }
|
|
371
366
|
catch { throw new Error("Server returned an invalid agent manifest."); }
|
|
372
|
-
const resources = [
|
|
367
|
+
const resources = [
|
|
368
|
+
manifest.hostRuntime,
|
|
369
|
+
manifest.hostRuntimeSchema,
|
|
370
|
+
...(Array.isArray(manifest.hostRuntimeFiles) ? manifest.hostRuntimeFiles : []),
|
|
371
|
+
manifest.bootstrap,
|
|
372
|
+
].filter(Boolean);
|
|
373
373
|
for (const item of resources) {
|
|
374
374
|
const resourceUrl = new URL(item.path, relayUrl).toString();
|
|
375
375
|
console.log(`CodexPanel: 预下载资源 / Downloading ${item.fileName || item.path}`);
|
|
@@ -409,37 +409,18 @@ async function pollSetup(relayUrl, flowId, flowSecret) {
|
|
|
409
409
|
throw new Error("Setup login timed out after 10 minutes.");
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
-
async function terminalLogin(relayUrl, flowId, flowSecret) {
|
|
413
|
-
const rl = createInterface();
|
|
414
|
-
try {
|
|
415
|
-
const username = (await question(rl, "CodexPanel username: ")).trim();
|
|
416
|
-
const password = await question(rl, "CodexPanel password: ", { silent: true });
|
|
417
|
-
try {
|
|
418
|
-
return await requestJson("POST", relayUrl, "/api/desktop/setup/login-token", { flowId, flowSecret, username, password }, {}, 30000);
|
|
419
|
-
} catch (error) {
|
|
420
|
-
if (error.data?.code !== "device_rebind_required") throw error;
|
|
421
|
-
console.log("这台电脑已绑定到其他账号,不会自动切换绑定。");
|
|
422
|
-
const answer = (await question(rl, "Type YES to rebind this computer to the current account / 输入 YES 切换绑定: ")).trim();
|
|
423
|
-
if (answer !== "YES") throw new Error("Setup kept the existing binding. Run npx -y codexpanel again if you want to switch later.");
|
|
424
|
-
return await requestJson("POST", relayUrl, "/api/desktop/setup/login-token", { flowId, flowSecret, username, password, confirmRebind: true }, {}, 30000);
|
|
425
|
-
}
|
|
426
|
-
} finally {
|
|
427
|
-
rl.close();
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
412
|
async function tokenLogin(relayUrl, flowId, flowSecret) {
|
|
432
413
|
const rl = createInterface();
|
|
433
414
|
try {
|
|
434
415
|
const token = (await question(rl, "One-time CodexPanel terminal token from setup page: ")).trim();
|
|
435
416
|
try {
|
|
436
|
-
return await requestJson("POST", relayUrl, "/api/desktop/setup/login-token", { flowId, flowSecret, token }, {}, 30000);
|
|
417
|
+
return await requestJson("POST", relayUrl, "/api/iam/desktop/setup/login-token", { flowId, flowSecret, token }, {}, 30000);
|
|
437
418
|
} catch (error) {
|
|
438
419
|
if (error.data?.code !== "device_rebind_required") throw error;
|
|
439
420
|
console.log("这台电脑已绑定到其他账号,不会自动切换绑定。");
|
|
440
421
|
const answer = (await question(rl, "Type YES to rebind this computer to the current account / 输入 YES 切换绑定: ")).trim();
|
|
441
422
|
if (answer !== "YES") throw new Error("Setup kept the existing binding. Run npx -y codexpanel again if you want to switch later.");
|
|
442
|
-
return await requestJson("POST", relayUrl, "/api/desktop/setup/login-token", { flowId, flowSecret, token, confirmRebind: true }, {}, 30000);
|
|
423
|
+
return await requestJson("POST", relayUrl, "/api/iam/desktop/setup/login-token", { flowId, flowSecret, token, confirmRebind: true }, {}, 30000);
|
|
443
424
|
}
|
|
444
425
|
} finally {
|
|
445
426
|
rl.close();
|
|
@@ -454,16 +435,15 @@ async function runBootstrap(relayUrl, approval, options) {
|
|
|
454
435
|
url.searchParams.set("deviceId", approval.deviceId || stableDeviceId());
|
|
455
436
|
if (approval.deviceName) url.searchParams.set("deviceName", approval.deviceName);
|
|
456
437
|
if (options.workspace) url.searchParams.set("workspace", options.workspace);
|
|
457
|
-
url.searchParams.set("mode", "
|
|
438
|
+
url.searchParams.set("mode", "host-runtime");
|
|
458
439
|
url.searchParams.set("autoStart", options.autoStart ? "1" : "0");
|
|
459
|
-
url.searchParams.set("tunnelEnabled", options.tunnelEnabled ? "1" : "0");
|
|
460
440
|
|
|
461
441
|
if (options.printCommand) console.log(powershellCommand(url));
|
|
462
442
|
if (process.platform !== "win32") throw new Error("CodexPanel desktop agent installation currently supports Windows. Use the login URL on Windows to bind this computer.");
|
|
463
443
|
|
|
464
444
|
console.log("\nCodexPanel: 正在下载最终安装脚本 / Downloading final bootstrap script...");
|
|
465
445
|
const script = await downloadText(url.toString(), 30000);
|
|
466
|
-
if (!/CodexPanel/i.test(script) || !/
|
|
446
|
+
if (!/CodexPanel/i.test(script) || !/host-runtime/i.test(script)) throw new Error("Downloaded bootstrap script did not look like the CodexPanel Windows installer.");
|
|
467
447
|
const tmp = path.join(os.tmpdir(), `codexpanel-bootstrap-${Date.now()}-${Math.random().toString(16).slice(2)}.ps1`);
|
|
468
448
|
fs.writeFileSync(tmp, Buffer.concat([Buffer.from([0xEF, 0xBB, 0xBF]), Buffer.from(script, "utf8")]));
|
|
469
449
|
console.log("CodexPanel: 正在启动 Windows 安装器 / Starting Windows installer...");
|
|
@@ -487,11 +467,9 @@ async function install(options) {
|
|
|
487
467
|
deviceId: profile.deviceId,
|
|
488
468
|
deviceName: profile.deviceName,
|
|
489
469
|
workspace: profile.workspace,
|
|
490
|
-
browserLogin: !options.
|
|
491
|
-
terminalLogin: options.terminalLogin,
|
|
470
|
+
browserLogin: !options.tokenLogin,
|
|
492
471
|
tokenLogin: options.tokenLogin,
|
|
493
472
|
autoStart: options.autoStart,
|
|
494
|
-
tunnelEnabled: options.tunnelEnabled,
|
|
495
473
|
}, null, 2));
|
|
496
474
|
return;
|
|
497
475
|
}
|
|
@@ -513,9 +491,7 @@ async function install(options) {
|
|
|
513
491
|
if (start.oneTimeCode) console.log(`Binding code / 8 位绑定码: ${start.oneTimeCode}`);
|
|
514
492
|
|
|
515
493
|
let approval;
|
|
516
|
-
if (options.
|
|
517
|
-
approval = await terminalLogin(resolved.relayUrl, start.flowId, start.flowSecret);
|
|
518
|
-
} else if (options.tokenLogin) {
|
|
494
|
+
if (options.tokenLogin) {
|
|
519
495
|
approval = await tokenLogin(resolved.relayUrl, start.flowId, start.flowSecret);
|
|
520
496
|
} else {
|
|
521
497
|
if (options.browser) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
安装流程被分成两段。
|
|
14
14
|
|
|
15
|
-
第一段是资源准备:CLI 会先读取安装清单,再下载并校验电脑端
|
|
15
|
+
第一段是资源准备:CLI 会先读取安装清单,再下载并校验电脑端 host-runtime agent、运行时模块和安装脚本探针。这样做的好处是,用户按下 Enter 的时候,系统已经知道安装所需资源是可用的,不会出现“浏览器已经打开了,但安装器还在等下载”的割裂感。
|
|
16
16
|
|
|
17
17
|
第二段才是登录绑定:终端会提示 `资源已准备好。按 Enter 拉起浏览器登录并绑定这台电脑。`,用户确认后,浏览器才会打开登录页。这样更像一个正式安装流程,而不是悄悄弹出一个网页。
|
|
18
18
|
|
|
@@ -37,21 +37,7 @@
|
|
|
37
37
|
|
|
38
38
|
页面完成绑定后会提示:`绑定成功,可以关闭浏览器,或回到安装界面查看进度。`
|
|
39
39
|
|
|
40
|
-
##
|
|
41
|
-
|
|
42
|
-
除了浏览器登录,这个安装器也支持两种终端模式。
|
|
43
|
-
|
|
44
|
-
### 用户名和密码
|
|
45
|
-
|
|
46
|
-
运行:
|
|
47
|
-
|
|
48
|
-
```powershell
|
|
49
|
-
npx -y codexpanel login --terminal-login
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
然后按提示输入用户名和密码。它会像普通登录一样走 session,成功后继续完成设备绑定。
|
|
53
|
-
|
|
54
|
-
### 一次性 token
|
|
40
|
+
## 终端一次性 token
|
|
55
41
|
|
|
56
42
|
如果你想在浏览器里登录,但让终端继续完成绑定,可以先打开安装器打印的 `/desktop/setup?flowId=...` 页面,登录后点击“生成终端一次性 token”。然后运行:
|
|
57
43
|
|
|
@@ -116,7 +102,7 @@ npx -y codexpanel --server https://example.com
|
|
|
116
102
|
- relay 地址
|
|
117
103
|
- 最近心跳
|
|
118
104
|
- agent 运行状态
|
|
119
|
-
-
|
|
105
|
+
- host-runtime / WSS 状态
|
|
120
106
|
- 是否能打开或重启 agent
|
|
121
107
|
|
|
122
108
|
这块面板的目的,是让不熟悉命令行的人也能看懂“现在装到哪一步了”。
|
|
@@ -149,7 +135,6 @@ CLI 同时生成本机 profile,包括:
|
|
|
149
135
|
- `deviceName`
|
|
150
136
|
- `workspace`
|
|
151
137
|
- `autoStart`
|
|
152
|
-
- `tunnelEnabled`
|
|
153
138
|
- `panelPortHint`
|
|
154
139
|
|
|
155
140
|
`deviceId` 来自本机 hostname、Windows domain、系统用户名和 user profile 路径的稳定 hash。它不是随机数,所以同一台电脑同一系统用户重复安装时会得到同一个 deviceId,方便识别换绑和 token 轮换。
|
|
@@ -165,21 +150,25 @@ GET /agent/version
|
|
|
165
150
|
relay 返回 agent release manifest,里面包含:
|
|
166
151
|
|
|
167
152
|
- `agentVersion`
|
|
168
|
-
- `
|
|
169
|
-
- `
|
|
170
|
-
- `
|
|
171
|
-
- `
|
|
153
|
+
- `hostRuntime.path`
|
|
154
|
+
- `hostRuntime.sha256`
|
|
155
|
+
- `hostRuntimeFiles[]`
|
|
156
|
+
- `hostRuntimeSchema.path`
|
|
157
|
+
- `hostRuntimeSchema.sha256`
|
|
172
158
|
- `bootstrap.path`
|
|
173
159
|
- `bootstrap.sha256`
|
|
174
160
|
|
|
175
161
|
CLI 随后下载 manifest 指向的资源:
|
|
176
162
|
|
|
177
163
|
```text
|
|
178
|
-
GET /agent/
|
|
179
|
-
GET /agent/
|
|
164
|
+
GET /agent/host-runtime.cjs
|
|
165
|
+
GET /agent/runtime/*.cjs
|
|
166
|
+
GET /agent/generated/codex-app-server-schemas.ts
|
|
180
167
|
GET /agent/bootstrap.ps1
|
|
181
168
|
```
|
|
182
169
|
|
|
170
|
+
旧 `/agent/desktop-agent.js` 和 `/agent/app-server-connector.js` 下载入口已清退;安装器不再预检或下载旧 standalone bridge。
|
|
171
|
+
|
|
183
172
|
每个资源下载后都会按 manifest 中的 `sha256` 校验。只有这些资源全部可下载、hash 正确,安装器才进入登录阶段。这样做的意义是:用户按 Enter 打开浏览器前,安装器已经确认服务端资源完整,后续失败更容易定位到登录、绑定或本机启动,而不是混在下载阶段。
|
|
184
173
|
|
|
185
174
|
### 3. 创建 setup flow
|
|
@@ -218,20 +207,20 @@ GET /desktop/setup?flowId=...&code=...
|
|
|
218
207
|
页面会先调用:
|
|
219
208
|
|
|
220
209
|
```text
|
|
221
|
-
GET /api/auth/status
|
|
210
|
+
GET /api/iam/auth/status
|
|
222
211
|
POST /api/desktop/setup/poll
|
|
223
212
|
```
|
|
224
213
|
|
|
225
214
|
如果用户未登录,页面显示登录/注册表单。登录使用:
|
|
226
215
|
|
|
227
216
|
```text
|
|
228
|
-
POST /api/auth/login
|
|
217
|
+
POST /api/iam/auth/login
|
|
229
218
|
```
|
|
230
219
|
|
|
231
220
|
注册使用:
|
|
232
221
|
|
|
233
222
|
```text
|
|
234
|
-
POST /api/auth/register
|
|
223
|
+
POST /api/iam/auth/register
|
|
235
224
|
```
|
|
236
225
|
|
|
237
226
|
成功后 relay 设置 `codexpanel_session` cookie。浏览器后续审批靠 session cookie 鉴权。
|
|
@@ -274,7 +263,7 @@ relay 会校验:
|
|
|
274
263
|
- `deviceToken`:给安装后的 agent 使用的长期设备 token 明文,只在这个 flow 内短暂存在。
|
|
275
264
|
- `deviceToken.hash`:后续会写入设备记录,服务端长期保存 hash,不保存明文。
|
|
276
265
|
- `approvedByUserId`
|
|
277
|
-
- `
|
|
266
|
+
- `approvedByPermissionGroupId`
|
|
278
267
|
- `userId`
|
|
279
268
|
|
|
280
269
|
浏览器显示:
|
|
@@ -284,29 +273,7 @@ relay 会校验:
|
|
|
284
273
|
Binding succeeded. You can close this browser or return to the installer.
|
|
285
274
|
```
|
|
286
275
|
|
|
287
|
-
### 5.
|
|
288
|
-
|
|
289
|
-
如果用户选择终端用户名密码:
|
|
290
|
-
|
|
291
|
-
```powershell
|
|
292
|
-
npx -y codexpanel login --terminal-login
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
CLI 会在终端询问用户名和密码,然后调用:
|
|
296
|
-
|
|
297
|
-
```text
|
|
298
|
-
POST /api/desktop/setup/login-token
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
请求体包含:
|
|
302
|
-
|
|
303
|
-
- `flowId`
|
|
304
|
-
- `flowSecret`
|
|
305
|
-
- `username`
|
|
306
|
-
- `password`
|
|
307
|
-
- 可选 `confirmRebind`
|
|
308
|
-
|
|
309
|
-
relay 使用和 Web 登录相同的用户体系校验密码、登录失败限流、用户状态和角色,然后复用 setup approve 逻辑完成审批。
|
|
276
|
+
### 5. 终端一次性 token 登录
|
|
310
277
|
|
|
311
278
|
如果用户选择一次性 token 登录:
|
|
312
279
|
|
|
@@ -316,7 +283,7 @@ relay 使用和 Web 登录相同的用户体系校验密码、登录失败限流
|
|
|
316
283
|
4. 页面调用:
|
|
317
284
|
|
|
318
285
|
```text
|
|
319
|
-
POST /api/desktop/setup/terminal-token
|
|
286
|
+
POST /api/iam/desktop/setup/terminal-token
|
|
320
287
|
```
|
|
321
288
|
|
|
322
289
|
relay 校验当前浏览器 session、flowId 和 code,然后给这个 setup flow 写入短时 token hash。页面只显示 token 明文一次。
|
|
@@ -330,7 +297,7 @@ npx -y codexpanel login --token-login
|
|
|
330
297
|
并调用:
|
|
331
298
|
|
|
332
299
|
```text
|
|
333
|
-
POST /api/desktop/setup/login-token
|
|
300
|
+
POST /api/iam/desktop/setup/login-token
|
|
334
301
|
```
|
|
335
302
|
|
|
336
303
|
请求体包含:
|
|
@@ -362,14 +329,12 @@ Windows bootstrap 用 `setupToken` 向 relay 换取当前 flow 的安装上下
|
|
|
362
329
|
- `DeviceName`
|
|
363
330
|
- `Workspace`
|
|
364
331
|
- `AgentVersion`
|
|
365
|
-
- `TunnelEnabled`
|
|
366
|
-
- `TunnelLocalHost`
|
|
367
|
-
- `TunnelLocalPort`
|
|
368
332
|
|
|
369
333
|
bootstrap 在 Windows 本机做这些事:
|
|
370
334
|
|
|
371
335
|
1. 创建 `%LOCALAPPDATA%\CodexPanelAgent`。
|
|
372
|
-
2. 下载 `
|
|
336
|
+
2. 下载 `host-runtime.cjs`、`runtime/*.cjs` 和
|
|
337
|
+
`generated/codex-app-server-schemas.ts`。
|
|
373
338
|
3. 查找 Node 和 Codex 可执行文件。
|
|
374
339
|
4. 动态选择本地状态面板端口。
|
|
375
340
|
5. 生成 `agent-runtime.json`。
|
|
@@ -380,7 +345,7 @@ bootstrap 在 Windows 本机做这些事:
|
|
|
380
345
|
POST /api/desktop/setup/complete
|
|
381
346
|
```
|
|
382
347
|
|
|
383
|
-
8. 写入本地配置、启动
|
|
348
|
+
8. 写入本地配置、启动 `host-runtime.cjs`,并打开本地状态面板。
|
|
384
349
|
9. 有限次数确认云端是否已经看到这台设备在线;如果网络或心跳暂时未就绪,安装不会无限等待,会提示查看控制台、本地面板或日志。
|
|
385
350
|
|
|
386
351
|
`agent-runtime.json` 是本机 agent 的主要数据来源,包含:
|
|
@@ -390,11 +355,11 @@ POST /api/desktop/setup/complete
|
|
|
390
355
|
- deviceId
|
|
391
356
|
- deviceName
|
|
392
357
|
- deviceToken 明文
|
|
358
|
+
- deviceWssUrl,最终 Device Agent WSS 入口,HTTPS relay 会派生为 `wss://<host>/device-wss`
|
|
393
359
|
- workspace
|
|
394
360
|
- Codex 路径
|
|
395
|
-
-
|
|
361
|
+
- provider transport(固定为 `stdio://`)
|
|
396
362
|
- 本地 panel host/port/token
|
|
397
|
-
- tunnel 配置
|
|
398
363
|
- helper 脚本路径
|
|
399
364
|
- 日志路径
|
|
400
365
|
|
|
@@ -425,93 +390,47 @@ relay 收到请求后会:
|
|
|
425
390
|
|
|
426
391
|
## 技术工作原理:agent 启动后做什么
|
|
427
392
|
|
|
428
|
-
agent
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
3. `startLocalPanel()`
|
|
435
|
-
4. 如果开启 SSE,执行 `connectSse()`
|
|
436
|
-
5. `startTunnelLoop()`
|
|
437
|
-
6. `startPollLoop()`
|
|
438
|
-
|
|
439
|
-
### 上线注册
|
|
440
|
-
|
|
441
|
-
agent 调用:
|
|
442
|
-
|
|
443
|
-
```text
|
|
444
|
-
POST /api/device/register
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
请求体来自 `onlinePayload()`,包含:
|
|
448
|
-
|
|
449
|
-
- `agentId`
|
|
450
|
-
- `userId`
|
|
451
|
-
- `deviceId`
|
|
452
|
-
- `deviceName`
|
|
453
|
-
- host、系统用户、domain、platform
|
|
454
|
-
- agentVersion
|
|
455
|
-
- desktopStatus
|
|
456
|
-
- workspace
|
|
457
|
-
- codexPath
|
|
458
|
-
- sandbox 和 approvalPolicy
|
|
459
|
-
- mode
|
|
460
|
-
- appServerListen
|
|
461
|
-
- tunnel 状态
|
|
462
|
-
- panelUrl 和 panelPort
|
|
463
|
-
- npmPackageVersion
|
|
464
|
-
- capabilities
|
|
465
|
-
|
|
466
|
-
capabilities 会告诉 relay 这台设备能做什么,例如:
|
|
467
|
-
|
|
468
|
-
- `codex-cli`
|
|
469
|
-
- `app-server-proxy`
|
|
470
|
-
- `codex-desktop`
|
|
471
|
-
- `threads`
|
|
472
|
-
- `turns`
|
|
473
|
-
- `approvals`
|
|
474
|
-
- `cloudflare-tunnel-client`
|
|
475
|
-
- `wss-direct-rpc`
|
|
476
|
-
|
|
477
|
-
注册成功后,agent 还会发一个 `agent.online` event 到:
|
|
478
|
-
|
|
479
|
-
```text
|
|
480
|
-
POST /api/events
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
### 控制事件轮询和心跳
|
|
484
|
-
|
|
485
|
-
agent 的主循环是 `startPollLoop()`。它每 10 秒执行一次 `pollOnce()`。
|
|
393
|
+
workspace/package agent 的默认入口已经切到 `host-runtime.cjs`,并会读取
|
|
394
|
+
`CODEXPANEL_AGENT_CONFIG` 指向的 `agent-runtime.json`,把 `userId`、`deviceId`、
|
|
395
|
+
`deviceToken`、`workspace`、`codexPath` 和 `deviceWssUrl` 映射成
|
|
396
|
+
`CODEXPANEL_*` 运行环境。relay 当前提供 `/device-wss` upgrade transport,
|
|
397
|
+
用于 device-token `device.hello`、heartbeat、Agent Protocol event、command result
|
|
398
|
+
和 `server.command` 下发。旧 `desktop-agent.js` Windows standalone 下载入口已清退。
|
|
486
399
|
|
|
487
|
-
|
|
400
|
+
standalone `host-runtime.cjs` 启动时会先读取 `agent-runtime.json`,然后初始化最终链路组件。
|
|
488
401
|
|
|
489
|
-
|
|
490
|
-
POST /api/agent/pull-control
|
|
491
|
-
```
|
|
402
|
+
启动时立即执行:
|
|
492
403
|
|
|
493
|
-
|
|
404
|
+
1. 创建 `DeviceWssClient`,使用 `deviceToken` 对 `/device-wss` 发送 `device.hello`。
|
|
405
|
+
2. 启动 `HostRuntimeAdapter`,通过 `stdio://` JSONL 初始化 Codex app-server。
|
|
406
|
+
3. 通过 WSS 上报 Agent Protocol v1 event、heartbeat、command result 和 resource chunk。
|
|
407
|
+
4. 启动 `runtime/local-panel.cjs` 提供本机 `/api/status` 和固定 helper action。
|
|
494
408
|
|
|
495
|
-
|
|
496
|
-
|
|
409
|
+
legacy tunnel 已退役,agent 包内不再包含 `startTunnelLoop()`、
|
|
410
|
+
cloudflared 启动器或本地 WSS direct RPC 服务。
|
|
411
|
+
legacy poll fallback 也已退役,agent 包内不再包含 `startPollLoop()`、
|
|
412
|
+
`pollOnce()` 或 `/api/agent/pull-control` 控制轮询。
|
|
497
413
|
|
|
498
|
-
|
|
414
|
+
### 上线握手
|
|
499
415
|
|
|
500
|
-
|
|
501
|
-
-
|
|
502
|
-
- `
|
|
503
|
-
|
|
504
|
-
- agent client 数等运行信息。
|
|
416
|
+
安装完成时,`POST /api/desktop/setup/complete` 会把设备记录写成
|
|
417
|
+
`mode=host-runtime`,并保存服务端侧 device token hash。本机 agent 启动后不再信任
|
|
418
|
+
client-supplied user/device 权限字段,而是通过 `/device-wss` 的 `device.hello`
|
|
419
|
+
使用服务端签发的 device token 建立长连接。
|
|
505
420
|
|
|
506
|
-
|
|
421
|
+
握手后,设备能力来自 host runtime 的 provider metadata 和 capability map。设备事件会保留
|
|
422
|
+
Agent Protocol envelope 与 raw provider payload,不再通过旧 `/api/events` 或
|
|
423
|
+
`/api/device/register` 作为主注册路径。
|
|
424
|
+
旧 `/api/device/register`、`/api/device/update`、`/api/device/ping` 和
|
|
425
|
+
`/api/device/offline` HTTP lifecycle route 已退役;在线、心跳和离线状态均由
|
|
426
|
+
`/device-wss` 的 hello、heartbeat、close/stale heartbeat 决定。
|
|
507
427
|
|
|
508
|
-
|
|
509
|
-
2. 对每个 event 调用 `handleBridgeEvent()`。
|
|
510
|
-
3. 更新 `lastPolledEventId`。
|
|
511
|
-
4. 持久化本地 agent state。
|
|
512
|
-
5. 更新本地状态面板里的 `lastHeartbeatAt`。
|
|
428
|
+
### Legacy 控制事件轮询已删除
|
|
513
429
|
|
|
514
|
-
|
|
430
|
+
最终架构不把轮询作为正常交互路径。`enableLegacyPoll`、
|
|
431
|
+
`CODEXPANEL_AGENT_ENABLE_LEGACY_POLL`、`startPollLoop()`、`pollOnce()` 和
|
|
432
|
+
`POST /api/agent/pull-control` 都已退役;设备命令由服务端 command
|
|
433
|
+
channel 下发,事件由 SSE 或设备 WSS 通道同步。
|
|
515
434
|
|
|
516
435
|
### 事件如何被处理
|
|
517
436
|
|
|
@@ -523,7 +442,7 @@ agent 收到后会:
|
|
|
523
442
|
|
|
524
443
|
当前主要事件类型:
|
|
525
444
|
|
|
526
|
-
- `
|
|
445
|
+
- `server.command`:由最终 `/commands` 网关下发到 host-runtime,例如 listSessions、readSession、startThread、resumeThread、startTurn、steerTurn、interruptTurn、resolveApproval。
|
|
527
446
|
- `task.start`:启动 Codex CLI task,并先发 approval request。
|
|
528
447
|
- `approval.resolve`:用户批准或拒绝后继续或取消任务。
|
|
529
448
|
- `run.cancel`:取消当前运行中的 Codex CLI。
|
|
@@ -536,57 +455,22 @@ CLI task 启动后,agent 会把 stdout/stderr 和 Codex JSON event 转成 rela
|
|
|
536
455
|
- `run.completed`
|
|
537
456
|
- `agent.error`
|
|
538
457
|
|
|
539
|
-
### SSE
|
|
540
|
-
|
|
541
|
-
如果启用 SSE,agent 还会连接:
|
|
542
|
-
|
|
543
|
-
```text
|
|
544
|
-
GET /events?role=agent&client=<deviceId>
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
SSE 连接成功后,relay 可以直接推事件给 agent。SSE 失败或断开时:
|
|
548
|
-
|
|
549
|
-
- 非 200 状态:2 秒后重连。
|
|
550
|
-
- 连接 end:1.5 秒后重连。
|
|
551
|
-
- 网络 error:1.5 秒后重连。
|
|
552
|
-
|
|
553
|
-
即使 SSE 不可用,10 秒一次的 `/api/agent/pull-control` 轮询仍然是兜底控制通道。
|
|
458
|
+
### Agent SSE 辅助通道已删除
|
|
554
459
|
|
|
555
|
-
|
|
460
|
+
`GET /events?role=agent&client=<deviceId>` 已退役。最终架构中,`GET /events`
|
|
461
|
+
只服务 Web / Mobile / Admin 客户端的 SSE 读取;设备侧命令、心跳、事件上报和
|
|
462
|
+
`command.result` 都通过 `/device-wss` 完成。
|
|
556
463
|
|
|
557
|
-
|
|
464
|
+
### Legacy WSS/直连 tunnel 已删除
|
|
558
465
|
|
|
559
|
-
|
|
466
|
+
最终架构不使用 direct tunnel/domain lease 作为主链路。`--legacy-tunnel`
|
|
467
|
+
和 `--no-tunnel` 都已退役,CLI 会拒绝这些参数;relay bootstrap 不再写入
|
|
468
|
+
tunnel/cloudflared 配置,agent 包内也不再包含本地 direct RPC tunnel 实现。
|
|
560
469
|
|
|
561
|
-
|
|
562
|
-
|
|
470
|
+
历史上的 `/api/domain-lease/*`、cloudflared token、entry URL 和 WSS URL
|
|
471
|
+
只作为 WS-F 清退对象保留,不再作为新安装或正式产品控制路径。
|
|
563
472
|
|
|
564
|
-
|
|
565
|
-
POST /api/domain-lease/claim
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
3. 如果已有 lease,调用:
|
|
569
|
-
|
|
570
|
-
```text
|
|
571
|
-
POST /api/domain-lease/heartbeat
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
4. relay 返回更新后的 lease,agent 更新本地 tunnel 状态。
|
|
575
|
-
|
|
576
|
-
lease 中包含 entry URL、data host、WSS URL、cloudflared token 等直连所需信息。agent 会启动 cloudflared,把外部 WSS 连接导到本机 tunnel local service。
|
|
577
|
-
|
|
578
|
-
如果 heartbeat 失败,并且错误类似:
|
|
579
|
-
|
|
580
|
-
- reclaim required
|
|
581
|
-
- unknown domain lease
|
|
582
|
-
- domain lease is released
|
|
583
|
-
- hostname is outdated
|
|
584
|
-
|
|
585
|
-
agent 会停止 cloudflared、清空 lease,并在 1 秒后重新 claim。其他失败则按 `TUNNEL_HEARTBEAT_MS` 继续重试。
|
|
586
|
-
|
|
587
|
-
### 自更新循环
|
|
588
|
-
|
|
589
|
-
agent 在启动和每次 control poll 时检查服务端 release。
|
|
473
|
+
### Release 资源更新
|
|
590
474
|
|
|
591
475
|
数据来源:
|
|
592
476
|
|
|
@@ -594,16 +478,16 @@ agent 在启动和每次 control poll 时检查服务端 release。
|
|
|
594
478
|
GET /agent/version
|
|
595
479
|
```
|
|
596
480
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
如果 `agentVersion` 与本地不同,agent 会下载:
|
|
481
|
+
安装器会预检并下载:
|
|
600
482
|
|
|
601
483
|
```text
|
|
602
|
-
GET /agent/
|
|
603
|
-
GET /agent/
|
|
484
|
+
GET /agent/host-runtime.cjs
|
|
485
|
+
GET /agent/runtime/*.cjs
|
|
486
|
+
GET /agent/generated/codex-app-server-schemas.ts
|
|
604
487
|
```
|
|
605
488
|
|
|
606
|
-
下载后校验 sha256
|
|
489
|
+
下载后校验 sha256,再写入本机安装目录。旧 `desktop-agent.js` 和
|
|
490
|
+
`app-server-connector.js` 不再作为安装、启动或自更新入口。
|
|
607
491
|
|
|
608
492
|
### 本地状态面板
|
|
609
493
|
|
|
@@ -636,8 +520,7 @@ POST /api/action
|
|
|
636
520
|
- workspace
|
|
637
521
|
- 当前工作模式、审批权限、sandbox、approvalPolicy、approvalsReviewer
|
|
638
522
|
- 最近心跳和最近错误
|
|
639
|
-
- Codex
|
|
640
|
-
- WSS/tunnel 状态
|
|
523
|
+
- host-runtime / Codex stdio bridge 状态
|
|
641
524
|
- helper 脚本路径
|
|
642
525
|
- 下一步建议
|
|
643
526
|
|
|
@@ -658,9 +541,9 @@ POST /api/action
|
|
|
658
541
|
交互过程是:
|
|
659
542
|
|
|
660
543
|
1. 用户在控制台选择“默认模式 / 计划模式 / 目标模式”或“请求批准 / 替我审批 / 完全访问”。
|
|
661
|
-
2.
|
|
544
|
+
2. 控制台通过服务端控制门面下发状态更新,最终由 agent 处理标准化后的控制事件。
|
|
662
545
|
3. 电脑端 agent 把标准化后的 `controlState` 写入本机 `agent-runtime.json` 和 agent state,字段包括 `sandbox`、`approvalPolicy`、`approvalsReviewer`、`networkAccess`、`workMode`、`permissionPreset`。
|
|
663
|
-
4. agent
|
|
546
|
+
4. agent 通过服务端事件/心跳和本地状态面板 `/api/status` 把真实状态回传给 relay 和控制台。
|
|
664
547
|
5. 后续 `thread/start`、`thread/resume`、`turn/start` 会从设备已保存的 `controlState` 补默认值,保证真正发给 Codex app-server 的权限和模式与控制台看到的一致。
|
|
665
548
|
|
|
666
549
|
审批权限的映射是:
|
|
@@ -688,14 +571,14 @@ POST /api/action
|
|
|
688
571
|
| deviceId | 本机 hostname/domain/user/profile hash | 识别同一台电脑和换绑 |
|
|
689
572
|
| deviceName | CLI 参数或 hostname + 系统用户名 | 展示给用户确认 |
|
|
690
573
|
| flowId/flowSecret/code | `/api/desktop/setup/start` | 连接 CLI、浏览器和 relay 的短时安装流 |
|
|
691
|
-
| session cookie | `/api/auth/login` 或 `/api/auth/register` | 浏览器用户身份 |
|
|
574
|
+
| session cookie | `/api/iam/auth/login` 或 `/api/iam/auth/register` | 浏览器用户身份 |
|
|
692
575
|
| setupToken | `/api/desktop/setup/approve` 成功后生成 | Windows bootstrap 下载安装上下文 |
|
|
693
576
|
| deviceToken | setup flow 审批后生成 | agent 长期鉴权,明文只在本机保存 |
|
|
694
577
|
| deviceTokenHash | relay 计算并保存 | 服务端验证 deviceToken |
|
|
695
578
|
| panelToken | bootstrap 本机生成 | 本地状态面板鉴权 |
|
|
696
|
-
| controlState |
|
|
697
|
-
| agentRelease | `/agent/version`
|
|
698
|
-
| heartbeat 状态 |
|
|
579
|
+
| controlState | 服务端控制门面下发的标准控制事件 | 决定后续 Codex 工作模式、审批路由、sandbox 和网络权限 |
|
|
580
|
+
| agentRelease | `/agent/version` | agent 自更新 |
|
|
581
|
+
| heartbeat 状态 | 设备 WSS/SSE 路径 | 面板展示、服务端在线状态 |
|
|
699
582
|
|
|
700
583
|
## 重试和失败提示
|
|
701
584
|
|
|
@@ -708,7 +591,6 @@ POST /api/action
|
|
|
708
591
|
- PowerShell bootstrap 失败:终端显示安装目录、日志路径和失败原因。
|
|
709
592
|
- agent 心跳失败:agent 记录 `lastHeartbeatError`,面板展示错误和建议,下一轮继续重试。
|
|
710
593
|
- SSE 断开:1.5 到 2 秒后重连。
|
|
711
|
-
- tunnel lease 失效:停止 cloudflared 并重新 claim。
|
|
712
594
|
- 自更新失败:记录事件并继续运行旧版本。
|
|
713
595
|
|
|
714
596
|
## 版本和更新怎么看
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codexpanel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "CodexPanel mobile control plane monorepo.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -28,16 +28,21 @@
|
|
|
28
28
|
"verify:version": "node scripts/sync-product-version.mjs --check",
|
|
29
29
|
"prebuild": "npm run sync:version",
|
|
30
30
|
"build": "npm run build --workspaces --if-present",
|
|
31
|
-
"check": "npm run verify:version && npm run scan:encoding && npm run verify:api-contracts && npm run verify:storage && npm run check --workspaces --if-present",
|
|
31
|
+
"check": "npm run verify:version && npm run scan:encoding && npm run verify:api-contracts && npm run verify:client-gateway && npm run verify:server-gateway-boundary && npm run verify:iam && npm run verify:storage && npm run verify:domain-config && node scripts/money-smoke.cjs && npm run check --workspaces --if-present",
|
|
32
32
|
"release:local": "node scripts/release-local.mjs",
|
|
33
33
|
"release:server": "bash scripts/server-autodeploy.sh",
|
|
34
|
+
"release:test": "node scripts/test-release.mjs",
|
|
34
35
|
"release:production": "node scripts/production-release.mjs",
|
|
35
36
|
"verify:release": "node scripts/verify-release.mjs",
|
|
36
37
|
"test:e2e": "playwright test",
|
|
37
38
|
"scan:encoding": "node scripts/scan-encoding.cjs",
|
|
38
39
|
"verify:api-contracts": "node scripts/verify-api-contracts.mjs",
|
|
40
|
+
"verify:client-gateway": "node scripts/verify-client-gateway-contract.mjs",
|
|
41
|
+
"verify:server-gateway-boundary": "node scripts/verify-server-gateway-boundary.mjs",
|
|
42
|
+
"verify:iam": "node scripts/verify-iam-contract.mjs && npm run check -w @codexpanel/iam-service",
|
|
39
43
|
"generate:codex": "npm run generate -w @codexpanel/codex-app-server",
|
|
40
44
|
"verify:storage": "node scripts/verify-storage-boundary.mjs",
|
|
45
|
+
"verify:domain-config": "node scripts/verify-domain-config.mjs",
|
|
41
46
|
"smoke:storage": "node scripts/storage-smoke.cjs",
|
|
42
47
|
"purge:retired-desktop-residue": "node scripts/purge-retired-desktop-residue.mjs"
|
|
43
48
|
},
|