@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
@@ -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;