@walkhi/code-relax 0.1.0-beta.13 → 0.1.0-beta.14
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 +3 -7
- package/dist/bin/codex-remote.mjs +18 -47
- package/dist/skills/code-relax/SKILL.md +3 -3
- package/dist/src/desktop-launcher.mjs +1 -1
- package/dist/src/self-relay/lifecycle.mjs +1 -1
- package/dist/src/service-doctor.mjs +14 -1
- package/package.json +1 -1
- package/tools/postinstall.mjs +3 -22
package/README.md
CHANGED
|
@@ -19,13 +19,13 @@ npm i -g @walkhi/code-relax@beta --allow-scripts=@walkhi/code-relax --foreground
|
|
|
19
19
|
```powershell
|
|
20
20
|
relax start # 启动/复用 Bridge、受管 Codex 内核和已配置中继
|
|
21
21
|
relax pair # 启动/复用服务并打开配对二维码
|
|
22
|
-
relax
|
|
22
|
+
relax doctor # 集中查看服务、登录自启和最近恢复结果
|
|
23
23
|
relax stop
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Codex 内核由官方 `codex app-server --listen ws://127.0.0.1:45839` 提供执行服务,Desktop 和 Bridge 均可连接;它只监听回环地址。Bridge 独立报告内核健康、Desktop 接入和手机可写状态;Desktop 退出后,手机仍可操作内核中的任务。同一 thread 已被另一个实例持有时,手机只读且可重新检测。
|
|
27
27
|
|
|
28
|
-
手机工作站详情可确认让 Desktop 接入 Codex 内核,独立进程逐步执行并保留 Bridge/中继连接,见[启用流程](../docs/shared-recovery.md)。本机恢复命令为 `relax shared-recover --confirm`,查看恢复结果用 `relax
|
|
28
|
+
手机工作站详情可确认让 Desktop 接入 Codex 内核,独立进程逐步执行并保留 Bridge/中继连接,见[启用流程](../docs/shared-recovery.md)。本机恢复命令为 `relax shared-recover --confirm`,查看恢复结果用 `relax doctor`。Bridge 状态匹配且健康时直接复用;未知进程占用 45831/45839 时拒绝接管。`stop` 只停止 Bridge,不停止 Desktop 或受管 app-server。
|
|
29
29
|
|
|
30
30
|
受管 app-server 通过隐藏控制台启动,使后续控制台子进程继承隐藏窗口。手机的项目、任务列表和新建任务均使用受管内核,不依赖 Desktop 工具管道;项目和任务 ID 保持原生值。
|
|
31
31
|
|
|
@@ -37,14 +37,10 @@ Codex 内核由官方 `codex app-server --listen ws://127.0.0.1:45839` 提供执
|
|
|
37
37
|
|
|
38
38
|
```powershell
|
|
39
39
|
relax start --json
|
|
40
|
-
relax relay-start --json
|
|
41
|
-
relax relay-status --json
|
|
42
40
|
relax pair --json
|
|
43
|
-
relax relay-stop --json
|
|
44
|
-
relax relay-revoke --json
|
|
45
41
|
```
|
|
46
42
|
|
|
47
|
-
`start` 启动内核和 Bridge
|
|
43
|
+
`start` 启动内核和 Bridge,并在 Bridge 内恢复已配置中继。每台移动设备通过 `pair` 配对一次;新增设备不替换已有设备。Bridge 把 Windows hostname 和当前 Wi-Fi/有线网络候选经已认证 WebSocket 临时发送给手机,手机本地保存电脑名,只在设备身份验证通过后保存可达地址;服务器不持久化这些 Code Relax 中继 hostname、IP 或网络名。OpenAI 官方认证和设备配对仍直接访问 OpenAI;完成后,手机使用统一配对产生的设备凭据,通过独立元数据 WebSocket 把官方环境 ID、名称和 hostname 写入设备记录,不上传 OpenAI access token、配对码或登录态。
|
|
48
44
|
|
|
49
45
|
Bridge 的局域网端口仅提供鉴权 WebSocket,普通 HTTP 请求返回 404;手机聊天页面随 HAP 安装。
|
|
50
46
|
|
|
@@ -4,7 +4,7 @@ import { lifecycle, readRecord } from '../src/service-lifecycle.mjs';
|
|
|
4
4
|
import { runtimePaths } from '../src/platform/windows/runtime-paths.mjs';
|
|
5
5
|
import * as host from '../src/platform/windows/service-host.mjs';
|
|
6
6
|
import { installService, uninstallService, packageRoot } from '../src/service-install.mjs';
|
|
7
|
-
import { installSkill, removeSkill, inspectSkill } from '../src/skill-install.mjs';
|
|
7
|
+
import { installSkill, removeSkill, inspectSkill } from '../src/skill-install.mjs';
|
|
8
8
|
import { doctor } from '../src/service-doctor.mjs';
|
|
9
9
|
import { launchDesktop } from '../src/desktop-launcher.mjs';
|
|
10
10
|
import { desktopHost, removeDesktopShortcut, residentStartup, resolveStateRoot } from '../src/platform/windows/desktop-host.mjs';
|
|
@@ -14,31 +14,25 @@ import path from 'node:path';
|
|
|
14
14
|
import fs from 'node:fs';
|
|
15
15
|
import { spawn } from 'node:child_process';
|
|
16
16
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
17
|
-
import { requestRecovery,
|
|
18
|
-
import { configureManagedEnvironment, clearManagedEnvironment
|
|
17
|
+
import { requestRecovery, recoveryWarning } from '../src/shared-recovery.mjs';
|
|
18
|
+
import { configureManagedEnvironment, clearManagedEnvironment } from '../src/platform/windows/user-environment.mjs';
|
|
19
19
|
import { resolveCodexRuntime } from '../src/platform/windows/codex-runtime.mjs';
|
|
20
|
-
import { managedAppServerUrl } from '../src/managed-app-server.mjs';
|
|
21
20
|
import { controlRequest } from '../src/service-control.mjs';
|
|
22
21
|
import { processAlive } from '../src/background-process.mjs';
|
|
23
22
|
|
|
24
23
|
const help = `Code Relax(Windows)
|
|
25
24
|
用法:relax <命令> [--json];下列命令均可用
|
|
26
|
-
|
|
25
|
+
常用指令:
|
|
27
26
|
start 启动或复用 Bridge 和 Codex 内核,并恢复已配置的私有中继
|
|
28
|
-
shared-recover --confirm 重新打开 Desktop 并接入 Codex 内核;不强制重启已有内核
|
|
29
|
-
shared-recovery-status 查看最近一次恢复的实际阶段和结果
|
|
30
|
-
autostart-enable / autostart-disable / autostart-status 管理当前用户登录时启动 Bridge
|
|
31
27
|
pair 启动或复用工作站服务,并打开一次性配对二维码
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
relay-revoke 撤销这台电脑的全部移动设备授权(停止连接器不会撤销授权)
|
|
38
|
-
doctor 检查安装、Node、Codex 内核、网络和 Skill,提示防火墙处理方式
|
|
39
|
-
skill-install 安装/更新当前用户的 Codex Skill,不覆盖自定义文件
|
|
40
|
-
skill-remove 移除本包安装且未修改的 Skill
|
|
28
|
+
stop 停止服务
|
|
29
|
+
维护指令:
|
|
30
|
+
setup 部署并启动固定目录服务、受管 app-server 和登录自启动;安全配置 Desktop 后续接入
|
|
31
|
+
doctor 检查服务、登录自启、最近恢复结果、网络和 Skill
|
|
32
|
+
shared-recover --confirm 重新打开 Desktop 并接入 Codex 内核;不强制重启已有内核
|
|
41
33
|
uninstall 停止并移除固定目录服务和本包 Skill,清理自有 Desktop 环境变量,保留其他配置与附件
|
|
34
|
+
其他指令:
|
|
35
|
+
autostart-enable 启用当前用户登录启动
|
|
42
36
|
默认监听 0.0.0.0,移动端与电脑需在同一受信任局域网。
|
|
43
37
|
--json 输出机器可读结果;统一二维码不含 LAN 地址、Bridge Token 或中继地址。`;
|
|
44
38
|
|
|
@@ -48,7 +42,7 @@ async function main() {
|
|
|
48
42
|
console.log(help);
|
|
49
43
|
return;
|
|
50
44
|
}
|
|
51
|
-
if (!['autostart-enable', '
|
|
45
|
+
if (!['autostart-enable', 'shared-recover', 'setup', 'doctor', 'uninstall', 'start', 'pair', 'stop'].includes(command)
|
|
52
46
|
|| flags.some(flag => flag !== '--json' && !(command === 'shared-recover' && flag === '--confirm'))) {
|
|
53
47
|
throw new Error('无效命令或参数。使用 --help 查看用法。');
|
|
54
48
|
}
|
|
@@ -56,13 +50,8 @@ async function main() {
|
|
|
56
50
|
const json = flags.includes('--json');
|
|
57
51
|
const paths = runtimePaths();
|
|
58
52
|
const { stateRoot, serviceRoot } = paths;
|
|
59
|
-
if (['relay-revoke', 'relay-start', 'relay-status', 'relay-stop'].includes(command)) {
|
|
60
|
-
const result = await selfRelay(command, stateRoot);
|
|
61
|
-
console.log(json ? JSON.stringify(result) : `${result.displayText}${result.qrPath ? '\n二维码:' + result.qrPath : ''}`);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
53
|
const installedEntry = path.join(serviceRoot, 'dist/bin/codex-remote.mjs');
|
|
65
|
-
if (['autostart-enable', '
|
|
54
|
+
if (['autostart-enable', 'shared-recover', 'start', 'pair', 'stop'].includes(command) && path.resolve(fileURLToPath(import.meta.url)).toLowerCase() !== installedEntry.toLowerCase()) {
|
|
66
55
|
if (!fs.existsSync(installedEntry)) throw new Error('尚未部署服务,请先执行 relax setup。');
|
|
67
56
|
const executable = readRecord(path.join(serviceRoot, 'installation.json'))?.nodeExecutable;
|
|
68
57
|
if (!executable || !fs.existsSync(executable)) throw new Error('安装时选用的 Node 不存在,请重新执行 relax setup。');
|
|
@@ -77,13 +66,6 @@ async function main() {
|
|
|
77
66
|
if (!installation?.nodeExecutable) throw new Error('请先执行 setup。');
|
|
78
67
|
const action = command.slice('autostart-'.length);
|
|
79
68
|
let environment;
|
|
80
|
-
if (action === 'disable') {
|
|
81
|
-
environment = await clearManagedEnvironment(stateRoot);
|
|
82
|
-
const current = await readManagedEnvironment();
|
|
83
|
-
if (current.userValue === managedAppServerUrl || (!current.userValue && current.machineValue === managedAppServerUrl)) {
|
|
84
|
-
throw new Error('Desktop 仍会连接 Code Relax 内核;请先处理非本包管理的 CODEX_APP_SERVER_WS_URL,未关闭登录启动。');
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
69
|
if (action === 'enable') {
|
|
88
70
|
const running = await service.status();
|
|
89
71
|
if (!running.running) await service.start();
|
|
@@ -103,10 +85,9 @@ async function main() {
|
|
|
103
85
|
environment?.configured ? ';已配置 Desktop 接入地址' : '';
|
|
104
86
|
console.log(json ? JSON.stringify(output) : `登录启动:${result.enabled ? '已启用' : '已关闭'}${environmentText}`); return;
|
|
105
87
|
}
|
|
106
|
-
if (command === 'shared-recover'
|
|
107
|
-
if (
|
|
108
|
-
const result =
|
|
109
|
-
? await requestRecovery(stateRoot, { confirmed: true }) : await recoveryStatus(stateRoot);
|
|
88
|
+
if (command === 'shared-recover') {
|
|
89
|
+
if (!flags.includes('--confirm')) throw new Error(recoveryWarning + ' 使用 --confirm 确认。');
|
|
90
|
+
const result = await requestRecovery(stateRoot, { confirmed: true });
|
|
110
91
|
console.log(json ? JSON.stringify(result) : `${result.message || result.status}${result.error ? '\n' + result.error : ''}\n${result.operationId || ''}`.trim());
|
|
111
92
|
return;
|
|
112
93
|
}
|
|
@@ -131,13 +112,6 @@ async function main() {
|
|
|
131
112
|
if (!result.ready) process.exitCode = 1;
|
|
132
113
|
return;
|
|
133
114
|
}
|
|
134
|
-
if (command === 'skill-install' || command === 'skill-remove') {
|
|
135
|
-
const result = command === 'skill-install'
|
|
136
|
-
? installSkill(path.join(packageRoot(path.dirname(fileURLToPath(import.meta.url))), 'dist/skills/code-relax/SKILL.md'))
|
|
137
|
-
: removeSkill();
|
|
138
|
-
console.log(json ? JSON.stringify(result) : `${result.message}\n${result.target}`);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
115
|
if (command === 'uninstall') {
|
|
142
116
|
const skill = inspectSkill();
|
|
143
117
|
let environment;
|
|
@@ -253,6 +227,7 @@ async function main() {
|
|
|
253
227
|
}
|
|
254
228
|
}
|
|
255
229
|
const autostart = await residentStartup(installed, 'enable');
|
|
230
|
+
const skill = installSkill(path.join(serviceRoot, 'dist/skills/code-relax/SKILL.md'));
|
|
256
231
|
if (desktopNeedsAttach && !desktopRestarted) {
|
|
257
232
|
const currentDesktop = await desktopHost.inspect();
|
|
258
233
|
if (currentDesktop.running) await desktopHost.closeDesktop(currentDesktop.desktop);
|
|
@@ -262,6 +237,7 @@ async function main() {
|
|
|
262
237
|
bridge = await service.status();
|
|
263
238
|
}
|
|
264
239
|
installed.autostart = autostart;
|
|
240
|
+
installed.skill = skill;
|
|
265
241
|
installed.restarted = resumeAfterUpdate;
|
|
266
242
|
installed.environment = environment;
|
|
267
243
|
installed.managedAppServer = bridge.managedAppServer;
|
|
@@ -287,11 +263,6 @@ async function main() {
|
|
|
287
263
|
}
|
|
288
264
|
if (!json && command === 'start') console.log('正在检查电脑连接并准备 Bridge…');
|
|
289
265
|
const result = await service[command]();
|
|
290
|
-
if (command === 'status') {
|
|
291
|
-
console.log(json ? JSON.stringify(result) : result.running
|
|
292
|
-
? `服务正常:${result.mode},版本 ${result.version || '未知'},端口 ${result.port},PID ${result.pid}` : '服务未就绪。');
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
266
|
const { accessUrl, qrPath: _legacyQrPath, ...metadata } = result;
|
|
296
267
|
const notice = '本机服务检查通过,移动端连通性尚未验证。若电脑弹出 Node 网络访问提示,请先处理 Windows 防火墙授权,再用移动端连接。';
|
|
297
268
|
let relay;
|
|
@@ -6,12 +6,12 @@ description: 启动、检查或停止 Code Relax 工作站服务,生成并展
|
|
|
6
6
|
# Code Relax
|
|
7
7
|
|
|
8
8
|
- `relax start --json` 启动 Bridge 和受管 Codex 内核,无需打开 Desktop;支持纯 CLI 安装。Desktop 正常打开后接入已配置内核;需要重新接入时使用 App 的“重启 Codex Desktop”或经确认的 `relax shared-recover --confirm`。已配置 Code Relax 中继时,启动命令同时恢复中继连接器和已有设备授权。
|
|
9
|
-
- npm 全局安装会自动执行服务部署和 Skill
|
|
10
|
-
- `setup` 默认启用当前用户登录启动工作站服务,不自动打开 Codex;用 `
|
|
9
|
+
- npm 全局安装会自动执行服务部署和 Skill 安装;修复安装时运行 `relax setup --json`。
|
|
10
|
+
- `setup` 默认启用当前用户登录启动工作站服务,不自动打开 Codex;用 `doctor` 查看、`autostart-enable` 恢复登录启动。恢复完整控制会保留 Bridge 和中继,但仍须明确确认可能中断全部完整控制任务。
|
|
11
11
|
- 用户要求启动 Code Relax 时,执行一次 `relax start --json`。用户要求二维码或当前启动结果包含二维码时,展示 CLI 返回的 `qrPath` 和 `displayText`;没有设备授权时可用 `relax pair --json` 生成新的配对二维码。
|
|
12
12
|
- 配对二维码是短期 HTTPS 邀请,不属于工作站或某一台移动设备,也不含 LAN IP、端口、Bridge Token 或中继地址。新增设备不撤销已有设备。
|
|
13
13
|
- 已授权设备可直接使用 Code Relax 中继。App 以工作站名显示已配对电脑,进入后选择局域网、Code Relax 中继或 OpenAI 中继。启动命令会经已认证中继临时刷新当前 LAN 候选;App 只保存设备凭据验证成功的网络记录。
|
|
14
|
-
- `status --json` 查询工作站服务;`
|
|
14
|
+
- `status --json` 查询工作站服务;`stop --json` 停止 Bridge,不停止受管 Codex 内核。中继连接由 Bridge 管理,不提供单独的中继 CLI 命令。
|
|
15
15
|
- 启动或配对成功后,将 `qrPath` 以 Markdown 图片嵌入回复,并逐字展示 `displayText`。不要把二维码内容、Token 或 Base64 写进消息。
|
|
16
16
|
- 不自动修改防火墙或发送测试消息。`lanAccess: unverified` 或 `mobileAccess: unverified` 只表示设备链路尚未现场验证。
|
|
17
17
|
- 服务位于用户 `Programs/CodexRemote/service`;配置在 `LocalAppData/CodexRemote`;附件在 `ProgramData/CodexRemote/attachments`。不要删除配置、附件或凭据来排障。
|
|
@@ -7,7 +7,7 @@ export { portAvailable };
|
|
|
7
7
|
export async function launchDesktop({ stateRoot, host, environment = process.env, available = portAvailable, wait = delay, timeout = 30000, recoveryId, progress = () => {} }) {
|
|
8
8
|
fs.mkdirSync(stateRoot, { recursive: true });
|
|
9
9
|
const recovery = readRecord(path.join(stateRoot, 'shared-recovery.lock'));
|
|
10
|
-
if (recovery && recovery.operationId !== recoveryId) throw new Error('Codex
|
|
10
|
+
if (recovery && recovery.operationId !== recoveryId) throw new Error('Codex 内核正在启动,请用 relax doctor 查看恢复状态。');
|
|
11
11
|
const lock = path.join(stateRoot, 'desktop-launch.lock');
|
|
12
12
|
let descriptor;
|
|
13
13
|
let managed;
|
|
@@ -111,7 +111,7 @@ async function createRelayRuntime(stateRoot) {
|
|
|
111
111
|
bridgeReady: health.status === 'ok' && health.execution?.ready === true && health.sharedAppServerConnected === true, bridgeError,
|
|
112
112
|
expiresAt: expiresAt ? new Date(expiresAt).toISOString() : null,
|
|
113
113
|
...(available ? { qrPath } : {}), mobileAccess: 'unverified', lastError,
|
|
114
|
-
displayText: `Code Relax 中继 · ${health.transportMode === 'lan-shared' ? 'Codex 内核已就绪' : 'Codex 内核未就绪'}\n${next}
|
|
114
|
+
displayText: `Code Relax 中继 · ${health.transportMode === 'lan-shared' ? 'Codex 内核已就绪' : 'Codex 内核未就绪'}\n${next}` };
|
|
115
115
|
};
|
|
116
116
|
async function shutdown() {
|
|
117
117
|
if (closing) return;
|
|
@@ -2,6 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { readRecord } from './service-lifecycle.mjs';
|
|
4
4
|
import { inspectSkill } from './skill-install.mjs';
|
|
5
|
+
import { residentStartup } from './platform/windows/desktop-host.mjs';
|
|
6
|
+
import { recoveryStatus } from './shared-recovery.mjs';
|
|
5
7
|
|
|
6
8
|
export async function doctor({ paths, host, service, skillTarget }) {
|
|
7
9
|
const checks = [];
|
|
@@ -41,10 +43,21 @@ export async function doctor({ paths, host, service, skillTarget }) {
|
|
|
41
43
|
if (state.managedAppServer?.launcherRestartRequired) add('managed-app-server-launcher', 'warning',
|
|
42
44
|
'安装文件已经更新,但当前 app-server 仍由旧启动链创建;隐藏控制台等运行时修复尚未生效,请执行 relax setup 或重启 Windows。');
|
|
43
45
|
});
|
|
46
|
+
await check('autostart', async () => {
|
|
47
|
+
const record = readRecord(path.join(paths.serviceRoot, 'installation.json'));
|
|
48
|
+
if (!record?.nodeExecutable) throw Error('尚未安装服务,无法检查登录自启。');
|
|
49
|
+
const state = await residentStartup({ serviceRoot: paths.serviceRoot, nodeExecutable: record.nodeExecutable }, 'status');
|
|
50
|
+
add('autostart', state.enabled ? 'ok' : 'warning', `当前用户登录自启:${state.enabled ? '已启用' : '未启用'}。`);
|
|
51
|
+
});
|
|
52
|
+
await check('desktop-recovery', async () => {
|
|
53
|
+
const state = await recoveryStatus(paths.stateRoot);
|
|
54
|
+
add('desktop-recovery', state.status === 'failed' ? 'warning' : 'ok',
|
|
55
|
+
`最近 Desktop 接入:${state.message || state.status}${state.error ? `;${state.error}` : ''}`);
|
|
56
|
+
});
|
|
44
57
|
await check('skill', async () => {
|
|
45
58
|
const state = inspectSkill(skillTarget);
|
|
46
59
|
add('skill', state.managed ? 'ok' : 'warning', state.managed ? '本包管理的 Skill 已安装。'
|
|
47
|
-
: state.installed ? '存在同名自定义 Skill,本包不会覆盖。' : '
|
|
60
|
+
: state.installed ? '存在同名自定义 Skill,本包不会覆盖。' : 'Skill 未安装;重新执行 relax setup。');
|
|
48
61
|
});
|
|
49
62
|
add('firewall', 'warning', '移动端连通性未验证。首次启动请允许记录的 Node 在受信任的专用网络通信;若曾拒绝,可在 Windows 安全中心的防火墙“允许应用通过防火墙”中检查该 Node。不要关闭整个防火墙。');
|
|
50
63
|
return { ready: !checks.some(item => item.status === 'error'), lanAccess: 'unverified', checks };
|
package/package.json
CHANGED
package/tools/postinstall.mjs
CHANGED
|
@@ -66,13 +66,6 @@ export function formatSetupOutput(output, previous = {}) {
|
|
|
66
66
|
} catch { return output; }
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export function formatSkillOutput(output) {
|
|
70
|
-
try {
|
|
71
|
-
const result = JSON.parse(output);
|
|
72
|
-
return `Code Relax Skill:${result.message || (result.installed ? '已安装' : '已检查')}\n`;
|
|
73
|
-
} catch { return output; }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
69
|
function installationSnapshot() {
|
|
77
70
|
const recordPath = path.join(serviceRoot, 'installation.json');
|
|
78
71
|
if (!fs.existsSync(serviceRoot)) return { exists: false, valid: false };
|
|
@@ -113,34 +106,22 @@ function setupFailureGuidance(previous, current) {
|
|
|
113
106
|
return '这是首次安装,尚未形成可确认的固定服务。\n修复上方错误后重新运行 npm 安装命令;无需执行卸载。';
|
|
114
107
|
}
|
|
115
108
|
|
|
116
|
-
function skillFailureGuidance(previous, current) {
|
|
117
|
-
const service = current.valid
|
|
118
|
-
? `${previous.valid ? `服务已从 ${previous.version} 更新到` : '固定服务已安装为'} ${current.version}`
|
|
119
|
-
: 'setup 已返回成功,但当前固定服务记录无法确认';
|
|
120
|
-
return `${service};Codex Skill 安装失败。\n请执行 relax skill-install,再执行 relax doctor;无需重新卸载服务。`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
109
|
const invokedDirectly = process.argv[1]
|
|
124
110
|
&& path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
|
|
125
111
|
if (invokedDirectly && process.env.npm_config_global === 'true') {
|
|
126
112
|
const entry = fileURLToPath(new URL('../dist/bin/codex-remote.mjs', import.meta.url));
|
|
127
113
|
const previous = installationSnapshot();
|
|
128
|
-
let stage = 'setup';
|
|
129
114
|
appendLog(`[postinstall] start package=${process.env.npm_package_version || 'unknown'} ${systemSummary()}`);
|
|
130
115
|
try {
|
|
131
|
-
runStage(entry,
|
|
132
|
-
stage = 'skill-install';
|
|
133
|
-
runStage(entry, stage, ['skill-install', '--json'], formatSkillOutput);
|
|
116
|
+
runStage(entry, 'setup', ['setup', '--json'], output => formatSetupOutput(output, previous));
|
|
134
117
|
console.log(`安装日志:${installLog}`);
|
|
135
118
|
appendLog('[postinstall] success');
|
|
136
119
|
} catch (error) {
|
|
137
120
|
const current = installationSnapshot();
|
|
138
|
-
console.error(`\nCode Relax 安装未完全完成:\n${
|
|
139
|
-
? setupFailureGuidance(previous, current)
|
|
140
|
-
: skillFailureGuidance(previous, current)}`);
|
|
121
|
+
console.error(`\nCode Relax 安装未完全完成:\n${setupFailureGuidance(previous, current)}`);
|
|
141
122
|
if (error?.code && error.code !== 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') console.error(`启动安装步骤失败:${error.message}`);
|
|
142
123
|
console.error(`安装日志:${installLog}`);
|
|
143
|
-
appendLog(
|
|
124
|
+
appendLog('[postinstall] failed stage=setup');
|
|
144
125
|
process.exitCode = Number.isInteger(error?.status) && error.status > 0 ? error.status : 1;
|
|
145
126
|
}
|
|
146
127
|
}
|