@walkhi/code-relax 0.1.0-beta.30 → 0.1.0-beta.31

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.
Files changed (185) hide show
  1. package/README.md +95 -89
  2. package/dist/bin/cleanup-accounts.mjs +63 -63
  3. package/dist/bin/codex-remote.mjs +374 -367
  4. package/dist/bin/manage-relay-admin.mjs +20 -20
  5. package/dist/bin/migrate-relay-db.mjs +109 -109
  6. package/dist/bin/publish-android-release.mjs +62 -58
  7. package/dist/bin/self-relay-admin.mjs +8 -8
  8. package/dist/bin/self-relay-demo.mjs +33 -33
  9. package/dist/bin/self-relay-server.mjs +66 -66
  10. package/dist/build-platform.json +1 -1
  11. package/dist/get-lan-networks.ps1 +40 -40
  12. package/dist/get-shared-service-url.ps1 +6 -6
  13. package/dist/shared/app-server-events.cjs +422 -412
  14. package/dist/shared/code-browser.cjs +159 -159
  15. package/dist/shared/file-changes.cjs +31 -31
  16. package/dist/shared/file-citations.cjs +117 -117
  17. package/dist/shared/file-mentions.cjs +92 -92
  18. package/dist/shared/message-delivery.cjs +54 -54
  19. package/dist/shared/p2p-data-channel.cjs +72 -72
  20. package/dist/shared/p2p-diagnostics.cjs +28 -28
  21. package/dist/shared/p2p-file-channel.cjs +74 -74
  22. package/dist/shared/plan-pricing.cjs +32 -32
  23. package/dist/shared/question-attention.cjs +40 -0
  24. package/dist/shared/server-requests.cjs +219 -219
  25. package/dist/shared/timeline-sync.cjs +173 -173
  26. package/dist/skills/code-relax/SKILL.md +22 -22
  27. package/dist/src/archived-threads.mjs +22 -0
  28. package/dist/src/background-process.mjs +18 -18
  29. package/dist/src/bounded-process-log.mjs +63 -63
  30. package/dist/src/celsius-client.mjs +107 -107
  31. package/dist/src/code-browser.mjs +100 -100
  32. package/dist/src/desktop-launcher.mjs +46 -46
  33. package/dist/src/dots-approval-timeline.mjs +99 -99
  34. package/dist/src/dots-avatar.mjs +42 -42
  35. package/dist/src/dots-chat-projection.mjs +50 -50
  36. package/dist/src/dots-chat-server.mjs +461 -461
  37. package/dist/src/dots-message-images.mjs +319 -319
  38. package/dist/src/dots-profile.mjs +77 -77
  39. package/dist/src/dots-room-activity.mjs +94 -94
  40. package/dist/src/dots-room-client.mjs +135 -135
  41. package/dist/src/dots-room-projection.mjs +94 -94
  42. package/dist/src/dots-schedules.mjs +66 -66
  43. package/dist/src/dots-summary-tasks.mjs +32 -32
  44. package/dist/src/dots-task-catalog.mjs +430 -430
  45. package/dist/src/dots-task-files.mjs +52 -52
  46. package/dist/src/execution-health.mjs +26 -26
  47. package/dist/src/file-transfer-range.mjs +22 -22
  48. package/dist/src/file-upload-session.mjs +130 -130
  49. package/dist/src/hosted-history-app-server.mjs +103 -103
  50. package/dist/src/hosted-history-cache.mjs +113 -113
  51. package/dist/src/lan-socket-server.mjs +265 -263
  52. package/dist/src/managed-app-server.mjs +244 -244
  53. package/dist/src/message-images.mjs +9 -9
  54. package/dist/src/message-upload-files.mjs +74 -74
  55. package/dist/src/onboarding.mjs +12 -12
  56. package/dist/src/plan-pricing.mjs +47 -47
  57. package/dist/src/platform/README.md +12 -12
  58. package/dist/src/platform/codex-protocol.mjs +46 -46
  59. package/dist/src/platform/codex-runtime.mjs +3 -3
  60. package/dist/src/platform/credential-store.mjs +3 -3
  61. package/dist/src/platform/desktop-host.mjs +3 -3
  62. package/dist/src/platform/desktop-project-registration.mjs +3 -3
  63. package/dist/src/platform/desktop-project-store.mjs +208 -149
  64. package/dist/src/platform/file-opener.mjs +9 -0
  65. package/dist/src/platform/linux/README.md +3 -3
  66. package/dist/src/platform/linux/codex-runtime.mjs +56 -0
  67. package/dist/src/platform/linux/credential-store.mjs +28 -0
  68. package/dist/src/platform/linux/desktop-host.mjs +83 -0
  69. package/dist/src/platform/linux/desktop-installation.mjs +36 -0
  70. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
  71. package/dist/src/platform/linux/network-info.mjs +11 -0
  72. package/dist/src/platform/linux/process.mjs +42 -0
  73. package/dist/src/platform/linux/project-locations.mjs +17 -0
  74. package/dist/src/platform/linux/resident-startup.mjs +52 -0
  75. package/dist/src/platform/linux/retained-runtime.mjs +76 -0
  76. package/dist/src/platform/linux/service-host.mjs +18 -0
  77. package/dist/src/platform/linux/system.mjs +49 -0
  78. package/dist/src/platform/linux/user-environment.mjs +7 -0
  79. package/dist/src/platform/macos/README.md +3 -3
  80. package/dist/src/platform/macos/codex-runtime.mjs +125 -125
  81. package/dist/src/platform/macos/credential-store.mjs +10 -10
  82. package/dist/src/platform/macos/desktop-host.mjs +82 -82
  83. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -10
  84. package/dist/src/platform/macos/network-info.mjs +8 -28
  85. package/dist/src/platform/macos/project-locations.mjs +8 -8
  86. package/dist/src/platform/macos/resident-startup.mjs +84 -84
  87. package/dist/src/platform/macos/retained-runtime.mjs +127 -127
  88. package/dist/src/platform/macos/service-host.mjs +18 -18
  89. package/dist/src/platform/macos/system.mjs +29 -29
  90. package/dist/src/platform/macos/user-environment.mjs +7 -7
  91. package/dist/src/platform/native-package.mjs +58 -46
  92. package/dist/src/platform/network-info.mjs +3 -3
  93. package/dist/src/platform/path-identity.mjs +5 -5
  94. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  95. package/dist/src/platform/posix/runtime-paths.mjs +25 -25
  96. package/dist/src/platform/project-locations.mjs +3 -3
  97. package/dist/src/platform/runtime-paths.mjs +8 -8
  98. package/dist/src/platform/select.mjs +5 -5
  99. package/dist/src/platform/service-host.mjs +3 -3
  100. package/dist/src/platform/user-environment.mjs +3 -3
  101. package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
  102. package/dist/src/platform/windows/activate-desktop.ps1 +48 -48
  103. package/dist/src/platform/windows/background-process.mjs +30 -30
  104. package/dist/src/platform/windows/codex-runtime.mjs +215 -215
  105. package/dist/src/platform/windows/credential-store.mjs +27 -27
  106. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  107. package/dist/src/platform/windows/desktop-helper.mjs +179 -179
  108. package/dist/src/platform/windows/desktop-host.mjs +58 -58
  109. package/dist/src/platform/windows/desktop-info.ps1 +14 -14
  110. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
  111. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
  112. package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -21
  113. package/dist/src/platform/windows/dots-profile.mjs +49 -49
  114. package/dist/src/platform/windows/launch-worker.mjs +41 -41
  115. package/dist/src/platform/windows/network-info.mjs +34 -34
  116. package/dist/src/platform/windows/project-locations.mjs +16 -16
  117. package/dist/src/platform/windows/resident-start.js +20 -20
  118. package/dist/src/platform/windows/resident-start.ps1 +7 -7
  119. package/dist/src/platform/windows/resident-startup.ps1 +83 -83
  120. package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
  121. package/dist/src/platform/windows/runtime-paths.mjs +20 -20
  122. package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
  123. package/dist/src/platform/windows/service-host.mjs +31 -31
  124. package/dist/src/platform/windows/start-hidden-console.ps1 +49 -49
  125. package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
  126. package/dist/src/platform/windows/user-environment.mjs +50 -50
  127. package/dist/src/platform/windows/user-environment.ps1 +41 -41
  128. package/dist/src/project-archive.mjs +30 -0
  129. package/dist/src/project-create.mjs +121 -119
  130. package/dist/src/project-management.mjs +59 -69
  131. package/dist/src/project-remove.mjs +55 -0
  132. package/dist/src/project-update.mjs +56 -0
  133. package/dist/src/runtime-discovery.mjs +47 -47
  134. package/dist/src/self-relay/account-cleanup.mjs +30 -30
  135. package/dist/src/self-relay/account-connections.mjs +52 -52
  136. package/dist/src/self-relay/admin-cloud.mjs +101 -101
  137. package/dist/src/self-relay/admin-page.mjs +80 -80
  138. package/dist/src/self-relay/admin-server.mjs +74 -74
  139. package/dist/src/self-relay/admin-state.mjs +127 -127
  140. package/dist/src/self-relay/admin-style.mjs +14 -14
  141. package/dist/src/self-relay/android-releases.mjs +61 -61
  142. package/dist/src/self-relay/client.mjs +61 -61
  143. package/dist/src/self-relay/connector.mjs +439 -438
  144. package/dist/src/self-relay/debug-allowlist.mjs +33 -33
  145. package/dist/src/self-relay/demo.mjs +37 -37
  146. package/dist/src/self-relay/device-info.mjs +27 -0
  147. package/dist/src/self-relay/device-store.mjs +92 -84
  148. package/dist/src/self-relay/diagnostics.mjs +54 -54
  149. package/dist/src/self-relay/email-accounts.mjs +183 -183
  150. package/dist/src/self-relay/file-wire.mjs +13 -13
  151. package/dist/src/self-relay/lan-authorizations.mjs +71 -42
  152. package/dist/src/self-relay/lifecycle.mjs +264 -250
  153. package/dist/src/self-relay/long-pairing.mjs +40 -40
  154. package/dist/src/self-relay/mysql-state.mjs +118 -105
  155. package/dist/src/self-relay/p2p-probe.mjs +159 -159
  156. package/dist/src/self-relay/privacy-page.mjs +26 -26
  157. package/dist/src/self-relay/public-releases.mjs +34 -0
  158. package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
  159. package/dist/src/self-relay/server.mjs +1006 -932
  160. package/dist/src/self-relay/usage-daily.mjs +55 -55
  161. package/dist/src/self-relay/usage-meter.mjs +141 -141
  162. package/dist/src/self-relay/windows-route-helper.mjs +75 -75
  163. package/dist/src/self-relay/wire.mjs +36 -36
  164. package/dist/src/server.mjs +1861 -1778
  165. package/dist/src/service-control.mjs +82 -82
  166. package/dist/src/service-doctor.mjs +110 -110
  167. package/dist/src/service-health.mjs +33 -33
  168. package/dist/src/service-install.mjs +91 -91
  169. package/dist/src/service-lifecycle.mjs +138 -138
  170. package/dist/src/service-restart.mjs +81 -81
  171. package/dist/src/shared-app-server.mjs +615 -611
  172. package/dist/src/shared-catalog.mjs +132 -132
  173. package/dist/src/shared-recovery.mjs +146 -146
  174. package/dist/src/shared-thread-stream.mjs +164 -164
  175. package/dist/src/skill-install.mjs +51 -51
  176. package/dist/src/thread-title-generation.mjs +150 -150
  177. package/dist/src/turn-model-history.mjs +47 -47
  178. package/dist/src/usage-reset.mjs +17 -17
  179. package/dist/src/workstation-downloads.mjs +120 -120
  180. package/dist/tools/rtc-route-helper.ps1 +99 -99
  181. package/dist/tools/start-rtc-route-helper.ps1 +15 -15
  182. package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
  183. package/dist/web/vendor/markdown-it.umd.min.js +13 -13
  184. package/package.json +7 -6
  185. package/tools/postinstall.mjs +127 -127
package/README.md CHANGED
@@ -1,89 +1,95 @@
1
- # Code Relax 工作站服务
2
-
3
- Windows 与 Mac 的统一 npm 分发源码已就绪:主包按操作系统和 CPU 自动安装对应原生组件,普通用户无需自行编译。此结构尚未发布,线上 beta.29 仍为 Windows 包;Mac 原生包需在测试机编译验收。开发者使用 `npm run pack:release -- --output <新的输出目录>` 生成统一主包与当前平台组件包,详见 [npm 分发](../docs/npm-distribution.md)。
4
-
5
- 桌面服务让 HarmonyOS 客户端控制 Windows 上的Codex 内核。当前要求 Windows x64、Node >=22.12,以及已登录的 Codex Desktop 配套内核或独立 Codex CLI;npm 包不包含 Node,也不修改官方程序文件。
6
-
7
- ## 安装
8
-
9
- 公开 beta 已发布到 npm,安装或更新使用:
10
-
11
- ```powershell
12
- npm i -g @walkhi/code-relax@beta --allow-scripts=@walkhi/code-relax --foreground-scripts
13
- ```
14
-
15
- 首次安装前先保存工作,并从 Codex 菜单或 Windows 托盘完全退出 Codex。更新无需先停止正在运行的桌面服务;安装器会短暂停止、替换并恢复服务。npm 全局安装会自动执行 `setup` 和 Skill 安装。`setup` 将完整服务部署到 `%LOCALAPPDATA%\Programs\CodexRemote\service`,记录安装所用 Node 的绝对路径,不复制 Node。配置、日志和二维码以 `installation.json.stateRoot` 为准,通常位于 `%LOCALAPPDATA%\CodexRemote`,MSIX 环境可能解析到包缓存目录;附件位于 `%ProgramData%\CodexRemote\attachments`。
16
-
17
- `setup` 立即启动桌面服务和固定 45839 Codex 内核,并启用当前用户登录自启;自启不打开 Codex。Codex 内核健康后配置当前用户 `CODEX_APP_SERVER_WS_URL`,之后正常启动的 Desktop 自动接入。若安装时 Desktop 仍未接入,安装器会关闭并重新打开 Desktop;安装前应保存工作。普通更新无接入或启动链迁移需求时复用现有内核。
18
-
19
- ## Codex 内核
20
-
21
- ```powershell
22
- relax start # 启动/复用桌面服务、Codex 内核和已配置的 Code Relax 中继
23
- relax pair # 启动/复用服务并打开配对二维码
24
- relax doctor # 查看工作站是否正常及需要处理的问题
25
- relax diag # 输出供客服或技术人员使用的详细诊断报告
26
- relax kernel-restart --confirm # 重启内核并启用已选更新,等待新内核就绪
27
- ```
28
-
29
- Codex 内核由官方 `codex app-server --listen ws://127.0.0.1:45839` 提供执行服务,Desktop 和桌面服务均可连接;它只监听回环地址。桌面服务独立报告内核健康、Desktop 接入和手机可写状态;Desktop 退出后,手机仍可操作内核中的任务。同一 thread 已被另一个实例持有时,手机只读且可重新检测。
30
-
31
- 发现待启用内核更新时,手机“重启并更新”和 `relax kernel-restart --confirm` 复用同一受控停启流程:先验证候选,再停止旧内核,确认新内核协议就绪后恢复 Desktop,并按清单清理不再运行的旧版保留目录。手机工作站详情可确认让 Desktop 接入Codex 内核,独立进程逐步执行并保留桌面服务/中继连接,见[启用流程](../docs/shared-recovery.md)。本机恢复命令为 `relax desktop-recover --confirm`,查看恢复记录用 `relax diag`。桌面服务状态匹配且健康时直接复用;未知进程占用 45831/45839 时拒绝接管。
32
-
33
- Codex 内核通过隐藏控制台启动,使后续控制台子进程继承隐藏窗口。手机的项目、任务列表和新建任务均使用Codex 内核,不依赖 Desktop 工具管道;项目和任务 ID 保持原生值。
34
-
35
- Codex 内核未就绪时,手机停留在连接界面并显示“Codex 内核未就绪”;桌面服务保留健康和恢复接口,聊天请求返回错误。
36
-
37
- ## Dots 委派任务
38
-
39
- `dots-tasks --json` 只读查询当前 ChatGPT 账号在托管 app-server 中的 Dots 委派任务及最近轮次状态。本机内核须已启动并登录 ChatGPT,凭据只用于工作站到官方主机的连接。任务合入 LAN 和 Code Relax 中继列表,按执行目录归入项目,标题旁显示云朵,委派正文与发送者分开显示。委派任务已接入发送/引导、停止、审批和实时事件;点点主聊天已接官方消息房间,保留独立正文与执行任务边界。当前实现与手机验证范围见 [Dots 接入](../docs/dots-integration.md)和[消息房间修复](../docs/dots-messaging-repair.md)。
40
-
41
- 源码运行:
42
-
43
- ```powershell
44
- node bridge/bin/codex-remote.mjs dots-tasks --json
45
- ```
46
-
47
- 构建产物使用 `node bridge/dist/bin/codex-remote.mjs dots-tasks --json`;已更新的本机全局 CLI 使用 `relax dots-tasks --json`,公共 npm 包尚未同步这项功能。任务目录归属与执行环境不同,本次托管任务携带 Windows 工作目录;协议与实读证据见 [Dots 接入](../docs/dots-integration.md)。
48
-
49
- ## Code Relax 中继
50
-
51
- 私有中继仍需要本机桌面服务:
52
-
53
- ```powershell
54
- relax start --json
55
- relax pair --json
56
- ```
57
-
58
- `start` 启动内核和桌面服务,并在桌面服务内恢复已配置中继。每台移动设备通过 `pair` 配对一次;新增设备不替换已有设备。桌面服务把 Windows hostname 和当前 Wi-Fi/有线网络候选经已认证 WebSocket 临时发送给手机,手机本地保存电脑名,只在设备身份验证通过后保存可达地址;服务器不持久化这些 Code Relax 中继 hostname、IP 或网络名。OpenAI 官方认证和设备配对仍直接访问 OpenAI;完成后,手机使用统一配对产生的设备凭据,通过独立元数据 WebSocket 把官方环境 ID、名称和 hostname 写入设备记录,不上传 OpenAI access token、配对码或登录态。
59
-
60
- 桌面服务的局域网端口仅提供鉴权 WebSocket,普通 HTTP 请求返回 404;手机聊天页面随 HAP 安装。
61
-
62
- ## 更新与卸载
63
-
64
- 更新固定服务:
65
-
66
- ```powershell
67
- npm i -g @walkhi/code-relax@beta --allow-scripts=@walkhi/code-relax --foreground-scripts
68
- ```
69
-
70
- `setup` 先暂存并校验新版本,再短暂停止桌面服务/中继完成切换;原服务此前正在运行时会自动恢复,新版本启动失败则回滚并启动旧版本。无需提前停止服务。Node 路径变化或原 Node 被删除后需重新 setup。升级可能因 Node 可执行文件路径变化而再次触发 Windows 防火墙授权。
71
-
72
- 卸载:
73
-
74
- ```powershell
75
- relax uninstall
76
- npm uninstall -g @walkhi/code-relax
77
- ```
78
-
79
- `uninstall` 停止桌面服务后先核对并清理 Code Relax 所有的当前用户 `CODEX_APP_SERVER_WS_URL`;用户后来修改或不归属 Code Relax 的值会保留。随后删除固定程序和本包管理且未修改的 Skill;保留其他配置、Token、日志、长期设备凭据、附件和自定义 Skill。它不卸载 Node、Codex Desktop,也不强制结束完整控制的执行进程。
80
-
81
- ## 发布边界
82
-
83
- registry 测试包使用 `@walkhi/code-relax@beta`,主 CLI 为 `relax`,并保留 `code-relax` 兼容入口。安装摘要通过 `--foreground-scripts` 显示,首次安装或启动链迁移时干净重启 Desktop,分别报告已安装与实际运行版本;登录启动使用当前用户任务计划。通信协议仍为 7。详细规则见 [npm 分发](../docs/npm-distribution.md)。
84
-
85
- ### CLI 输出(2026-10-15)
86
-
87
- 安装和启动成功后只显示结果与 `relax pair` 入口;配对成功显示二维码文件。`relax doctor` 报告工作站健康及实际问题,独立的 `relax diag` 默认用可读文本输出系统、Node、服务进程、逐项检查和 Desktop 接入排障记录;常规命令不再提供 `--detail`;`--json` 保留给安装器等现有程序调用。服务未运行或内核未就绪判为失败;未连接局域网只提示,不阻断中继使用。手机连通性不作为 CLI 检查项,doctor 不创建配对邀请。
88
-
89
- `doctor` 默认逐项说明安装文件、工作站服务和 Codex 内核的当前状态,问题集中给出处理方法。上次 Desktop 接入记录仅由 `diag` 读取,并明确标注排障用途;常规 `doctor` 不读取该记录,不参与当前健康判定,不拼接互相矛盾的阶段消息与最终错误。
1
+ # Code Relax 工作站服务
2
+
3
+ Windows 与 Mac 的统一 npm 平台分发已随 beta.30 发布,普通用户无需编译原生组件。Linux x64/arm64 平台适配已加入源码,尚未随新版本发布;Ubuntu x64 的官方内核启动、依赖加载和构建已验证,真实桌面账号、钥匙环与手机收发待验收。详见 [跨平台工作站](../docs/workstation-platforms.md) 与 [npm 分发](../docs/npm-distribution.md)。
4
+
5
+ 工作站服务让 Android/HarmonyOS 客户端使用同一个 Codex 内核。要求 Node >=22.12;Linux 使用官方 ChatGPT 桌面版内置内核,不以独立 CLI 代替点点能力。npm 包不包含 Node 或官方桌面版,也不修改官方程序文件。
6
+
7
+ ## 安装
8
+
9
+ 公开 beta 已发布到 npm,安装或更新使用:
10
+
11
+ ```powershell
12
+ npm i -g @walkhi/code-relax@latest --allow-scripts=@walkhi/code-relax --foreground-scripts
13
+ ```
14
+
15
+ 以下目录与环境变量配置说明适用于 Windows;Mac/Linux 见下方跨平台入口。首次安装前先保存工作,并从 Codex 菜单或 Windows 托盘完全退出 Codex。更新无需先停止正在运行的桌面服务;安装器会短暂停止、替换并恢复服务。npm 全局安装会自动执行 `setup` 和 Skill 安装。`setup` 将完整服务部署到 `%LOCALAPPDATA%\Programs\CodexRemote\service`,记录安装所用 Node 的绝对路径,不复制 Node。配置、日志和二维码以 `installation.json.stateRoot` 为准,通常位于 `%LOCALAPPDATA%\CodexRemote`,MSIX 环境可能解析到包缓存目录;附件位于 `%ProgramData%\CodexRemote\attachments`。
16
+
17
+ `setup` 立即启动桌面服务和固定 45839 Codex 内核,并启用当前用户登录自启;自启不打开 Codex。Codex 内核健康后配置当前用户 `CODEX_APP_SERVER_WS_URL`,之后正常启动的 Desktop 自动接入。若安装时 Desktop 仍未接入,安装器会关闭并重新打开 Desktop;安装前应保存工作。普通更新无接入或启动链迁移需求时复用现有内核。
18
+
19
+ ## Linux 源码测试
20
+
21
+ 公开 beta.30 尚不含 Linux,不能用上面的 registry 命令取得本轮修改。Linux 源码测试需要官方 ChatGPT 桌面版、Node >=22.12、`secret-tool` 和已解锁的 Secret Service;Ubuntu/Debian 的工具包为 `libsecret-tools`。以普通用户在图形桌面会话中安装,不使用 sudo 运行 npm/relax。
22
+
23
+ 数据与服务位于 XDG Data 下的 `CodexRemote`,状态位于 XDG State 下;登录启动使用 `~/.config/autostart/cn.walkhi.code-relax.desktop`(遵循 XDG_CONFIG_HOME)。只为启动的 Desktop 实例设置托管地址;普通应用菜单启动不会自动继承,需使用 `relax desktop-recover --confirm` 接入。目录、自定义 CODEX_HOME、运行时更新与验收边界见 [Linux 实现](../docs/workstation-platforms.md#linux-工作站2026-10-15)。Mac 的钥匙串/LaunchAgent 说明也在该文档。
24
+
25
+ ## Codex 内核
26
+
27
+ ```powershell
28
+ relax start # 启动/复用桌面服务、Codex 内核和已配置的 Code Relax 中继
29
+ relax pair # 启动/复用服务并打开配对二维码
30
+ relax doctor # 查看工作站是否正常及需要处理的问题
31
+ relax diag # 输出供客服或技术人员使用的详细诊断报告
32
+ relax kernel-restart --confirm # 重启内核并启用已选更新,等待新内核就绪
33
+ ```
34
+
35
+ Codex 内核由官方 `codex app-server --listen ws://127.0.0.1:45839` 提供执行服务,Desktop 和桌面服务均可连接;它只监听回环地址。桌面服务独立报告内核健康、Desktop 接入和手机可写状态;Desktop 退出后,手机仍可操作内核中的任务。同一 thread 已被另一个实例持有时,手机只读且可重新检测。
36
+
37
+ 发现待启用内核更新时,手机“重启并更新”和 `relax kernel-restart --confirm` 复用同一受控停启流程:先验证候选,再停止旧内核,确认新内核协议就绪后恢复 Desktop,并按清单清理不再运行的旧版保留目录。手机工作站详情可确认让 Desktop 接入Codex 内核,独立进程逐步执行并保留桌面服务/中继连接,见[启用流程](../docs/shared-recovery.md)。本机恢复命令为 `relax desktop-recover --confirm`,查看恢复记录用 `relax diag`。桌面服务状态匹配且健康时直接复用;未知进程占用 45831/45839 时拒绝接管。
38
+
39
+ Codex 内核通过隐藏控制台启动,使后续控制台子进程继承隐藏窗口。手机的项目、任务列表和新建任务均使用Codex 内核,不依赖 Desktop 工具管道;项目和任务 ID 保持原生值。
40
+
41
+ Codex 内核未就绪时,手机停留在连接界面并显示“Codex 内核未就绪”;桌面服务保留健康和恢复接口,聊天请求返回错误。
42
+
43
+ ## Dots 委派任务
44
+
45
+ `dots-tasks --json` 只读查询当前 ChatGPT 账号在托管 app-server 中的 Dots 委派任务及最近轮次状态。本机内核须已启动并登录 ChatGPT,凭据只用于工作站到官方主机的连接。任务合入 LAN 和 Code Relax 中继列表,按执行目录归入项目,标题旁显示云朵,委派正文与发送者分开显示。委派任务已接入发送/引导、停止、审批和实时事件;点点主聊天已接官方消息房间,保留独立正文与执行任务边界。当前实现与手机验证范围见 [Dots 接入](../docs/dots-integration.md)和[消息房间修复](../docs/dots-messaging-repair.md)。
46
+
47
+ 源码运行:
48
+
49
+ ```powershell
50
+ node bridge/bin/codex-remote.mjs dots-tasks --json
51
+ ```
52
+
53
+ 构建产物使用 `node bridge/dist/bin/codex-remote.mjs dots-tasks --json`;已更新的本机全局 CLI 使用 `relax dots-tasks --json`,公共 npm 包尚未同步这项功能。任务目录归属与执行环境不同,本次托管任务携带 Windows 工作目录;协议与实读证据见 [Dots 接入](../docs/dots-integration.md)。
54
+
55
+ ## Code Relax 中继
56
+
57
+ 私有中继仍需要本机桌面服务:
58
+
59
+ ```powershell
60
+ relax start --json
61
+ relax pair --json
62
+ ```
63
+
64
+ `start` 启动内核和桌面服务,并在桌面服务内恢复已配置中继。每台移动设备通过 `pair` 配对一次;新增设备不替换已有设备。桌面服务把 Windows hostname 和当前 Wi-Fi/有线网络候选经已认证 WebSocket 临时发送给手机,手机本地保存电脑名,只在设备身份验证通过后保存可达地址;服务器不持久化这些 Code Relax 中继 hostname、IP 或网络名。OpenAI 官方认证和设备配对仍直接访问 OpenAI;完成后,手机使用统一配对产生的设备凭据,通过独立元数据 WebSocket 把官方环境 ID、名称和 hostname 写入设备记录,不上传 OpenAI access token、配对码或登录态。
65
+
66
+ 桌面服务的局域网端口仅提供鉴权 WebSocket,普通 HTTP 请求返回 404;手机聊天页面随 HAP 安装。
67
+
68
+ ## 更新与卸载
69
+
70
+ 更新固定服务:
71
+
72
+ ```powershell
73
+ npm i -g @walkhi/code-relax@latest --allow-scripts=@walkhi/code-relax --foreground-scripts
74
+ ```
75
+
76
+ `setup` 先暂存并校验新版本,再短暂停止桌面服务/中继完成切换;原服务此前正在运行时会自动恢复,新版本启动失败则回滚并启动旧版本。无需提前停止服务。Node 路径变化或原 Node 被删除后需重新 setup。升级可能因 Node 可执行文件路径变化而再次触发 Windows 防火墙授权。
77
+
78
+ 卸载:
79
+
80
+ ```powershell
81
+ relax uninstall
82
+ npm uninstall -g @walkhi/code-relax
83
+ ```
84
+
85
+ `uninstall` 停止桌面服务后先核对并清理 Code Relax 所有的当前用户 `CODEX_APP_SERVER_WS_URL`;用户后来修改或不归属 Code Relax 的值会保留。随后删除固定程序和本包管理且未修改的 Skill;保留其他配置、Token、日志、长期设备凭据、附件和自定义 Skill。它不卸载 Node、Codex Desktop,也不强制结束完整控制的执行进程。
86
+
87
+ ## 发布边界
88
+
89
+ registry 默认安装使用 `@walkhi/code-relax@latest`,主 CLI 为 `relax`,并保留 `code-relax` 兼容入口。安装摘要通过 `--foreground-scripts` 显示,首次安装或启动链迁移时干净重启 Desktop,分别报告已安装与实际运行版本;登录启动使用当前用户任务计划。通信协议仍为 7。详细规则见 [npm 分发](../docs/npm-distribution.md)。
90
+
91
+ ### CLI 输出(2026-10-15)
92
+
93
+ 安装和启动成功后只显示结果与 `relax pair` 入口;配对成功显示二维码文件。`relax doctor` 报告工作站健康及实际问题,独立的 `relax diag` 默认用可读文本输出系统、Node、服务进程、逐项检查和 Desktop 接入排障记录;常规命令不再提供 `--detail`;`--json` 保留给安装器等现有程序调用。服务未运行或内核未就绪判为失败;未连接局域网只提示,不阻断中继使用。手机连通性不作为 CLI 检查项,doctor 不创建配对邀请。
94
+
95
+ `doctor` 默认逐项说明安装文件、工作站服务和 Codex 内核的当前状态,问题集中给出处理方法。上次 Desktop 接入记录仅由 `diag` 读取,并明确标注排障用途;常规 `doctor` 不读取该记录,不参与当前健康判定,不拼接互相矛盾的阶段消息与最终错误。
@@ -1,63 +1,63 @@
1
- #!/usr/bin/env node
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { createHmac } from 'node:crypto';
5
- import mysql from 'mysql2/promise';
6
- import { relayDiagnostics } from '../src/self-relay/diagnostics.mjs';
7
- import { purgeCancelledAccounts } from '../src/self-relay/account-cleanup.mjs';
8
-
9
- const config = JSON.parse(await fs.readFile('/etc/codex-relay/mysql.json', 'utf8'));
10
- const account = JSON.parse(await fs.readFile('/etc/codex-relay/account.json', 'utf8'));
11
- if (typeof account.codePepper !== 'string' || account.codePepper.length < 32) throw new Error('Invalid account cleanup configuration');
12
- const pool = mysql.createPool({ ...config, connectionLimit: 1, dateStrings: true, timezone: 'Z' });
13
- const startedAt = Date.now();
14
- let stage = 'accounts';
15
- const diagnostics = relayDiagnostics(pool, () => console.error('Account cleanup diagnostic write failed'));
16
- const record = (event, detail) => diagnostics.record('account-cleanup', event, detail);
17
- let purged = 0;
18
- try {
19
- await record('started', 'scheduled');
20
- purged = await purgeCancelledAccounts(pool, value => createHmac('sha256', account.codePepper).update(value).digest('hex'));
21
- await record('accounts-cleared', `count=${purged}`);
22
- stage = 'backups';
23
-
24
- // Only deployment SQL snapshots; never follow symlinks or remove release directories.
25
- const root = '/opt/codex-relay/releases';
26
- if (await fs.realpath(root) !== root) throw new Error('Unexpected backup root');
27
- const files = [];
28
- for (const entry of await fs.readdir(root, { withFileTypes: true })) {
29
- if (entry.isDirectory() && /^deploy-\d{8}T\d{6}Z$/.test(entry.name)) {
30
- files.push(path.join(root, entry.name, 'database.sql'));
31
- }
32
- }
33
- // Pre-deployment snapshots observed on this installation, before the release tool existed.
34
- for (const directory of ['before-email-20260925', 'before-usage-20260925']) {
35
- files.push(path.join(root, directory, 'code_relax.sql'));
36
- }
37
- let backups = 0;
38
- for (const file of files) {
39
- let stat;
40
- try { stat = await fs.lstat(file); } catch (error) { if (error.code === 'ENOENT') continue; throw error; }
41
- if (!stat.isFile() || stat.isSymbolicLink() || Date.now() - stat.mtimeMs < 86400000) continue;
42
- if (await fs.realpath(file) !== file) throw new Error('Unexpected backup path');
43
- await fs.unlink(file); backups++;
44
- }
45
- await record('backups-cleared', `count=${backups}`);
46
- stage = 'recovery-logs';
47
- // Rotate even on a quiet server so expired binlogs are actually purged.
48
- const maintenance = await mysql.createConnection({ user: 'root', socketPath: '/var/run/mysqld/mysqld.sock' });
49
- try {
50
- const [settings] = await maintenance.query('SELECT @@GLOBAL.binlog_expire_logs_seconds AS seconds, @@GLOBAL.binlog_expire_logs_auto_purge AS autoPurge');
51
- if (Number(settings[0].seconds) <= 0 || Number(settings[0].seconds) > 259200 || Number(settings[0].autoPurge) !== 1) {
52
- throw new Error('MySQL recovery-log retention exceeds account cleanup policy');
53
- }
54
- await maintenance.query('FLUSH BINARY LOGS');
55
- } finally { await maintenance.end(); }
56
- await record('completed', `users=${purged};backups=${backups};ms=${Date.now() - startedAt}`);
57
- console.log(JSON.stringify({ event: 'account-cleanup', purged, expiredBackups: backups, recoveryLogsRotated: true }));
58
- } catch (error) {
59
- // Codes and stage only: SQL errors may contain email addresses or credentials.
60
- const code = String(error.code || 'operation-failed').toLowerCase().replace(/[^a-z0-9-]/g, '-').slice(0, 32);
61
- await record('failed', `stage=${stage};code=${code}`);
62
- throw error;
63
- } finally { await pool.end(); }
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { createHmac } from 'node:crypto';
5
+ import mysql from 'mysql2/promise';
6
+ import { relayDiagnostics } from '../src/self-relay/diagnostics.mjs';
7
+ import { purgeCancelledAccounts } from '../src/self-relay/account-cleanup.mjs';
8
+
9
+ const config = JSON.parse(await fs.readFile('/etc/codex-relay/mysql.json', 'utf8'));
10
+ const account = JSON.parse(await fs.readFile('/etc/codex-relay/account.json', 'utf8'));
11
+ if (typeof account.codePepper !== 'string' || account.codePepper.length < 32) throw new Error('Invalid account cleanup configuration');
12
+ const pool = mysql.createPool({ ...config, connectionLimit: 1, dateStrings: true, timezone: 'Z' });
13
+ const startedAt = Date.now();
14
+ let stage = 'accounts';
15
+ const diagnostics = relayDiagnostics(pool, () => console.error('Account cleanup diagnostic write failed'));
16
+ const record = (event, detail) => diagnostics.record('account-cleanup', event, detail);
17
+ let purged = 0;
18
+ try {
19
+ await record('started', 'scheduled');
20
+ purged = await purgeCancelledAccounts(pool, value => createHmac('sha256', account.codePepper).update(value).digest('hex'));
21
+ await record('accounts-cleared', `count=${purged}`);
22
+ stage = 'backups';
23
+
24
+ // Only deployment SQL snapshots; never follow symlinks or remove release directories.
25
+ const root = '/opt/codex-relay/releases';
26
+ if (await fs.realpath(root) !== root) throw new Error('Unexpected backup root');
27
+ const files = [];
28
+ for (const entry of await fs.readdir(root, { withFileTypes: true })) {
29
+ if (entry.isDirectory() && /^deploy-\d{8}T\d{6}Z$/.test(entry.name)) {
30
+ files.push(path.join(root, entry.name, 'database.sql'));
31
+ }
32
+ }
33
+ // Pre-deployment snapshots observed on this installation, before the release tool existed.
34
+ for (const directory of ['before-email-20260925', 'before-usage-20260925']) {
35
+ files.push(path.join(root, directory, 'code_relax.sql'));
36
+ }
37
+ let backups = 0;
38
+ for (const file of files) {
39
+ let stat;
40
+ try { stat = await fs.lstat(file); } catch (error) { if (error.code === 'ENOENT') continue; throw error; }
41
+ if (!stat.isFile() || stat.isSymbolicLink() || Date.now() - stat.mtimeMs < 86400000) continue;
42
+ if (await fs.realpath(file) !== file) throw new Error('Unexpected backup path');
43
+ await fs.unlink(file); backups++;
44
+ }
45
+ await record('backups-cleared', `count=${backups}`);
46
+ stage = 'recovery-logs';
47
+ // Rotate even on a quiet server so expired binlogs are actually purged.
48
+ const maintenance = await mysql.createConnection({ user: 'root', socketPath: '/var/run/mysqld/mysqld.sock' });
49
+ try {
50
+ const [settings] = await maintenance.query('SELECT @@GLOBAL.binlog_expire_logs_seconds AS seconds, @@GLOBAL.binlog_expire_logs_auto_purge AS autoPurge');
51
+ if (Number(settings[0].seconds) <= 0 || Number(settings[0].seconds) > 259200 || Number(settings[0].autoPurge) !== 1) {
52
+ throw new Error('MySQL recovery-log retention exceeds account cleanup policy');
53
+ }
54
+ await maintenance.query('FLUSH BINARY LOGS');
55
+ } finally { await maintenance.end(); }
56
+ await record('completed', `users=${purged};backups=${backups};ms=${Date.now() - startedAt}`);
57
+ console.log(JSON.stringify({ event: 'account-cleanup', purged, expiredBackups: backups, recoveryLogsRotated: true }));
58
+ } catch (error) {
59
+ // Codes and stage only: SQL errors may contain email addresses or credentials.
60
+ const code = String(error.code || 'operation-failed').toLowerCase().replace(/[^a-z0-9-]/g, '-').slice(0, 32);
61
+ await record('failed', `stage=${stage};code=${code}`);
62
+ throw error;
63
+ } finally { await pool.end(); }