@walkhi/code-relax 0.1.0-beta.35 → 0.1.0-beta.36
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/dist/bin/codex-remote.mjs +30 -27
- package/dist/build-platform.json +1 -1
- package/dist/src/server.mjs +8 -0
- package/dist/src/service-install.mjs +7 -1
- package/dist/src/service-update-worker.mjs +42 -0
- package/dist/src/service-update.mjs +71 -0
- package/package.json +4 -4
- package/tools/postinstall.mjs +3 -2
|
@@ -265,7 +265,8 @@ async function main() {
|
|
|
265
265
|
if (previousInstallation?.nodeExecutable) await removeDesktopShortcut({ serviceRoot, nodeExecutable: previousInstallation.nodeExecutable });
|
|
266
266
|
const desktopBeforeSetup = desktopHost.desktopIntegration === false ? { running: false, desktop: null } : await desktopHost.inspect();
|
|
267
267
|
const runtime = await resolveCodexRuntime(physicalStateRoot, { refresh: true });
|
|
268
|
-
const
|
|
268
|
+
const preserveCodex = options.includes('--preserve-codex');
|
|
269
|
+
const desktopNeedsRestart = desktopBeforeSetup.running && !preserveCodex;
|
|
269
270
|
const previous = hadInstalledService ? await service.status() : { processRunning: false };
|
|
270
271
|
const legacyRelaySession = readRecord(path.join(stateRoot, 'self-relay-session.json'));
|
|
271
272
|
const previousRelay = legacyRelaySession ? await controlRequest(legacyRelaySession) : await selfRelay('relay-status', stateRoot);
|
|
@@ -286,7 +287,7 @@ async function main() {
|
|
|
286
287
|
let stoppedForUpdate = false;
|
|
287
288
|
const installed = await installService(packageRoot(path.dirname(fileURLToPath(import.meta.url))), serviceRoot, executable, physicalStateRoot, {
|
|
288
289
|
beforeActivate: async () => {
|
|
289
|
-
if (
|
|
290
|
+
if (desktopNeedsRestart) await desktopHost.closeDesktop(desktopBeforeSetup.desktop);
|
|
290
291
|
stoppedForUpdate = resumeAfterUpdate;
|
|
291
292
|
if (previousRelay.running && legacyRelaySession) {
|
|
292
293
|
await controlRequest(legacyRelaySession, 'POST');
|
|
@@ -315,46 +316,45 @@ async function main() {
|
|
|
315
316
|
? await configureManagedEnvironment(physicalStateRoot) : { configured: false, reason: desktopHost.desktopIntegration === false ? 'desktop-integration-unsupported' : 'desktop-not-installed' };
|
|
316
317
|
let managedAppServerMigrated = false;
|
|
317
318
|
const managedAppServerMigration = {
|
|
318
|
-
required: bridge.managedAppServer.launcherRestartRequired === true,
|
|
319
|
+
required: bridge.managedAppServer.launcherRestartRequired === true || bridge.managedAppServer.updatePending === true,
|
|
320
|
+
reason: bridge.managedAppServer.updatePending ? 'runtime-update' : bridge.managedAppServer.launcherRestartRequired ? 'launcher-migration' : null,
|
|
319
321
|
restarted: false,
|
|
320
322
|
previousPid: bridge.managedAppServer.pid || null,
|
|
323
|
+
previousVersion: bridge.managedAppServer.version || null,
|
|
321
324
|
};
|
|
322
325
|
let desktopRestarted = false;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
326
|
+
if (managedAppServerMigration.required && !preserveCodex) {
|
|
327
|
+
const requested = await service.restartManagedAppServer(true);
|
|
328
|
+
const operationId = requested.restart?.operationId;
|
|
329
|
+
if (!operationId) throw new Error('安装更新未取得 Codex 内核重启操作编号。');
|
|
330
|
+
const complete = () => bridge.running && bridge.managedAppServer?.state === 'ready'
|
|
331
|
+
&& bridge.managedAppServer.restart?.operationId === operationId
|
|
332
|
+
&& bridge.managedAppServer.restart.status === 'completed'
|
|
333
|
+
&& !bridge.managedAppServer.launcherRestartRequired && !bridge.managedAppServer.updatePending;
|
|
334
|
+
const migrationDeadline = Date.now() + 60_000;
|
|
331
335
|
do {
|
|
332
336
|
await delay(250);
|
|
333
337
|
bridge = await service.status();
|
|
334
|
-
if (bridge.managedAppServer?.restart?.status === 'failed') {
|
|
335
|
-
throw new Error(bridge.managedAppServer.restart.error || 'Codex
|
|
338
|
+
if (bridge.managedAppServer?.restart?.operationId === operationId && bridge.managedAppServer.restart.status === 'failed') {
|
|
339
|
+
throw new Error(bridge.managedAppServer.restart.error || '安装期间 Codex 内核更新失败。');
|
|
336
340
|
}
|
|
337
|
-
} while (Date.now() < migrationDeadline && (
|
|
338
|
-
if (!
|
|
339
|
-
throw new Error('Codex 内核未在 40 秒内完成启动链迁移。');
|
|
340
|
-
}
|
|
341
|
+
} while (Date.now() < migrationDeadline && !complete());
|
|
342
|
+
if (!complete()) throw new Error('安装期间 Codex 内核未在 60 秒内完成更新;未确认新内核就绪。');
|
|
341
343
|
managedAppServerMigrated = true;
|
|
342
344
|
managedAppServerMigration.restarted = true;
|
|
343
|
-
if (desktopAttached) {
|
|
344
|
-
await launchDesktop({ stateRoot: physicalStateRoot, host: desktopHost });
|
|
345
|
-
desktopRestarted = true;
|
|
346
|
-
desktopRestartReason = 'app-server-migration';
|
|
347
|
-
bridge = await service.status();
|
|
348
|
-
}
|
|
349
345
|
}
|
|
350
346
|
const skill = installSkill(path.join(serviceRoot, 'dist/skills/code-relax/SKILL.md'));
|
|
351
|
-
if (
|
|
347
|
+
if (desktopNeedsRestart) {
|
|
352
348
|
const currentDesktop = await desktopHost.inspect();
|
|
353
349
|
if (currentDesktop.running) await desktopHost.closeDesktop(currentDesktop.desktop);
|
|
354
350
|
await launchDesktop({ stateRoot: physicalStateRoot, host: desktopHost });
|
|
351
|
+
if (!await desktopHost.desktopReady()) throw new Error('安装期间 Codex 窗口已启动,但未确认连接到工作站内核。');
|
|
355
352
|
desktopRestarted = true;
|
|
356
|
-
|
|
357
|
-
|
|
353
|
+
}
|
|
354
|
+
bridge = await service.status();
|
|
355
|
+
if (!bridge.running || bridge.managedAppServer?.state !== 'ready' || !bridge.execution?.ready
|
|
356
|
+
|| (!preserveCodex && (bridge.managedAppServer.updatePending || bridge.managedAppServer.launcherRestartRequired))) {
|
|
357
|
+
throw new Error('安装文件已更新,但 Codex 内核更新或完整控制尚未就绪,安装未完成。');
|
|
358
358
|
}
|
|
359
359
|
installed.autostart = autostart;
|
|
360
360
|
installed.skill = skill;
|
|
@@ -366,8 +366,11 @@ async function main() {
|
|
|
366
366
|
installed.managedAppServerMigration = { ...managedAppServerMigration,
|
|
367
367
|
currentPid: bridge.managedAppServer.pid || null,
|
|
368
368
|
launcherRevision: bridge.managedAppServer.launcherRevision ?? null };
|
|
369
|
+
installed.preserveCodex = preserveCodex;
|
|
369
370
|
installed.desktopRestarted = desktopRestarted;
|
|
370
|
-
installed.desktopRestartReason =
|
|
371
|
+
installed.desktopRestartReason = desktopRestarted ? 'setup-reconnect' : null;
|
|
372
|
+
installed.desktopConnected = desktopRestarted ? true : null;
|
|
373
|
+
installed.executionReady = bridge.execution.ready;
|
|
371
374
|
if (options.includes('--desktop-helper')) installed.desktopHelper = await installDesktopHelper();
|
|
372
375
|
const migrationText = managedAppServerMigrated ? 'Codex 内核已更新并重启;请检查安装前正在进行的任务。\n' : '';
|
|
373
376
|
const displayText = `Code Relax ${installed.version} 安装成功。\n${migrationText}${desktopSetupSteps.join('\n')}`;
|
package/dist/build-platform.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"layout":"npm-platform-packages","version":"0.1.0-beta.
|
|
1
|
+
{"layout":"npm-platform-packages","version":"0.1.0-beta.36"}
|
package/dist/src/server.mjs
CHANGED
|
@@ -39,6 +39,7 @@ import { desktopDotsProfiles } from './platform/windows/dots-profile.mjs';
|
|
|
39
39
|
import { readPlanPricing } from './plan-pricing.mjs';
|
|
40
40
|
import { requestRecovery, recoveryStatus } from './shared-recovery.mjs';
|
|
41
41
|
import { requestServiceRestart } from './service-restart.mjs';
|
|
42
|
+
import { checkServiceUpdate, requestServiceUpdate, serviceUpdateStatus } from './service-update.mjs';
|
|
42
43
|
import { TurnModelHistory } from './turn-model-history.mjs';
|
|
43
44
|
import { browseCode } from './code-browser.mjs';
|
|
44
45
|
import { fileRange, fileRevision, FileRangeError } from './file-transfer-range.mjs';
|
|
@@ -265,6 +266,12 @@ async function handleRequest(request, response) {
|
|
|
265
266
|
if (body.confirmed !== true || typeof body.operationId !== 'string') throw new RequestError(400, '请确认重新打开当前 Codex 并接入 Codex 内核后再继续。');
|
|
266
267
|
sendJson(response, 202, await requestRecovery(stateRoot, body)); return;
|
|
267
268
|
}
|
|
269
|
+
if (url.pathname === '/api/service/update' && request.method === 'GET') {
|
|
270
|
+
sendJson(response, 200, await checkServiceUpdate(stateRoot)); return;
|
|
271
|
+
}
|
|
272
|
+
if (url.pathname === '/api/service/update' && request.method === 'POST') {
|
|
273
|
+
sendJson(response, 202, await requestServiceUpdate(stateRoot, await readJsonBody(request))); return;
|
|
274
|
+
}
|
|
268
275
|
if (request.method === 'POST' && url.pathname === '/api/service/restart') {
|
|
269
276
|
const body = await readJsonBody(request);
|
|
270
277
|
if (body.confirmed !== true) throw new RequestError(400, '请确认重启工作站服务后再继续。');
|
|
@@ -309,6 +316,7 @@ async function handleRequest(request, response) {
|
|
|
309
316
|
sendJson(response, 200, {
|
|
310
317
|
status: 'ok', resident: true, version: runtimeVersion,
|
|
311
318
|
keepAwake: powerManager.snapshot(),
|
|
319
|
+
serviceUpdate: serviceUpdateStatus(stateRoot),
|
|
312
320
|
execution: { ...executionHealth.state, ready: executionReady, canWrite: executionReady,
|
|
313
321
|
mode: executionMode },
|
|
314
322
|
managedAppServer: managedAppServer.snapshot(),
|
|
@@ -35,7 +35,13 @@ export async function installService(source, target, executable, stateRoot, hook
|
|
|
35
35
|
}
|
|
36
36
|
await exec(executable, [path.join(stage, 'dist/bin/codex-remote.mjs'), '--help'], { windowsHide: true });
|
|
37
37
|
await exec(executable, ['-e', 'const r=require("node:module").createRequire(process.argv[1]); r("sharp"); r("qrcode"); r("ws"); r("node-datachannel");', path.join(stage, 'package.json')], { windowsHide: true });
|
|
38
|
-
|
|
38
|
+
const previousInstallation = readRecord(path.join(target, 'installation.json'));
|
|
39
|
+
const npmCliPath = process.env.npm_execpath || previousInstallation?.npmCliPath;
|
|
40
|
+
const npmPrefix = process.env.npm_config_prefix || previousInstallation?.npmPrefix;
|
|
41
|
+
writeRecord(path.join(stage, 'installation.json'), {
|
|
42
|
+
npmCliPath: npmCliPath && path.isAbsolute(npmCliPath) && fs.existsSync(npmCliPath) ? fs.realpathSync(npmCliPath) : null,
|
|
43
|
+
npmPrefix: npmPrefix && path.isAbsolute(npmPrefix) ? npmPrefix : null,
|
|
44
|
+
installedAt: new Date().toISOString(), source, nodeExecutable: executable, stateRoot,
|
|
39
45
|
version: readRecord(path.join(source, 'package.json')).version });
|
|
40
46
|
const previous = fs.existsSync(target);
|
|
41
47
|
let previousMoved = false, activated = false;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
|
+
|
|
6
|
+
// Copied outside both npm and service directories before launch. Builtins only.
|
|
7
|
+
const jobFile = process.argv[2];
|
|
8
|
+
const job = JSON.parse(fs.readFileSync(jobFile, 'utf8'));
|
|
9
|
+
process.chdir(path.dirname(jobFile));
|
|
10
|
+
const read = () => JSON.parse(fs.readFileSync(job.statusFile, 'utf8'));
|
|
11
|
+
function update(values) {
|
|
12
|
+
const current = read();
|
|
13
|
+
if (current.operationId !== job.operationId) throw new Error('更新操作已被替换');
|
|
14
|
+
const temporary = `${job.statusFile}.${process.pid}.tmp`;
|
|
15
|
+
fs.writeFileSync(temporary, JSON.stringify({ ...current, ...values }), { mode: 0o600 });
|
|
16
|
+
fs.renameSync(temporary, job.statusFile);
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const deadline = Date.now() + 30000;
|
|
20
|
+
while (read().owner?.pid !== process.pid) {
|
|
21
|
+
if (Date.now() >= deadline) throw new Error('独立更新进程交接超时');
|
|
22
|
+
await delay(100);
|
|
23
|
+
}
|
|
24
|
+
await delay(1500);
|
|
25
|
+
update({ status: 'running', startedAt: new Date().toISOString() });
|
|
26
|
+
const args = [job.npmCliPath, 'install', '--global', `@walkhi/code-relax@${job.targetVersion}`,
|
|
27
|
+
'--foreground-scripts', '--allow-scripts=@walkhi/code-relax,node-datachannel,sharp', '--registry=https://registry.npmjs.org'];
|
|
28
|
+
if (job.npmPrefix) args.push('--prefix', job.npmPrefix);
|
|
29
|
+
const child = spawn(job.nodeExecutable, args, { cwd: path.dirname(jobFile), windowsHide: true,
|
|
30
|
+
stdio: ['ignore', 'inherit', 'inherit'] });
|
|
31
|
+
const code = await new Promise((resolve, reject) => {
|
|
32
|
+
child.once('error', reject); child.once('exit', (code, signal) => resolve(code ?? `signal ${signal}`));
|
|
33
|
+
});
|
|
34
|
+
if (code !== 0) throw new Error(`npm 安装失败(${code}),请查看工作站更新日志。`);
|
|
35
|
+
const installed = JSON.parse(fs.readFileSync(path.join(job.serviceRoot, 'installation.json'), 'utf8'));
|
|
36
|
+
if (installed.version !== job.targetVersion) throw new Error('安装结束,但工作站版本与目标版本不一致。');
|
|
37
|
+
update({ status: 'completed', finishedAt: new Date().toISOString(), installedVersion: installed.version });
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(error.stack || error.message);
|
|
40
|
+
update({ status: 'failed', finishedAt: new Date().toISOString(), error: error.message });
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { startBackground } from './background-process.mjs';
|
|
5
|
+
import { readRecord, writeRecord } from './service-lifecycle.mjs';
|
|
6
|
+
import { runtimePaths } from './platform/runtime-paths.mjs';
|
|
7
|
+
import { desktopHost } from './platform/desktop-host.mjs';
|
|
8
|
+
|
|
9
|
+
const versionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
10
|
+
const statusPath = root => path.join(root, 'service-update.json');
|
|
11
|
+
const installation = () => readRecord(path.join(runtimePaths().serviceRoot, 'installation.json'));
|
|
12
|
+
export function serviceUpdateStatus(root) {
|
|
13
|
+
const record = readRecord(statusPath(root));
|
|
14
|
+
const installed = installation();
|
|
15
|
+
return { supported: Boolean(installed?.npmCliPath && fs.existsSync(installed.npmCliPath)),
|
|
16
|
+
currentVersion: installed?.version || null,
|
|
17
|
+
operation: record ? { operationId: record.operationId, targetVersion: record.targetVersion,
|
|
18
|
+
status: record.status, error: record.error || null, installedVersion: record.installedVersion || null } : null };
|
|
19
|
+
}
|
|
20
|
+
export async function checkServiceUpdate(root) {
|
|
21
|
+
const result = await fetch('https://registry.npmjs.org/@walkhi%2fcode-relax/latest', { signal: AbortSignal.timeout(12000) });
|
|
22
|
+
if (!result.ok) throw new Error(`查询工作站版本失败:HTTP ${result.status}`);
|
|
23
|
+
const latest = await result.json();
|
|
24
|
+
if (!versionPattern.test(latest.version || '')) throw new Error('npm 返回的版本号无效');
|
|
25
|
+
return { ...serviceUpdateStatus(root), latestVersion: latest.version };
|
|
26
|
+
}
|
|
27
|
+
let scheduling = false;
|
|
28
|
+
export async function requestServiceUpdate(root, body) {
|
|
29
|
+
if (body.confirmed !== true || !/^[0-9a-f-]{36}$/i.test(body.operationId || '') || !versionPattern.test(body.targetVersion || '')) {
|
|
30
|
+
throw new Error('请确认工作站更新及目标版本后再继续。');
|
|
31
|
+
}
|
|
32
|
+
if (scheduling) throw new Error('工作站更新正在安排。');
|
|
33
|
+
scheduling = true;
|
|
34
|
+
try {
|
|
35
|
+
const previous = readRecord(statusPath(root));
|
|
36
|
+
if (previous?.operationId === body.operationId) return serviceUpdateStatus(root);
|
|
37
|
+
if (previous && ['scheduled', 'running'].includes(previous.status)) {
|
|
38
|
+
const actual = previous.owner?.pid ? await desktopHost.process(previous.owner.pid) : null;
|
|
39
|
+
if (actual && actual.created === previous.owner.created && actual.executable === previous.owner.executable) {
|
|
40
|
+
throw new Error('工作站已有更新正在执行。');
|
|
41
|
+
}
|
|
42
|
+
writeRecord(statusPath(root), { ...previous, status: 'failed', error: '更新进程已退出,未确认安装完成。' });
|
|
43
|
+
}
|
|
44
|
+
const latest = await checkServiceUpdate(root);
|
|
45
|
+
if (!latest.supported) throw new Error('当前安装缺少 npm 路径,请先安装支持手机更新的过渡版。');
|
|
46
|
+
if (latest.latestVersion !== body.targetVersion) throw new Error('最新版本已变化,请重新检查后确认。');
|
|
47
|
+
if (latest.currentVersion === body.targetVersion) throw new Error('工作站已是此版本。');
|
|
48
|
+
const installed = installation();
|
|
49
|
+
const directory = path.join(root, 'service-updates', body.operationId);
|
|
50
|
+
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
51
|
+
const worker = path.join(directory, 'worker.mjs');
|
|
52
|
+
fs.copyFileSync(fileURLToPath(new URL('./service-update-worker.mjs', import.meta.url)), worker);
|
|
53
|
+
const jobFile = path.join(directory, 'job.json');
|
|
54
|
+
writeRecord(jobFile, { ...body, statusFile: statusPath(root), npmCliPath: installed.npmCliPath,
|
|
55
|
+
npmPrefix: installed.npmPrefix, nodeExecutable: installed.nodeExecutable, serviceRoot: runtimePaths().serviceRoot });
|
|
56
|
+
writeRecord(statusPath(root), { operationId: body.operationId, targetVersion: body.targetVersion,
|
|
57
|
+
status: 'scheduled', requestedAt: new Date().toISOString() });
|
|
58
|
+
try {
|
|
59
|
+
const log = path.join(directory, 'update.log');
|
|
60
|
+
const pid = await startBackground(installed.nodeExecutable, [worker, jobFile], process.env, [log, log]);
|
|
61
|
+
const owner = await desktopHost.process(pid);
|
|
62
|
+
if (!owner) throw new Error('独立更新进程未启动。');
|
|
63
|
+
writeRecord(statusPath(root), { ...readRecord(statusPath(root)), owner: {
|
|
64
|
+
pid: owner.pid, created: owner.created, executable: owner.executable } });
|
|
65
|
+
} catch (error) {
|
|
66
|
+
writeRecord(statusPath(root), { ...readRecord(statusPath(root)), status: 'failed', error: error.message });
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
return serviceUpdateStatus(root);
|
|
70
|
+
} finally { scheduling = false; }
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walkhi/code-relax",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.36",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"tag": "beta"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@walkhi/code-relax-native-win32-x64": "0.1.0-beta.
|
|
45
|
-
"@walkhi/code-relax-native-darwin-arm64": "0.1.0-beta.
|
|
46
|
-
"@walkhi/code-relax-native-darwin-x64": "0.1.0-beta.
|
|
44
|
+
"@walkhi/code-relax-native-win32-x64": "0.1.0-beta.36",
|
|
45
|
+
"@walkhi/code-relax-native-darwin-arm64": "0.1.0-beta.36",
|
|
46
|
+
"@walkhi/code-relax-native-darwin-x64": "0.1.0-beta.36"
|
|
47
47
|
}
|
|
48
48
|
}
|
package/tools/postinstall.mjs
CHANGED
|
@@ -55,7 +55,8 @@ export function formatSetupOutput(output) {
|
|
|
55
55
|
const migration = result.managedAppServerMigration || {};
|
|
56
56
|
return `Code Relax ${result.version || '未知版本'} 服务安装完成。\n`
|
|
57
57
|
+ '工作站服务和 Codex 内核已就绪,已启用登录自启。\n'
|
|
58
|
-
+ (
|
|
58
|
+
+ (result.preserveCodex ? '过渡版保留当前 Codex 和内核运行状态;待手机发起下一次完整更新。\n' : migration.restarted ? `Codex 内核已更新:${migration.previousVersion || '未知'} → ${result.managedAppServer?.version || '未知'}(PID ${migration.previousPid} → ${migration.currentPid})。\n` : 'Codex 内核已是当前选定版本,无需切换。\n')
|
|
59
|
+
+ (result.preserveCodex ? '' : result.desktopRestarted ? '电脑版 Codex 已重启,并确认连接到工作站内核。\n' : '安装前未运行电脑版 Codex,保持关闭。\n')
|
|
59
60
|
+ (result.desktopRestartRecommended ? 'Codex Desktop:请完全退出后重新打开。\n' : '');
|
|
60
61
|
} catch { return output; }
|
|
61
62
|
}
|
|
@@ -150,7 +151,7 @@ if (invokedDirectly && process.env.npm_config_global === 'true') {
|
|
|
150
151
|
const { prepareNativePackage } = await import('../dist/src/platform/native-package.mjs');
|
|
151
152
|
prepareNativePackage(fileURLToPath(new URL('../', import.meta.url)));
|
|
152
153
|
setupStarted = true;
|
|
153
|
-
runStage(entry, 'setup', ['setup', '--json'], output => formatSetupOutput(output));
|
|
154
|
+
runStage(entry, 'setup', ['setup', '--json', '--preserve-codex'], output => formatSetupOutput(output));
|
|
154
155
|
try { runStage(entry, 'doctor', ['doctor']); }
|
|
155
156
|
catch (error) {
|
|
156
157
|
console.error('服务安装已完成,但安装后健康检查未通过;请查看上方诊断与安装日志。');
|