@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,127 +1,127 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { createHash, randomUUID } from 'node:crypto';
4
- import { readRecord, writeRecord } from '../../service-lifecycle.mjs';
5
-
6
- const owner = 'code-relax-macos-runtime';
7
- const manifestName = '.code-relax-runtime.json';
8
- function filesAt(root, relative = '') {
9
- const result = [];
10
- if (fs.lstatSync(root).isSymbolicLink()) throw new Error('Codex 运行时目录不能是符号链接。');
11
- for (const entry of fs.readdirSync(path.join(root, relative), { withFileTypes: true })) {
12
- const name = path.join(relative, entry.name);
13
- if (entry.isDirectory()) result.push(...filesAt(root, name));
14
- else if (entry.isFile()) result.push(name);
15
- else throw new Error(`Codex 运行时包含不支持的链接或文件:${name}`);
16
- }
17
- return result.sort();
18
- }
19
- export function packageRoot(executable) {
20
- const bundleSuffix = path.join('CodexCLI.app', 'Contents', 'MacOS', 'codex');
21
- const bundled = executable.endsWith(path.sep + bundleSuffix);
22
- const root = bundled ? path.resolve(executable, '../../../..') : path.dirname(path.dirname(executable));
23
- const metadata = readRecord(path.join(root, 'codex-package.json'));
24
- if ((!bundled && path.basename(path.dirname(executable)) !== 'bin') || path.basename(executable) !== 'codex'
25
- || metadata?.layoutVersion !== 1 || metadata.entrypoint !== 'bin/codex' || metadata.variant !== 'codex'
26
- || !['aarch64-apple-darwin', 'x86_64-apple-darwin'].includes(metadata.target)
27
- || metadata.resourcesDir !== 'codex-resources' || metadata.pathDir !== 'codex-path')
28
- throw new Error('需要完整的官方 Mac Codex 运行时包,不能仅复制 codex 单个文件。');
29
- for (const file of ['bin/codex', 'bin/codex-code-mode-host', 'codex-path/rg']) {
30
- if (!fs.statSync(path.join(root, file)).isFile()) throw new Error(`Codex 运行时缺少 ${file}`);
31
- }
32
- if (!fs.statSync(path.join(root, 'codex-resources')).isDirectory()) throw new Error('Codex 运行时缺少资源目录。');
33
- return root;
34
- }
35
- export function runtimeFingerprint(executable) {
36
- const root = packageRoot(executable);
37
- return JSON.stringify(filesAt(root).filter(name => name !== manifestName).map(name => {
38
- const stat = fs.statSync(path.join(root, name));
39
- return [name, stat.size, stat.mtimeMs, stat.ctimeMs, stat.ino, stat.mode & 0o777];
40
- }));
41
- }
42
- async function digestFiles(root) {
43
- const records = [];
44
- for (const name of filesAt(root).filter(name => name !== manifestName)) {
45
- const file = path.join(root, name), hash = createHash('sha256');
46
- const before = fs.statSync(file);
47
- for await (const chunk of fs.createReadStream(file)) hash.update(chunk);
48
- const after = fs.statSync(file);
49
- if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || before.ino !== after.ino)
50
- throw new Error('Codex 更新正在替换文件,请待安装完成后重试。');
51
- records.push({ name, size: after.size, mode: after.mode & 0o777, sha256: hash.digest('hex') });
52
- }
53
- return records;
54
- }
55
- const treeHash = files => createHash('sha256').update(JSON.stringify(files)).digest('hex');
56
- function removeFiles(root, names) {
57
- for (const name of names) {
58
- fs.unlinkSync(path.join(root, name));
59
- }
60
- // Empty package resource directories must also be removed, without recursive deletion.
61
- const collect = directory => {
62
- for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
63
- if (!entry.isDirectory()) throw new Error('运行时清理期间出现未知文件,保留目录。');
64
- const child = path.join(directory, entry.name); collect(child); fs.rmdirSync(child);
65
- }
66
- };
67
- collect(root);
68
- fs.rmdirSync(root);
69
- }
70
-
71
- export async function retainRuntime(executable, stateRoot, version) {
72
- const source = packageRoot(executable);
73
- const sourceFingerprint = runtimeFingerprint(executable);
74
- const files = await digestFiles(source);
75
- if (sourceFingerprint !== runtimeFingerprint(executable)) throw new Error('Codex 包在核对期间发生变化,未切换内核。');
76
- const digest = treeHash(files);
77
- const parent = path.join(stateRoot, 'macos-runtimes'), target = path.join(parent, digest);
78
- const retained = path.join(target, path.relative(source, executable));
79
- if (fs.existsSync(target)) {
80
- const manifest = readRecord(path.join(target, manifestName));
81
- if (manifest?.owner !== owner || manifest.digest !== digest || treeHash(await digestFiles(target)) !== digest)
82
- throw new Error('已保留的 Mac 内核内容不完整,未覆盖运行中的文件。');
83
- return { executable: retained, sourceExecutable: executable, sourceFingerprint };
84
- }
85
- fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
86
- const staging = path.join(parent, `.${randomUUID()}.tmp`);
87
- fs.mkdirSync(staging, { mode: 0o700 });
88
- const created = [];
89
- try {
90
- for (const file of files) {
91
- const destination = path.join(staging, file.name);
92
- fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
93
- // APFS can clone blocks, but each snapshot has independent file identity.
94
- // A hard link would let an in-place npm update modify the running kernel.
95
- fs.copyFileSync(path.join(source, file.name), destination, fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE);
96
- created.push(file.name);
97
- fs.chmodSync(destination, file.mode);
98
- }
99
- fs.mkdirSync(path.join(staging, 'codex-resources'), { recursive: true, mode: 0o700 });
100
- if (treeHash(await digestFiles(staging)) !== digest || sourceFingerprint !== runtimeFingerprint(executable))
101
- throw new Error('复制期间 Codex 包发生变化,未使用混合版本。');
102
- writeRecord(path.join(staging, manifestName), { owner, digest, version, files });
103
- created.push(manifestName);
104
- fs.renameSync(staging, target);
105
- return { executable: retained, sourceExecutable: executable, sourceFingerprint };
106
- } catch (error) {
107
- if (fs.existsSync(staging)) removeFiles(staging, created);
108
- throw error;
109
- }
110
- }
111
-
112
- export async function pruneRetainedDesktopRuntimes(stateRoot, keepExecutable) {
113
- const parent = path.join(stateRoot, 'macos-runtimes');
114
- if (!fs.existsSync(parent)) return;
115
- const keep = packageRoot(path.resolve(keepExecutable));
116
- // An installed (non-retained) process is not authority to clean this store.
117
- if (path.dirname(keep) !== path.resolve(parent) || !/^[a-f0-9]{64}$/.test(path.basename(keep))) return;
118
- for (const entry of fs.readdirSync(parent, { withFileTypes: true })) {
119
- const directory = path.join(parent, entry.name);
120
- if (!entry.isDirectory() || !/^[a-f0-9]{64}$/.test(entry.name) || directory === keep) continue;
121
- const manifest = readRecord(path.join(directory, manifestName));
122
- if (manifest?.owner !== owner || manifest.digest !== entry.name || !Array.isArray(manifest.files)) continue;
123
- const actual = await digestFiles(directory);
124
- if (treeHash(actual) !== entry.name || JSON.stringify(actual) !== JSON.stringify(manifest.files)) continue;
125
- removeFiles(directory, [...actual.map(file => file.name), manifestName]);
126
- }
127
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
+ import { readRecord, writeRecord } from '../../service-lifecycle.mjs';
5
+
6
+ const owner = 'code-relax-macos-runtime';
7
+ const manifestName = '.code-relax-runtime.json';
8
+ function filesAt(root, relative = '') {
9
+ const result = [];
10
+ if (fs.lstatSync(root).isSymbolicLink()) throw new Error('Codex 运行时目录不能是符号链接。');
11
+ for (const entry of fs.readdirSync(path.join(root, relative), { withFileTypes: true })) {
12
+ const name = path.join(relative, entry.name);
13
+ if (entry.isDirectory()) result.push(...filesAt(root, name));
14
+ else if (entry.isFile()) result.push(name);
15
+ else throw new Error(`Codex 运行时包含不支持的链接或文件:${name}`);
16
+ }
17
+ return result.sort();
18
+ }
19
+ export function packageRoot(executable) {
20
+ const bundleSuffix = path.join('CodexCLI.app', 'Contents', 'MacOS', 'codex');
21
+ const bundled = executable.endsWith(path.sep + bundleSuffix);
22
+ const root = bundled ? path.resolve(executable, '../../../..') : path.dirname(path.dirname(executable));
23
+ const metadata = readRecord(path.join(root, 'codex-package.json'));
24
+ if ((!bundled && path.basename(path.dirname(executable)) !== 'bin') || path.basename(executable) !== 'codex'
25
+ || metadata?.layoutVersion !== 1 || metadata.entrypoint !== 'bin/codex' || metadata.variant !== 'codex'
26
+ || !['aarch64-apple-darwin', 'x86_64-apple-darwin'].includes(metadata.target)
27
+ || metadata.resourcesDir !== 'codex-resources' || metadata.pathDir !== 'codex-path')
28
+ throw new Error('需要完整的官方 Mac Codex 运行时包,不能仅复制 codex 单个文件。');
29
+ for (const file of ['bin/codex', 'bin/codex-code-mode-host', 'codex-path/rg']) {
30
+ if (!fs.statSync(path.join(root, file)).isFile()) throw new Error(`Codex 运行时缺少 ${file}`);
31
+ }
32
+ if (!fs.statSync(path.join(root, 'codex-resources')).isDirectory()) throw new Error('Codex 运行时缺少资源目录。');
33
+ return root;
34
+ }
35
+ export function runtimeFingerprint(executable) {
36
+ const root = packageRoot(executable);
37
+ return JSON.stringify(filesAt(root).filter(name => name !== manifestName).map(name => {
38
+ const stat = fs.statSync(path.join(root, name));
39
+ return [name, stat.size, stat.mtimeMs, stat.ctimeMs, stat.ino, stat.mode & 0o777];
40
+ }));
41
+ }
42
+ async function digestFiles(root) {
43
+ const records = [];
44
+ for (const name of filesAt(root).filter(name => name !== manifestName)) {
45
+ const file = path.join(root, name), hash = createHash('sha256');
46
+ const before = fs.statSync(file);
47
+ for await (const chunk of fs.createReadStream(file)) hash.update(chunk);
48
+ const after = fs.statSync(file);
49
+ if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs || before.ino !== after.ino)
50
+ throw new Error('Codex 更新正在替换文件,请待安装完成后重试。');
51
+ records.push({ name, size: after.size, mode: after.mode & 0o777, sha256: hash.digest('hex') });
52
+ }
53
+ return records;
54
+ }
55
+ const treeHash = files => createHash('sha256').update(JSON.stringify(files)).digest('hex');
56
+ function removeFiles(root, names) {
57
+ for (const name of names) {
58
+ fs.unlinkSync(path.join(root, name));
59
+ }
60
+ // Empty package resource directories must also be removed, without recursive deletion.
61
+ const collect = directory => {
62
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
63
+ if (!entry.isDirectory()) throw new Error('运行时清理期间出现未知文件,保留目录。');
64
+ const child = path.join(directory, entry.name); collect(child); fs.rmdirSync(child);
65
+ }
66
+ };
67
+ collect(root);
68
+ fs.rmdirSync(root);
69
+ }
70
+
71
+ export async function retainRuntime(executable, stateRoot, version) {
72
+ const source = packageRoot(executable);
73
+ const sourceFingerprint = runtimeFingerprint(executable);
74
+ const files = await digestFiles(source);
75
+ if (sourceFingerprint !== runtimeFingerprint(executable)) throw new Error('Codex 包在核对期间发生变化,未切换内核。');
76
+ const digest = treeHash(files);
77
+ const parent = path.join(stateRoot, 'macos-runtimes'), target = path.join(parent, digest);
78
+ const retained = path.join(target, path.relative(source, executable));
79
+ if (fs.existsSync(target)) {
80
+ const manifest = readRecord(path.join(target, manifestName));
81
+ if (manifest?.owner !== owner || manifest.digest !== digest || treeHash(await digestFiles(target)) !== digest)
82
+ throw new Error('已保留的 Mac 内核内容不完整,未覆盖运行中的文件。');
83
+ return { executable: retained, sourceExecutable: executable, sourceFingerprint };
84
+ }
85
+ fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
86
+ const staging = path.join(parent, `.${randomUUID()}.tmp`);
87
+ fs.mkdirSync(staging, { mode: 0o700 });
88
+ const created = [];
89
+ try {
90
+ for (const file of files) {
91
+ const destination = path.join(staging, file.name);
92
+ fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
93
+ // APFS can clone blocks, but each snapshot has independent file identity.
94
+ // A hard link would let an in-place npm update modify the running kernel.
95
+ fs.copyFileSync(path.join(source, file.name), destination, fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE);
96
+ created.push(file.name);
97
+ fs.chmodSync(destination, file.mode);
98
+ }
99
+ fs.mkdirSync(path.join(staging, 'codex-resources'), { recursive: true, mode: 0o700 });
100
+ if (treeHash(await digestFiles(staging)) !== digest || sourceFingerprint !== runtimeFingerprint(executable))
101
+ throw new Error('复制期间 Codex 包发生变化,未使用混合版本。');
102
+ writeRecord(path.join(staging, manifestName), { owner, digest, version, files });
103
+ created.push(manifestName);
104
+ fs.renameSync(staging, target);
105
+ return { executable: retained, sourceExecutable: executable, sourceFingerprint };
106
+ } catch (error) {
107
+ if (fs.existsSync(staging)) removeFiles(staging, created);
108
+ throw error;
109
+ }
110
+ }
111
+
112
+ export async function pruneRetainedDesktopRuntimes(stateRoot, keepExecutable) {
113
+ const parent = path.join(stateRoot, 'macos-runtimes');
114
+ if (!fs.existsSync(parent)) return;
115
+ const keep = packageRoot(path.resolve(keepExecutable));
116
+ // An installed (non-retained) process is not authority to clean this store.
117
+ if (path.dirname(keep) !== path.resolve(parent) || !/^[a-f0-9]{64}$/.test(path.basename(keep))) return;
118
+ for (const entry of fs.readdirSync(parent, { withFileTypes: true })) {
119
+ const directory = path.join(parent, entry.name);
120
+ if (!entry.isDirectory() || !/^[a-f0-9]{64}$/.test(entry.name) || directory === keep) continue;
121
+ const manifest = readRecord(path.join(directory, manifestName));
122
+ if (manifest?.owner !== owner || manifest.digest !== entry.name || !Array.isArray(manifest.files)) continue;
123
+ const actual = await digestFiles(directory);
124
+ if (treeHash(actual) !== entry.name || JSON.stringify(actual) !== JSON.stringify(manifest.files)) continue;
125
+ removeFiles(directory, [...actual.map(file => file.name), manifestName]);
126
+ }
127
+ }
@@ -1,18 +1,18 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { runtimePaths } from '../runtime-paths.mjs';
4
- import { readRecord } from '../../service-lifecycle.mjs';
5
- import { lanNetworks } from './network-info.mjs';
6
- import { desktopHost } from './desktop-host.mjs';
7
-
8
- export { lanNetworks };
9
- export const lanAddress = async () => (await lanNetworks())[0]?.address || '';
10
- export const sharedServiceUrl = () => desktopHost.sharedServiceUrl();
11
- export async function bridgeEnvironment() {
12
- const environment = { ...process.env };
13
- const installed = readRecord(path.join(runtimePaths().serviceRoot, 'installation.json'));
14
- if (!installed?.nodeExecutable || !fs.existsSync(installed.nodeExecutable)) throw new Error('安装时选用的 Node 不存在,请重新执行 relax setup。');
15
- environment.CODEX_MCP_NODE_PATH = installed.nodeExecutable;
16
- delete environment.CODEX_APP_TOOLS_PIPE_PATH;
17
- return { executable: installed.nodeExecutable, environment };
18
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { runtimePaths } from '../runtime-paths.mjs';
4
+ import { readRecord } from '../../service-lifecycle.mjs';
5
+ import { lanNetworks } from './network-info.mjs';
6
+ import { desktopHost } from './desktop-host.mjs';
7
+
8
+ export { lanNetworks };
9
+ export const lanAddress = async () => (await lanNetworks())[0]?.address || '';
10
+ export const sharedServiceUrl = () => desktopHost.sharedServiceUrl();
11
+ export async function bridgeEnvironment() {
12
+ const environment = { ...process.env };
13
+ const installed = readRecord(path.join(runtimePaths().serviceRoot, 'installation.json'));
14
+ if (!installed?.nodeExecutable || !fs.existsSync(installed.nodeExecutable)) throw new Error('安装时选用的 Node 不存在,请重新执行 relax setup。');
15
+ environment.CODEX_MCP_NODE_PATH = installed.nodeExecutable;
16
+ delete environment.CODEX_APP_TOOLS_PIPE_PATH;
17
+ return { executable: installed.nodeExecutable, environment };
18
+ }
@@ -1,29 +1,29 @@
1
- import fs from 'node:fs';
2
- import { spawn } from 'node:child_process';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- export const hostBinary = fileURLToPath(new URL('../../../native/macos-host/CodeRelaxHost', import.meta.url));
6
- export function systemRequest(request) {
7
- if (process.platform !== 'darwin') return Promise.reject(new Error('需要 macOS 工作站。'));
8
- if (!fs.existsSync(hostBinary)) return Promise.reject(new Error('缺少 Mac 系统适配组件,请先在 Mac 上执行 npm run build。'));
9
- return new Promise((resolve, reject) => {
10
- const child = spawn(hostBinary, [], { stdio: ['pipe', 'pipe', 'pipe'] });
11
- let output = '', diagnostic = '', failure;
12
- const timer = setTimeout(() => { failure = new Error('macOS 系统操作超时;请检查钥匙串是否已解锁。'); child.kill(); }, 60000);
13
- child.stdout.setEncoding('utf8'); child.stderr.setEncoding('utf8');
14
- child.stdout.on('data', value => {
15
- output += value;
16
- if (Buffer.byteLength(output) > 2 * 1024 * 1024) { failure = new Error('macOS 系统响应过大。'); child.kill(); }
17
- });
18
- child.stderr.on('data', value => { diagnostic = (diagnostic + value).slice(0, 1024); });
19
- child.stdin.on('error', () => {});
20
- child.once('error', () => { clearTimeout(timer); reject(new Error('无法启动 macOS 系统适配组件。')); });
21
- child.once('close', code => {
22
- clearTimeout(timer);
23
- if (failure || code !== 0) return reject(failure || new Error(`macOS 系统操作失败:${diagnostic.trim() || code}`));
24
- try { resolve(JSON.parse(output)); }
25
- catch { reject(new Error('macOS 系统组件返回无效结果。')); }
26
- });
27
- child.stdin.end(JSON.stringify(request));
28
- });
29
- }
1
+ import fs from 'node:fs';
2
+ import { spawn } from 'node:child_process';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ export const hostBinary = fileURLToPath(new URL('../../../native/macos-host/CodeRelaxHost', import.meta.url));
6
+ export function systemRequest(request) {
7
+ if (process.platform !== 'darwin') return Promise.reject(new Error('需要 macOS 工作站。'));
8
+ if (!fs.existsSync(hostBinary)) return Promise.reject(new Error('缺少 Mac 系统适配组件,请先在 Mac 上执行 npm run build。'));
9
+ return new Promise((resolve, reject) => {
10
+ const child = spawn(hostBinary, [], { stdio: ['pipe', 'pipe', 'pipe'] });
11
+ let output = '', diagnostic = '', failure;
12
+ const timer = setTimeout(() => { failure = new Error('macOS 系统操作超时;请检查钥匙串是否已解锁。'); child.kill(); }, 60000);
13
+ child.stdout.setEncoding('utf8'); child.stderr.setEncoding('utf8');
14
+ child.stdout.on('data', value => {
15
+ output += value;
16
+ if (Buffer.byteLength(output) > 2 * 1024 * 1024) { failure = new Error('macOS 系统响应过大。'); child.kill(); }
17
+ });
18
+ child.stderr.on('data', value => { diagnostic = (diagnostic + value).slice(0, 1024); });
19
+ child.stdin.on('error', () => {});
20
+ child.once('error', () => { clearTimeout(timer); reject(new Error('无法启动 macOS 系统适配组件。')); });
21
+ child.once('close', code => {
22
+ clearTimeout(timer);
23
+ if (failure || code !== 0) return reject(failure || new Error(`macOS 系统操作失败:${diagnostic.trim() || code}`));
24
+ try { resolve(JSON.parse(output)); }
25
+ catch { reject(new Error('macOS 系统组件返回无效结果。')); }
26
+ });
27
+ child.stdin.end(JSON.stringify(request));
28
+ });
29
+ }
@@ -1,7 +1,7 @@
1
- // Launch Services receives these settings for one new application instance.
2
- // Do not mutate shell profiles or the login session's global environment.
3
- export async function configureManagedEnvironment() {
4
- return { configured: true, changed: false, owned: false, persistent: false,
5
- scope: 'application-launch', value: 'ws://127.0.0.1:45839' };
6
- }
7
- export async function clearManagedEnvironment() { return { removed: false, preserved: false, reason: 'application-launch-only' }; }
1
+ // Launch Services receives these settings for one new application instance.
2
+ // Do not mutate shell profiles or the login session's global environment.
3
+ export async function configureManagedEnvironment() {
4
+ return { configured: true, changed: false, owned: false, persistent: false,
5
+ scope: 'application-launch', value: 'ws://127.0.0.1:45839' };
6
+ }
7
+ export async function clearManagedEnvironment() { return { removed: false, preserved: false, reason: 'application-launch-only' }; }
@@ -1,46 +1,58 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { createRequire } from 'node:module';
4
-
5
- export const nativeTargets = Object.freeze({
6
- 'win32-x64': { packageName: '@walkhi/code-relax-native-win32-x64', file: 'desktop-helper/CodeRelaxDesktop.exe' },
7
- 'darwin-arm64': { packageName: '@walkhi/code-relax-native-darwin-arm64', file: 'macos-host/CodeRelaxHost' },
8
- 'darwin-x64': { packageName: '@walkhi/code-relax-native-darwin-x64', file: 'macos-host/CodeRelaxHost' },
9
- });
10
-
11
- const readJson = file => JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
12
-
13
- // Native dependencies are selected by npm's os/cpu filters. Materialize only
14
- // this platform's helper before setup touches the installed workstation.
15
- export function prepareNativePackage(packageRoot, { platform = process.platform, arch = process.arch } = {}) {
16
- const target = nativeTargets[`${platform}-${arch}`];
17
- if (!target) throw new Error(`尚不支持此工作站平台:${platform}-${arch}。`);
18
- const marker = path.join(packageRoot, 'dist/build-platform.json');
19
- const build = readJson(marker);
20
- const destination = path.join(packageRoot, 'dist/native', target.file);
21
- if (build.platform === platform && build.arch === arch) {
22
- if (!fs.statSync(destination).isFile()) throw new Error('安装包缺少当前平台原生组件,请重新安装。');
23
- return { platform, arch, materialized: false };
24
- }
25
- const pkg = readJson(path.join(packageRoot, 'package.json'));
26
- if (build.layout !== 'npm-platform-packages' || build.version !== pkg.version)
27
- throw new Error('安装包构建与当前系统、架构或版本不匹配,请重新安装。');
28
- let manifest;
29
- try { manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(`${target.packageName}/package.json`); }
30
- catch (error) {
31
- if (error.code !== 'MODULE_NOT_FOUND') throw error;
32
- throw new Error(`缺少 ${target.packageName}@${pkg.version};请勿省略 optional 依赖,使用 npm install -g @walkhi/code-relax@${pkg.version} --include=optional 重新安装。`);
33
- }
34
- const native = readJson(manifest);
35
- if (native.name !== target.packageName || native.version !== pkg.version
36
- || native.os?.length !== 1 || native.os[0] !== platform || native.cpu?.length !== 1 || native.cpu[0] !== arch)
37
- throw new Error('原生组件包的系统、架构或版本不匹配,未更新工作站。');
38
- const source = path.join(path.dirname(manifest), 'native', target.file);
39
- if (!fs.statSync(source).isFile()) throw new Error('平台包缺少原生组件,未更新工作站。');
40
- fs.mkdirSync(path.dirname(destination), { recursive: true });
41
- fs.copyFileSync(source, destination);
42
- if (platform === 'darwin') fs.chmodSync(destination, 0o755);
43
- // Only commit the selected platform after the component has been copied.
44
- fs.writeFileSync(marker, JSON.stringify({ platform, arch, version: pkg.version }));
45
- return { platform, arch, packageName: native.name, materialized: true };
46
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+
5
+ export const nativeTargets = Object.freeze({
6
+ 'win32-x64': { packageName: '@walkhi/code-relax-native-win32-x64', file: 'desktop-helper/CodeRelaxDesktop.exe' },
7
+ 'darwin-arm64': { packageName: '@walkhi/code-relax-native-darwin-arm64', file: 'macos-host/CodeRelaxHost' },
8
+ 'darwin-x64': { packageName: '@walkhi/code-relax-native-darwin-x64', file: 'macos-host/CodeRelaxHost' },
9
+ 'linux-x64': { packageName: null, file: null },
10
+ 'linux-arm64': { packageName: null, file: null },
11
+ });
12
+
13
+ const readJson = file => JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
14
+
15
+ // Native dependencies are selected by npm's os/cpu filters. Materialize only
16
+ // this platform's helper before setup touches the installed workstation.
17
+ export function prepareNativePackage(packageRoot, { platform = process.platform, arch = process.arch } = {}) {
18
+ const target = nativeTargets[`${platform}-${arch}`];
19
+ if (!target) throw new Error(`尚不支持此工作站平台:${platform}-${arch}。`);
20
+ const marker = path.join(packageRoot, 'dist/build-platform.json');
21
+ const build = readJson(marker);
22
+ if (!target.file) {
23
+ const pkg = readJson(path.join(packageRoot, 'package.json'));
24
+ if (!(build.platform === platform && build.arch === arch)
25
+ && !(build.layout === 'npm-platform-packages' && build.version === pkg.version))
26
+ throw new Error('安装包构建与当前系统、架构或版本不匹配,请重新安装。');
27
+ // Linux adapters use Node/procfs and the session's Secret Service. There is
28
+ // no private native helper to download; sharp/RTC select their own binaries.
29
+ fs.writeFileSync(marker, JSON.stringify({ platform, arch, version: pkg.version }));
30
+ return { platform, arch, materialized: false };
31
+ }
32
+ const destination = path.join(packageRoot, 'dist/native', target.file);
33
+ if (build.platform === platform && build.arch === arch) {
34
+ if (!fs.statSync(destination).isFile()) throw new Error('安装包缺少当前平台原生组件,请重新安装。');
35
+ return { platform, arch, materialized: false };
36
+ }
37
+ const pkg = readJson(path.join(packageRoot, 'package.json'));
38
+ if (build.layout !== 'npm-platform-packages' || build.version !== pkg.version)
39
+ throw new Error('安装包构建与当前系统、架构或版本不匹配,请重新安装。');
40
+ let manifest;
41
+ try { manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(`${target.packageName}/package.json`); }
42
+ catch (error) {
43
+ if (error.code !== 'MODULE_NOT_FOUND') throw error;
44
+ throw new Error(`缺少 ${target.packageName}@${pkg.version};请勿省略 optional 依赖,使用 npm install -g @walkhi/code-relax@${pkg.version} --include=optional --allow-scripts=@walkhi/code-relax --foreground-scripts 重新安装。`);
45
+ }
46
+ const native = readJson(manifest);
47
+ if (native.name !== target.packageName || native.version !== pkg.version
48
+ || native.os?.length !== 1 || native.os[0] !== platform || native.cpu?.length !== 1 || native.cpu[0] !== arch)
49
+ throw new Error('原生组件包的系统、架构或版本不匹配,未更新工作站。');
50
+ const source = path.join(path.dirname(manifest), 'native', target.file);
51
+ if (!fs.statSync(source).isFile()) throw new Error('平台包缺少原生组件,未更新工作站。');
52
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
53
+ fs.copyFileSync(source, destination);
54
+ if (platform === 'darwin') fs.chmodSync(destination, 0o755);
55
+ // Only commit the selected platform after the component has been copied.
56
+ fs.writeFileSync(marker, JSON.stringify({ platform, arch, version: pkg.version }));
57
+ return { platform, arch, packageName: native.name, materialized: true };
58
+ }
@@ -1,3 +1,3 @@
1
- import { platformModule } from './select.mjs';
2
- const adapter = await import(platformModule('network-info'));
3
- export const { lanNetworks } = adapter;
1
+ import { platformModule } from './select.mjs';
2
+ const adapter = await import(platformModule('network-info'));
3
+ export const { lanNetworks } = adapter;
@@ -1,5 +1,5 @@
1
- import path from 'node:path';
2
- export function executableKey(file, platform = process.platform) {
3
- const resolved = path.resolve(file);
4
- return platform === 'win32' ? resolved.toLowerCase() : resolved;
5
- }
1
+ import path from 'node:path';
2
+ export function executableKey(file, platform = process.platform) {
3
+ const resolved = path.resolve(file);
4
+ return platform === 'win32' ? resolved.toLowerCase() : resolved;
5
+ }
@@ -0,0 +1,23 @@
1
+ import net from 'node:net';
2
+
3
+ const privateIpv4 = address => {
4
+ if (net.isIP(address) !== 4) return false;
5
+ const [a, b] = address.split('.').map(Number);
6
+ return a === 10 || (a === 192 && b === 168) || (a === 172 && b >= 16 && b <= 31) || (a === 100 && b >= 64 && b <= 127);
7
+ };
8
+ export function networkCandidates(hardware, addresses) {
9
+ const results = [];
10
+ const seen = new Set();
11
+ for (const item of hardware) {
12
+ if (!['wifi', 'ethernet'].includes(item.networkType) || typeof item.interfaceName !== 'string') continue;
13
+ const label = value => String(value).replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, 80);
14
+ const interfaceName = label(item.interfaceName), networkName = label(item.networkName || interfaceName);
15
+ if (!interfaceName || !networkName) continue;
16
+ for (const info of addresses[item.interfaceName] || []) {
17
+ if (info.internal || !privateIpv4(info.address) || seen.has(info.address)) continue;
18
+ seen.add(info.address);
19
+ results.push({ address: info.address, interfaceName, networkName, networkType: item.networkType });
20
+ }
21
+ }
22
+ return results.slice(0, 8);
23
+ }
@@ -1,25 +1,25 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
-
4
- // These are user-owned locations; resolving paths does not create or migrate data.
5
- export function runtimePaths(environment = process.env, home = os.homedir(), platform = process.platform) {
6
- if (!['darwin', 'linux'].includes(platform)) throw new Error(`Unsupported POSIX host: ${platform}`);
7
- const join = path.posix.join;
8
- const xdgRoot = (name, fallback) => path.posix.isAbsolute(environment[name] || '')
9
- ? environment[name] : join(home, fallback);
10
- const dataRoot = platform === 'darwin'
11
- ? join(home, 'Library', 'Application Support', 'CodexRemote')
12
- : join(xdgRoot('XDG_DATA_HOME', '.local/share'), 'CodexRemote');
13
- const stateRoot = platform === 'darwin'
14
- ? join(dataRoot, 'state')
15
- : join(xdgRoot('XDG_STATE_HOME', '.local/state'), 'CodexRemote');
16
- const codexDataRoot = environment.CODEX_HOME?.trim() || join(home, '.codex');
17
- return {
18
- codexDataRoot,
19
- sessionsRoot: join(codexDataRoot, 'sessions'),
20
- stateRoot,
21
- serviceRoot: join(dataRoot, 'service'),
22
- uploadsRoot: join(dataRoot, 'attachments'),
23
- credentialsRoot: join(dataRoot, 'credentials'),
24
- };
25
- }
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ // These are user-owned locations; resolving paths does not create or migrate data.
5
+ export function runtimePaths(environment = process.env, home = os.homedir(), platform = process.platform) {
6
+ if (!['darwin', 'linux'].includes(platform)) throw new Error(`Unsupported POSIX host: ${platform}`);
7
+ const join = path.posix.join;
8
+ const xdgRoot = (name, fallback) => path.posix.isAbsolute(environment[name] || '')
9
+ ? environment[name] : join(home, fallback);
10
+ const dataRoot = platform === 'darwin'
11
+ ? join(home, 'Library', 'Application Support', 'CodexRemote')
12
+ : join(xdgRoot('XDG_DATA_HOME', '.local/share'), 'CodexRemote');
13
+ const stateRoot = platform === 'darwin'
14
+ ? join(dataRoot, 'state')
15
+ : join(xdgRoot('XDG_STATE_HOME', '.local/state'), 'CodexRemote');
16
+ const codexDataRoot = environment.CODEX_HOME?.trim() || join(home, '.codex');
17
+ return {
18
+ codexDataRoot,
19
+ sessionsRoot: join(codexDataRoot, 'sessions'),
20
+ stateRoot,
21
+ serviceRoot: join(dataRoot, 'service'),
22
+ uploadsRoot: join(dataRoot, 'attachments'),
23
+ credentialsRoot: join(dataRoot, 'credentials'),
24
+ };
25
+ }
@@ -1,3 +1,3 @@
1
- import { platformModule } from './select.mjs';
2
- const adapter = await import(platformModule('project-locations'));
3
- export const { projectLocations } = adapter;
1
+ import { platformModule } from './select.mjs';
2
+ const adapter = await import(platformModule('project-locations'));
3
+ export const { projectLocations } = adapter;
@@ -1,8 +1,8 @@
1
- import { runtimePaths as windowsPaths } from './windows/runtime-paths.mjs';
2
- import { runtimePaths as posixPaths } from './posix/runtime-paths.mjs';
3
-
4
- export function runtimePaths(environment = process.env, home, platform = process.platform) {
5
- if (platform === 'win32') return windowsPaths(environment, home);
6
- if (platform === 'darwin' || platform === 'linux') return posixPaths(environment, home, platform);
7
- throw new Error(`Unsupported workstation platform: ${platform}`);
8
- }
1
+ import { runtimePaths as windowsPaths } from './windows/runtime-paths.mjs';
2
+ import { runtimePaths as posixPaths } from './posix/runtime-paths.mjs';
3
+
4
+ export function runtimePaths(environment = process.env, home, platform = process.platform) {
5
+ if (platform === 'win32') return windowsPaths(environment, home);
6
+ if (platform === 'darwin' || platform === 'linux') return posixPaths(environment, home, platform);
7
+ throw new Error(`Unsupported workstation platform: ${platform}`);
8
+ }
@@ -1,5 +1,5 @@
1
- export function platformModule(name, platform = process.platform) {
2
- const directory = { win32: 'windows', darwin: 'macos' }[platform];
3
- if (!directory) throw new Error(`工作站暂不支持此系统:${platform}`);
4
- return new URL(`./${directory}/${name}.mjs`, import.meta.url);
5
- }
1
+ export function platformModule(name, platform = process.platform) {
2
+ const directory = { win32: 'windows', darwin: 'macos', linux: 'linux' }[platform];
3
+ if (!directory) throw new Error(`工作站暂不支持此系统:${platform}`);
4
+ return new URL(`./${directory}/${name}.mjs`, import.meta.url);
5
+ }
@@ -1,3 +1,3 @@
1
- import { platformModule } from './select.mjs';
2
- const adapter = await import(platformModule('service-host'));
3
- export const { sharedServiceUrl, lanAddress, lanNetworks, bridgeEnvironment } = adapter;
1
+ import { platformModule } from './select.mjs';
2
+ const adapter = await import(platformModule('service-host'));
3
+ export const { sharedServiceUrl, lanAddress, lanNetworks, bridgeEnvironment } = adapter;