tt-help-cli-ycl 1.4.92 → 1.4.93
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/package.json +1 -1
- package/src/watchdog/agent.js +5 -15
- package/src/watchdog/upgrader.js +174 -132
package/package.json
CHANGED
package/src/watchdog/agent.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { ProcessManager } from "./process-manager.js";
|
|
17
17
|
import {
|
|
18
18
|
getVersion,
|
|
19
|
-
|
|
19
|
+
spawnUpgrader,
|
|
20
20
|
isGlobalInstall,
|
|
21
21
|
startUpgradeChecker,
|
|
22
22
|
} from "./upgrader.js";
|
|
@@ -574,23 +574,13 @@ export class WatchdogAgent {
|
|
|
574
574
|
|
|
575
575
|
this.upgradeChecker = startUpgradeChecker({
|
|
576
576
|
onUpdateAvailable: async ({ current, latest }) => {
|
|
577
|
-
//
|
|
578
|
-
//
|
|
577
|
+
// 独立升级器方案(无中间文件):spawn 升级进程 U(fire-and-forget),U 负责「下载 / 起新版 / 信号通知退出」。
|
|
578
|
+
// 下载期间业务/心跳不受影响;下载失败 U 自行退出,本进程保持运行;
|
|
579
|
+
// 新版起来后 U 发 SIGTERM → 本进程 gracefulShutdown 停业务/daemon 后退出,新版接管。
|
|
579
580
|
console.error(
|
|
580
581
|
`[upgrade] ${current} → ${latest},启动升级程序(下载期间业务不受影响)`,
|
|
581
582
|
);
|
|
582
|
-
|
|
583
|
-
if (outcome !== "ok") {
|
|
584
|
-
console.error(
|
|
585
|
-
`[upgrade] 升级未完成(${outcome === "fail" ? "下载失败" : "升级程序无响应"}),保持当前版本运行`,
|
|
586
|
-
);
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
console.error(
|
|
590
|
-
`[upgrade] 新版本已下载,停止业务与 daemon 后退出,新版将接管`,
|
|
591
|
-
);
|
|
592
|
-
await this.stop();
|
|
593
|
-
process.exit(0);
|
|
583
|
+
spawnUpgrader(this._buildRestartParams());
|
|
594
584
|
},
|
|
595
585
|
});
|
|
596
586
|
}
|
package/src/watchdog/upgrader.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 自动升级模块 — 检测 tt-help-cli-ycl 新版本并执行升级
|
|
3
3
|
*
|
|
4
|
-
* 仅全局安装模式启用。每 10 分钟查询 npm registry
|
|
5
|
-
*
|
|
4
|
+
* 仅全局安装模式启用。每 10 分钟查询 npm registry 发现新版本,
|
|
5
|
+
* 由旧 watchdog spawn 独立升级进程 U:U 负责「下载 → 验证代码 → 杀旧 → 启新」,
|
|
6
|
+
* 见 runUpgradeMode。旧 watchdog 收到 SIGTERM 后优雅退出,新版接管。
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import { exec, spawn } from "child_process";
|
|
9
|
-
import { readFileSync,
|
|
10
|
+
import { readFileSync, openSync } from "fs";
|
|
10
11
|
import { fileURLToPath } from "url";
|
|
11
12
|
import { dirname, join } from "path";
|
|
12
13
|
import { homedir, tmpdir } from "os";
|
|
@@ -113,6 +114,60 @@ export function performUpgrade() {
|
|
|
113
114
|
});
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* 下载后验证:确认 npm install 真正换了版本,且新版主模块能加载(冒烟)
|
|
119
|
+
* 验证通过才允许杀旧 → 启新;失败则旧 watchdog 不受影响继续运行
|
|
120
|
+
*/
|
|
121
|
+
async function verifyUpgradedPackage(beforeVersion) {
|
|
122
|
+
const now = getVersion();
|
|
123
|
+
if (now === "unknown")
|
|
124
|
+
return { ok: false, reason: "读不到全局包版本(package.json 缺失)" };
|
|
125
|
+
if (now === beforeVersion)
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
reason: `版本未变化(仍 ${now}),疑似 npm install 未生效`,
|
|
129
|
+
};
|
|
130
|
+
const smoke = await smokeTestMainModule();
|
|
131
|
+
if (!smoke.ok) return { ok: false, reason: smoke.reason };
|
|
132
|
+
return { ok: true, version: now };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** 冒烟:spawn 子进程加载新版主模块(仅加载不执行业务),能加载则代码/依赖基本可用 */
|
|
136
|
+
function smokeTestMainModule(timeoutMs = 15_000) {
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
const mainModulePath = join(__dirname, "..", "..", "src", "main.js");
|
|
139
|
+
const code = `import(${JSON.stringify(
|
|
140
|
+
mainModulePath,
|
|
141
|
+
)}).then(()=>process.exit(0)).catch((e)=>{console.error(e&&e.message||e);process.exit(1)})`;
|
|
142
|
+
let child;
|
|
143
|
+
try {
|
|
144
|
+
child = spawn(process.execPath, ["--input-type=module", "-e", code], {
|
|
145
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
146
|
+
windowsHide: true,
|
|
147
|
+
});
|
|
148
|
+
} catch (e) {
|
|
149
|
+
resolve({ ok: false, reason: `冒烟启动失败: ${e.message}` });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
let err = "";
|
|
153
|
+
child.stderr?.on("data", (d) => (err += d.toString()));
|
|
154
|
+
const timer = setTimeout(() => {
|
|
155
|
+
try {
|
|
156
|
+
child.kill("SIGKILL");
|
|
157
|
+
} catch {}
|
|
158
|
+
resolve({ ok: false, reason: `冒烟超时 ${timeoutMs}ms` });
|
|
159
|
+
}, timeoutMs);
|
|
160
|
+
child.on("exit", (code) => {
|
|
161
|
+
clearTimeout(timer);
|
|
162
|
+
resolve(
|
|
163
|
+
code === 0
|
|
164
|
+
? { ok: true }
|
|
165
|
+
: { ok: false, reason: `冒烟失败: ${err.slice(0, 200)}` },
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
116
171
|
/**
|
|
117
172
|
* 启动定时升级检测
|
|
118
173
|
* @param {{intervalMs?: number, onUpdateAvailable?: function}} opts
|
|
@@ -161,194 +216,181 @@ export function startUpgradeChecker({
|
|
|
161
216
|
// 独立升级器(直接运行模式)
|
|
162
217
|
// ══════════════════════════════════════════════
|
|
163
218
|
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
// 结果文件:tmpdir/tt-help-upgrade-result.json
|
|
170
|
-
// - 由升级器写入 { status: "ok" | "fail", error?, at },旧 watchdog 轮询读取
|
|
171
|
-
//
|
|
172
|
-
// 用法(旧 watchdog 调用):
|
|
173
|
-
// node upgrader.js --upgrade --old-pid <pid> [--interactive] [--server ...] [--client-id ...] [--interval ...] [--no-daemon]
|
|
174
|
-
|
|
175
|
-
const UPGRADE_RESULT_FILE = join(tmpdir(), "tt-help-upgrade-result.json");
|
|
219
|
+
// 设计(定稿,无中间文件):旧 watchdog 检测到新版 → spawn 独立升级进程 U(detached)
|
|
220
|
+
// U:npm install 下载 →【验证代码】→【杀旧】→【启新】
|
|
221
|
+
// - 下载失败 / 验证未通过(版本未变、新版主模块加载失败)→ 不杀旧,旧 watchdog 继续跑
|
|
222
|
+
// - 验证通过 → SIGTERM 旧 watchdog(优雅,5s 未退强杀兜底)→ 无并存窗口 → 启新版一定能起来
|
|
223
|
+
// 关键:U 的 stdout/stderr 独立落盘(tmpdir/tt-help-upgrader-*.log),旧 watchdog 退出后日志不断,可定位。
|
|
176
224
|
|
|
177
|
-
|
|
225
|
+
/** 打开日志文件(追加模式)并打印路径;失败返回 undefined(stdio 回退到 ignore) */
|
|
226
|
+
function openLogFile(prefix, label, what) {
|
|
178
227
|
try {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
228
|
+
const logPath = join(tmpdir(), `${prefix}-${Date.now()}.log`);
|
|
229
|
+
console.error(`[${label}] ${what}: ${logPath}`);
|
|
230
|
+
return openSync(logPath, "a");
|
|
231
|
+
} catch {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* spawn 子进程(升级器 / 新版 watchdog 共用)
|
|
238
|
+
* interactive:继承控制台;否则 detached 后台运行,stdout/stderr 落到 logFd
|
|
239
|
+
* @param {string[]} args 子进程完整参数(node 脚本路径 + 参数)
|
|
240
|
+
* @returns {import("child_process").ChildProcess}
|
|
241
|
+
*/
|
|
242
|
+
function spawnChild(args, { interactive = false, logFd } = {}) {
|
|
243
|
+
const child = spawn(process.execPath, args, {
|
|
244
|
+
detached: !interactive,
|
|
245
|
+
stdio: interactive
|
|
246
|
+
? "inherit"
|
|
247
|
+
: ["ignore", logFd || "ignore", logFd || "ignore"],
|
|
248
|
+
windowsHide: !interactive,
|
|
249
|
+
});
|
|
250
|
+
if (!interactive) child.unref();
|
|
251
|
+
return child;
|
|
185
252
|
}
|
|
186
253
|
|
|
187
|
-
/**
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
254
|
+
/**
|
|
255
|
+
* 终止进程:先 SIGTERM(优雅),轮询等待退出;窗口内未退出则 SIGKILL 兜底。
|
|
256
|
+
* Windows 注意:process.kill(pid, "SIGTERM") 在 Windows 上即强制终止(无优雅退出)。
|
|
257
|
+
* @returns {Promise<boolean>} 是否在窗口内退出(false = 已强杀兜底)
|
|
258
|
+
*/
|
|
259
|
+
async function terminateProcess(pid, waitMs = 5000) {
|
|
260
|
+
if (!pid) return true;
|
|
261
|
+
try {
|
|
262
|
+
process.kill(pid, "SIGTERM");
|
|
263
|
+
} catch {
|
|
264
|
+
return true; // 进程已不存在
|
|
265
|
+
}
|
|
266
|
+
const deadline = Date.now() + waitMs;
|
|
267
|
+
while (Date.now() < deadline) {
|
|
194
268
|
try {
|
|
195
|
-
process.kill(pid, 0); //
|
|
269
|
+
process.kill(pid, 0); // 0 仅探测存活;抛错 = 已退出
|
|
270
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
196
271
|
} catch {
|
|
197
|
-
return true;
|
|
272
|
+
return true;
|
|
198
273
|
}
|
|
199
|
-
await new Promise((r) => setTimeout(r, pollMs));
|
|
200
274
|
}
|
|
275
|
+
try {
|
|
276
|
+
process.kill(pid, "SIGKILL");
|
|
277
|
+
} catch {}
|
|
201
278
|
return false;
|
|
202
279
|
}
|
|
203
280
|
|
|
281
|
+
/** 新版 watchdog 的 CLI 参数(透传 server/clientId/interval/noDaemon,保证与旧版一致) */
|
|
282
|
+
function buildWatchdogArgs({ serverUrl, clientId, intervalSec, noDaemon }) {
|
|
283
|
+
const args = ["watchdog"];
|
|
284
|
+
if (serverUrl) args.push("--server", serverUrl);
|
|
285
|
+
if (clientId) args.push("--client-id", clientId);
|
|
286
|
+
args.push("--interval", String(intervalSec));
|
|
287
|
+
if (noDaemon) args.push("--no-daemon");
|
|
288
|
+
return args;
|
|
289
|
+
}
|
|
290
|
+
|
|
204
291
|
/**
|
|
205
292
|
* 独立升级器主流程(由旧 watchdog 以子进程方式启动)
|
|
293
|
+
* 顺序:下载 → 验证代码 → 杀旧 → 启新。任一步失败都不杀旧,业务不断。
|
|
206
294
|
* @param {string[]} argv - process.argv.slice(2)
|
|
207
295
|
*/
|
|
208
296
|
export async function runUpgradeMode(argv) {
|
|
209
297
|
const get = (f) => (argv.includes(f) ? argv[argv.indexOf(f) + 1] : undefined);
|
|
210
|
-
const oldPid = parseInt(get("--old-pid"), 10) || 0;
|
|
211
298
|
const interactive = argv.includes("--interactive");
|
|
212
299
|
const serverUrl = get("--server");
|
|
213
300
|
const clientId = get("--client-id");
|
|
214
301
|
const intervalSec = parseInt(get("--interval"), 10) || 15;
|
|
215
302
|
const noDaemon = argv.includes("--no-daemon");
|
|
303
|
+
// 旧 watchdog pid:显式 --old-pid > 父进程 pid(默认由旧 watchdog spawn 时即父进程)
|
|
304
|
+
const oldPid =
|
|
305
|
+
(argv.includes("--old-pid") ? parseInt(get("--old-pid"), 10) : 0) ||
|
|
306
|
+
process.ppid;
|
|
216
307
|
|
|
308
|
+
// 1) 下载
|
|
217
309
|
console.error(
|
|
218
310
|
`[upgrade-proc] 开始下载新版: npm install -g ${PACKAGE}@latest ...`,
|
|
219
311
|
);
|
|
312
|
+
const beforeVersion = getVersion(); // 下载前版本,用于验证 npm install 真正生效
|
|
220
313
|
const result = await performUpgrade();
|
|
221
314
|
if (!result.success) {
|
|
222
315
|
const err = result.output.substring(0, 300);
|
|
223
|
-
console.error(`[upgrade-proc]
|
|
224
|
-
writeUpgradeResult({ status: "fail", error: err });
|
|
316
|
+
console.error(`[upgrade-proc] 下载失败,保持当前版本运行: ${err}`);
|
|
225
317
|
process.exit(1);
|
|
226
318
|
}
|
|
227
|
-
console.error(`[upgrade-proc] 下载完成,通知旧 watchdog 退出`);
|
|
228
|
-
writeUpgradeResult({ status: "ok" });
|
|
229
319
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
320
|
+
// 2) 下载后验证:版本确实更新 + 新版主模块能加载(冒烟);通过才允许杀旧
|
|
321
|
+
const verify = await verifyUpgradedPackage(beforeVersion);
|
|
322
|
+
if (!verify.ok) {
|
|
323
|
+
console.error(
|
|
324
|
+
`[upgrade-proc] 新版本验证未通过,不切换,保持当前版本运行: ${verify.reason}`,
|
|
325
|
+
);
|
|
326
|
+
process.exit(1);
|
|
238
327
|
}
|
|
328
|
+
console.error(`[upgrade-proc] 新版本验证通过 (${verify.version})`);
|
|
239
329
|
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (clientId) args.push("--client-id", clientId);
|
|
250
|
-
args.push("--interval", String(intervalSec));
|
|
251
|
-
if (noDaemon) args.push("--no-daemon");
|
|
252
|
-
|
|
253
|
-
let logFd = undefined;
|
|
254
|
-
try {
|
|
255
|
-
const logPath = join(tmpdir(), `tt-help-watchdog-${Date.now()}.log`);
|
|
256
|
-
logFd = openSync(logPath, "a");
|
|
257
|
-
console.error(`[upgrade-proc] 新版 watchdog 日志: ${logPath}`);
|
|
258
|
-
} catch {}
|
|
330
|
+
// 3) 杀旧:SIGTERM 优雅优先,5s 未退强杀兜底(U 不卡死)
|
|
331
|
+
console.error(`[upgrade-proc] 通知旧 watchdog (pid=${oldPid}) 退出 ...`);
|
|
332
|
+
const exited = await terminateProcess(oldPid, 5000);
|
|
333
|
+
if (exited) {
|
|
334
|
+
console.error(`[upgrade-proc] 旧 watchdog 已退出`);
|
|
335
|
+
} else {
|
|
336
|
+
console.error(`[upgrade-proc] 旧 watchdog 未在 5s 内退出,已强制终止`);
|
|
337
|
+
await new Promise((r) => setTimeout(r, 1000)); // 给强杀一点生效时间
|
|
338
|
+
}
|
|
259
339
|
|
|
260
|
-
|
|
340
|
+
// 4) 旧进程已退,启新版 watchdog(无并存窗口,一定能起来)
|
|
341
|
+
console.error(`[upgrade-proc] 启动新版 watchdog ...`);
|
|
342
|
+
const cliPath = join(__dirname, "..", "..", "cli.js");
|
|
343
|
+
const args = buildWatchdogArgs({ serverUrl, clientId, intervalSec, noDaemon });
|
|
344
|
+
const logFd = openLogFile(
|
|
345
|
+
"tt-help-watchdog",
|
|
346
|
+
"upgrade-proc",
|
|
347
|
+
"新版 watchdog 日志",
|
|
348
|
+
);
|
|
261
349
|
try {
|
|
262
|
-
child =
|
|
263
|
-
|
|
264
|
-
stdio: interactive
|
|
265
|
-
? "inherit"
|
|
266
|
-
: ["ignore", logFd || "ignore", logFd || "ignore"],
|
|
267
|
-
windowsHide: !interactive,
|
|
268
|
-
});
|
|
269
|
-
if (!interactive) child.unref();
|
|
350
|
+
const child = spawnChild([cliPath, ...args], { interactive, logFd });
|
|
351
|
+
console.error(`[upgrade-proc] 新版 watchdog 已启动 (pid=${child.pid})`);
|
|
270
352
|
} catch (e) {
|
|
271
353
|
console.error(`[upgrade-proc] 新版 watchdog 启动失败: ${e.message}`);
|
|
272
354
|
process.exit(1);
|
|
273
355
|
}
|
|
274
|
-
console.error(`[upgrade-proc] 新版 watchdog 已启动 (pid=${child.pid})`);
|
|
275
356
|
process.exit(0);
|
|
276
357
|
}
|
|
277
358
|
|
|
278
359
|
/**
|
|
279
|
-
* 旧 watchdog 调用:spawn
|
|
280
|
-
*
|
|
360
|
+
* 旧 watchdog 调用:spawn 独立升级器(fire-and-forget,不等待、不写中间文件)
|
|
361
|
+
* 升级器负责「下载 → 验证 → 杀旧 → 启新」(见 runUpgradeMode);本进程收到 SIGTERM 优雅退出。
|
|
362
|
+
* 非交互时升级器 stdout/stderr 落盘(进程退出后日志不断,可定位问题)
|
|
363
|
+
* @returns {import("child_process").ChildProcess|null} 升级器子进程(spawn 失败返回 null)
|
|
281
364
|
*/
|
|
282
|
-
export function
|
|
365
|
+
export function spawnUpgrader({
|
|
283
366
|
serverUrl,
|
|
284
367
|
clientId,
|
|
285
368
|
intervalSec = 15,
|
|
286
369
|
spawnDaemon = true,
|
|
287
370
|
interactive = false,
|
|
288
371
|
oldPid = 0,
|
|
289
|
-
timeoutMs = 180_000,
|
|
290
372
|
} = {}) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const args = ["--upgrade", "--old-pid", String(oldPid || process.pid)];
|
|
299
|
-
if (serverUrl) args.push("--server", serverUrl);
|
|
300
|
-
if (clientId) args.push("--client-id", clientId);
|
|
301
|
-
args.push("--interval", String(intervalSec));
|
|
302
|
-
if (!spawnDaemon) args.push("--no-daemon");
|
|
303
|
-
if (interactive) args.push("--interactive");
|
|
304
|
-
|
|
305
|
-
let child;
|
|
306
|
-
try {
|
|
307
|
-
child = spawn(process.execPath, [upgraderPath, ...args], {
|
|
308
|
-
// 交互:升级器接管控制台(后续新版 watchdog 也继承窗口);非交互:后台 + 日志
|
|
309
|
-
detached: !interactive,
|
|
310
|
-
stdio: interactive ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
311
|
-
windowsHide: !interactive,
|
|
312
|
-
});
|
|
313
|
-
if (!interactive) child.unref();
|
|
314
|
-
} catch (e) {
|
|
315
|
-
console.error(`[watchdog] 升级程序启动失败: ${e.message}`);
|
|
316
|
-
resolve("fail");
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
child.stderr?.on("data", (d) => {
|
|
320
|
-
const m = d.toString().trim();
|
|
321
|
-
if (m) console.error(`[upgrade-proc] ${m.substring(0, 200)}`);
|
|
322
|
-
});
|
|
373
|
+
const upgraderPath = fileURLToPath(import.meta.url);
|
|
374
|
+
const args = ["--upgrade", "--old-pid", String(oldPid || process.ppid)];
|
|
375
|
+
if (serverUrl) args.push("--server", serverUrl);
|
|
376
|
+
if (clientId) args.push("--client-id", clientId);
|
|
377
|
+
args.push("--interval", String(intervalSec));
|
|
378
|
+
if (!spawnDaemon) args.push("--no-daemon");
|
|
379
|
+
if (interactive) args.push("--interactive");
|
|
323
380
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
} catch {}
|
|
333
|
-
if (Date.now() - started > timeoutMs) {
|
|
334
|
-
clearInterval(timer);
|
|
335
|
-
try {
|
|
336
|
-
unlinkSync(UPGRADE_RESULT_FILE);
|
|
337
|
-
} catch {}
|
|
338
|
-
console.error(
|
|
339
|
-
`[watchdog] 升级程序 ${timeoutMs / 1000}s 无结果,放弃等待`,
|
|
340
|
-
);
|
|
341
|
-
resolve("timeout");
|
|
342
|
-
}
|
|
343
|
-
}, 2000);
|
|
344
|
-
});
|
|
381
|
+
const logFd = openLogFile("tt-help-upgrader", "watchdog", "升级程序日志");
|
|
382
|
+
try {
|
|
383
|
+
return spawnChild([upgraderPath, ...args], { interactive, logFd });
|
|
384
|
+
} catch (e) {
|
|
385
|
+
console.error(`[watchdog] 升级程序启动失败: ${e.message}`);
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
345
388
|
}
|
|
346
389
|
|
|
347
390
|
// 直接运行时自动进入独立升级器模式
|
|
348
391
|
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
349
392
|
runUpgradeMode(process.argv.slice(2)).catch((e) => {
|
|
350
393
|
console.error(`[upgrade-proc] 升级器异常: ${e.message}`);
|
|
351
|
-
writeUpgradeResult({ status: "fail", error: e.message });
|
|
352
394
|
process.exit(1);
|
|
353
395
|
});
|
|
354
396
|
}
|