@walkhi/code-relax 0.1.0-beta.1 → 0.1.0-beta.11
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 +53 -64
- package/dist/bin/codex-remote.mjs +90 -71
- package/dist/bin/self-relay-admin.mjs +8 -0
- package/dist/bin/self-relay-demo.mjs +1 -1
- package/dist/bin/self-relay-server.mjs +5 -2
- package/dist/shared/app-server-events.cjs +64 -24
- package/dist/shared/file-mentions.cjs +35 -0
- package/dist/shared/p2p-data-channel.cjs +50 -0
- package/dist/skills/code-relax/SKILL.md +1 -1
- package/dist/src/desktop-launcher.mjs +16 -21
- package/dist/src/file-upload-session.mjs +124 -0
- package/dist/src/lan-socket-server.mjs +59 -10
- package/dist/src/managed-app-server.mjs +36 -14
- package/dist/src/message-images.mjs +0 -18
- package/dist/src/onboarding.mjs +6 -8
- package/dist/src/platform/README.md +1 -1
- package/dist/src/platform/windows/IsolatedProcess.cs +7 -3
- package/dist/src/platform/windows/background-process.mjs +7 -2
- package/dist/src/platform/windows/codex-runtime.mjs +117 -0
- package/dist/src/platform/windows/desktop-host.mjs +17 -3
- package/dist/src/platform/windows/desktop-info.ps1 +2 -4
- package/dist/src/platform/windows/desktop-shortcuts.ps1 +7 -14
- package/dist/src/platform/windows/launch-worker.mjs +42 -0
- package/dist/src/platform/windows/resident-startup.ps1 +58 -18
- package/dist/src/platform/windows/service-host.mjs +4 -16
- package/dist/src/platform/windows/start-hidden-console.ps1 +18 -8
- package/dist/src/platform/windows/stop-desktop.ps1 +7 -4
- package/dist/src/runtime-discovery.mjs +43 -0
- package/dist/src/self-relay/admin-cloud.mjs +101 -0
- package/dist/src/self-relay/admin-page.mjs +71 -0
- package/dist/src/self-relay/admin-server.mjs +61 -0
- package/dist/src/self-relay/admin-state.mjs +122 -0
- package/dist/src/self-relay/client.mjs +2 -0
- package/dist/src/self-relay/connector.mjs +107 -20
- package/dist/src/self-relay/demo.mjs +4 -5
- package/dist/src/self-relay/lifecycle.mjs +58 -70
- package/dist/src/self-relay/p2p-probe.mjs +123 -83
- package/dist/src/self-relay/server.mjs +224 -29
- package/dist/src/server.mjs +616 -1299
- package/dist/src/service-doctor.mjs +14 -26
- package/dist/src/service-install.mjs +1 -4
- package/dist/src/service-lifecycle.mjs +12 -12
- package/dist/src/shared-app-server.mjs +206 -46
- package/dist/src/shared-catalog.mjs +5 -25
- package/dist/src/shared-recovery.mjs +11 -9
- package/dist/src/shared-thread-stream.mjs +1 -1
- package/dist/src/skill-install.mjs +0 -13
- package/package.json +16 -18
- package/tools/postinstall.mjs +140 -3
- package/dist/src/app-server-client.mjs +0 -468
- package/dist/src/app-server-tasks.mjs +0 -355
- package/dist/src/debug-ui.mjs +0 -131
- package/dist/src/platform/windows/desktop-monitor.mjs +0 -34
- package/dist/src/platform/windows/desktop-shortcut-run.ps1 +0 -5
- package/dist/src/platform/windows/desktop-tools.mjs +0 -48
- package/dist/src/platform/windows/launch-worker.ps1 +0 -13
- package/dist/src/thread-catalog.mjs +0 -42
- package/dist/tools/find-desktop-pipe.mjs +0 -10
- package/dist/web/approval-ui.js +0 -188
- package/dist/web/capabilities.js +0 -57
- package/dist/web/chat-transport.js +0 -196
- package/dist/web/chat.css +0 -736
- package/dist/web/chat.js +0 -5149
- package/dist/web/client-platform.js +0 -11
- package/dist/web/harmony-platform.js +0 -51
- package/dist/web/highlight-powershell.min.js +0 -39
- package/dist/web/highlight.min.js +0 -1262
- package/dist/web/history-cache.js +0 -146
- package/dist/web/index.html +0 -222
- package/dist/web/lan-session.js +0 -122
- package/dist/web/p2p-diagnostics.js +0 -28
- package/dist/web/p2p-probe.js +0 -83
- package/dist/web/resources.json +0 -1
- package/dist/web/self-relay-session.js +0 -119
- package/dist/web/server-requests.js +0 -67
- package/dist/web/timeline-sync.js +0 -67
- package/dist/web/vendor/README.md +0 -14
- package/dist/web/vendor/highlight-powershell.min.js +0 -39
- package/dist/web/vendor/highlight.LICENSE.txt +0 -29
- package/dist/web/vendor/highlight.min.js +0 -1262
|
@@ -8,6 +8,7 @@ import { startBackground } from './background-process.mjs';
|
|
|
8
8
|
import { desktopHost } from './platform/windows/desktop-host.mjs';
|
|
9
9
|
import * as serviceHost from './platform/windows/service-host.mjs';
|
|
10
10
|
import { launchDesktop, portAvailable } from './desktop-launcher.mjs';
|
|
11
|
+
import { configureManagedEnvironment } from './platform/windows/user-environment.mjs';
|
|
11
12
|
|
|
12
13
|
const script = fileURLToPath(import.meta.url);
|
|
13
14
|
const recordPath = root => path.join(root, 'shared-recovery.json');
|
|
@@ -17,9 +18,9 @@ const active = value => value && ['scheduled', 'running'].includes(value.status)
|
|
|
17
18
|
const recoverySteps = () => [
|
|
18
19
|
['checking', '检查启动条件'], ['stopping-desktop', '关闭当前 Codex'],
|
|
19
20
|
['starting-app-server', '确认 Codex 内核'], ['starting-desktop', '重新打开 Codex'],
|
|
20
|
-
['checking-execution', '
|
|
21
|
+
['checking-execution', '确认移动端控制就绪'],
|
|
21
22
|
].map(([id, label]) => ({ id, label, status: 'pending' }));
|
|
22
|
-
export const recoveryWarning = 'Codex Desktop 需要重新打开,才能接入同一个 Codex
|
|
23
|
+
export const recoveryWarning = 'Codex Desktop 需要重新打开,才能接入同一个 Codex 内核。移动端连接和 Codex 内核会保持运行;旧 Desktop 实例中的任务可能中断,未发送内容请先保存。切换不会清空项目、对话历史、设置或设备授权。';
|
|
23
24
|
|
|
24
25
|
export async function recoveryStatus(stateRoot, host = desktopHost) {
|
|
25
26
|
const record = readRecord(recordPath(stateRoot));
|
|
@@ -40,11 +41,11 @@ export async function recoveryStatus(stateRoot, host = desktopHost) {
|
|
|
40
41
|
export async function planRecovery(stateRoot, host, available = portAvailable) {
|
|
41
42
|
const info = await host.inspect();
|
|
42
43
|
const action = await host.sharedServiceUrl() ? 'restart' : 'switch';
|
|
43
|
-
if (!info.desktop
|
|
44
|
-
const record = readRecord(path.join(stateRoot, 'managed-app-server.json'))
|
|
44
|
+
if (!info.desktop) throw new Error('未找到 Codex Desktop;CLI 用户无需执行 Desktop 接入。');
|
|
45
|
+
const record = readRecord(path.join(stateRoot, 'managed-app-server.json'));
|
|
45
46
|
const actual = record ? await host.process(record.pid) : null;
|
|
46
47
|
if (actual && (!sameProcess(record, actual) || !actual.command?.includes('app-server') || !actual.command.includes('ws://127.0.0.1:45839'))) {
|
|
47
|
-
throw new Error('
|
|
48
|
+
throw new Error('Codex 内核进程身份与记录不符,未结束任何进程。');
|
|
48
49
|
}
|
|
49
50
|
if (!actual && !await available()) throw new Error('45839 被未识别进程占用,未关闭 Desktop 或结束任何进程。');
|
|
50
51
|
return { desktop: info.desktop, action };
|
|
@@ -63,7 +64,7 @@ export async function requestRecovery(stateRoot, { confirmed, operationId = rand
|
|
|
63
64
|
catch (error) { if (error.code === 'EEXIST') throw new Error('已有恢复操作,请先查看恢复状态。'); throw error; }
|
|
64
65
|
fs.writeFileSync(lock, JSON.stringify({ operationId })); fs.closeSync(lock);
|
|
65
66
|
const record = { operationId, status: 'scheduled', stage: 'prepared', startedAt: new Date().toISOString(),
|
|
66
|
-
message: '
|
|
67
|
+
message: 'Codex 连接请求已接受,电脑管理连接保持运行。', steps: recoverySteps(), ...planned };
|
|
67
68
|
try {
|
|
68
69
|
writeRecord(recordPath(stateRoot), record);
|
|
69
70
|
const log = path.join(stateRoot, 'shared-recovery.log');
|
|
@@ -83,7 +84,7 @@ export async function runRecovery(stateRoot, operationId, dependencies = {}) {
|
|
|
83
84
|
const host = dependencies.host || desktopHost;
|
|
84
85
|
const service = dependencies.service || lifecycle({ stateRoot, host: serviceHost });
|
|
85
86
|
const available = dependencies.available || portAvailable;
|
|
86
|
-
const start = dependencies.launch ||
|
|
87
|
+
const start = dependencies.launch || launchDesktop;
|
|
87
88
|
let record = readRecord(recordPath(stateRoot));
|
|
88
89
|
if (record?.operationId !== operationId || readRecord(lockPath(stateRoot))?.operationId !== operationId) throw new Error('恢复实例已失效。');
|
|
89
90
|
record.steps ||= recoverySteps();
|
|
@@ -99,6 +100,7 @@ export async function runRecovery(stateRoot, operationId, dependencies = {}) {
|
|
|
99
100
|
try {
|
|
100
101
|
stage('checking');
|
|
101
102
|
const planned = await planRecovery(stateRoot, host, available);
|
|
103
|
+
await configureManagedEnvironment(stateRoot);
|
|
102
104
|
record = { ...record, ...planned };
|
|
103
105
|
if (fs.existsSync(path.join(stateRoot, 'desktop-launch.lock'))) throw new Error('Desktop 正在启动,未与启动操作并行恢复。');
|
|
104
106
|
stage('stopping-desktop'); await host.closeDesktop(record.desktop);
|
|
@@ -109,9 +111,9 @@ export async function runRecovery(stateRoot, operationId, dependencies = {}) {
|
|
|
109
111
|
await waitFor(async () => {
|
|
110
112
|
const health = await service.status();
|
|
111
113
|
return health.running && health.mode === 'lan-shared' && health.execution?.ready === true;
|
|
112
|
-
}, '
|
|
114
|
+
}, 'Codex 内核未就绪;Bridge 和中继管理连接仍保留。');
|
|
113
115
|
record = { ...record, status: 'completed', stage: 'ready', steps: record.steps.map(step => ({ ...step, status: 'completed' })),
|
|
114
|
-
message: 'Codex Desktop 已接入 Codex
|
|
116
|
+
message: 'Codex Desktop 已接入 Codex 内核;移动端控制保持就绪,原任务未自动重发。' };
|
|
115
117
|
} catch (error) { record = { ...record, status: 'failed', error: error.message,
|
|
116
118
|
steps: record.steps.map(step => ({ ...step, status: step.status === 'running' ? 'failed' : step.status })),
|
|
117
119
|
message: 'Codex Desktop 未完成接入,已停止后续步骤。' }; }
|
|
@@ -43,7 +43,7 @@ export async function streamSharedThread(response, threadId, client, normalize,
|
|
|
43
43
|
if (!threadListNotificationMethods.has(method)) return;
|
|
44
44
|
emit('thread-list-changed', { method, threadId: params.threadId || params.thread?.id || '' });
|
|
45
45
|
};
|
|
46
|
-
const onDisconnected = () => { emit('bridge-error', { error: '
|
|
46
|
+
const onDisconnected = () => { emit('bridge-error', { error: 'Codex 内核连接已断开,请重新连接。' }); response.end(); finish(); };
|
|
47
47
|
response.once('close', () => { closed = true; finish(); });
|
|
48
48
|
client.on('turn', onTurn); client.on('settings', onSettings); client.on('disconnected', onDisconnected);
|
|
49
49
|
client.on('queue-changed', onQueue);
|
|
@@ -6,11 +6,8 @@ import { readRecord, writeRecord } from './service-lifecycle.mjs';
|
|
|
6
6
|
|
|
7
7
|
const owner = '@walkhi/code-relax';
|
|
8
8
|
const markerName = '.code-relax-install.json';
|
|
9
|
-
const legacyOwner = '@closedhi/codex-remote';
|
|
10
|
-
const legacyMarkerName = '.codex-remote-install.json';
|
|
11
9
|
const digest = value => createHash('sha256').update(value).digest('hex');
|
|
12
10
|
export const skillDirectory = (home = os.homedir()) => path.join(home, '.agents/skills/code-relax');
|
|
13
|
-
const legacySkillDirectory = (home = os.homedir()) => path.join(home, '.agents/skills/codex-remote');
|
|
14
11
|
|
|
15
12
|
// Refuse junctions/symlinks along managed paths before writing or removing files.
|
|
16
13
|
export function assertPlainPath(target) {
|
|
@@ -33,15 +30,6 @@ export function inspectSkill(target = skillDirectory()) {
|
|
|
33
30
|
return { installed: true, managed, target };
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
export function removeLegacyManagedSkill(target = legacySkillDirectory()) {
|
|
37
|
-
const marker = readRecord(path.join(target, legacyMarkerName));
|
|
38
|
-
const file = path.join(target, 'SKILL.md');
|
|
39
|
-
if (marker?.owner !== legacyOwner || !fs.existsSync(file) || marker.sha256 !== digest(fs.readFileSync(file))
|
|
40
|
-
|| !fs.readdirSync(target).every(name => ['SKILL.md', legacyMarkerName].includes(name))) return false;
|
|
41
|
-
fs.unlinkSync(file); fs.unlinkSync(path.join(target, legacyMarkerName)); fs.rmdirSync(target);
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
33
|
export function installSkill(source, target = skillDirectory()) {
|
|
46
34
|
const state = inspectSkill(target);
|
|
47
35
|
if (state.installed && !state.managed) throw new Error(`同名 Skill 非本包管理或已被修改,请保留并手动处理:${target}`);
|
|
@@ -49,7 +37,6 @@ export function installSkill(source, target = skillDirectory()) {
|
|
|
49
37
|
fs.mkdirSync(target, { recursive: true });
|
|
50
38
|
fs.writeFileSync(path.join(target, 'SKILL.md'), content);
|
|
51
39
|
writeRecord(path.join(target, markerName), { owner, sha256: digest(content) });
|
|
52
|
-
if (path.resolve(target) === path.resolve(skillDirectory())) removeLegacyManagedSkill();
|
|
53
40
|
return { installed: true, target, message: 'Skill 已安装/更新;若 Codex 尚未显示,请重新打开 Codex。' };
|
|
54
41
|
}
|
|
55
42
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "@walkhi/code-relax",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"license": "UNLICENSED",
|
|
2
|
+
"name": "@walkhi/code-relax",
|
|
3
|
+
"version": "0.1.0-beta.11",
|
|
4
|
+
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"postinstall": "node ./tools/postinstall.mjs",
|
|
8
8
|
"relay:demo": "node ./bin/self-relay-demo.mjs",
|
|
9
9
|
"relay:check": "node ./bin/self-relay-demo.mjs --check",
|
|
10
|
-
"probe:app-server": "node ./tools/probe-app-server.mjs",
|
|
11
|
-
"probe:app-server-tasks": "node ./tools/probe-app-server-tasks.mjs",
|
|
12
10
|
"build": "node ./tools/build-package.mjs",
|
|
13
11
|
"prepack": "npm run build"
|
|
14
12
|
},
|
|
@@ -18,10 +16,10 @@
|
|
|
18
16
|
"sharp": "0.35.4",
|
|
19
17
|
"ws": "8.21.3"
|
|
20
18
|
},
|
|
21
|
-
"bin": {
|
|
22
|
-
"relax": "dist/bin/codex-remote.mjs",
|
|
23
|
-
"code-relax": "dist/bin/codex-remote.mjs"
|
|
24
|
-
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"relax": "dist/bin/codex-remote.mjs",
|
|
21
|
+
"code-relax": "dist/bin/codex-remote.mjs"
|
|
22
|
+
},
|
|
25
23
|
"description": "Windows Code Relax LAN Bridge and CLI",
|
|
26
24
|
"engines": {
|
|
27
25
|
"node": ">=22.12.0"
|
|
@@ -36,15 +34,15 @@
|
|
|
36
34
|
"dist/",
|
|
37
35
|
"tools/postinstall.mjs"
|
|
38
36
|
],
|
|
39
|
-
"bundleDependencies": [
|
|
37
|
+
"bundleDependencies": [
|
|
40
38
|
"node-datachannel",
|
|
41
39
|
"qrcode",
|
|
42
40
|
"sharp",
|
|
43
|
-
"ws"
|
|
44
|
-
],
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public",
|
|
47
|
-
"registry": "https://registry.npmjs.org",
|
|
48
|
-
"tag": "beta"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
41
|
+
"ws"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public",
|
|
45
|
+
"registry": "https://registry.npmjs.org",
|
|
46
|
+
"tag": "beta"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/tools/postinstall.mjs
CHANGED
|
@@ -1,9 +1,146 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
2
5
|
import { fileURLToPath } from 'node:url';
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
const serviceRoot = path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'),
|
|
8
|
+
'Programs', 'CodexRemote', 'service');
|
|
9
|
+
const stateRoot = path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'CodexRemote');
|
|
10
|
+
const installLog = path.join(stateRoot, 'install.log');
|
|
11
|
+
|
|
12
|
+
function sanitizeLog(value) {
|
|
13
|
+
let result = String(value).replace(/npm_[A-Za-z0-9]+/gu, '[redacted npm token]')
|
|
14
|
+
.replace(/([?&]token=)[^&\s"']+/giu, '$1[redacted]');
|
|
15
|
+
const paths = [['LOCALAPPDATA', process.env.LOCALAPPDATA], ['APPDATA', process.env.APPDATA],
|
|
16
|
+
['USERPROFILE', process.env.USERPROFILE], ['TEMP', process.env.TEMP], ['TMP', process.env.TMP]]
|
|
17
|
+
.filter(([, item]) => item).sort((left, right) => right[1].length - left[1].length);
|
|
18
|
+
for (const [name, item] of paths) {
|
|
19
|
+
const pattern = item.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
20
|
+
result = result.replace(new RegExp(pattern, 'giu'), `%${name}%`);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function appendLog(message) {
|
|
26
|
+
fs.mkdirSync(stateRoot, { recursive: true });
|
|
27
|
+
fs.appendFileSync(installLog, `${new Date().toISOString()} ${sanitizeLog(message)}\n`, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function systemSummary() {
|
|
31
|
+
const npm = String(process.env.npm_config_user_agent || 'unknown').replace(/[\r\n]/gu, ' ');
|
|
32
|
+
return `platform=${process.platform} osRelease=${os.release()} osVersion=${os.version()} `
|
|
33
|
+
+ `osArch=${os.arch()} processArch=${process.arch} node=${process.version} npmUserAgent=${npm}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function runStage(entry, stage, args, formatOutput = value => value) {
|
|
37
|
+
appendLog(`[${stage}] start`);
|
|
38
|
+
try {
|
|
39
|
+
const output = execFileSync(process.execPath, [entry, ...args], { encoding: 'utf8', windowsHide: true,
|
|
40
|
+
stdio: ['inherit', 'pipe', 'pipe'] });
|
|
41
|
+
if (output) { process.stdout.write(formatOutput(output)); appendLog(`[${stage}] stdout\n${output.trimEnd()}`); }
|
|
42
|
+
appendLog(`[${stage}] success`);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error?.stdout) { process.stdout.write(error.stdout); appendLog(`[${stage}] stdout\n${String(error.stdout).trimEnd()}`); }
|
|
45
|
+
if (error?.stderr) { process.stderr.write(error.stderr); appendLog(`[${stage}] stderr\n${String(error.stderr).trimEnd()}`); }
|
|
46
|
+
appendLog(`[${stage}] failed: ${error?.stack || error?.message || String(error)}`);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function formatSetupOutput(output, previous = {}) {
|
|
52
|
+
try {
|
|
53
|
+
const result = JSON.parse(output);
|
|
54
|
+
const migration = result.managedAppServerMigration || {};
|
|
55
|
+
const pid = migration.currentPid || result.managedAppServer?.pid || '未知';
|
|
56
|
+
const appServer = migration.restarted
|
|
57
|
+
? `已重启(旧启动链迁移,PID ${migration.previousPid || '未知'} → ${pid})`
|
|
58
|
+
: previous.valid
|
|
59
|
+
? `未重启(PID ${pid},启动代次 ${migration.launcherRevision ?? '未知'} 已是最新)`
|
|
60
|
+
: `已启动(PID ${pid},启动代次 ${migration.launcherRevision ?? '未知'})`;
|
|
61
|
+
return `Code Relax ${result.version || '未知版本'} 安装成功。\n`
|
|
62
|
+
+ `受管 app-server:${appServer}\n`
|
|
63
|
+
+ `Bridge:${result.restarted ? '已恢复并健康' : '已启动并健康'}\n`
|
|
64
|
+
+ `${result.desktopRestarted ? `Codex Desktop:已干净重启并重新接入(${result.desktopRestartReason === 'attach-managed-server' ? '接入受管内核' : 'app-server 迁移'})\n` : ''}`
|
|
65
|
+
+ `${result.desktopRestartRecommended ? 'Codex Desktop:建议完全退出后重新打开。\n' : ''}`;
|
|
66
|
+
} catch { return output; }
|
|
67
|
+
}
|
|
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
|
+
function installationSnapshot() {
|
|
77
|
+
const recordPath = path.join(serviceRoot, 'installation.json');
|
|
78
|
+
if (!fs.existsSync(serviceRoot)) return { exists: false, valid: false };
|
|
79
|
+
try {
|
|
80
|
+
const record = JSON.parse(fs.readFileSync(recordPath, 'utf8').replace(/^\uFEFF/u, ''));
|
|
81
|
+
return { exists: true, valid: Boolean(record?.installedAt && record?.version),
|
|
82
|
+
version: record?.version, installedAt: record?.installedAt };
|
|
83
|
+
} catch {
|
|
84
|
+
return { exists: true, valid: false };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sameInstallation(left, right) {
|
|
89
|
+
return left.valid && right.valid && left.version === right.version && left.installedAt === right.installedAt;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function setupFailureGuidance(previous, current) {
|
|
93
|
+
if (previous.valid && sameInstallation(previous, current)) {
|
|
94
|
+
return `检测到旧安装 ${previous.version};新版本部署未完成,旧安装记录仍保留。\n`
|
|
95
|
+
+ '请先执行 relax doctor;修复报错原因后重新运行 npm 安装命令,无需卸载旧版本。';
|
|
96
|
+
}
|
|
97
|
+
if (previous.valid && current.valid) {
|
|
98
|
+
return `检测到旧安装 ${previous.version};服务文件已更新为 ${current.version},但 setup 后续步骤失败。\n`
|
|
99
|
+
+ '请执行 relax doctor 确认当前状态,修复后执行 relax setup;无需先卸载。';
|
|
100
|
+
}
|
|
101
|
+
if (previous.exists && current.valid) {
|
|
102
|
+
return `检测到既有服务目录,但无法确认有效的旧安装记录;服务文件现已写入 ${current.version},setup 后续步骤仍失败。\n`
|
|
103
|
+
+ '请执行 relax doctor 确认当前状态,修复后执行 relax setup;不要直接删除配置目录。';
|
|
104
|
+
}
|
|
105
|
+
if (previous.exists) {
|
|
106
|
+
return '检测到既有服务目录,但无法确认有效的旧安装记录;本次部署未完成。\n'
|
|
107
|
+
+ '请执行 relax doctor,根据检查结果修复后重新运行 npm 安装命令,不要直接删除配置目录。';
|
|
108
|
+
}
|
|
109
|
+
if (current.valid) {
|
|
110
|
+
return `这是首次安装;服务文件 ${current.version} 已写入,但 setup 后续步骤失败。\n`
|
|
111
|
+
+ '如果 relax 命令仍可用,请执行 relax doctor,再执行 relax setup;否则修复原因后重新运行 npm 安装命令。';
|
|
112
|
+
}
|
|
113
|
+
return '这是首次安装,尚未形成可确认的固定服务。\n修复上方错误后重新运行 npm 安装命令;无需执行卸载。';
|
|
114
|
+
}
|
|
115
|
+
|
|
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
|
+
const invokedDirectly = process.argv[1]
|
|
124
|
+
&& path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
|
|
125
|
+
if (invokedDirectly && process.env.npm_config_global === 'true') {
|
|
5
126
|
const entry = fileURLToPath(new URL('../dist/bin/codex-remote.mjs', import.meta.url));
|
|
6
|
-
|
|
7
|
-
|
|
127
|
+
const previous = installationSnapshot();
|
|
128
|
+
let stage = 'setup';
|
|
129
|
+
appendLog(`[postinstall] start package=${process.env.npm_package_version || 'unknown'} ${systemSummary()}`);
|
|
130
|
+
try {
|
|
131
|
+
runStage(entry, stage, ['setup', '--json'], output => formatSetupOutput(output, previous));
|
|
132
|
+
stage = 'skill-install';
|
|
133
|
+
runStage(entry, stage, ['skill-install', '--json'], formatSkillOutput);
|
|
134
|
+
console.log(`安装日志:${installLog}`);
|
|
135
|
+
appendLog('[postinstall] success');
|
|
136
|
+
} catch (error) {
|
|
137
|
+
const current = installationSnapshot();
|
|
138
|
+
console.error(`\nCode Relax 安装未完全完成:\n${stage === 'setup'
|
|
139
|
+
? setupFailureGuidance(previous, current)
|
|
140
|
+
: skillFailureGuidance(previous, current)}`);
|
|
141
|
+
if (error?.code && error.code !== 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') console.error(`启动安装步骤失败:${error.message}`);
|
|
142
|
+
console.error(`安装日志:${installLog}`);
|
|
143
|
+
appendLog(`[postinstall] failed stage=${stage}`);
|
|
144
|
+
process.exitCode = Number.isInteger(error?.status) && error.status > 0 ? error.status : 1;
|
|
8
145
|
}
|
|
9
146
|
}
|