@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.
- package/README.md +95 -89
- package/dist/bin/cleanup-accounts.mjs +63 -63
- package/dist/bin/codex-remote.mjs +374 -367
- package/dist/bin/manage-relay-admin.mjs +20 -20
- package/dist/bin/migrate-relay-db.mjs +109 -109
- package/dist/bin/publish-android-release.mjs +62 -58
- package/dist/bin/self-relay-admin.mjs +8 -8
- package/dist/bin/self-relay-demo.mjs +33 -33
- package/dist/bin/self-relay-server.mjs +66 -66
- package/dist/build-platform.json +1 -1
- package/dist/get-lan-networks.ps1 +40 -40
- package/dist/get-shared-service-url.ps1 +6 -6
- package/dist/shared/app-server-events.cjs +422 -412
- package/dist/shared/code-browser.cjs +159 -159
- package/dist/shared/file-changes.cjs +31 -31
- package/dist/shared/file-citations.cjs +117 -117
- package/dist/shared/file-mentions.cjs +92 -92
- package/dist/shared/message-delivery.cjs +54 -54
- package/dist/shared/p2p-data-channel.cjs +72 -72
- package/dist/shared/p2p-diagnostics.cjs +28 -28
- package/dist/shared/p2p-file-channel.cjs +74 -74
- package/dist/shared/plan-pricing.cjs +32 -32
- package/dist/shared/question-attention.cjs +40 -0
- package/dist/shared/server-requests.cjs +219 -219
- package/dist/shared/timeline-sync.cjs +173 -173
- package/dist/skills/code-relax/SKILL.md +22 -22
- package/dist/src/archived-threads.mjs +22 -0
- package/dist/src/background-process.mjs +18 -18
- package/dist/src/bounded-process-log.mjs +63 -63
- package/dist/src/celsius-client.mjs +107 -107
- package/dist/src/code-browser.mjs +100 -100
- package/dist/src/desktop-launcher.mjs +46 -46
- package/dist/src/dots-approval-timeline.mjs +99 -99
- package/dist/src/dots-avatar.mjs +42 -42
- package/dist/src/dots-chat-projection.mjs +50 -50
- package/dist/src/dots-chat-server.mjs +461 -461
- package/dist/src/dots-message-images.mjs +319 -319
- package/dist/src/dots-profile.mjs +77 -77
- package/dist/src/dots-room-activity.mjs +94 -94
- package/dist/src/dots-room-client.mjs +135 -135
- package/dist/src/dots-room-projection.mjs +94 -94
- package/dist/src/dots-schedules.mjs +66 -66
- package/dist/src/dots-summary-tasks.mjs +32 -32
- package/dist/src/dots-task-catalog.mjs +430 -430
- package/dist/src/dots-task-files.mjs +52 -52
- package/dist/src/execution-health.mjs +26 -26
- package/dist/src/file-transfer-range.mjs +22 -22
- package/dist/src/file-upload-session.mjs +130 -130
- package/dist/src/hosted-history-app-server.mjs +103 -103
- package/dist/src/hosted-history-cache.mjs +113 -113
- package/dist/src/lan-socket-server.mjs +265 -263
- package/dist/src/managed-app-server.mjs +244 -244
- package/dist/src/message-images.mjs +9 -9
- package/dist/src/message-upload-files.mjs +74 -74
- package/dist/src/onboarding.mjs +12 -12
- package/dist/src/plan-pricing.mjs +47 -47
- package/dist/src/platform/README.md +12 -12
- package/dist/src/platform/codex-protocol.mjs +46 -46
- package/dist/src/platform/codex-runtime.mjs +3 -3
- package/dist/src/platform/credential-store.mjs +3 -3
- package/dist/src/platform/desktop-host.mjs +3 -3
- package/dist/src/platform/desktop-project-registration.mjs +3 -3
- package/dist/src/platform/desktop-project-store.mjs +208 -149
- package/dist/src/platform/file-opener.mjs +9 -0
- package/dist/src/platform/linux/README.md +3 -3
- package/dist/src/platform/linux/codex-runtime.mjs +56 -0
- package/dist/src/platform/linux/credential-store.mjs +28 -0
- package/dist/src/platform/linux/desktop-host.mjs +83 -0
- package/dist/src/platform/linux/desktop-installation.mjs +36 -0
- package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
- package/dist/src/platform/linux/network-info.mjs +11 -0
- package/dist/src/platform/linux/process.mjs +42 -0
- package/dist/src/platform/linux/project-locations.mjs +17 -0
- package/dist/src/platform/linux/resident-startup.mjs +52 -0
- package/dist/src/platform/linux/retained-runtime.mjs +76 -0
- package/dist/src/platform/linux/service-host.mjs +18 -0
- package/dist/src/platform/linux/system.mjs +49 -0
- package/dist/src/platform/linux/user-environment.mjs +7 -0
- package/dist/src/platform/macos/README.md +3 -3
- package/dist/src/platform/macos/codex-runtime.mjs +125 -125
- package/dist/src/platform/macos/credential-store.mjs +10 -10
- package/dist/src/platform/macos/desktop-host.mjs +82 -82
- package/dist/src/platform/macos/desktop-project-registration.mjs +11 -10
- package/dist/src/platform/macos/network-info.mjs +8 -28
- package/dist/src/platform/macos/project-locations.mjs +8 -8
- package/dist/src/platform/macos/resident-startup.mjs +84 -84
- package/dist/src/platform/macos/retained-runtime.mjs +127 -127
- package/dist/src/platform/macos/service-host.mjs +18 -18
- package/dist/src/platform/macos/system.mjs +29 -29
- package/dist/src/platform/macos/user-environment.mjs +7 -7
- package/dist/src/platform/native-package.mjs +58 -46
- package/dist/src/platform/network-info.mjs +3 -3
- package/dist/src/platform/path-identity.mjs +5 -5
- package/dist/src/platform/posix/network-candidates.mjs +23 -0
- package/dist/src/platform/posix/runtime-paths.mjs +25 -25
- package/dist/src/platform/project-locations.mjs +3 -3
- package/dist/src/platform/runtime-paths.mjs +8 -8
- package/dist/src/platform/select.mjs +5 -5
- package/dist/src/platform/service-host.mjs +3 -3
- package/dist/src/platform/user-environment.mjs +3 -3
- package/dist/src/platform/windows/IsolatedProcess.cs +79 -79
- package/dist/src/platform/windows/activate-desktop.ps1 +48 -48
- package/dist/src/platform/windows/background-process.mjs +30 -30
- package/dist/src/platform/windows/codex-runtime.mjs +215 -215
- package/dist/src/platform/windows/credential-store.mjs +27 -27
- package/dist/src/platform/windows/credential-store.ps1 +49 -49
- package/dist/src/platform/windows/desktop-helper.mjs +179 -179
- package/dist/src/platform/windows/desktop-host.mjs +58 -58
- package/dist/src/platform/windows/desktop-info.ps1 +14 -14
- package/dist/src/platform/windows/desktop-project-registration.mjs +4 -4
- package/dist/src/platform/windows/desktop-runtime.ps1 +10 -10
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -21
- package/dist/src/platform/windows/dots-profile.mjs +49 -49
- package/dist/src/platform/windows/launch-worker.mjs +41 -41
- package/dist/src/platform/windows/network-info.mjs +34 -34
- package/dist/src/platform/windows/project-locations.mjs +16 -16
- package/dist/src/platform/windows/resident-start.js +20 -20
- package/dist/src/platform/windows/resident-start.ps1 +7 -7
- package/dist/src/platform/windows/resident-startup.ps1 +83 -83
- package/dist/src/platform/windows/resolve-state-root.ps1 +14 -14
- package/dist/src/platform/windows/runtime-paths.mjs +20 -20
- package/dist/src/platform/windows/rustdesk-video-probe.mjs +167 -167
- package/dist/src/platform/windows/service-host.mjs +31 -31
- package/dist/src/platform/windows/start-hidden-console.ps1 +49 -49
- package/dist/src/platform/windows/stop-desktop.ps1 +22 -22
- package/dist/src/platform/windows/user-environment.mjs +50 -50
- package/dist/src/platform/windows/user-environment.ps1 +41 -41
- package/dist/src/project-archive.mjs +30 -0
- package/dist/src/project-create.mjs +121 -119
- package/dist/src/project-management.mjs +59 -69
- package/dist/src/project-remove.mjs +55 -0
- package/dist/src/project-update.mjs +56 -0
- package/dist/src/runtime-discovery.mjs +47 -47
- package/dist/src/self-relay/account-cleanup.mjs +30 -30
- package/dist/src/self-relay/account-connections.mjs +52 -52
- package/dist/src/self-relay/admin-cloud.mjs +101 -101
- package/dist/src/self-relay/admin-page.mjs +80 -80
- package/dist/src/self-relay/admin-server.mjs +74 -74
- package/dist/src/self-relay/admin-state.mjs +127 -127
- package/dist/src/self-relay/admin-style.mjs +14 -14
- package/dist/src/self-relay/android-releases.mjs +61 -61
- package/dist/src/self-relay/client.mjs +61 -61
- package/dist/src/self-relay/connector.mjs +439 -438
- package/dist/src/self-relay/debug-allowlist.mjs +33 -33
- package/dist/src/self-relay/demo.mjs +37 -37
- package/dist/src/self-relay/device-info.mjs +27 -0
- package/dist/src/self-relay/device-store.mjs +92 -84
- package/dist/src/self-relay/diagnostics.mjs +54 -54
- package/dist/src/self-relay/email-accounts.mjs +183 -183
- package/dist/src/self-relay/file-wire.mjs +13 -13
- package/dist/src/self-relay/lan-authorizations.mjs +71 -42
- package/dist/src/self-relay/lifecycle.mjs +264 -250
- package/dist/src/self-relay/long-pairing.mjs +40 -40
- package/dist/src/self-relay/mysql-state.mjs +118 -105
- package/dist/src/self-relay/p2p-probe.mjs +159 -159
- package/dist/src/self-relay/privacy-page.mjs +26 -26
- package/dist/src/self-relay/public-releases.mjs +34 -0
- package/dist/src/self-relay/rtc-file-bridge.mjs +41 -41
- package/dist/src/self-relay/server.mjs +1006 -932
- package/dist/src/self-relay/usage-daily.mjs +55 -55
- package/dist/src/self-relay/usage-meter.mjs +141 -141
- package/dist/src/self-relay/windows-route-helper.mjs +75 -75
- package/dist/src/self-relay/wire.mjs +36 -36
- package/dist/src/server.mjs +1861 -1778
- package/dist/src/service-control.mjs +82 -82
- package/dist/src/service-doctor.mjs +110 -110
- package/dist/src/service-health.mjs +33 -33
- package/dist/src/service-install.mjs +91 -91
- package/dist/src/service-lifecycle.mjs +138 -138
- package/dist/src/service-restart.mjs +81 -81
- package/dist/src/shared-app-server.mjs +615 -611
- package/dist/src/shared-catalog.mjs +132 -132
- package/dist/src/shared-recovery.mjs +146 -146
- package/dist/src/shared-thread-stream.mjs +164 -164
- package/dist/src/skill-install.mjs +51 -51
- package/dist/src/thread-title-generation.mjs +150 -150
- package/dist/src/turn-model-history.mjs +47 -47
- package/dist/src/usage-reset.mjs +17 -17
- package/dist/src/workstation-downloads.mjs +120 -120
- package/dist/tools/rtc-route-helper.ps1 +99 -99
- package/dist/tools/start-rtc-route-helper.ps1 +15 -15
- package/dist/web/vendor/markdown-it.LICENSE.txt +22 -22
- package/dist/web/vendor/markdown-it.umd.min.js +13 -13
- package/package.json +7 -6
- 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
|
-
|
|
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
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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 => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[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 => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[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
|
+
}
|