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

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 (187) 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 +63 -58
  7. package/dist/bin/publish-harmony-release.mjs +46 -0
  8. package/dist/bin/release-upload-server.mjs +174 -0
  9. package/dist/bin/self-relay-admin.mjs +8 -8
  10. package/dist/bin/self-relay-demo.mjs +33 -33
  11. package/dist/bin/self-relay-server.mjs +66 -66
  12. package/dist/build-platform.json +1 -1
  13. package/dist/get-lan-networks.ps1 +40 -40
  14. package/dist/get-shared-service-url.ps1 +6 -6
  15. package/dist/shared/app-server-events.cjs +422 -412
  16. package/dist/shared/code-browser.cjs +159 -159
  17. package/dist/shared/file-changes.cjs +31 -31
  18. package/dist/shared/file-citations.cjs +117 -117
  19. package/dist/shared/file-mentions.cjs +92 -92
  20. package/dist/shared/message-delivery.cjs +54 -54
  21. package/dist/shared/p2p-data-channel.cjs +72 -72
  22. package/dist/shared/p2p-diagnostics.cjs +28 -28
  23. package/dist/shared/p2p-file-channel.cjs +78 -74
  24. package/dist/shared/plan-pricing.cjs +32 -32
  25. package/dist/shared/question-attention.cjs +69 -0
  26. package/dist/shared/server-requests.cjs +219 -219
  27. package/dist/shared/timeline-sync.cjs +173 -173
  28. package/dist/skills/code-relax/SKILL.md +22 -22
  29. package/dist/src/archived-threads.mjs +22 -0
  30. package/dist/src/background-process.mjs +18 -18
  31. package/dist/src/bounded-process-log.mjs +63 -63
  32. package/dist/src/celsius-client.mjs +107 -107
  33. package/dist/src/code-browser.mjs +100 -100
  34. package/dist/src/desktop-launcher.mjs +46 -46
  35. package/dist/src/dots-approval-timeline.mjs +99 -99
  36. package/dist/src/dots-avatar.mjs +42 -42
  37. package/dist/src/dots-chat-projection.mjs +50 -50
  38. package/dist/src/dots-chat-server.mjs +461 -461
  39. package/dist/src/dots-message-images.mjs +319 -319
  40. package/dist/src/dots-profile.mjs +77 -77
  41. package/dist/src/dots-room-activity.mjs +94 -94
  42. package/dist/src/dots-room-client.mjs +135 -135
  43. package/dist/src/dots-room-projection.mjs +94 -94
  44. package/dist/src/dots-schedules.mjs +66 -66
  45. package/dist/src/dots-summary-tasks.mjs +32 -32
  46. package/dist/src/dots-task-catalog.mjs +430 -430
  47. package/dist/src/dots-task-files.mjs +52 -52
  48. package/dist/src/execution-health.mjs +26 -26
  49. package/dist/src/file-transfer-range.mjs +22 -22
  50. package/dist/src/file-upload-session.mjs +130 -130
  51. package/dist/src/hosted-history-app-server.mjs +103 -103
  52. package/dist/src/hosted-history-cache.mjs +113 -113
  53. package/dist/src/lan-socket-server.mjs +265 -263
  54. package/dist/src/managed-app-server.mjs +244 -244
  55. package/dist/src/message-images.mjs +9 -9
  56. package/dist/src/message-upload-files.mjs +74 -74
  57. package/dist/src/onboarding.mjs +12 -12
  58. package/dist/src/plan-pricing.mjs +47 -47
  59. package/dist/src/platform/README.md +12 -12
  60. package/dist/src/platform/codex-protocol.mjs +46 -46
  61. package/dist/src/platform/codex-runtime.mjs +3 -3
  62. package/dist/src/platform/credential-store.mjs +3 -3
  63. package/dist/src/platform/desktop-host.mjs +3 -3
  64. package/dist/src/platform/desktop-project-registration.mjs +3 -3
  65. package/dist/src/platform/desktop-project-store.mjs +208 -149
  66. package/dist/src/platform/file-opener.mjs +9 -0
  67. package/dist/src/platform/linux/README.md +3 -3
  68. package/dist/src/platform/linux/codex-runtime.mjs +56 -0
  69. package/dist/src/platform/linux/credential-store.mjs +28 -0
  70. package/dist/src/platform/linux/desktop-host.mjs +83 -0
  71. package/dist/src/platform/linux/desktop-installation.mjs +36 -0
  72. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
  73. package/dist/src/platform/linux/network-info.mjs +11 -0
  74. package/dist/src/platform/linux/process.mjs +42 -0
  75. package/dist/src/platform/linux/project-locations.mjs +17 -0
  76. package/dist/src/platform/linux/resident-startup.mjs +52 -0
  77. package/dist/src/platform/linux/retained-runtime.mjs +76 -0
  78. package/dist/src/platform/linux/service-host.mjs +18 -0
  79. package/dist/src/platform/linux/system.mjs +49 -0
  80. package/dist/src/platform/linux/user-environment.mjs +7 -0
  81. package/dist/src/platform/macos/README.md +3 -3
  82. package/dist/src/platform/macos/codex-runtime.mjs +125 -125
  83. package/dist/src/platform/macos/credential-store.mjs +10 -10
  84. package/dist/src/platform/macos/desktop-host.mjs +82 -82
  85. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -10
  86. package/dist/src/platform/macos/network-info.mjs +8 -28
  87. package/dist/src/platform/macos/project-locations.mjs +8 -8
  88. package/dist/src/platform/macos/resident-startup.mjs +84 -84
  89. package/dist/src/platform/macos/retained-runtime.mjs +127 -127
  90. package/dist/src/platform/macos/service-host.mjs +18 -18
  91. package/dist/src/platform/macos/system.mjs +29 -29
  92. package/dist/src/platform/macos/user-environment.mjs +7 -7
  93. package/dist/src/platform/native-package.mjs +58 -46
  94. package/dist/src/platform/network-info.mjs +3 -3
  95. package/dist/src/platform/path-identity.mjs +5 -5
  96. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  97. package/dist/src/platform/posix/runtime-paths.mjs +25 -25
  98. package/dist/src/platform/project-locations.mjs +3 -3
  99. package/dist/src/platform/runtime-paths.mjs +8 -8
  100. package/dist/src/platform/select.mjs +5 -5
  101. package/dist/src/platform/service-host.mjs +3 -3
  102. package/dist/src/platform/user-environment.mjs +3 -3
  103. package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
  104. package/dist/src/platform/windows/activate-desktop.ps1 +48 -48
  105. package/dist/src/platform/windows/background-process.mjs +30 -30
  106. package/dist/src/platform/windows/codex-runtime.mjs +215 -215
  107. package/dist/src/platform/windows/credential-store.mjs +27 -27
  108. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  109. package/dist/src/platform/windows/desktop-helper.mjs +179 -179
  110. package/dist/src/platform/windows/desktop-host.mjs +58 -58
  111. package/dist/src/platform/windows/desktop-info.ps1 +14 -14
  112. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
  113. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
  114. package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -21
  115. package/dist/src/platform/windows/dots-profile.mjs +49 -49
  116. package/dist/src/platform/windows/launch-worker.mjs +41 -41
  117. package/dist/src/platform/windows/network-info.mjs +34 -34
  118. package/dist/src/platform/windows/project-locations.mjs +16 -16
  119. package/dist/src/platform/windows/resident-start.js +20 -20
  120. package/dist/src/platform/windows/resident-start.ps1 +7 -7
  121. package/dist/src/platform/windows/resident-startup.ps1 +83 -83
  122. package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
  123. package/dist/src/platform/windows/runtime-paths.mjs +20 -20
  124. package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
  125. package/dist/src/platform/windows/service-host.mjs +31 -31
  126. package/dist/src/platform/windows/start-hidden-console.ps1 +49 -49
  127. package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
  128. package/dist/src/platform/windows/user-environment.mjs +50 -50
  129. package/dist/src/platform/windows/user-environment.ps1 +41 -41
  130. package/dist/src/project-archive.mjs +30 -0
  131. package/dist/src/project-create.mjs +121 -119
  132. package/dist/src/project-management.mjs +59 -69
  133. package/dist/src/project-remove.mjs +55 -0
  134. package/dist/src/project-update.mjs +56 -0
  135. package/dist/src/runtime-discovery.mjs +47 -47
  136. package/dist/src/self-relay/account-cleanup.mjs +30 -30
  137. package/dist/src/self-relay/account-connections.mjs +52 -52
  138. package/dist/src/self-relay/admin-cloud.mjs +101 -101
  139. package/dist/src/self-relay/admin-page.mjs +80 -80
  140. package/dist/src/self-relay/admin-server.mjs +74 -74
  141. package/dist/src/self-relay/admin-state.mjs +127 -127
  142. package/dist/src/self-relay/admin-style.mjs +14 -14
  143. package/dist/src/self-relay/android-releases.mjs +61 -61
  144. package/dist/src/self-relay/client.mjs +61 -61
  145. package/dist/src/self-relay/connector.mjs +439 -438
  146. package/dist/src/self-relay/debug-allowlist.mjs +33 -33
  147. package/dist/src/self-relay/demo.mjs +37 -37
  148. package/dist/src/self-relay/device-info.mjs +27 -0
  149. package/dist/src/self-relay/device-store.mjs +92 -84
  150. package/dist/src/self-relay/diagnostics.mjs +54 -54
  151. package/dist/src/self-relay/email-accounts.mjs +183 -183
  152. package/dist/src/self-relay/file-wire.mjs +13 -13
  153. package/dist/src/self-relay/lan-authorizations.mjs +71 -42
  154. package/dist/src/self-relay/lifecycle.mjs +264 -250
  155. package/dist/src/self-relay/long-pairing.mjs +40 -40
  156. package/dist/src/self-relay/mysql-state.mjs +118 -105
  157. package/dist/src/self-relay/p2p-probe.mjs +159 -159
  158. package/dist/src/self-relay/privacy-page.mjs +26 -26
  159. package/dist/src/self-relay/public-releases.mjs +52 -0
  160. package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
  161. package/dist/src/self-relay/server.mjs +1006 -932
  162. package/dist/src/self-relay/usage-daily.mjs +55 -55
  163. package/dist/src/self-relay/usage-meter.mjs +141 -141
  164. package/dist/src/self-relay/windows-route-helper.mjs +75 -75
  165. package/dist/src/self-relay/wire.mjs +36 -36
  166. package/dist/src/server.mjs +1861 -1778
  167. package/dist/src/service-control.mjs +82 -82
  168. package/dist/src/service-doctor.mjs +110 -110
  169. package/dist/src/service-health.mjs +33 -33
  170. package/dist/src/service-install.mjs +91 -91
  171. package/dist/src/service-lifecycle.mjs +138 -138
  172. package/dist/src/service-restart.mjs +81 -81
  173. package/dist/src/shared-app-server.mjs +615 -611
  174. package/dist/src/shared-catalog.mjs +132 -132
  175. package/dist/src/shared-recovery.mjs +146 -146
  176. package/dist/src/shared-thread-stream.mjs +164 -164
  177. package/dist/src/skill-install.mjs +51 -51
  178. package/dist/src/thread-title-generation.mjs +150 -150
  179. package/dist/src/turn-model-history.mjs +47 -47
  180. package/dist/src/usage-reset.mjs +17 -17
  181. package/dist/src/workstation-downloads.mjs +120 -120
  182. package/dist/tools/rtc-route-helper.ps1 +99 -99
  183. package/dist/tools/start-rtc-route-helper.ps1 +15 -15
  184. package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
  185. package/dist/web/vendor/markdown-it.umd.min.js +13 -13
  186. package/package.json +7 -6
  187. package/tools/postinstall.mjs +127 -127
@@ -1,74 +1,74 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import sharp from 'sharp';
4
- import media from '../shared/code-browser.cjs';
5
- import { validateInlineImage } from './message-images.mjs';
6
- import { MAX_FILE_BYTES, MAX_FILE_COUNT, MAX_TOTAL_BYTES } from './file-upload-session.mjs';
7
-
8
- const mimeTypes = { pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv',
9
- json: 'application/json', zip: 'application/zip', doc: 'application/msword',
10
- docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
11
- xls: 'application/vnd.ms-excel', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
12
- ppt: 'application/vnd.ms-powerpoint', pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' };
13
-
14
- async function localFile(file, directory) {
15
- if (typeof file?.path !== 'string' || typeof file.name !== 'string' || !file.name || /[\r\n\0]/.test(file.name)) {
16
- throw new Error('附件信息无效,请重新选择。');
17
- }
18
- const root = await fs.realpath(directory), resolved = await fs.realpath(file.path);
19
- if (path.relative(root, path.dirname(resolved)) !== '') throw new Error('附件必须来自本次工作站上传目录。');
20
- const handle = await fs.open(resolved, 'r');
21
- try {
22
- const stat = await handle.stat();
23
- if (!stat.isFile() || stat.size < 1 || stat.size > MAX_FILE_BYTES) throw new Error('附件为空或超过 20 MiB。');
24
- const bytes = await handle.readFile();
25
- if (bytes.length !== stat.size) throw new Error('附件在读取时发生变化,请重新上传。');
26
- const mimeType = media.mediaMime(file) || mimeTypes[path.extname(file.name).slice(1).toLowerCase()] || 'application/octet-stream';
27
- if (!/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i.test(mimeType)) throw new Error('附件类型无效。');
28
- return { name: file.name, mimeType, bytes };
29
- } finally { await handle.close(); }
30
- }
31
-
32
- export async function readMessageUploads(body, uploadsRoot) {
33
- const images = body.images ?? [], originals = body.originalImages ?? [], files = body.files ?? [];
34
- if (![images, originals, files].every(Array.isArray) || images.length + originals.length > 4 || files.length + originals.length > MAX_FILE_COUNT) {
35
- throw new Error('每条消息最多 4 张图片、4 个文件。');
36
- }
37
- const uploads = [];
38
- let imageBytes = 0, fileBytes = 0;
39
- for (const image of images) {
40
- let file;
41
- if (typeof image === 'string' && image.startsWith('data:')) {
42
- validateInlineImage(image);
43
- const mimeType = image.slice(5, image.indexOf(';'));
44
- file = { name: `image.${mimeType.split('/')[1]}`, mimeType, bytes: Buffer.from(image.slice(image.indexOf(',') + 1), 'base64') };
45
- } else {
46
- if (typeof image !== 'string' || !/^[a-f0-9]{64}$/.test(image)) throw new Error('图片标识无效。');
47
- let extension = 'webp';
48
- try { await fs.access(path.join(uploadsRoot, `${image}.webp`)); }
49
- catch (error) { if (error.code !== 'ENOENT') throw error; extension = 'png'; }
50
- file = await localFile({ path: path.join(uploadsRoot, `${image}.${extension}`), name: `image.${extension}` }, uploadsRoot);
51
- }
52
- imageBytes += file.bytes.length;
53
- if (imageBytes > 8 * 1024 * 1024) throw new Error('压缩图片合计不能超过 8 MiB。');
54
- uploads.push(file);
55
- }
56
- for (const original of originals) {
57
- if (typeof original !== 'string' || !/\.(png|jpe?g|webp|gif)$/i.test(original)) throw new Error('原图路径无效。');
58
- }
59
- for (const file of [...originals.map(value => ({ path: value, name: path.basename(value) })), ...files]) {
60
- const uploaded = await localFile(file, path.join(uploadsRoot, 'incoming'));
61
- fileBytes += uploaded.bytes.length;
62
- if (fileBytes > MAX_TOTAL_BYTES) throw new Error('文件合计不能超过 40 MiB。');
63
- uploads.push(uploaded);
64
- }
65
- for (const file of uploads) {
66
- if (/^image\/(?:png|jpeg|webp|gif)$/.test(file.mimeType)) {
67
- const metadata = await sharp(file.bytes).metadata();
68
- file.width = metadata.width; file.height = metadata.height;
69
- if (!file.width || !file.height) throw new Error('无法读取 图片尺寸。');
70
- }
71
- }
72
- if (uploads.filter(file => file.width).length > 4) throw new Error('每条消息最多 4 张图片。');
73
- return uploads;
74
- }
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import sharp from 'sharp';
4
+ import media from '../shared/code-browser.cjs';
5
+ import { validateInlineImage } from './message-images.mjs';
6
+ import { MAX_FILE_BYTES, MAX_FILE_COUNT, MAX_TOTAL_BYTES } from './file-upload-session.mjs';
7
+
8
+ const mimeTypes = { pdf: 'application/pdf', txt: 'text/plain', md: 'text/markdown', csv: 'text/csv',
9
+ json: 'application/json', zip: 'application/zip', doc: 'application/msword',
10
+ docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
11
+ xls: 'application/vnd.ms-excel', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
12
+ ppt: 'application/vnd.ms-powerpoint', pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' };
13
+
14
+ async function localFile(file, directory) {
15
+ if (typeof file?.path !== 'string' || typeof file.name !== 'string' || !file.name || /[\r\n\0]/.test(file.name)) {
16
+ throw new Error('附件信息无效,请重新选择。');
17
+ }
18
+ const root = await fs.realpath(directory), resolved = await fs.realpath(file.path);
19
+ if (path.relative(root, path.dirname(resolved)) !== '') throw new Error('附件必须来自本次工作站上传目录。');
20
+ const handle = await fs.open(resolved, 'r');
21
+ try {
22
+ const stat = await handle.stat();
23
+ if (!stat.isFile() || stat.size < 1 || stat.size > MAX_FILE_BYTES) throw new Error('附件为空或超过 20 MiB。');
24
+ const bytes = await handle.readFile();
25
+ if (bytes.length !== stat.size) throw new Error('附件在读取时发生变化,请重新上传。');
26
+ const mimeType = media.mediaMime(file) || mimeTypes[path.extname(file.name).slice(1).toLowerCase()] || 'application/octet-stream';
27
+ if (!/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i.test(mimeType)) throw new Error('附件类型无效。');
28
+ return { name: file.name, mimeType, bytes };
29
+ } finally { await handle.close(); }
30
+ }
31
+
32
+ export async function readMessageUploads(body, uploadsRoot) {
33
+ const images = body.images ?? [], originals = body.originalImages ?? [], files = body.files ?? [];
34
+ if (![images, originals, files].every(Array.isArray) || images.length + originals.length > 4 || files.length + originals.length > MAX_FILE_COUNT) {
35
+ throw new Error('每条消息最多 4 张图片、4 个文件。');
36
+ }
37
+ const uploads = [];
38
+ let imageBytes = 0, fileBytes = 0;
39
+ for (const image of images) {
40
+ let file;
41
+ if (typeof image === 'string' && image.startsWith('data:')) {
42
+ validateInlineImage(image);
43
+ const mimeType = image.slice(5, image.indexOf(';'));
44
+ file = { name: `image.${mimeType.split('/')[1]}`, mimeType, bytes: Buffer.from(image.slice(image.indexOf(',') + 1), 'base64') };
45
+ } else {
46
+ if (typeof image !== 'string' || !/^[a-f0-9]{64}$/.test(image)) throw new Error('图片标识无效。');
47
+ let extension = 'webp';
48
+ try { await fs.access(path.join(uploadsRoot, `${image}.webp`)); }
49
+ catch (error) { if (error.code !== 'ENOENT') throw error; extension = 'png'; }
50
+ file = await localFile({ path: path.join(uploadsRoot, `${image}.${extension}`), name: `image.${extension}` }, uploadsRoot);
51
+ }
52
+ imageBytes += file.bytes.length;
53
+ if (imageBytes > 8 * 1024 * 1024) throw new Error('压缩图片合计不能超过 8 MiB。');
54
+ uploads.push(file);
55
+ }
56
+ for (const original of originals) {
57
+ if (typeof original !== 'string' || !/\.(png|jpe?g|webp|gif)$/i.test(original)) throw new Error('原图路径无效。');
58
+ }
59
+ for (const file of [...originals.map(value => ({ path: value, name: path.basename(value) })), ...files]) {
60
+ const uploaded = await localFile(file, path.join(uploadsRoot, 'incoming'));
61
+ fileBytes += uploaded.bytes.length;
62
+ if (fileBytes > MAX_TOTAL_BYTES) throw new Error('文件合计不能超过 40 MiB。');
63
+ uploads.push(uploaded);
64
+ }
65
+ for (const file of uploads) {
66
+ if (/^image\/(?:png|jpeg|webp|gif)$/.test(file.mimeType)) {
67
+ const metadata = await sharp(file.bytes).metadata();
68
+ file.width = metadata.width; file.height = metadata.height;
69
+ if (!file.width || !file.height) throw new Error('无法读取 图片尺寸。');
70
+ }
71
+ }
72
+ if (uploads.filter(file => file.width).length > 4) throw new Error('每条消息最多 4 张图片。');
73
+ return uploads;
74
+ }
@@ -1,12 +1,12 @@
1
- export function phonePairingSteps(_transportMode, paired = false) {
2
- return paired ? ['已配对设备可直接连接。添加设备:relax pair']
3
- : ['生成配对二维码:relax pair', '用 Code Relax App 扫码添加工作站。'];
4
- }
5
- export const desktopSetupSteps = [
6
- '工作站服务和 Codex 内核已就绪,已启用登录自启。',
7
- '生成配对二维码:relax pair',
8
- ];
9
-
10
- export function pairingDisplayText(nextSteps) {
11
- return ['Code Relax 工作站已启动。', ...nextSteps].join('\n');
12
- }
1
+ export function phonePairingSteps(_transportMode, paired = false) {
2
+ return paired ? ['已配对设备可直接连接。添加设备:relax pair']
3
+ : ['生成配对二维码:relax pair', '用 Code Relax App 扫码添加工作站。'];
4
+ }
5
+ export const desktopSetupSteps = [
6
+ '工作站服务和 Codex 内核已就绪,已启用登录自启。',
7
+ '生成配对二维码:relax pair',
8
+ ];
9
+
10
+ export function pairingDisplayText(nextSteps) {
11
+ return ['Code Relax 工作站已启动。', ...nextSteps].join('\n');
12
+ }
@@ -1,47 +1,47 @@
1
- import http2 from 'node:http2';
2
- import pricing from '../shared/plan-pricing.cjs';
3
-
4
- export async function readPlanPricing(client) {
5
- await client.connect();
6
- const token = async () => (await client.request('getAuthStatus', { includeToken: true, refreshToken: false })).authToken;
7
- const authToken = await token();
8
- if (!authToken) return null;
9
- const identity = value => {
10
- const auth = JSON.parse(Buffer.from(value.split('.')[1], 'base64url'))['https://api.openai.com/auth'];
11
- return JSON.stringify([auth?.chatgpt_account_id, auth?.chatgpt_user_id]);
12
- };
13
- const initialIdentity = identity(authToken), accountId = JSON.parse(initialIdentity)[0];
14
- if (!accountId) return null;
15
- const session = http2.connect('https://chatgpt.com');
16
- session.on('error', () => {}); // Each stream reports its own request error.
17
- try {
18
- const get = route => new Promise((resolve, reject) => {
19
- const request = session.request({ ':method': 'GET', ':path': '/backend-api' + route,
20
- authorization: `Bearer ${authToken}`, 'chatgpt-account-id': accountId,
21
- 'user-agent': 'Code-Relax/0.1.0', accept: 'application/json' });
22
- let body = '', status, size = 0, ended = false;
23
- const timer = setTimeout(() => request.destroy(new Error('套餐费用查询超时。')), 15000);
24
- request.setEncoding('utf8');
25
- request.on('response', headers => { status = headers[':status']; });
26
- request.on('data', chunk => {
27
- size += Buffer.byteLength(chunk);
28
- if (size > 1024 * 1024) request.destroy(new Error('套餐费用响应过大。'));
29
- else body += chunk;
30
- });
31
- request.on('error', reject);
32
- request.on('close', () => { clearTimeout(timer); if (!ended) reject(new Error('套餐费用连接已断开。')); });
33
- request.on('end', () => {
34
- ended = true;
35
- try {
36
- if (status !== 200) throw new Error(`套餐费用查询失败(HTTP ${status})。`);
37
- resolve(JSON.parse(body));
38
- } catch (error) { reject(error); }
39
- });
40
- request.end();
41
- });
42
- const value = await pricing.read(accountId, get);
43
- const current = await token();
44
- if (!current || identity(current) !== initialIdentity) throw new Error('套餐查询期间账号已变化。');
45
- return value;
46
- } finally { session.destroy(); }
47
- }
1
+ import http2 from 'node:http2';
2
+ import pricing from '../shared/plan-pricing.cjs';
3
+
4
+ export async function readPlanPricing(client) {
5
+ await client.connect();
6
+ const token = async () => (await client.request('getAuthStatus', { includeToken: true, refreshToken: false })).authToken;
7
+ const authToken = await token();
8
+ if (!authToken) return null;
9
+ const identity = value => {
10
+ const auth = JSON.parse(Buffer.from(value.split('.')[1], 'base64url'))['https://api.openai.com/auth'];
11
+ return JSON.stringify([auth?.chatgpt_account_id, auth?.chatgpt_user_id]);
12
+ };
13
+ const initialIdentity = identity(authToken), accountId = JSON.parse(initialIdentity)[0];
14
+ if (!accountId) return null;
15
+ const session = http2.connect('https://chatgpt.com');
16
+ session.on('error', () => {}); // Each stream reports its own request error.
17
+ try {
18
+ const get = route => new Promise((resolve, reject) => {
19
+ const request = session.request({ ':method': 'GET', ':path': '/backend-api' + route,
20
+ authorization: `Bearer ${authToken}`, 'chatgpt-account-id': accountId,
21
+ 'user-agent': 'Code-Relax/0.1.0', accept: 'application/json' });
22
+ let body = '', status, size = 0, ended = false;
23
+ const timer = setTimeout(() => request.destroy(new Error('套餐费用查询超时。')), 15000);
24
+ request.setEncoding('utf8');
25
+ request.on('response', headers => { status = headers[':status']; });
26
+ request.on('data', chunk => {
27
+ size += Buffer.byteLength(chunk);
28
+ if (size > 1024 * 1024) request.destroy(new Error('套餐费用响应过大。'));
29
+ else body += chunk;
30
+ });
31
+ request.on('error', reject);
32
+ request.on('close', () => { clearTimeout(timer); if (!ended) reject(new Error('套餐费用连接已断开。')); });
33
+ request.on('end', () => {
34
+ ended = true;
35
+ try {
36
+ if (status !== 200) throw new Error(`套餐费用查询失败(HTTP ${status})。`);
37
+ resolve(JSON.parse(body));
38
+ } catch (error) { reject(error); }
39
+ });
40
+ request.end();
41
+ });
42
+ const value = await pricing.read(accountId, get);
43
+ const current = await token();
44
+ if (!current || identity(current) !== initialIdentity) throw new Error('套餐查询期间账号已变化。');
45
+ return value;
46
+ } finally { session.destroy(); }
47
+ }
@@ -1,12 +1,12 @@
1
- # 被控主机平台适配
2
-
3
- 当前只实现 Windows Codex:
4
-
5
- - `windows/runtime-paths.mjs`:固定服务、用户状态、Codex 数据、附件和长期凭据路径。
6
- - `windows/service-host.mjs`:网卡、Bridge 进程环境和旧状态迁移。
7
- - `windows/desktop-host.mjs`、PowerShell 脚本:普通/共享 Desktop、快捷方式和隐藏控制台启动。
8
- - `windows/credential-store.mjs/.ps1`:私有中继电脑凭据的 DPAPI 与 ACL。
9
-
10
- Windows 专有脚本兼容系统自带 PowerShell 5.1。通用生命周期使用 Node 接口;系统检测和凭据保护不扩散到业务代码。
11
-
12
- macOS、Linux 目录仅保留规划说明,尚无路径、IPC、进程或安装实现。接入排在控制端扩展之后,并需分别验证 Codex Desktop/app-server 的可用连接方式;只有独立 app-server 时不能宣称接管 Desktop 当前任务。
1
+ # 被控主机平台适配
2
+
3
+ 当前只实现 Windows Codex:
4
+
5
+ - `windows/runtime-paths.mjs`:固定服务、用户状态、Codex 数据、附件和长期凭据路径。
6
+ - `windows/service-host.mjs`:网卡、Bridge 进程环境和旧状态迁移。
7
+ - `windows/desktop-host.mjs`、PowerShell 脚本:普通/共享 Desktop、快捷方式和隐藏控制台启动。
8
+ - `windows/credential-store.mjs/.ps1`:私有中继电脑凭据的 DPAPI 与 ACL。
9
+
10
+ Windows 专有脚本兼容系统自带 PowerShell 5.1。通用生命周期使用 Node 接口;系统检测和凭据保护不扩散到业务代码。
11
+
12
+ macOS、Linux 目录仅保留规划说明,尚无路径、IPC、进程或安装实现。接入排在控制端扩展之后,并需分别验证 Codex Desktop/app-server 的可用连接方式;只有独立 app-server 时不能宣称接管 Desktop 当前任务。
@@ -1,46 +1,46 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { execFile } from 'node:child_process';
4
- import { promisify } from 'node:util';
5
-
6
- const exec = promisify(execFile);
7
- const requiredMethods = ['thread/start', 'thread/resume', 'thread/list', 'thread/turns/list',
8
- 'thread/items/list', 'turn/start', 'turn/steer', 'turn/interrupt', 'model/list', 'project/list',
9
- 'project/create', 'project/update', 'project/move',
10
- 'fs/getMetadata', 'fs/readDirectory', 'fs/createDirectory', 'command/exec',
11
- 'thread/settings/update', 'configRequirements/read', 'thread/queue/list', 'thread/queue/add',
12
- 'thread/queue/delete', 'thread/queue/start', 'thread/read', 'thread/unsubscribe', 'config/read', 'account/rateLimits/read'];
13
-
14
- export function compareVersions(a, b) {
15
- const parse = v => v.split(/[.-]/).map(part => /^\d+$/.test(part) ? Number(part) : part);
16
- const aa = parse(a), bb = parse(b);
17
- for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
18
- if (aa[i] === bb[i]) continue;
19
- if (aa[i] === undefined) return 1;
20
- if (bb[i] === undefined) return -1;
21
- return typeof aa[i] === 'number' && typeof bb[i] === 'number' ? aa[i] - bb[i] : String(aa[i]).localeCompare(String(bb[i]));
22
- }
23
- return 0;
24
- }
25
-
26
- export async function verifyProtocol(executable, stateRoot) {
27
- const directory = fs.mkdtempSync(path.join(stateRoot, 'cli-schema-'));
28
- try {
29
- await exec(executable, ['app-server', 'generate-json-schema', '--experimental', '--out', directory],
30
- { windowsHide: true, timeout: 30000, maxBuffer: 4 * 1024 * 1024 });
31
- const request = fs.readFileSync(path.join(directory, 'ClientRequest.json'), 'utf8');
32
- const missing = requiredMethods.filter(method => !request.includes(`"${method}"`));
33
- const turns = fs.readFileSync(path.join(directory, 'v2/ThreadTurnsListParams.json'), 'utf8');
34
- if (!turns.includes('"itemsView"') || !turns.includes('"summary"')) missing.push('turn summary pagination');
35
- if (missing.length) throw new Error(`缺少协议能力:${missing.join(', ')}`);
36
- } finally {
37
- const remove = dir => {
38
- for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
39
- const file = path.join(dir, item.name);
40
- if (item.isDirectory()) remove(file); else fs.unlinkSync(file);
41
- }
42
- fs.rmdirSync(dir);
43
- };
44
- remove(directory);
45
- }
46
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execFile } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+
6
+ const exec = promisify(execFile);
7
+ const requiredMethods = ['thread/start', 'thread/resume', 'thread/list', 'thread/turns/list',
8
+ 'thread/items/list', 'turn/start', 'turn/steer', 'turn/interrupt', 'model/list', 'project/list',
9
+ 'project/create', 'project/update', 'project/move',
10
+ 'fs/getMetadata', 'fs/readDirectory', 'fs/createDirectory', 'command/exec',
11
+ 'thread/settings/update', 'configRequirements/read', 'thread/queue/list', 'thread/queue/add',
12
+ 'thread/queue/delete', 'thread/queue/start', 'thread/read', 'thread/unsubscribe', 'config/read', 'account/rateLimits/read'];
13
+
14
+ export function compareVersions(a, b) {
15
+ const parse = v => v.split(/[.-]/).map(part => /^\d+$/.test(part) ? Number(part) : part);
16
+ const aa = parse(a), bb = parse(b);
17
+ for (let i = 0; i < Math.max(aa.length, bb.length); i++) {
18
+ if (aa[i] === bb[i]) continue;
19
+ if (aa[i] === undefined) return 1;
20
+ if (bb[i] === undefined) return -1;
21
+ return typeof aa[i] === 'number' && typeof bb[i] === 'number' ? aa[i] - bb[i] : String(aa[i]).localeCompare(String(bb[i]));
22
+ }
23
+ return 0;
24
+ }
25
+
26
+ export async function verifyProtocol(executable, stateRoot) {
27
+ const directory = fs.mkdtempSync(path.join(stateRoot, 'cli-schema-'));
28
+ try {
29
+ await exec(executable, ['app-server', 'generate-json-schema', '--experimental', '--out', directory],
30
+ { windowsHide: true, timeout: 30000, maxBuffer: 4 * 1024 * 1024 });
31
+ const request = fs.readFileSync(path.join(directory, 'ClientRequest.json'), 'utf8');
32
+ const missing = requiredMethods.filter(method => !request.includes(`"${method}"`));
33
+ const turns = fs.readFileSync(path.join(directory, 'v2/ThreadTurnsListParams.json'), 'utf8');
34
+ if (!turns.includes('"itemsView"') || !turns.includes('"summary"')) missing.push('turn summary pagination');
35
+ if (missing.length) throw new Error(`缺少协议能力:${missing.join(', ')}`);
36
+ } finally {
37
+ const remove = dir => {
38
+ for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
39
+ const file = path.join(dir, item.name);
40
+ if (item.isDirectory()) remove(file); else fs.unlinkSync(file);
41
+ }
42
+ fs.rmdirSync(dir);
43
+ };
44
+ remove(directory);
45
+ }
46
+ }
@@ -1,3 +1,3 @@
1
- import { platformModule } from './select.mjs';
2
- const adapter = await import(platformModule('codex-runtime'));
3
- export const { discoverCodex, resolveCodexRuntime, runtimeFilesAvailable, codeModeHostPath, runtimeFingerprint, pruneRetainedDesktopRuntimes } = adapter;
1
+ import { platformModule } from './select.mjs';
2
+ const adapter = await import(platformModule('codex-runtime'));
3
+ export const { discoverCodex, resolveCodexRuntime, runtimeFilesAvailable, codeModeHostPath, runtimeFingerprint, pruneRetainedDesktopRuntimes } = adapter;
@@ -1,3 +1,3 @@
1
- import { platformModule } from './select.mjs';
2
- const adapter = await import(platformModule('credential-store'));
3
- export const { credentialStore } = adapter;
1
+ import { platformModule } from './select.mjs';
2
+ const adapter = await import(platformModule('credential-store'));
3
+ export const { credentialStore } = adapter;
@@ -1,3 +1,3 @@
1
- import { platformModule } from './select.mjs';
2
- const adapter = await import(platformModule('desktop-host'));
3
- export const { desktopHost, resolveStateRoot, removeDesktopShortcut, residentStartup } = adapter;
1
+ import { platformModule } from './select.mjs';
2
+ const adapter = await import(platformModule('desktop-host'));
3
+ export const { desktopHost, resolveStateRoot, removeDesktopShortcut, residentStartup } = adapter;
@@ -1,3 +1,3 @@
1
- import { platformModule } from './select.mjs';
2
- const adapter = await import(platformModule('desktop-project-registration'));
3
- export const { withDesktopProjectRegistration } = adapter;
1
+ import { platformModule } from './select.mjs';
2
+ const adapter = await import(platformModule('desktop-project-registration'));
3
+ export const { withDesktopProjectRegistration } = adapter;