@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,82 +1,82 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { startBackground } from '../../background-process.mjs';
4
- import { systemRequest } from './system.mjs';
5
- import { assertPlainPath } from '../../skill-install.mjs';
6
- import { setTimeout as delay } from 'node:timers/promises';
7
- import { runtimePaths } from '../runtime-paths.mjs';
8
- import { readRecord } from '../../service-lifecycle.mjs';
9
- export { residentStartup } from './resident-startup.mjs';
10
-
11
- const managedUrl = 'ws://127.0.0.1:45839';
12
- export function createDesktopHost({ request = systemRequest, wait = delay, now = Date.now, timeout = 10000,
13
- stateRoot = () => runtimePaths().stateRoot, fingerprint = file => {
14
- const stat = fs.statSync(path.join(file, 'Contents/Info.plist'));
15
- return `${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;
16
- } } = {}) {
17
- return {
18
- desktopIntegration: true,
19
- async inspect() {
20
- const info = await request({ operation: 'desktop-info' });
21
- return { ...info, desktopIdentity: info.desktop ? `${info.desktop}:${info.version}:${info.build}:${fingerprint(info.desktop)}` : null };
22
- },
23
- process: pid => request({ operation: 'process', pid }),
24
- start: startBackground,
25
- async stop(pid, expected) {
26
- if (!expected || expected.pid !== pid) throw new Error('停止内核前缺少进程身份,未结束任何进程。');
27
- return request({ operation: 'stop', expected });
28
- },
29
- async sharedServiceUrl() {
30
- return (await this.inspect()).connectedToManagedServer ? managedUrl : '';
31
- },
32
- async startDesktop(bundlePath, environment) {
33
- if (environment.CODEX_APP_SERVER_WS_URL !== managedUrl) throw new Error('Desktop 内核地址与工作站不一致。');
34
- const runtime = readRecord(path.join(stateRoot(), 'codex-runtime.json'));
35
- const codexHome = environment.CODEX_HOME || runtime?.codexHome;
36
- if (!codexHome || !path.isAbsolute(codexHome)) throw new Error('缺少已选内核的 CODEX_HOME,请先运行 relax setup。');
37
- const info = await this.inspect();
38
- if (info.running) throw new Error('Desktop 仍在运行,未创建第二个实例。');
39
- if (info.desktop !== bundlePath) throw new Error('Codex Desktop 安装位置已变化,请重新检查。');
40
- return request({ operation: 'desktop-start', bundlePath,
41
- environment: { CODEX_HOME: codexHome, CODEX_APP_SERVER_WS_URL: managedUrl } });
42
- },
43
- async closeDesktop(bundlePath) {
44
- const info = await this.inspect();
45
- if (!info.running) return;
46
- if (info.desktop !== bundlePath || !info.process) throw new Error('Desktop 进程身份已变化,未请求退出。');
47
- await request({ operation: 'desktop-close', bundlePath, expected: info.process });
48
- const deadline = now() + timeout;
49
- while ((await this.inspect()).running) {
50
- if (now() >= deadline) throw new Error('Desktop 尚未正常退出,请在 Mac 上处理未保存内容后重试;未强制结束进程。');
51
- await wait(200);
52
- }
53
- },
54
- async desktopReady() {
55
- const info = await this.inspect();
56
- return info.running && info.finishedLaunching && info.connectedToManagedServer;
57
- },
58
- async prepareKernelRestart() {
59
- const info = await this.inspect();
60
- if (!info.running || !info.connectedToManagedServer) return null;
61
- await this.closeDesktop(info.desktop);
62
- return info.desktop;
63
- },
64
- async resumeAfterKernelRestart(bundlePath, environment) {
65
- if (!bundlePath) return;
66
- await this.startDesktop(bundlePath, environment);
67
- const deadline = now() + 30000;
68
- while (!await this.desktopReady()) {
69
- if (now() >= deadline) throw new Error('内核已就绪,但 Mac Desktop 尚未重新接入。');
70
- await wait(500);
71
- }
72
- },
73
- ready: async url => { try { return (await fetch(url, { signal: AbortSignal.timeout(1000) })).ok; } catch { return false; } },
74
- };
75
- }
76
- export const desktopHost = createDesktopHost();
77
- export async function resolveStateRoot(stateRoot) {
78
- assertPlainPath(stateRoot);
79
- fs.mkdirSync(stateRoot, { recursive: true, mode: 0o700 });
80
- return fs.realpathSync(stateRoot);
81
- }
82
- export async function removeDesktopShortcut() { return { removed: false, reason: 'not-used-on-macos' }; }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { startBackground } from '../../background-process.mjs';
4
+ import { systemRequest } from './system.mjs';
5
+ import { assertPlainPath } from '../../skill-install.mjs';
6
+ import { setTimeout as delay } from 'node:timers/promises';
7
+ import { runtimePaths } from '../runtime-paths.mjs';
8
+ import { readRecord } from '../../service-lifecycle.mjs';
9
+ export { residentStartup } from './resident-startup.mjs';
10
+
11
+ const managedUrl = 'ws://127.0.0.1:45839';
12
+ export function createDesktopHost({ request = systemRequest, wait = delay, now = Date.now, timeout = 10000,
13
+ stateRoot = () => runtimePaths().stateRoot, fingerprint = file => {
14
+ const stat = fs.statSync(path.join(file, 'Contents/Info.plist'));
15
+ return `${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;
16
+ } } = {}) {
17
+ return {
18
+ desktopIntegration: true,
19
+ async inspect() {
20
+ const info = await request({ operation: 'desktop-info' });
21
+ return { ...info, desktopIdentity: info.desktop ? `${info.desktop}:${info.version}:${info.build}:${fingerprint(info.desktop)}` : null };
22
+ },
23
+ process: pid => request({ operation: 'process', pid }),
24
+ start: startBackground,
25
+ async stop(pid, expected) {
26
+ if (!expected || expected.pid !== pid) throw new Error('停止内核前缺少进程身份,未结束任何进程。');
27
+ return request({ operation: 'stop', expected });
28
+ },
29
+ async sharedServiceUrl() {
30
+ return (await this.inspect()).connectedToManagedServer ? managedUrl : '';
31
+ },
32
+ async startDesktop(bundlePath, environment) {
33
+ if (environment.CODEX_APP_SERVER_WS_URL !== managedUrl) throw new Error('Desktop 内核地址与工作站不一致。');
34
+ const runtime = readRecord(path.join(stateRoot(), 'codex-runtime.json'));
35
+ const codexHome = environment.CODEX_HOME || runtime?.codexHome;
36
+ if (!codexHome || !path.isAbsolute(codexHome)) throw new Error('缺少已选内核的 CODEX_HOME,请先运行 relax setup。');
37
+ const info = await this.inspect();
38
+ if (info.running) throw new Error('Desktop 仍在运行,未创建第二个实例。');
39
+ if (info.desktop !== bundlePath) throw new Error('Codex Desktop 安装位置已变化,请重新检查。');
40
+ return request({ operation: 'desktop-start', bundlePath,
41
+ environment: { CODEX_HOME: codexHome, CODEX_APP_SERVER_WS_URL: managedUrl } });
42
+ },
43
+ async closeDesktop(bundlePath) {
44
+ const info = await this.inspect();
45
+ if (!info.running) return;
46
+ if (info.desktop !== bundlePath || !info.process) throw new Error('Desktop 进程身份已变化,未请求退出。');
47
+ await request({ operation: 'desktop-close', bundlePath, expected: info.process });
48
+ const deadline = now() + timeout;
49
+ while ((await this.inspect()).running) {
50
+ if (now() >= deadline) throw new Error('Desktop 尚未正常退出,请在 Mac 上处理未保存内容后重试;未强制结束进程。');
51
+ await wait(200);
52
+ }
53
+ },
54
+ async desktopReady() {
55
+ const info = await this.inspect();
56
+ return info.running && info.finishedLaunching && info.connectedToManagedServer;
57
+ },
58
+ async prepareKernelRestart() {
59
+ const info = await this.inspect();
60
+ if (!info.running || !info.connectedToManagedServer) return null;
61
+ await this.closeDesktop(info.desktop);
62
+ return info.desktop;
63
+ },
64
+ async resumeAfterKernelRestart(bundlePath, environment) {
65
+ if (!bundlePath) return;
66
+ await this.startDesktop(bundlePath, environment);
67
+ const deadline = now() + 30000;
68
+ while (!await this.desktopReady()) {
69
+ if (now() >= deadline) throw new Error('内核已就绪,但 Mac Desktop 尚未重新接入。');
70
+ await wait(500);
71
+ }
72
+ },
73
+ ready: async url => { try { return (await fetch(url, { signal: AbortSignal.timeout(1000) })).ok; } catch { return false; } },
74
+ };
75
+ }
76
+ export const desktopHost = createDesktopHost();
77
+ export async function resolveStateRoot(stateRoot) {
78
+ assertPlainPath(stateRoot);
79
+ fs.mkdirSync(stateRoot, { recursive: true, mode: 0o700 });
80
+ return fs.realpathSync(stateRoot);
81
+ }
82
+ export async function removeDesktopShortcut() { return { removed: false, reason: 'not-used-on-macos' }; }
@@ -1,10 +1,11 @@
1
- import { desktopHost } from './desktop-host.mjs';
2
- import { withDesktopProjectRegistration as register } from '../desktop-project-store.mjs';
3
-
4
- // Verified against the Mac 26.1007.71326 local-projects/project-order store.
5
- export async function withDesktopProjectRegistration(options, action) {
6
- const host = options.host || desktopHost;
7
- if (!(await host.inspect()).desktop)
8
- return action({ register: async () => ({ status: 'not-applicable', reason: 'desktop-not-installed' }) });
9
- return register({ ...options, host }, action);
10
- }
1
+ import { desktopHost } from './desktop-host.mjs';
2
+ import { withDesktopProjectRegistration as register } from '../desktop-project-store.mjs';
3
+
4
+ // Verified against the Mac 26.1007.71326 local-projects/project-order store.
5
+ export async function withDesktopProjectRegistration(options, action) {
6
+ const host = options.host || desktopHost;
7
+ if (!(await host.inspect()).desktop)
8
+ return action({ register: async () => ({ status: 'not-applicable', reason: 'desktop-not-installed' }),
9
+ remove: async (_project, _operation, _save, removeNative) => { await removeNative(); return { status: 'not-applicable', reason: 'desktop-not-installed' }; } });
10
+ return register({ ...options, host }, action);
11
+ }
@@ -1,28 +1,8 @@
1
- import os from 'node:os';
2
- import net from 'node:net';
3
- import { systemRequest } from './system.mjs';
4
-
5
- const privateIpv4 = address => {
6
- if (net.isIP(address) !== 4) return false;
7
- const [a, b] = address.split('.').map(Number);
8
- return a === 10 || (a === 192 && b === 168) || (a === 172 && b >= 16 && b <= 31) || (a === 100 && b >= 64 && b <= 127);
9
- };
10
- export function networkCandidates(hardware, addresses) {
11
- const results = [];
12
- const seen = new Set();
13
- for (const item of hardware) {
14
- if (!['wifi', 'ethernet'].includes(item.networkType) || typeof item.interfaceName !== 'string') continue;
15
- const label = value => String(value).replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, 80);
16
- const interfaceName = label(item.interfaceName), networkName = label(item.networkName || interfaceName);
17
- if (!interfaceName || !networkName) continue;
18
- for (const info of addresses[item.interfaceName] || []) {
19
- if (info.internal || !privateIpv4(info.address) || seen.has(info.address)) continue;
20
- seen.add(info.address);
21
- results.push({ address: info.address, interfaceName, networkName, networkType: item.networkType });
22
- }
23
- }
24
- return results.slice(0, 8);
25
- }
26
- export async function lanNetworks() {
27
- return networkCandidates(await systemRequest({ operation: 'network-interfaces' }), os.networkInterfaces());
28
- }
1
+ import os from 'node:os';
2
+ import { systemRequest } from './system.mjs';
3
+ import { networkCandidates } from '../posix/network-candidates.mjs';
4
+ export { networkCandidates } from '../posix/network-candidates.mjs';
5
+
6
+ export async function lanNetworks() {
7
+ return networkCandidates(await systemRequest({ operation: 'network-interfaces' }), os.networkInterfaces());
8
+ }
@@ -1,8 +1,8 @@
1
- import path from 'node:path';
2
- import { systemRequest } from './system.mjs';
3
-
4
- export async function projectLocations() {
5
- const { home, documents, downloads } = await systemRequest({ operation: 'locations' });
6
- if (![home, documents, downloads].every(value => typeof value === 'string' && path.isAbsolute(value))) throw new Error('Mac 用户目录无效。');
7
- return { home, downloads, defaultParent: path.join(documents, 'ChatGPT'), roots: ['/'] };
8
- }
1
+ import path from 'node:path';
2
+ import { systemRequest } from './system.mjs';
3
+
4
+ export async function projectLocations() {
5
+ const { home, documents, downloads } = await systemRequest({ operation: 'locations' });
6
+ if (![home, documents, downloads].every(value => typeof value === 'string' && path.isAbsolute(value))) throw new Error('Mac 用户目录无效。');
7
+ return { home, downloads, defaultParent: path.join(documents, 'ChatGPT'), roots: ['/'] };
8
+ }
@@ -1,84 +1,84 @@
1
- import fs from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { execFile } from 'node:child_process';
5
- import { promisify } from 'node:util';
6
- import { readRecord } from '../../service-lifecycle.mjs';
7
- import { assertPlainPath } from '../../skill-install.mjs';
8
- import { runtimePaths } from '../runtime-paths.mjs';
9
-
10
- const exec = promisify(execFile);
11
- export const launchAgentLabel = 'cn.walkhi.code-relax';
12
- const xml = value => String(value).replace(/[&<>"']/g, character => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&apos;' }[character]));
13
- export function launchAgentPlist({ serviceRoot, nodeExecutable, stateRoot, home, environment }) {
14
- const entry = path.join(serviceRoot, 'dist/bin/codex-remote.mjs');
15
- // npm adds temporary .bin directories during postinstall. Login must use the
16
- // user's stable tool paths after the source archive has been removed.
17
- const stablePath = String(environment.PATH || '').split(':')
18
- .filter(value => value.startsWith('/') && !value.endsWith('/node_modules/.bin')).join(':');
19
- const variables = { HOME: home, PATH: stablePath || '/usr/bin:/bin:/usr/sbin:/sbin' };
20
- for (const name of ['CODEX_HOME', 'CODEX_INSTALL_DIR', 'CODE_RELAX_CODEX_PATH']) if (environment[name]) variables[name] = environment[name];
21
- const strings = [nodeExecutable, entry, ...Object.values(variables), stateRoot];
22
- if (strings.some(value => /[\u0000-\u0008\u000b\u000c\u000e-\u001f]/.test(value))) throw new Error('登录启动参数含无效控制字符。');
23
- return `<?xml version="1.0" encoding="UTF-8"?>
24
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
25
- <plist version="1.0"><dict>
26
- <key>Label</key><string>${launchAgentLabel}</string>
27
- <key>ProgramArguments</key><array>${[nodeExecutable, entry, 'start', '--json'].map(value => `<string>${xml(value)}</string>`).join('')}</array>
28
- <key>RunAtLoad</key><true/>
29
- <key>AbandonProcessGroup</key><true/>
30
- <key>EnvironmentVariables</key><dict>${Object.entries(variables).map(([key, value]) => `<key>${key}</key><string>${xml(value)}</string>`).join('')}</dict>
31
- <key>StandardOutPath</key><string>${xml(path.join(stateRoot, 'login.stdout.log'))}</string>
32
- <key>StandardErrorPath</key><string>${xml(path.join(stateRoot, 'login.stderr.log'))}</string>
33
- </dict></plist>
34
- `;
35
- }
36
-
37
- export async function residentStartup({ serviceRoot, nodeExecutable }, action = 'status') {
38
- if (!['status', 'enable', 'disable'].includes(action)) throw new Error('无效的登录启动操作。');
39
- const uid = process.getuid();
40
- if (uid === 0) throw new Error('请以普通 Mac 用户安装 Code Relax,不使用 sudo。');
41
- const home = os.homedir();
42
- const file = path.join(home, 'Library/LaunchAgents', `${launchAgentLabel}.plist`);
43
- const domain = `gui/${uid}`, target = `${domain}/${launchAgentLabel}`;
44
- assertPlainPath(file);
45
- let current;
46
- if (fs.existsSync(file)) {
47
- const { stdout } = await exec('/usr/bin/plutil', ['-convert', 'json', '-o', '-', file], { timeout: 5000 });
48
- current = JSON.parse(stdout);
49
- if (current.Label !== launchAgentLabel || current.ProgramArguments?.[1] !== path.join(serviceRoot, 'dist/bin/codex-remote.mjs'))
50
- throw new Error('同名 LaunchAgent 不属于此安装,未覆盖或移除。');
51
- }
52
- const { stdout: disabled } = await exec('/bin/launchctl', ['print-disabled', domain], { timeout: 5000 });
53
- const isDisabled = /"cn\.walkhi\.code-relax"\s*=>\s*true/.test(disabled);
54
- const expectedArguments = [nodeExecutable, path.join(serviceRoot, 'dist/bin/codex-remote.mjs'), 'start', '--json'];
55
- const enabled = JSON.stringify(current?.ProgramArguments) === JSON.stringify(expectedArguments)
56
- && current.RunAtLoad === true && current.AbandonProcessGroup === true
57
- && current.EnvironmentVariables?.HOME === home && !isDisabled;
58
- if (action === 'status') return { enabled, path: file, mechanism: 'LaunchAgent' };
59
- if (action === 'disable') {
60
- if (!current) return { enabled: false, removed: false, path: file };
61
- let loaded = false;
62
- try { await exec('/bin/launchctl', ['print', target], { timeout: 5000 }); loaded = true; }
63
- catch (error) { if (error.code !== 113) throw error; }
64
- if (loaded) await exec('/bin/launchctl', ['bootout', target], { timeout: 5000 });
65
- await exec('/bin/launchctl', ['disable', target], { timeout: 5000 });
66
- fs.unlinkSync(file);
67
- return { enabled: false, removed: true, path: file };
68
- }
69
- const { stateRoot } = runtimePaths();
70
- const runtime = readRecord(path.join(stateRoot, 'codex-runtime.json'));
71
- const environment = { ...process.env, ...(runtime?.codexHome ? { CODEX_HOME: runtime.codexHome } : {}) };
72
- fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
73
- fs.mkdirSync(stateRoot, { recursive: true, mode: 0o700 });
74
- const temporary = `${file}.${process.pid}.tmp`;
75
- try {
76
- fs.writeFileSync(temporary, launchAgentPlist({ serviceRoot, nodeExecutable, stateRoot, home, environment }), { flag: 'wx', mode: 0o600 });
77
- await exec('/usr/bin/plutil', ['-lint', temporary], { timeout: 5000 });
78
- fs.renameSync(temporary, file);
79
- } finally { if (fs.existsSync(temporary)) fs.unlinkSync(temporary); }
80
- await exec('/bin/launchctl', ['enable', target], { timeout: 5000 });
81
- // setup/start already launches the service. Bootstrap would run a second start
82
- // concurrently; launchd will load this user agent at the next GUI login.
83
- return { enabled: true, path: file, mechanism: 'LaunchAgent', appliesAtNextLogin: true };
84
- }
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ import { readRecord } from '../../service-lifecycle.mjs';
7
+ import { assertPlainPath } from '../../skill-install.mjs';
8
+ import { runtimePaths } from '../runtime-paths.mjs';
9
+
10
+ const exec = promisify(execFile);
11
+ export const launchAgentLabel = 'cn.walkhi.code-relax';
12
+ const xml = value => String(value).replace(/[&<>"']/g, character => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&apos;' }[character]));
13
+ export function launchAgentPlist({ serviceRoot, nodeExecutable, stateRoot, home, environment }) {
14
+ const entry = path.join(serviceRoot, 'dist/bin/codex-remote.mjs');
15
+ // npm adds temporary .bin directories during postinstall. Login must use the
16
+ // user's stable tool paths after the source archive has been removed.
17
+ const stablePath = String(environment.PATH || '').split(':')
18
+ .filter(value => value.startsWith('/') && !value.endsWith('/node_modules/.bin')).join(':');
19
+ const variables = { HOME: home, PATH: stablePath || '/usr/bin:/bin:/usr/sbin:/sbin' };
20
+ for (const name of ['CODEX_HOME', 'CODEX_INSTALL_DIR', 'CODE_RELAX_CODEX_PATH']) if (environment[name]) variables[name] = environment[name];
21
+ const strings = [nodeExecutable, entry, ...Object.values(variables), stateRoot];
22
+ if (strings.some(value => /[\u0000-\u0008\u000b\u000c\u000e-\u001f]/.test(value))) throw new Error('登录启动参数含无效控制字符。');
23
+ return `<?xml version="1.0" encoding="UTF-8"?>
24
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
25
+ <plist version="1.0"><dict>
26
+ <key>Label</key><string>${launchAgentLabel}</string>
27
+ <key>ProgramArguments</key><array>${[nodeExecutable, entry, 'start', '--json'].map(value => `<string>${xml(value)}</string>`).join('')}</array>
28
+ <key>RunAtLoad</key><true/>
29
+ <key>AbandonProcessGroup</key><true/>
30
+ <key>EnvironmentVariables</key><dict>${Object.entries(variables).map(([key, value]) => `<key>${key}</key><string>${xml(value)}</string>`).join('')}</dict>
31
+ <key>StandardOutPath</key><string>${xml(path.join(stateRoot, 'login.stdout.log'))}</string>
32
+ <key>StandardErrorPath</key><string>${xml(path.join(stateRoot, 'login.stderr.log'))}</string>
33
+ </dict></plist>
34
+ `;
35
+ }
36
+
37
+ export async function residentStartup({ serviceRoot, nodeExecutable }, action = 'status') {
38
+ if (!['status', 'enable', 'disable'].includes(action)) throw new Error('无效的登录启动操作。');
39
+ const uid = process.getuid();
40
+ if (uid === 0) throw new Error('请以普通 Mac 用户安装 Code Relax,不使用 sudo。');
41
+ const home = os.homedir();
42
+ const file = path.join(home, 'Library/LaunchAgents', `${launchAgentLabel}.plist`);
43
+ const domain = `gui/${uid}`, target = `${domain}/${launchAgentLabel}`;
44
+ assertPlainPath(file);
45
+ let current;
46
+ if (fs.existsSync(file)) {
47
+ const { stdout } = await exec('/usr/bin/plutil', ['-convert', 'json', '-o', '-', file], { timeout: 5000 });
48
+ current = JSON.parse(stdout);
49
+ if (current.Label !== launchAgentLabel || current.ProgramArguments?.[1] !== path.join(serviceRoot, 'dist/bin/codex-remote.mjs'))
50
+ throw new Error('同名 LaunchAgent 不属于此安装,未覆盖或移除。');
51
+ }
52
+ const { stdout: disabled } = await exec('/bin/launchctl', ['print-disabled', domain], { timeout: 5000 });
53
+ const isDisabled = /"cn\.walkhi\.code-relax"\s*=>\s*true/.test(disabled);
54
+ const expectedArguments = [nodeExecutable, path.join(serviceRoot, 'dist/bin/codex-remote.mjs'), 'start', '--json'];
55
+ const enabled = JSON.stringify(current?.ProgramArguments) === JSON.stringify(expectedArguments)
56
+ && current.RunAtLoad === true && current.AbandonProcessGroup === true
57
+ && current.EnvironmentVariables?.HOME === home && !isDisabled;
58
+ if (action === 'status') return { enabled, path: file, mechanism: 'LaunchAgent' };
59
+ if (action === 'disable') {
60
+ if (!current) return { enabled: false, removed: false, path: file };
61
+ let loaded = false;
62
+ try { await exec('/bin/launchctl', ['print', target], { timeout: 5000 }); loaded = true; }
63
+ catch (error) { if (error.code !== 113) throw error; }
64
+ if (loaded) await exec('/bin/launchctl', ['bootout', target], { timeout: 5000 });
65
+ await exec('/bin/launchctl', ['disable', target], { timeout: 5000 });
66
+ fs.unlinkSync(file);
67
+ return { enabled: false, removed: true, path: file };
68
+ }
69
+ const { stateRoot } = runtimePaths();
70
+ const runtime = readRecord(path.join(stateRoot, 'codex-runtime.json'));
71
+ const environment = { ...process.env, ...(runtime?.codexHome ? { CODEX_HOME: runtime.codexHome } : {}) };
72
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
73
+ fs.mkdirSync(stateRoot, { recursive: true, mode: 0o700 });
74
+ const temporary = `${file}.${process.pid}.tmp`;
75
+ try {
76
+ fs.writeFileSync(temporary, launchAgentPlist({ serviceRoot, nodeExecutable, stateRoot, home, environment }), { flag: 'wx', mode: 0o600 });
77
+ await exec('/usr/bin/plutil', ['-lint', temporary], { timeout: 5000 });
78
+ fs.renameSync(temporary, file);
79
+ } finally { if (fs.existsSync(temporary)) fs.unlinkSync(temporary); }
80
+ await exec('/bin/launchctl', ['enable', target], { timeout: 5000 });
81
+ // setup/start already launches the service. Bootstrap would run a second start
82
+ // concurrently; launchd will load this user agent at the next GUI login.
83
+ return { enabled: true, path: file, mechanism: 'LaunchAgent', appliesAtNextLogin: true };
84
+ }