@napuketto/loader 0.0.29 → 0.0.31
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/host/self-host.cjs +361 -70
- package/dist/index.cjs +97 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +16 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +97 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -227,6 +227,85 @@ function isNativeNoiseLine(line) {
|
|
|
227
227
|
return NATIVE_NOISE.test(line);
|
|
228
228
|
}
|
|
229
229
|
//#endregion
|
|
230
|
+
//#region src/pid-cmdline.ts
|
|
231
|
+
/**
|
|
232
|
+
* pid-cmdline.ts:跨进程命令行查询(instance-lock pid 复用根治,2026-09-10)。
|
|
233
|
+
*
|
|
234
|
+
* 背景(2026-09-08 实测事故):锁内 pid 的持有者已死、pid 被 Windows 复用给
|
|
235
|
+
* 无关进程时,process.kill(pid, 0) 探活成功 → 误判存活 → 新实例永久拒绝启动
|
|
236
|
+
* (只能手动删锁恢复)。根治:探活通过后再查该 pid 当前的真实命令行,与锁内
|
|
237
|
+
* cmdline 摘要比对——查不到(进程已退/查询失败)或不一致(pid 复用)均视为
|
|
238
|
+
* 持有者已死,允许接管。误判方向取舍:误判「死亡」→ 双实例抢 QQ 原生锁,
|
|
239
|
+
* 后启动者挂起退出(可恢复);误判「存活」→ 永久锁死需人工干预——前者代价
|
|
240
|
+
* 远低,故查询失败宁可接管。
|
|
241
|
+
*
|
|
242
|
+
* 平台实现:Windows 走 PowerShell Get-CimInstance Win32_Process 跨进程查询
|
|
243
|
+
* (仅在启动判活时调用一次,冷启动 ~0.5-2s 可接受;wmic 在新 Win11 已移除,
|
|
244
|
+
* 不可用)。Linux 走 /proc/<pid>/cmdline(\0 分隔)。其余平台(darwin 等)
|
|
245
|
+
* 不支持——判活退回纯 pid 探测(旧行为)。
|
|
246
|
+
*
|
|
247
|
+
* 比对不要求逐字符相等:锁内摘要来自 process.execArgv/argv join,查询侧
|
|
248
|
+
* 拿到的是操作系统记录的原始命令行,两者存在引号 / 大小写 / 路径分隔符差异
|
|
249
|
+
* ——统一经 normalizeCmdline 规范化后比对。残余差异源(8.3 短路径、相对
|
|
250
|
+
* 启动路径)极罕见,误判后果同上(可恢复),接受。
|
|
251
|
+
*/
|
|
252
|
+
/** 当前平台是否支持跨进程 cmdline 查询(不支持则判活退回纯 pid 探测)。 */
|
|
253
|
+
const CMDLINE_PROBE_SUPPORTED = node_process.default.platform === "win32" || node_process.default.platform === "linux";
|
|
254
|
+
/**
|
|
255
|
+
* 查询指定 pid 的当前命令行。
|
|
256
|
+
*
|
|
257
|
+
* @returns 命令行原文;进程不存在 / 命令行不可得(权限、系统进程 null)/
|
|
258
|
+
* 查询机制失败(超时、PowerShell 缺失)均返回 null(调用方视为持有者已死)
|
|
259
|
+
*/
|
|
260
|
+
function readCmdlineForPid(pid) {
|
|
261
|
+
if (node_process.default.platform === "win32") return readWindowsCmdline(pid);
|
|
262
|
+
if (node_process.default.platform === "linux") return readLinuxCmdline(pid);
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
/** Windows:PowerShell Get-CimInstance 查询(进程不存在时输出空 → null)。 */
|
|
266
|
+
function readWindowsCmdline(pid) {
|
|
267
|
+
try {
|
|
268
|
+
const cmdline = (0, node_child_process.execFileSync)("powershell.exe", [
|
|
269
|
+
"-NoProfile",
|
|
270
|
+
"-NonInteractive",
|
|
271
|
+
"-ExecutionPolicy",
|
|
272
|
+
"Bypass",
|
|
273
|
+
"-Command",
|
|
274
|
+
`[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;(Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}').CommandLine`
|
|
275
|
+
], {
|
|
276
|
+
encoding: "utf-8",
|
|
277
|
+
timeout: 1e4,
|
|
278
|
+
windowsHide: true
|
|
279
|
+
}).trim();
|
|
280
|
+
return cmdline === "" ? null : cmdline;
|
|
281
|
+
} catch {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Linux:/proc/<pid>/cmdline(\0 分隔转空格;进程不存在抛错 → null)。 */
|
|
286
|
+
function readLinuxCmdline(pid) {
|
|
287
|
+
try {
|
|
288
|
+
const cmdline = (0, node_fs.readFileSync)(`/proc/${pid}/cmdline`, "utf-8").replaceAll("\0", " ").trim();
|
|
289
|
+
return cmdline === "" ? null : cmdline;
|
|
290
|
+
} catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* 命令行规范化(比对用):统一大小写 / 去引号 / 路径分隔符归一 / 折叠空白。
|
|
296
|
+
* Windows 路径大小写不敏感,lowercase 安全。
|
|
297
|
+
*/
|
|
298
|
+
function normalizeCmdline(cmdline) {
|
|
299
|
+
return cmdline.toLowerCase().replaceAll("\"", "").replaceAll("\\", "/").replace(/\s+/g, " ").trim();
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* 本进程命令行摘要(写锁用):execArgv(node 选项)+ argv(脚本与参数),
|
|
303
|
+
* 与操作系统记录的完整命令行结构一致(规范化后可比对)。
|
|
304
|
+
*/
|
|
305
|
+
function selfCmdline() {
|
|
306
|
+
return [...node_process.default.execArgv, ...node_process.default.argv].join(" ").trim();
|
|
307
|
+
}
|
|
308
|
+
//#endregion
|
|
230
309
|
//#region src/instance-lock.ts
|
|
231
310
|
/**
|
|
232
311
|
* instance-lock.ts:单实例锁(2026-08-07 根治「多实例抢数据目录锁挂起」)
|
|
@@ -242,6 +321,12 @@ function isNativeNoiseLine(line) {
|
|
|
242
321
|
* - 锁存在但 PID 已死 → 崩溃残留,自动清理(rm + 重新获取)
|
|
243
322
|
* - 锁存在且 PID 存活 → 已被占用,拒绝启动(返回占用 PID 供提示)
|
|
244
323
|
*
|
|
324
|
+
* PID 复用根治(2026-09-10):PID 探活通过后再比对锁内 cmdline 摘要与该
|
|
325
|
+
* PID 当前真实命令行(pid-cmdline.ts)——查不到或不一致 → PID 已被无关
|
|
326
|
+
* 进程复用(持有者已死),视为残留允许接管。旧锁(无 cmdline 字段)无法
|
|
327
|
+
* 校验,保守维持「占用」判定(一次性过渡:新锁写入时自动带摘要)。详见
|
|
328
|
+
* pid-cmdline.ts 头注释的误判代价取舍。
|
|
329
|
+
*
|
|
245
330
|
* 退出清理:进程正常退出时 releaseInstanceLock 删除锁文件;崩溃/强杀残留
|
|
246
331
|
* 由下次启动的「PID 存活检测」兜底(PID 已死 → 残留视为过期)。
|
|
247
332
|
*
|
|
@@ -285,6 +370,15 @@ function checkInstanceLock(dataDir) {
|
|
|
285
370
|
pid: null,
|
|
286
371
|
lockPath
|
|
287
372
|
};
|
|
373
|
+
const lockCmdline = typeof info.cmdline === "string" && info.cmdline !== "" ? info.cmdline : void 0;
|
|
374
|
+
if (lockCmdline !== void 0 && CMDLINE_PROBE_SUPPORTED) {
|
|
375
|
+
const current = readCmdlineForPid(info.pid);
|
|
376
|
+
if (current === null || normalizeCmdline(current) !== normalizeCmdline(lockCmdline)) return {
|
|
377
|
+
occupied: false,
|
|
378
|
+
pid: null,
|
|
379
|
+
lockPath
|
|
380
|
+
};
|
|
381
|
+
}
|
|
288
382
|
return {
|
|
289
383
|
occupied: true,
|
|
290
384
|
pid: info.pid,
|
|
@@ -301,10 +395,11 @@ function acquireInstanceLock(dataDir, cmdline) {
|
|
|
301
395
|
if (pid === node_process.default.pid) return true;
|
|
302
396
|
return false;
|
|
303
397
|
}
|
|
398
|
+
const effectiveCmdline = cmdline !== void 0 && cmdline !== "" ? cmdline : selfCmdline();
|
|
304
399
|
const info = {
|
|
305
400
|
pid: node_process.default.pid,
|
|
306
401
|
startedAt: Date.now(),
|
|
307
|
-
...
|
|
402
|
+
...effectiveCmdline !== "" ? { cmdline: effectiveCmdline } : {}
|
|
308
403
|
};
|
|
309
404
|
try {
|
|
310
405
|
(0, node_fs.rmSync)(lockPath, { force: true });
|
|
@@ -1297,6 +1392,7 @@ exports.linuxSevenZipUrl = linuxSevenZipUrl;
|
|
|
1297
1392
|
exports.loadQqReleases = loadQqReleases;
|
|
1298
1393
|
exports.locateQqPath = locateQqPath;
|
|
1299
1394
|
exports.nodeZipUrl = nodeZipUrl;
|
|
1395
|
+
exports.readCmdlineForPid = readCmdlineForPid;
|
|
1300
1396
|
exports.registerLockCleanup = registerLockCleanup;
|
|
1301
1397
|
exports.releaseInstanceLock = releaseInstanceLock;
|
|
1302
1398
|
exports.resolveDownloadUrl = resolveDownloadUrl;
|
package/dist/index.d.cts
CHANGED
|
@@ -370,6 +370,12 @@ declare function isNativeNoiseLine(line: string): boolean;
|
|
|
370
370
|
* - 锁存在但 PID 已死 → 崩溃残留,自动清理(rm + 重新获取)
|
|
371
371
|
* - 锁存在且 PID 存活 → 已被占用,拒绝启动(返回占用 PID 供提示)
|
|
372
372
|
*
|
|
373
|
+
* PID 复用根治(2026-09-10):PID 探活通过后再比对锁内 cmdline 摘要与该
|
|
374
|
+
* PID 当前真实命令行(pid-cmdline.ts)——查不到或不一致 → PID 已被无关
|
|
375
|
+
* 进程复用(持有者已死),视为残留允许接管。旧锁(无 cmdline 字段)无法
|
|
376
|
+
* 校验,保守维持「占用」判定(一次性过渡:新锁写入时自动带摘要)。详见
|
|
377
|
+
* pid-cmdline.ts 头注释的误判代价取舍。
|
|
378
|
+
*
|
|
373
379
|
* 退出清理:进程正常退出时 releaseInstanceLock 删除锁文件;崩溃/强杀残留
|
|
374
380
|
* 由下次启动的「PID 存活检测」兜底(PID 已死 → 残留视为过期)。
|
|
375
381
|
*
|
|
@@ -620,6 +626,15 @@ declare const ENV: {
|
|
|
620
626
|
readonly IPC: "NAPUTO_IPC";
|
|
621
627
|
};
|
|
622
628
|
//#endregion
|
|
629
|
+
//#region src/pid-cmdline.d.ts
|
|
630
|
+
/**
|
|
631
|
+
* 查询指定 pid 的当前命令行。
|
|
632
|
+
*
|
|
633
|
+
* @returns 命令行原文;进程不存在 / 命令行不可得(权限、系统进程 null)/
|
|
634
|
+
* 查询机制失败(超时、PowerShell 缺失)均返回 null(调用方视为持有者已死)
|
|
635
|
+
*/
|
|
636
|
+
declare function readCmdlineForPid(pid: number): string | null;
|
|
637
|
+
//#endregion
|
|
623
638
|
//#region src/qq-download.d.ts
|
|
624
639
|
/** 下载选项。 */
|
|
625
640
|
interface DownloadOptions {
|
|
@@ -757,5 +772,5 @@ declare function buildSpawnCommand(options: {
|
|
|
757
772
|
selfHostPath: string;
|
|
758
773
|
}): SpawnCommand;
|
|
759
774
|
//#endregion
|
|
760
|
-
export { DownloadError, type DownloadOptions, type DownloadResult, ENV, type ExtractOptions, INSTANCE_LOCK_FILE, IPC_VERSION, type InstanceLockCheck, type InstanceLockInfo, type IpcActionMessage, type IpcActionPayload, IpcActionPayloadSchema, type IpcBootPhase, IpcBootPhaseSchema, type IpcControlMessage, type IpcControlPayload, IpcControlPayloadSchema, type IpcEventMessage, type IpcEventPayload, IpcEventPayloadSchema, type IpcLogLevel, IpcLogLevelSchema, type IpcLogMessage, type IpcLogPayload, IpcLogPayloadSchema, type IpcLoginMessage, type IpcLoginPayload, IpcLoginPayloadSchema, type IpcMessage, IpcMessageSchema, type IpcPingMessage, type IpcPongMessage, type IpcQrMessage, type IpcResultMessage, type IpcResultPayload, IpcResultPayloadSchema, type IpcStatusMessage, type IpcStatusPayload, IpcStatusPayloadSchema, type LaunchOptions, type LaunchResult, QQ_FILES_DIR_NAME, type QqFileSource, type QqInstallInfo, type QqReleaseEntry, QqReleasesError, type QqReleasesFile, type ResolveQqFilesOptions, type SevenZipResult, type SpawnCommand, WIN_NODE_DIR_NAME, type WinNodeInfo, acquireInstanceLock, buildSpawnCommand, checkInstanceLock, clearCacheVersion, defaultStubDir, downloadFile, ensureLinuxSevenZip, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, isNativeNoiseLine, latestRelease, launchSelfHost, linuxSevenZipUrl, loadQqReleases, locateQqPath, nodeZipUrl, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
|
|
775
|
+
export { DownloadError, type DownloadOptions, type DownloadResult, ENV, type ExtractOptions, INSTANCE_LOCK_FILE, IPC_VERSION, type InstanceLockCheck, type InstanceLockInfo, type IpcActionMessage, type IpcActionPayload, IpcActionPayloadSchema, type IpcBootPhase, IpcBootPhaseSchema, type IpcControlMessage, type IpcControlPayload, IpcControlPayloadSchema, type IpcEventMessage, type IpcEventPayload, IpcEventPayloadSchema, type IpcLogLevel, IpcLogLevelSchema, type IpcLogMessage, type IpcLogPayload, IpcLogPayloadSchema, type IpcLoginMessage, type IpcLoginPayload, IpcLoginPayloadSchema, type IpcMessage, IpcMessageSchema, type IpcPingMessage, type IpcPongMessage, type IpcQrMessage, type IpcResultMessage, type IpcResultPayload, IpcResultPayloadSchema, type IpcStatusMessage, type IpcStatusPayload, IpcStatusPayloadSchema, type LaunchOptions, type LaunchResult, QQ_FILES_DIR_NAME, type QqFileSource, type QqInstallInfo, type QqReleaseEntry, QqReleasesError, type QqReleasesFile, type ResolveQqFilesOptions, type SevenZipResult, type SpawnCommand, WIN_NODE_DIR_NAME, type WinNodeInfo, acquireInstanceLock, buildSpawnCommand, checkInstanceLock, clearCacheVersion, defaultStubDir, downloadFile, ensureLinuxSevenZip, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, isNativeNoiseLine, latestRelease, launchSelfHost, linuxSevenZipUrl, loadQqReleases, locateQqPath, nodeZipUrl, readCmdlineForPid, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
|
|
761
776
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/host/ipc/ipc-types.ts","../src/host/ipc/native-noise.ts","../src/instance-lock.ts","../src/locate-qq.ts","../src/win-node.ts","../src/launcher.ts","../src/qq-download.ts","../src/qq-extract.ts","../src/qq-releases.ts","../src/wine.ts"],"mappings":";;;;cAaa;;cAGA,oBAAkB,EAAA;;;;;;;;;cAUlB,mBAAiB,EAAA;;;;;;;;;cAajB,wBAAsB,EAAA;;;;;;;;;;;;;;GAIjC,EAAA,KAAA;;cAGW,uBAAqB,EAAA;;;;;;;;;;;;;;GAKhC,EAAA,KAAA;;cAGW,uBAAqB,EAAA;;;;GAIhC,EAAA,KAAA;;cAGW,qBAAmB,EAAA;;;;;;;;;;GAG9B,EAAA,KAAA;;cAGW,wBAAsB,EAAA,mBAAA,EAAA;;;;;;;;;GAGjC,EAAA,KAAA;;cAGW,wBAAsB,EAAA;;;GAGjC,EAAA,KAAA;;cAGW,yBAAuB,EAAA,mBAAA,EAAA;;;;;;;;;;GASlC,EAAA,KAAA;cAII,wBAAsB,EAAA;;;;;;;;;;;;;;;;;;GAI1B,EAAA,KAAA;cAEI,uBAAqB,EAAA;;;;;;;;;;;;;;;;;;GAIzB,EAAA,KAAA;cAEI,oBAAkB,EAAA;;;;;;;GAItB,EAAA,KAAA;cAEI,uBAAqB,EAAA;;;;;;;;GAIzB,EAAA,KAAA;cAEI,wBAAsB,EAAA;;;;;;;;;;;;;;GAK1B,EAAA,KAAA;cAEI,qBAAmB,EAAA;;;;;;;;;;;;;;GAIvB,EAAA,KAAA;cAEI,sBAAoB,EAAA;;;GAGxB,EAAA,KAAA;cAEI,sBAAoB,EAAA;;;GAGxB,EAAA,KAAA;cAEI,wBAAsB,EAAA;;;;;;;;GAK1B,EAAA,KAAA;cAEI,yBAAuB,EAAA;;;;;;;;;;;;;;GAI3B,EAAA,KAAA;;cAGW,kBAAgB,EAAA,uBAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAW3B,EAAA,KAAA;KAIU,eAAe,EAAE,aAAa;KAC9B,cAAc,EAAE,aAAa;KAC7B,mBAAmB,EAAE,aAAa;KAClC,kBAAkB,EAAE,aAAa;KACjC,kBAAkB,EAAE,aAAa;KACjC,gBAAgB,EAAE,aAAa;KAC/B,mBAAmB,EAAE,aAAa;KAClC,mBAAmB,EAAE,aAAa;KAClC,oBAAoB,EAAE,aAAa;KACnC,mBAAmB,EAAE,aAAa;KAClC,kBAAkB,EAAE,aAAa;KACjC,eAAe,EAAE,aAAa;KAC9B,kBAAkB,EAAE,aAAa;KACjC,mBAAmB,EAAE,aAAa;KAClC,gBAAgB,EAAE,aAAa;KAC/B,iBAAiB,EAAE,aAAa;KAChC,iBAAiB,EAAE,aAAa;KAChC,mBAAmB,EAAE,aAAa;KAClC,oBAAoB,EAAE,aAAa;KACnC,aAAa,EAAE,aAAa;;;;;;;;;iBC9JxB,kBAAkB
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/host/ipc/ipc-types.ts","../src/host/ipc/native-noise.ts","../src/instance-lock.ts","../src/locate-qq.ts","../src/win-node.ts","../src/launcher.ts","../src/pid-cmdline.ts","../src/qq-download.ts","../src/qq-extract.ts","../src/qq-releases.ts","../src/wine.ts"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -370,6 +370,12 @@ declare function isNativeNoiseLine(line: string): boolean;
|
|
|
370
370
|
* - 锁存在但 PID 已死 → 崩溃残留,自动清理(rm + 重新获取)
|
|
371
371
|
* - 锁存在且 PID 存活 → 已被占用,拒绝启动(返回占用 PID 供提示)
|
|
372
372
|
*
|
|
373
|
+
* PID 复用根治(2026-09-10):PID 探活通过后再比对锁内 cmdline 摘要与该
|
|
374
|
+
* PID 当前真实命令行(pid-cmdline.ts)——查不到或不一致 → PID 已被无关
|
|
375
|
+
* 进程复用(持有者已死),视为残留允许接管。旧锁(无 cmdline 字段)无法
|
|
376
|
+
* 校验,保守维持「占用」判定(一次性过渡:新锁写入时自动带摘要)。详见
|
|
377
|
+
* pid-cmdline.ts 头注释的误判代价取舍。
|
|
378
|
+
*
|
|
373
379
|
* 退出清理:进程正常退出时 releaseInstanceLock 删除锁文件;崩溃/强杀残留
|
|
374
380
|
* 由下次启动的「PID 存活检测」兜底(PID 已死 → 残留视为过期)。
|
|
375
381
|
*
|
|
@@ -620,6 +626,15 @@ declare const ENV: {
|
|
|
620
626
|
readonly IPC: "NAPUTO_IPC";
|
|
621
627
|
};
|
|
622
628
|
//#endregion
|
|
629
|
+
//#region src/pid-cmdline.d.ts
|
|
630
|
+
/**
|
|
631
|
+
* 查询指定 pid 的当前命令行。
|
|
632
|
+
*
|
|
633
|
+
* @returns 命令行原文;进程不存在 / 命令行不可得(权限、系统进程 null)/
|
|
634
|
+
* 查询机制失败(超时、PowerShell 缺失)均返回 null(调用方视为持有者已死)
|
|
635
|
+
*/
|
|
636
|
+
declare function readCmdlineForPid(pid: number): string | null;
|
|
637
|
+
//#endregion
|
|
623
638
|
//#region src/qq-download.d.ts
|
|
624
639
|
/** 下载选项。 */
|
|
625
640
|
interface DownloadOptions {
|
|
@@ -757,5 +772,5 @@ declare function buildSpawnCommand(options: {
|
|
|
757
772
|
selfHostPath: string;
|
|
758
773
|
}): SpawnCommand;
|
|
759
774
|
//#endregion
|
|
760
|
-
export { DownloadError, type DownloadOptions, type DownloadResult, ENV, type ExtractOptions, INSTANCE_LOCK_FILE, IPC_VERSION, type InstanceLockCheck, type InstanceLockInfo, type IpcActionMessage, type IpcActionPayload, IpcActionPayloadSchema, type IpcBootPhase, IpcBootPhaseSchema, type IpcControlMessage, type IpcControlPayload, IpcControlPayloadSchema, type IpcEventMessage, type IpcEventPayload, IpcEventPayloadSchema, type IpcLogLevel, IpcLogLevelSchema, type IpcLogMessage, type IpcLogPayload, IpcLogPayloadSchema, type IpcLoginMessage, type IpcLoginPayload, IpcLoginPayloadSchema, type IpcMessage, IpcMessageSchema, type IpcPingMessage, type IpcPongMessage, type IpcQrMessage, type IpcResultMessage, type IpcResultPayload, IpcResultPayloadSchema, type IpcStatusMessage, type IpcStatusPayload, IpcStatusPayloadSchema, type LaunchOptions, type LaunchResult, QQ_FILES_DIR_NAME, type QqFileSource, type QqInstallInfo, type QqReleaseEntry, QqReleasesError, type QqReleasesFile, type ResolveQqFilesOptions, type SevenZipResult, type SpawnCommand, WIN_NODE_DIR_NAME, type WinNodeInfo, acquireInstanceLock, buildSpawnCommand, checkInstanceLock, clearCacheVersion, defaultStubDir, downloadFile, ensureLinuxSevenZip, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, isNativeNoiseLine, latestRelease, launchSelfHost, linuxSevenZipUrl, loadQqReleases, locateQqPath, nodeZipUrl, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
|
|
775
|
+
export { DownloadError, type DownloadOptions, type DownloadResult, ENV, type ExtractOptions, INSTANCE_LOCK_FILE, IPC_VERSION, type InstanceLockCheck, type InstanceLockInfo, type IpcActionMessage, type IpcActionPayload, IpcActionPayloadSchema, type IpcBootPhase, IpcBootPhaseSchema, type IpcControlMessage, type IpcControlPayload, IpcControlPayloadSchema, type IpcEventMessage, type IpcEventPayload, IpcEventPayloadSchema, type IpcLogLevel, IpcLogLevelSchema, type IpcLogMessage, type IpcLogPayload, IpcLogPayloadSchema, type IpcLoginMessage, type IpcLoginPayload, IpcLoginPayloadSchema, type IpcMessage, IpcMessageSchema, type IpcPingMessage, type IpcPongMessage, type IpcQrMessage, type IpcResultMessage, type IpcResultPayload, IpcResultPayloadSchema, type IpcStatusMessage, type IpcStatusPayload, IpcStatusPayloadSchema, type LaunchOptions, type LaunchResult, QQ_FILES_DIR_NAME, type QqFileSource, type QqInstallInfo, type QqReleaseEntry, QqReleasesError, type QqReleasesFile, type ResolveQqFilesOptions, type SevenZipResult, type SpawnCommand, WIN_NODE_DIR_NAME, type WinNodeInfo, acquireInstanceLock, buildSpawnCommand, checkInstanceLock, clearCacheVersion, defaultStubDir, downloadFile, ensureLinuxSevenZip, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, isNativeNoiseLine, latestRelease, launchSelfHost, linuxSevenZipUrl, loadQqReleases, locateQqPath, nodeZipUrl, readCmdlineForPid, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
|
|
761
776
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/host/ipc/ipc-types.ts","../src/host/ipc/native-noise.ts","../src/instance-lock.ts","../src/locate-qq.ts","../src/win-node.ts","../src/launcher.ts","../src/qq-download.ts","../src/qq-extract.ts","../src/qq-releases.ts","../src/wine.ts"],"mappings":";;;;cAaa;;cAGA,oBAAkB,EAAA;;;;;;;;;cAUlB,mBAAiB,EAAA;;;;;;;;;cAajB,wBAAsB,EAAA;;;;;;;;;;;;;;GAIjC,EAAA,KAAA;;cAGW,uBAAqB,EAAA;;;;;;;;;;;;;;GAKhC,EAAA,KAAA;;cAGW,uBAAqB,EAAA;;;;GAIhC,EAAA,KAAA;;cAGW,qBAAmB,EAAA;;;;;;;;;;GAG9B,EAAA,KAAA;;cAGW,wBAAsB,EAAA,mBAAA,EAAA;;;;;;;;;GAGjC,EAAA,KAAA;;cAGW,wBAAsB,EAAA;;;GAGjC,EAAA,KAAA;;cAGW,yBAAuB,EAAA,mBAAA,EAAA;;;;;;;;;;GASlC,EAAA,KAAA;cAII,wBAAsB,EAAA;;;;;;;;;;;;;;;;;;GAI1B,EAAA,KAAA;cAEI,uBAAqB,EAAA;;;;;;;;;;;;;;;;;;GAIzB,EAAA,KAAA;cAEI,oBAAkB,EAAA;;;;;;;GAItB,EAAA,KAAA;cAEI,uBAAqB,EAAA;;;;;;;;GAIzB,EAAA,KAAA;cAEI,wBAAsB,EAAA;;;;;;;;;;;;;;GAK1B,EAAA,KAAA;cAEI,qBAAmB,EAAA;;;;;;;;;;;;;;GAIvB,EAAA,KAAA;cAEI,sBAAoB,EAAA;;;GAGxB,EAAA,KAAA;cAEI,sBAAoB,EAAA;;;GAGxB,EAAA,KAAA;cAEI,wBAAsB,EAAA;;;;;;;;GAK1B,EAAA,KAAA;cAEI,yBAAuB,EAAA;;;;;;;;;;;;;;GAI3B,EAAA,KAAA;;cAGW,kBAAgB,EAAA,uBAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAW3B,EAAA,KAAA;KAIU,eAAe,EAAE,aAAa;KAC9B,cAAc,EAAE,aAAa;KAC7B,mBAAmB,EAAE,aAAa;KAClC,kBAAkB,EAAE,aAAa;KACjC,kBAAkB,EAAE,aAAa;KACjC,gBAAgB,EAAE,aAAa;KAC/B,mBAAmB,EAAE,aAAa;KAClC,mBAAmB,EAAE,aAAa;KAClC,oBAAoB,EAAE,aAAa;KACnC,mBAAmB,EAAE,aAAa;KAClC,kBAAkB,EAAE,aAAa;KACjC,eAAe,EAAE,aAAa;KAC9B,kBAAkB,EAAE,aAAa;KACjC,mBAAmB,EAAE,aAAa;KAClC,gBAAgB,EAAE,aAAa;KAC/B,iBAAiB,EAAE,aAAa;KAChC,iBAAiB,EAAE,aAAa;KAChC,mBAAmB,EAAE,aAAa;KAClC,oBAAoB,EAAE,aAAa;KACnC,aAAa,EAAE,aAAa;;;;;;;;;iBC9JxB,kBAAkB
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/host/ipc/ipc-types.ts","../src/host/ipc/native-noise.ts","../src/instance-lock.ts","../src/locate-qq.ts","../src/win-node.ts","../src/launcher.ts","../src/pid-cmdline.ts","../src/qq-download.ts","../src/qq-extract.ts","../src/qq-releases.ts","../src/wine.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -203,6 +203,85 @@ function isNativeNoiseLine(line) {
|
|
|
203
203
|
return NATIVE_NOISE.test(line);
|
|
204
204
|
}
|
|
205
205
|
//#endregion
|
|
206
|
+
//#region src/pid-cmdline.ts
|
|
207
|
+
/**
|
|
208
|
+
* pid-cmdline.ts:跨进程命令行查询(instance-lock pid 复用根治,2026-09-10)。
|
|
209
|
+
*
|
|
210
|
+
* 背景(2026-09-08 实测事故):锁内 pid 的持有者已死、pid 被 Windows 复用给
|
|
211
|
+
* 无关进程时,process.kill(pid, 0) 探活成功 → 误判存活 → 新实例永久拒绝启动
|
|
212
|
+
* (只能手动删锁恢复)。根治:探活通过后再查该 pid 当前的真实命令行,与锁内
|
|
213
|
+
* cmdline 摘要比对——查不到(进程已退/查询失败)或不一致(pid 复用)均视为
|
|
214
|
+
* 持有者已死,允许接管。误判方向取舍:误判「死亡」→ 双实例抢 QQ 原生锁,
|
|
215
|
+
* 后启动者挂起退出(可恢复);误判「存活」→ 永久锁死需人工干预——前者代价
|
|
216
|
+
* 远低,故查询失败宁可接管。
|
|
217
|
+
*
|
|
218
|
+
* 平台实现:Windows 走 PowerShell Get-CimInstance Win32_Process 跨进程查询
|
|
219
|
+
* (仅在启动判活时调用一次,冷启动 ~0.5-2s 可接受;wmic 在新 Win11 已移除,
|
|
220
|
+
* 不可用)。Linux 走 /proc/<pid>/cmdline(\0 分隔)。其余平台(darwin 等)
|
|
221
|
+
* 不支持——判活退回纯 pid 探测(旧行为)。
|
|
222
|
+
*
|
|
223
|
+
* 比对不要求逐字符相等:锁内摘要来自 process.execArgv/argv join,查询侧
|
|
224
|
+
* 拿到的是操作系统记录的原始命令行,两者存在引号 / 大小写 / 路径分隔符差异
|
|
225
|
+
* ——统一经 normalizeCmdline 规范化后比对。残余差异源(8.3 短路径、相对
|
|
226
|
+
* 启动路径)极罕见,误判后果同上(可恢复),接受。
|
|
227
|
+
*/
|
|
228
|
+
/** 当前平台是否支持跨进程 cmdline 查询(不支持则判活退回纯 pid 探测)。 */
|
|
229
|
+
const CMDLINE_PROBE_SUPPORTED = process$1.platform === "win32" || process$1.platform === "linux";
|
|
230
|
+
/**
|
|
231
|
+
* 查询指定 pid 的当前命令行。
|
|
232
|
+
*
|
|
233
|
+
* @returns 命令行原文;进程不存在 / 命令行不可得(权限、系统进程 null)/
|
|
234
|
+
* 查询机制失败(超时、PowerShell 缺失)均返回 null(调用方视为持有者已死)
|
|
235
|
+
*/
|
|
236
|
+
function readCmdlineForPid(pid) {
|
|
237
|
+
if (process$1.platform === "win32") return readWindowsCmdline(pid);
|
|
238
|
+
if (process$1.platform === "linux") return readLinuxCmdline(pid);
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
/** Windows:PowerShell Get-CimInstance 查询(进程不存在时输出空 → null)。 */
|
|
242
|
+
function readWindowsCmdline(pid) {
|
|
243
|
+
try {
|
|
244
|
+
const cmdline = execFileSync("powershell.exe", [
|
|
245
|
+
"-NoProfile",
|
|
246
|
+
"-NonInteractive",
|
|
247
|
+
"-ExecutionPolicy",
|
|
248
|
+
"Bypass",
|
|
249
|
+
"-Command",
|
|
250
|
+
`[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;(Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}').CommandLine`
|
|
251
|
+
], {
|
|
252
|
+
encoding: "utf-8",
|
|
253
|
+
timeout: 1e4,
|
|
254
|
+
windowsHide: true
|
|
255
|
+
}).trim();
|
|
256
|
+
return cmdline === "" ? null : cmdline;
|
|
257
|
+
} catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/** Linux:/proc/<pid>/cmdline(\0 分隔转空格;进程不存在抛错 → null)。 */
|
|
262
|
+
function readLinuxCmdline(pid) {
|
|
263
|
+
try {
|
|
264
|
+
const cmdline = readFileSync(`/proc/${pid}/cmdline`, "utf-8").replaceAll("\0", " ").trim();
|
|
265
|
+
return cmdline === "" ? null : cmdline;
|
|
266
|
+
} catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 命令行规范化(比对用):统一大小写 / 去引号 / 路径分隔符归一 / 折叠空白。
|
|
272
|
+
* Windows 路径大小写不敏感,lowercase 安全。
|
|
273
|
+
*/
|
|
274
|
+
function normalizeCmdline(cmdline) {
|
|
275
|
+
return cmdline.toLowerCase().replaceAll("\"", "").replaceAll("\\", "/").replace(/\s+/g, " ").trim();
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 本进程命令行摘要(写锁用):execArgv(node 选项)+ argv(脚本与参数),
|
|
279
|
+
* 与操作系统记录的完整命令行结构一致(规范化后可比对)。
|
|
280
|
+
*/
|
|
281
|
+
function selfCmdline() {
|
|
282
|
+
return [...process$1.execArgv, ...process$1.argv].join(" ").trim();
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
206
285
|
//#region src/instance-lock.ts
|
|
207
286
|
/**
|
|
208
287
|
* instance-lock.ts:单实例锁(2026-08-07 根治「多实例抢数据目录锁挂起」)
|
|
@@ -218,6 +297,12 @@ function isNativeNoiseLine(line) {
|
|
|
218
297
|
* - 锁存在但 PID 已死 → 崩溃残留,自动清理(rm + 重新获取)
|
|
219
298
|
* - 锁存在且 PID 存活 → 已被占用,拒绝启动(返回占用 PID 供提示)
|
|
220
299
|
*
|
|
300
|
+
* PID 复用根治(2026-09-10):PID 探活通过后再比对锁内 cmdline 摘要与该
|
|
301
|
+
* PID 当前真实命令行(pid-cmdline.ts)——查不到或不一致 → PID 已被无关
|
|
302
|
+
* 进程复用(持有者已死),视为残留允许接管。旧锁(无 cmdline 字段)无法
|
|
303
|
+
* 校验,保守维持「占用」判定(一次性过渡:新锁写入时自动带摘要)。详见
|
|
304
|
+
* pid-cmdline.ts 头注释的误判代价取舍。
|
|
305
|
+
*
|
|
221
306
|
* 退出清理:进程正常退出时 releaseInstanceLock 删除锁文件;崩溃/强杀残留
|
|
222
307
|
* 由下次启动的「PID 存活检测」兜底(PID 已死 → 残留视为过期)。
|
|
223
308
|
*
|
|
@@ -261,6 +346,15 @@ function checkInstanceLock(dataDir) {
|
|
|
261
346
|
pid: null,
|
|
262
347
|
lockPath
|
|
263
348
|
};
|
|
349
|
+
const lockCmdline = typeof info.cmdline === "string" && info.cmdline !== "" ? info.cmdline : void 0;
|
|
350
|
+
if (lockCmdline !== void 0 && CMDLINE_PROBE_SUPPORTED) {
|
|
351
|
+
const current = readCmdlineForPid(info.pid);
|
|
352
|
+
if (current === null || normalizeCmdline(current) !== normalizeCmdline(lockCmdline)) return {
|
|
353
|
+
occupied: false,
|
|
354
|
+
pid: null,
|
|
355
|
+
lockPath
|
|
356
|
+
};
|
|
357
|
+
}
|
|
264
358
|
return {
|
|
265
359
|
occupied: true,
|
|
266
360
|
pid: info.pid,
|
|
@@ -277,10 +371,11 @@ function acquireInstanceLock(dataDir, cmdline) {
|
|
|
277
371
|
if (pid === process$1.pid) return true;
|
|
278
372
|
return false;
|
|
279
373
|
}
|
|
374
|
+
const effectiveCmdline = cmdline !== void 0 && cmdline !== "" ? cmdline : selfCmdline();
|
|
280
375
|
const info = {
|
|
281
376
|
pid: process$1.pid,
|
|
282
377
|
startedAt: Date.now(),
|
|
283
|
-
...
|
|
378
|
+
...effectiveCmdline !== "" ? { cmdline: effectiveCmdline } : {}
|
|
284
379
|
};
|
|
285
380
|
try {
|
|
286
381
|
rmSync(lockPath, { force: true });
|
|
@@ -1236,6 +1331,6 @@ async function ensureQqFiles(options = {}) {
|
|
|
1236
1331
|
}
|
|
1237
1332
|
}
|
|
1238
1333
|
//#endregion
|
|
1239
|
-
export { DownloadError, ENV, INSTANCE_LOCK_FILE, IPC_VERSION, IpcActionPayloadSchema, IpcBootPhaseSchema, IpcControlPayloadSchema, IpcEventPayloadSchema, IpcLogLevelSchema, IpcLogPayloadSchema, IpcLoginPayloadSchema, IpcMessageSchema, IpcResultPayloadSchema, IpcStatusPayloadSchema, QQ_FILES_DIR_NAME, QqReleasesError, WIN_NODE_DIR_NAME, acquireInstanceLock, buildSpawnCommand, checkInstanceLock, clearCacheVersion, defaultStubDir, downloadFile, ensureLinuxSevenZip, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, isNativeNoiseLine, latestRelease, launchSelfHost, linuxSevenZipUrl, loadQqReleases, locateQqPath, nodeZipUrl, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
|
|
1334
|
+
export { DownloadError, ENV, INSTANCE_LOCK_FILE, IPC_VERSION, IpcActionPayloadSchema, IpcBootPhaseSchema, IpcControlPayloadSchema, IpcEventPayloadSchema, IpcLogLevelSchema, IpcLogPayloadSchema, IpcLoginPayloadSchema, IpcMessageSchema, IpcResultPayloadSchema, IpcStatusPayloadSchema, QQ_FILES_DIR_NAME, QqReleasesError, WIN_NODE_DIR_NAME, acquireInstanceLock, buildSpawnCommand, checkInstanceLock, clearCacheVersion, defaultStubDir, downloadFile, ensureLinuxSevenZip, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, isNativeNoiseLine, latestRelease, launchSelfHost, linuxSevenZipUrl, loadQqReleases, locateQqPath, nodeZipUrl, readCmdlineForPid, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
|
|
1240
1335
|
|
|
1241
1336
|
//# sourceMappingURL=index.mjs.map
|