@walkhi/code-relax 0.1.0-beta.2 → 0.1.0-beta.20
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 +55 -71
- package/dist/bin/codex-remote.mjs +150 -126
- package/dist/bin/manage-relay-admin.mjs +20 -0
- package/dist/bin/migrate-relay-db.mjs +107 -0
- package/dist/bin/self-relay-admin.mjs +8 -0
- package/dist/bin/self-relay-demo.mjs +2 -2
- package/dist/bin/self-relay-server.mjs +18 -10
- package/dist/shared/app-server-events.cjs +42 -15
- package/dist/shared/file-mentions.cjs +35 -0
- package/dist/shared/server-requests.cjs +86 -58
- package/dist/skills/code-relax/SKILL.md +7 -6
- package/dist/src/desktop-launcher.mjs +16 -21
- package/dist/src/file-upload-session.mjs +124 -0
- package/dist/src/lan-socket-server.mjs +52 -9
- package/dist/src/managed-app-server.mjs +44 -13
- package/dist/src/message-images.mjs +0 -18
- package/dist/src/onboarding.mjs +9 -11
- package/dist/src/platform/README.md +1 -1
- package/dist/src/platform/windows/background-process.mjs +1 -1
- package/dist/src/platform/windows/codex-runtime.mjs +251 -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-runtime.ps1 +10 -0
- 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-start.js +20 -0
- package/dist/src/platform/windows/resident-startup.ps1 +76 -20
- package/dist/src/platform/windows/service-host.mjs +4 -16
- package/dist/src/platform/windows/start-hidden-console.ps1 +10 -5
- package/dist/src/platform/windows/stop-desktop.ps1 +7 -4
- package/dist/src/platform/windows/user-environment.mjs +1 -1
- 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 +79 -0
- package/dist/src/self-relay/admin-server.mjs +66 -0
- package/dist/src/self-relay/admin-state.mjs +82 -19
- package/dist/src/self-relay/admin-style.mjs +9 -0
- package/dist/src/self-relay/admin-usage-page.mjs +28 -0
- package/dist/src/self-relay/connector.mjs +243 -30
- package/dist/src/self-relay/demo.mjs +3 -4
- package/dist/src/self-relay/email-accounts.mjs +141 -0
- package/dist/src/self-relay/lifecycle.mjs +99 -142
- package/dist/src/self-relay/mysql-state.mjs +104 -0
- package/dist/src/self-relay/p2p-probe.mjs +1 -1
- package/dist/src/self-relay/server.mjs +335 -59
- package/dist/src/self-relay/usage-diagnostics.mjs +46 -0
- package/dist/src/self-relay/usage-meter.mjs +125 -0
- package/dist/src/server.mjs +731 -1439
- package/dist/src/service-control.mjs +4 -4
- package/dist/src/service-doctor.mjs +27 -29
- package/dist/src/service-health.mjs +2 -2
- package/dist/src/service-install.mjs +1 -4
- package/dist/src/service-lifecycle.mjs +19 -19
- package/dist/src/shared-app-server.mjs +277 -270
- package/dist/src/shared-catalog.mjs +1 -11
- 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 +17 -18
- package/tools/postinstall.mjs +69 -15
- package/dist/src/app-server-client.mjs +0 -468
- package/dist/src/app-server-tasks.mjs +0 -358
- 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 -20
- package/dist/src/thread-catalog.mjs +0 -47
- package/dist/tools/find-desktop-pipe.mjs +0 -10
- package/dist/web/activity-view.js +0 -283
- package/dist/web/approval-ui.js +0 -188
- package/dist/web/capabilities.js +0 -57
- package/dist/web/chat-transport.js +0 -202
- package/dist/web/chat.css +0 -782
- package/dist/web/chat.js +0 -3830
- package/dist/web/client-platform.js +0 -11
- package/dist/web/community-view.js +0 -20
- package/dist/web/community.css +0 -77
- package/dist/web/community.html +0 -37
- package/dist/web/composer-controller.js +0 -101
- package/dist/web/conversation-controller.js +0 -99
- package/dist/web/disclosure-state-controller.js +0 -95
- package/dist/web/draft-controller.js +0 -103
- package/dist/web/harmony-platform.js +0 -52
- package/dist/web/highlight-powershell.min.js +0 -39
- package/dist/web/highlight.min.js +0 -1262
- package/dist/web/history-cache.js +0 -240
- package/dist/web/history-controller.js +0 -167
- package/dist/web/index.html +0 -325
- package/dist/web/lan-session.js +0 -122
- package/dist/web/link-action-controller.js +0 -212
- package/dist/web/message-send-controller.js +0 -177
- package/dist/web/message-view.js +0 -98
- package/dist/web/p2p-data-channel.js +0 -50
- package/dist/web/p2p-diagnostics.js +0 -28
- package/dist/web/p2p-probe.js +0 -123
- package/dist/web/page-resume.js +0 -28
- package/dist/web/pending-message-store.js +0 -108
- package/dist/web/queue-controller.js +0 -82
- package/dist/web/resources.json +0 -1
- package/dist/web/self-relay-session.js +0 -129
- package/dist/web/server-requests.js +0 -67
- package/dist/web/station-connection-controller.js +0 -75
- package/dist/web/task-list-view.js +0 -296
- package/dist/web/thread-attention-controller.js +0 -124
- package/dist/web/thread-context-controller.js +0 -30
- package/dist/web/thread-list-controller.js +0 -61
- package/dist/web/thread-list-sync.js +0 -86
- package/dist/web/thread-title-controller.js +0 -57
- package/dist/web/timeline-formatters.js +0 -249
- package/dist/web/timeline-reducer.js +0 -81
- package/dist/web/timeline-renderer.js +0 -161
- package/dist/web/timeline-scroll-controller.js +0 -50
- package/dist/web/timeline-sync.js +0 -67
- package/dist/web/usage-controller.js +0 -258
- 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
- package/dist/web/vendor/lucide.LICENSE.txt +0 -17
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import events from '../shared/app-server-events.cjs';
|
|
2
2
|
|
|
3
3
|
export function sharedThreadProjectId(thread, projects) {
|
|
4
|
-
|
|
5
|
-
const local = thread.environments?.find(environment => environment.environmentId === 'local');
|
|
6
|
-
if (local && Array.isArray(local.runtimeWorkspaceRoots)) {
|
|
7
|
-
for (const root of local.runtimeWorkspaceRoots) {
|
|
8
|
-
const projectId = events.threadProjectId({ cwd: root }, projects);
|
|
9
|
-
if (projectId) return projectId;
|
|
10
|
-
}
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
// Unloaded Desktop history omits environments and often has no persisted project ID.
|
|
14
|
-
return events.threadProjectId({ cwd: thread.cwd }, projects);
|
|
4
|
+
return events.catalogThreadProjectId(thread, projects);
|
|
15
5
|
}
|
|
16
6
|
|
|
17
7
|
// Mobile catalog order comes entirely from app-server authority, independent of Desktop UI state.
|
|
@@ -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 内核未就绪;桌面服务和中继连接仍保留。');
|
|
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,27 +1,26 @@
|
|
|
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.20",
|
|
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
|
},
|
|
15
13
|
"dependencies": {
|
|
14
|
+
"mysql2": "^3.24.4",
|
|
16
15
|
"node-datachannel": "0.33.4",
|
|
17
16
|
"qrcode": "1.5.4",
|
|
18
17
|
"sharp": "0.35.4",
|
|
19
18
|
"ws": "8.21.3"
|
|
20
19
|
},
|
|
21
|
-
"bin": {
|
|
22
|
-
"relax": "dist/bin/codex-remote.mjs",
|
|
23
|
-
"code-relax": "dist/bin/codex-remote.mjs"
|
|
24
|
-
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"relax": "dist/bin/codex-remote.mjs",
|
|
22
|
+
"code-relax": "dist/bin/codex-remote.mjs"
|
|
23
|
+
},
|
|
25
24
|
"description": "Windows Code Relax LAN Bridge and CLI",
|
|
26
25
|
"engines": {
|
|
27
26
|
"node": ">=22.12.0"
|
|
@@ -36,15 +35,15 @@
|
|
|
36
35
|
"dist/",
|
|
37
36
|
"tools/postinstall.mjs"
|
|
38
37
|
],
|
|
39
|
-
"bundleDependencies": [
|
|
38
|
+
"bundleDependencies": [
|
|
40
39
|
"node-datachannel",
|
|
41
40
|
"qrcode",
|
|
42
41
|
"sharp",
|
|
43
|
-
"ws"
|
|
44
|
-
],
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public",
|
|
47
|
-
"registry": "https://registry.npmjs.org",
|
|
48
|
-
"tag": "beta"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
42
|
+
"ws"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org",
|
|
47
|
+
"tag": "beta"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/tools/postinstall.mjs
CHANGED
|
@@ -6,6 +6,65 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
|
|
7
7
|
const serviceRoot = path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'),
|
|
8
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
|
+
+ `Codex 内核:${appServer}\n`
|
|
63
|
+
+ `桌面服务:${result.restarted ? '已恢复并健康' : '已启动并健康'}\n`
|
|
64
|
+
+ `${result.desktopRestarted ? `Codex Desktop:已干净重启并重新接入(${result.desktopRestartReason === 'attach-managed-server' ? '接入 Codex 内核' : '内核迁移'})\n` : ''}`
|
|
65
|
+
+ `${result.desktopRestartRecommended ? 'Codex Desktop:建议完全退出后重新打开。\n' : ''}`;
|
|
66
|
+
} catch { return output; }
|
|
67
|
+
}
|
|
9
68
|
|
|
10
69
|
function installationSnapshot() {
|
|
11
70
|
const recordPath = path.join(serviceRoot, 'installation.json');
|
|
@@ -47,27 +106,22 @@ function setupFailureGuidance(previous, current) {
|
|
|
47
106
|
return '这是首次安装,尚未形成可确认的固定服务。\n修复上方错误后重新运行 npm 安装命令;无需执行卸载。';
|
|
48
107
|
}
|
|
49
108
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
: 'setup 已返回成功,但当前固定服务记录无法确认';
|
|
54
|
-
return `${service};Codex Skill 安装失败。\n请执行 relax skill-install,再执行 relax doctor;无需重新卸载服务。`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (process.env.npm_config_global === 'true') {
|
|
109
|
+
const invokedDirectly = process.argv[1]
|
|
110
|
+
&& path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
|
|
111
|
+
if (invokedDirectly && process.env.npm_config_global === 'true') {
|
|
58
112
|
const entry = fileURLToPath(new URL('../dist/bin/codex-remote.mjs', import.meta.url));
|
|
59
113
|
const previous = installationSnapshot();
|
|
60
|
-
|
|
114
|
+
appendLog(`[postinstall] start package=${process.env.npm_package_version || 'unknown'} ${systemSummary()}`);
|
|
61
115
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
116
|
+
runStage(entry, 'setup', ['setup', '--json'], output => formatSetupOutput(output, previous));
|
|
117
|
+
console.log(`安装日志:${installLog}`);
|
|
118
|
+
appendLog('[postinstall] success');
|
|
65
119
|
} catch (error) {
|
|
66
120
|
const current = installationSnapshot();
|
|
67
|
-
console.error(`\nCode Relax 安装未完全完成:\n${
|
|
68
|
-
? setupFailureGuidance(previous, current)
|
|
69
|
-
: skillFailureGuidance(previous, current)}`);
|
|
121
|
+
console.error(`\nCode Relax 安装未完全完成:\n${setupFailureGuidance(previous, current)}`);
|
|
70
122
|
if (error?.code && error.code !== 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') console.error(`启动安装步骤失败:${error.message}`);
|
|
123
|
+
console.error(`安装日志:${installLog}`);
|
|
124
|
+
appendLog('[postinstall] failed stage=setup');
|
|
71
125
|
process.exitCode = Number.isInteger(error?.status) && error.status > 0 ? error.status : 1;
|
|
72
126
|
}
|
|
73
127
|
}
|