@napuketto/loader 0.0.7 → 0.0.9

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.
Binary file
Binary file
@@ -0,0 +1,146 @@
1
+ 7-Zip
2
+ ~~~~~
3
+ License for use and distribution
4
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
+
6
+ 7-Zip Copyright (C) 1999-2026 Igor Pavlov.
7
+
8
+ The licenses for files are:
9
+
10
+ - 7z.dll:
11
+ - The "GNU LGPL" as main license for most of the code
12
+ - The "GNU LGPL" with "unRAR license restriction" for some code
13
+ - The "BSD 3-clause License" for some code
14
+ - The "BSD 2-clause License" for some code
15
+ - All other files: the "GNU LGPL".
16
+
17
+ Redistributions in binary form must reproduce related license information from this file.
18
+
19
+ Note:
20
+ You can use 7-Zip on any computer, including a computer in a commercial
21
+ organization. You don't need to register or pay for 7-Zip.
22
+
23
+
24
+ GNU LGPL information
25
+ --------------------
26
+
27
+ This library is free software; you can redistribute it and/or
28
+ modify it under the terms of the GNU Lesser General Public
29
+ License as published by the Free Software Foundation; either
30
+ version 2.1 of the License, or (at your option) any later version.
31
+
32
+ This library is distributed in the hope that it will be useful,
33
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
34
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35
+ Lesser General Public License for more details.
36
+
37
+ You can receive a copy of the GNU Lesser General Public License from
38
+ http://www.gnu.org/
39
+
40
+
41
+
42
+
43
+ BSD 3-clause License in 7-Zip code
44
+ ----------------------------------
45
+
46
+ The "BSD 3-clause License" is used for the following code in 7z.dll
47
+ 1) LZFSE data decompression.
48
+ That code was derived from the code in the "LZFSE compression library" developed by Apple Inc,
49
+ that also uses the "BSD 3-clause License".
50
+ 2) ZSTD data decompression.
51
+ that code was developed using original zstd decoder code as reference code.
52
+ The original zstd decoder code was developed by Facebook Inc,
53
+ that also uses the "BSD 3-clause License".
54
+
55
+ Copyright (c) 2015-2016, Apple Inc. All rights reserved.
56
+ Copyright (c) Facebook, Inc. All rights reserved.
57
+ Copyright (c) 2023-2026 Igor Pavlov.
58
+
59
+ Text of the "BSD 3-clause License"
60
+ ----------------------------------
61
+
62
+ Redistribution and use in source and binary forms, with or without modification,
63
+ are permitted provided that the following conditions are met:
64
+
65
+ 1. Redistributions of source code must retain the above copyright notice, this
66
+ list of conditions and the following disclaimer.
67
+
68
+ 2. Redistributions in binary form must reproduce the above copyright notice,
69
+ this list of conditions and the following disclaimer in the documentation
70
+ and/or other materials provided with the distribution.
71
+
72
+ 3. Neither the name of the copyright holder nor the names of its contributors may
73
+ be used to endorse or promote products derived from this software without
74
+ specific prior written permission.
75
+
76
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
77
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
78
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
79
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
80
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
81
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
82
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
83
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
84
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
85
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
86
+
87
+ ---
88
+
89
+
90
+
91
+
92
+ BSD 2-clause License in 7-Zip code
93
+ ----------------------------------
94
+
95
+ The "BSD 2-clause License" is used for the XXH64 code in 7-Zip.
96
+
97
+ XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet.
98
+
99
+ Copyright (c) 2012-2021 Yann Collet.
100
+ Copyright (c) 2023-2026 Igor Pavlov.
101
+
102
+ Text of the "BSD 2-clause License"
103
+ ----------------------------------
104
+
105
+ Redistribution and use in source and binary forms, with or without modification,
106
+ are permitted provided that the following conditions are met:
107
+
108
+ 1. Redistributions of source code must retain the above copyright notice, this
109
+ list of conditions and the following disclaimer.
110
+
111
+ 2. Redistributions in binary form must reproduce the above copyright notice,
112
+ this list of conditions and the following disclaimer in the documentation
113
+ and/or other materials provided with the distribution.
114
+
115
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
116
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
117
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
118
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
119
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
120
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
121
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
122
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
123
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
124
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
125
+
126
+ ---
127
+
128
+
129
+
130
+
131
+ unRAR license restriction
132
+ -------------------------
133
+
134
+ The decompression engine for RAR archives was developed using source
135
+ code of unRAR program.
136
+ All copyrights to original unRAR code are owned by Alexander Roshal.
137
+
138
+ The license for original unRAR code has the following restriction:
139
+
140
+ The unRAR sources cannot be used to re-create the RAR compression algorithm,
141
+ which is proprietary. Distribution of modified unRAR sources in separate form
142
+ or as a part of other software is permitted, provided that it is clearly
143
+ stated in the documentation and source comments that the code may
144
+ not be used to develop a RAR (WinRAR) compatible archiver.
145
+
146
+ --
@@ -287,8 +287,135 @@ function createIpcActions(ctx) {
287
287
  return ctx.groupCache.listGroupsRefreshed();
288
288
  });
289
289
  actions.set("friend.getFriendList", async () => ctx.friendApi.getFriendList());
290
+ actions.set("diag.richMediaTmpPaths", async () => runRichMediaDiag(ctx.session));
291
+ actions.set("diag.flashCall", async (params) => {
292
+ const sess = ctx.session;
293
+ if (sess === void 0) return { error: "session 未注入" };
294
+ const flash = typeof sess.getFlashTransferService === "function" ? sess.getFlashTransferService() : null;
295
+ if (flash === null || flash === void 0) return { error: "flash 服务为 null" };
296
+ return callDiagnosticMethod(flash, params);
297
+ });
298
+ actions.set("diag.sessionCall", async (params) => {
299
+ if (ctx.session === void 0) return { error: "session 未注入" };
300
+ return callDiagnosticMethod(ctx.session, params);
301
+ });
302
+ actions.set("diag.engineCall", async (params) => {
303
+ if (ctx.engine === void 0) return { error: "engine 未注入" };
304
+ return callDiagnosticMethod(ctx.engine, params);
305
+ });
306
+ actions.set("diag.richMediaCall", async (params) => {
307
+ const sess = ctx.session;
308
+ if (sess === void 0) return { error: "session 未注入" };
309
+ const svc = typeof sess.getRichMediaService === "function" ? sess.getRichMediaService() : null;
310
+ if (svc === null || svc === void 0) return { error: "richMedia 服务为 null" };
311
+ return callDiagnosticMethod(svc, params);
312
+ });
313
+ actions.set("diag.msgServiceCall", async (params) => {
314
+ const sess = ctx.session;
315
+ if (sess === void 0) return { error: "session 未注入" };
316
+ const svc = typeof sess.getMsgService === "function" ? sess.getMsgService() : null;
317
+ if (svc === null || svc === void 0) return { error: "msgService 为 null" };
318
+ return callDiagnosticMethod(svc, params);
319
+ });
320
+ actions.set("diag.utilCall", async (params) => {
321
+ const util = ctx.util;
322
+ if (util === void 0) return { error: "util 未注入" };
323
+ const instance = typeof util.get === "function" ? util.get() : util;
324
+ if (instance === null || instance === void 0) return { error: "util 实例为 null" };
325
+ return callDiagnosticMethod(instance, params);
326
+ });
290
327
  return actions;
291
328
  }
329
+ /** 诊断:按方法名 + 参数数组调用对象任意方法(返回结构化结果,不抛)。
330
+ * ⚠️ NAPI 对象方法挂在实例自身(非原型),__methods 须枚举实例 ownKeys。 */
331
+ async function callDiagnosticMethod(obj, params) {
332
+ const method = typeof params["method"] === "string" ? params["method"] : "";
333
+ const args = Array.isArray(params["args"]) ? params["args"] : [];
334
+ const target = obj;
335
+ if (method === "__methods") {
336
+ const own = Object.getOwnPropertyNames(obj);
337
+ const proto = Object.getOwnPropertyNames(Object.getPrototypeOf(obj) ?? {});
338
+ return { methods: [.../* @__PURE__ */ new Set([...own, ...proto])] };
339
+ }
340
+ const fn = target[method];
341
+ if (typeof fn !== "function") return { error: `方法不存在: ${method}` };
342
+ try {
343
+ return {
344
+ ok: true,
345
+ ret: await fn.apply(obj, args)
346
+ };
347
+ } catch (e) {
348
+ return {
349
+ ok: false,
350
+ err: e instanceof Error ? e.message : String(e)
351
+ };
352
+ }
353
+ }
354
+ /** 诊断:枚举富媒体临时路径(getPicTmpPath 等 0 参方法)。 */
355
+ async function enumerateTmpPaths(svc) {
356
+ const tmp = {};
357
+ for (const name of [
358
+ "getPicTmpPath",
359
+ "getRichMeidaTmpPath",
360
+ "getPttTmpPath",
361
+ "getVideoTmpPath",
362
+ "getFileTmpPath",
363
+ "getTransferingTmpPath"
364
+ ]) {
365
+ const fn = svc[name];
366
+ if (typeof fn === "function") try {
367
+ tmp[name] = await fn.call(svc);
368
+ } catch (e) {
369
+ tmp[name] = `调用失败: ${e instanceof Error ? e.message : String(e)}`;
370
+ }
371
+ else tmp[name] = "方法缺失";
372
+ }
373
+ return tmp;
374
+ }
375
+ /** 诊断:闪传服务候选 side-effect 触发(§5.3 候选动作 1,参数宽松猜测)。 */
376
+ async function probeFlashSideEffects(flash) {
377
+ const flashObj = flash;
378
+ const sideEffects = {};
379
+ const candidates = [
380
+ ["addFileSetSimpleStatusListener", [() => void 0]],
381
+ ["setFlashTransferDir", [""]],
382
+ ["setFileSetDownloadDir", [""]],
383
+ ["getFileSetIdByCode", [""]]
384
+ ];
385
+ for (const [name, args] of candidates) {
386
+ const fn = flashObj[name];
387
+ if (typeof fn === "function") try {
388
+ sideEffects[name] = {
389
+ ok: true,
390
+ ret: await fn.apply(flash, args)
391
+ };
392
+ } catch (e) {
393
+ sideEffects[name] = {
394
+ ok: false,
395
+ err: e instanceof Error ? e.message : String(e)
396
+ };
397
+ }
398
+ else sideEffects[name] = { missing: true };
399
+ }
400
+ return sideEffects;
401
+ }
402
+ /** 诊断主流程:session → 富媒体服务 + 闪传服务的方法面与触发结果。 */
403
+ async function runRichMediaDiag(session) {
404
+ const sess = session;
405
+ if (sess === void 0) return { error: "session 未注入(IPC 上下文缺 session)" };
406
+ const out = {};
407
+ const svc = typeof sess.getRichMediaService === "function" ? sess.getRichMediaService() : null;
408
+ if (svc !== null && svc !== void 0) {
409
+ out["richMediaTmpPaths"] = await enumerateTmpPaths(svc);
410
+ out["richMediaMethods"] = Object.getOwnPropertyNames(Object.getPrototypeOf(svc));
411
+ } else out["richMediaService"] = "null";
412
+ const flash = typeof sess.getFlashTransferService === "function" ? sess.getFlashTransferService() : null;
413
+ if (flash !== null && flash !== void 0) {
414
+ out["flashMethods"] = Object.getOwnPropertyNames(Object.getPrototypeOf(flash));
415
+ out["flashSideEffects"] = await probeFlashSideEffects(flash);
416
+ } else out["flashService"] = "null";
417
+ return out;
418
+ }
292
419
  /** 统一动作调用:查找动作表 → 执行 → 返回结果或错误(不抛,调用方转 result)。 */
293
420
  async function callIpcAction(actions, action, params) {
294
421
  const handler = actions.get(action);
@@ -510,6 +637,9 @@ function startIpcMode(services) {
510
637
  groupCache: services.groupCache,
511
638
  friendApi: services.friendApi,
512
639
  self: services.self,
640
+ session: services.session,
641
+ engine: services.engine,
642
+ util: services.util,
513
643
  ...groupApi.uinToUid !== void 0 ? { uinToUid: groupApi.uinToUid } : {}
514
644
  }) });
515
645
  const stops = [];
@@ -612,6 +742,10 @@ function loadProtocolSections(kernel, uin) {
612
742
  * assemble-protocols.ts:OB11 / Satori 协议装配(非 IPC 模式,cli pnpm start)。
613
743
  * 2026-08-08:从 protocols.ts 拆分——protocols.ts 只留入口 + IPC 分支。
614
744
  */
745
+ /** Windows 反斜杠(file:// URL 化时替换)。 */
746
+ const BACKSLASH_RE = /\\/g;
747
+ /** adapter 入口 index.mjs 后缀(子路径导出替换用)。 */
748
+ const INDEX_MJS_RE = /index\.mjs$/;
615
749
  /** 装配 OB11 + Satori 适配器(登录成功后,非 IPC 模式)。 */
616
750
  async function assembleOb11AndSatori(kernel, services, loginResult) {
617
751
  const adapterEntry = env.NAPUTO_ADAPTER_ENTRY;
@@ -621,13 +755,13 @@ async function assembleOb11AndSatori(kernel, services, loginResult) {
621
755
  return;
622
756
  }
623
757
  try {
624
- const network = await import(`file://${networkEntry.replace(/\\/g, "/")}`);
625
- const onebot11Entry = adapterEntry.replace(/index\.mjs$/, "onebot11/index.mjs");
626
- const satoriEntry = adapterEntry.replace(/index\.mjs$/, "satori/index.mjs");
627
- const coreEntry = adapterEntry.replace(/index\.mjs$/, "core/index.mjs");
628
- const adapter = await import(`file://${onebot11Entry.replace(/\\/g, "/")}`);
629
- const satoriAdapter = await import(`file://${satoriEntry.replace(/\\/g, "/")}`);
630
- const adapterCore = await import(`file://${coreEntry.replace(/\\/g, "/")}`);
758
+ const network = await import(`file://${networkEntry.replace(BACKSLASH_RE, "/")}`);
759
+ const onebot11Entry = adapterEntry.replace(INDEX_MJS_RE, "onebot11/index.mjs");
760
+ const satoriEntry = adapterEntry.replace(INDEX_MJS_RE, "satori/index.mjs");
761
+ const coreEntry = adapterEntry.replace(INDEX_MJS_RE, "core/index.mjs");
762
+ const adapter = await import(`file://${onebot11Entry.replace(BACKSLASH_RE, "/")}`);
763
+ const satoriAdapter = await import(`file://${satoriEntry.replace(BACKSLASH_RE, "/")}`);
764
+ const adapterCore = await import(`file://${coreEntry.replace(BACKSLASH_RE, "/")}`);
631
765
  const { channel, groupCache } = services;
632
766
  const broadcaster = new network.EventBroadcaster();
633
767
  const { cfgFile, ob11Section, satoriSection } = loadProtocolSections(kernel, loginResult.uin);
@@ -772,7 +906,8 @@ async function createKernelServices(kernel, ctx, loginResult) {
772
906
  new kernel.MsgBridge(session, channel).register();
773
907
  setupMsgLogging(kernel, channel, logger);
774
908
  const groupApi = new kernel.GroupApi(session);
775
- const msgApi = new kernel.MsgApi(session);
909
+ const util = ctx.exports?.NodeQQNTWrapperUtil;
910
+ const msgApi = new kernel.MsgApi(session, channel, util);
776
911
  const friendApi = new kernel.FriendApi(session, { uidToUin: (uids) => groupApi.uidToUin(uids) });
777
912
  const groupChannel = new kernel.NTEventChannel("Group");
778
913
  new kernel.GroupBridge(session, groupChannel).register();
@@ -802,7 +937,10 @@ async function createKernelServices(kernel, ctx, loginResult) {
802
937
  self: {
803
938
  uin: loginResult.uin,
804
939
  nickname: loginResult.nick ?? ""
805
- }
940
+ },
941
+ session,
942
+ engine: ctx.engine,
943
+ util: ctx.exports?.NodeQQNTWrapperUtil
806
944
  };
807
945
  }
808
946
  //#endregion
@@ -855,32 +993,32 @@ function isSessionUsable(s) {
855
993
  return false;
856
994
  }
857
995
  }
996
+ /** 描述单个 session 对象(探测日志用;无效对象返回带断言信息的占位)。 */
997
+ function describeSession(s) {
998
+ if (!s) return "null";
999
+ try {
1000
+ const sess = s;
1001
+ const id = typeof sess.getSessionId === "function" ? String(sess.getSessionId()) : "?";
1002
+ const svc = typeof sess.getMsgService === "function" ? sess.getMsgService() : null;
1003
+ return `id=${id} msgSvc=${svc !== null && svc !== void 0 ? "READY" : "null"}`;
1004
+ } catch (e) {
1005
+ return `id=? msgSvc=断言(${errMsg(e).slice(0, 60)})`;
1006
+ }
1007
+ }
1008
+ /** 非空候选才追加探测描述。 */
1009
+ function pushProbe(out, label, candidate) {
1010
+ if (candidate) out.push(`${label}[${describeSession(candidate)}]`);
1011
+ }
858
1012
  /** session 就绪探测(5s 间隔,60s 上限)——观察 qqSession / get() 状态。 */
859
1013
  function startSessionProbe(state, _ctx, durationMs = 6e4) {
860
1014
  const sessionProbe = setInterval(() => {
861
1015
  try {
862
1016
  const S2 = state.wrapperExports?.["NodeIQQNTWrapperSession"];
863
1017
  const out = [];
864
- const describe = (s) => {
865
- if (!s) return "null";
866
- try {
867
- const sess = s;
868
- const id = typeof sess.getSessionId === "function" ? String(sess.getSessionId()) : "?";
869
- const svc = typeof sess.getMsgService === "function" ? sess.getMsgService() : null;
870
- return `id=${id} msgSvc=${svc !== null && svc !== void 0 ? "READY" : "null"}`;
871
- } catch (e) {
872
- return `id=? msgSvc=断言(${errMsg(e).slice(0, 60)})`;
873
- }
874
- };
875
- if (state.qqSession && typeof state.qqSession.getMsgService === "function") out.push(`qqSession[${describe(state.qqSession)}]`);
876
- if (S2 && typeof S2.get === "function") {
877
- const got = S2.get();
878
- out.push(`get()[${describe(got)}]`);
879
- }
880
- if (S2 && typeof S2.getNTWrapperSession === "function") {
881
- const gotNT = S2.getNTWrapperSession("Session");
882
- if (gotNT) out.push(`getNT("Session")[${describe(gotNT)}]`);
883
- }
1018
+ const qqSession = state.qqSession;
1019
+ if (qqSession && typeof qqSession.getMsgService === "function") pushProbe(out, "qqSession", qqSession);
1020
+ if (S2 && typeof S2.get === "function") pushProbe(out, "get()", S2.get());
1021
+ if (S2 && typeof S2.getNTWrapperSession === "function") pushProbe(out, "getNT(\"Session\")", S2.getNTWrapperSession("Session"));
884
1022
  log(`BOOT: session 探测: ${out.join(" | ")}`);
885
1023
  } catch (e) {
886
1024
  log(`BOOT: session 探测失败: ${errMsg(e)}`);
@@ -1129,11 +1267,13 @@ function buildLoginOpts(Appid, forcedUin, ref) {
1129
1267
  };
1130
1268
  const ipcMode = env.NAPUTO_IPC === "1";
1131
1269
  opts["onLoginProgress"] = (progress) => {
1132
- if (progress.qr !== void 0) if (ipcMode) sendQr(progress.qr.pngBase64, progress.qr.qrcodeUrl);
1133
- else node_process.default.stdout.write(`${QR_LINE_PREFIX}${JSON.stringify({
1134
- pngBase64: progress.qr.pngBase64,
1135
- qrcodeUrl: progress.qr.qrcodeUrl
1136
- })}\n`);
1270
+ if (progress.qr !== void 0) {
1271
+ if (ipcMode) sendQr(progress.qr.pngBase64, progress.qr.qrcodeUrl);
1272
+ else node_process.default.stdout.write(`${QR_LINE_PREFIX}${JSON.stringify({
1273
+ pngBase64: progress.qr.pngBase64,
1274
+ qrcodeUrl: progress.qr.qrcodeUrl
1275
+ })}\n`);
1276
+ }
1137
1277
  if (ipcMode && isLoginState(progress.state)) sendLogin(progress.state, progress.selfInfo);
1138
1278
  };
1139
1279
  return opts;
@@ -1284,6 +1424,43 @@ async function bootstrapWithCore(kernel, state, bootEnv, Appid) {
1284
1424
  function resolveAppid(kernel, bootEnv) {
1285
1425
  return typeof kernel.parseAppidFromMajor === "function" && bootEnv.wrapperPath && kernel.parseAppidFromMajor((0, node_path.join)((0, node_path.dirname)(bootEnv.wrapperPath), "major.node")) || typeof kernel.resolveAppidQua === "function" && kernel.resolveAppidQua(bootEnv.qqVersion || "").appid || "537237765";
1286
1426
  }
1427
+ /** 快速登录 + session 初始化(kernel lifecycle 路径;生命周期方法缺失则跳过)。 */
1428
+ async function quickLoginAndStartSession(kernel, ctx, Appid, bootEnv) {
1429
+ if (typeof kernel.quickLogin !== "function" || typeof kernel.initAndStartSession !== "function") {
1430
+ log("bootstrap: kernel missing lifecycle fns (quickLogin/initAndStartSession)");
1431
+ return;
1432
+ }
1433
+ if (typeof kernel.buildLoginConfig === "function" && ctx.loginService) {
1434
+ const loginCfg = kernel.buildLoginConfig(Appid, bootEnv.qqVersion || "", bootEnv.dataDir || ".");
1435
+ if (typeof ctx.loginService.initConfig === "function") {
1436
+ ctx.loginService.initConfig(loginCfg);
1437
+ log("bootstrap: loginService.initConfig OK");
1438
+ }
1439
+ }
1440
+ const loginResult = await kernel.quickLogin(ctx, {});
1441
+ log(`bootstrap: quickLogin OK, uin=${loginResult.uin}, uid=${loginResult.uid}`);
1442
+ const sessionConfig = kernel.buildSessionConfig({
1443
+ appid: Appid,
1444
+ fullVersion: bootEnv.qqVersion || "",
1445
+ selfUin: loginResult.uin,
1446
+ selfUid: loginResult.uid,
1447
+ accountPath: bootEnv.dataDir || ".",
1448
+ downloadPath: (0, node_path.join)(bootEnv.dataDir || ".", "temp")
1449
+ });
1450
+ const listener = kernel.createLifecycleSessionListener();
1451
+ await kernel.initAndStartSession(ctx, sessionConfig, listener, { timeoutMs: 2e4 });
1452
+ log("bootstrap: session init + startNT OK!");
1453
+ }
1454
+ /** 冒烟探测(NAPUTO_PROBE=1 时运行)。 */
1455
+ async function probeRuntime(kernel, ctx) {
1456
+ if (env.NAPUTO_PROBE !== "1" || typeof kernel.probeRuntime !== "function") return;
1457
+ try {
1458
+ const probe = kernel.probeRuntime(ctx);
1459
+ log(`bootstrap: probe done, session=${probe.session ? "ok" : "null"}, services=${Object.keys(probe.services ?? {}).length}`);
1460
+ } catch (e) {
1461
+ log(`bootstrap: probe error: ${errMsg(e)}`);
1462
+ }
1463
+ }
1287
1464
  /** 回退:旧装配路径(startNapuketto + 手工 lifecycle)。 */
1288
1465
  async function bootstrapFallback(kernel, state, bootEnv, Appid) {
1289
1466
  log("bootstrap: kernel has no NapukettoCore, falling back");
@@ -1296,34 +1473,8 @@ async function bootstrapFallback(kernel, state, bootEnv, Appid) {
1296
1473
  return;
1297
1474
  }
1298
1475
  log(`bootstrap: startNapuketto OK, engine=${typeof ctx.engine}, session=${ctx.session !== null}`);
1299
- if (typeof kernel.quickLogin === "function" && typeof kernel.initAndStartSession === "function") {
1300
- if (typeof kernel.buildLoginConfig === "function" && ctx.loginService) {
1301
- const loginCfg = kernel.buildLoginConfig(Appid, bootEnv.qqVersion || "", bootEnv.dataDir || ".");
1302
- if (typeof ctx.loginService.initConfig === "function") {
1303
- ctx.loginService.initConfig(loginCfg);
1304
- log("bootstrap: loginService.initConfig OK");
1305
- }
1306
- }
1307
- const loginResult = await kernel.quickLogin(ctx, {});
1308
- log(`bootstrap: quickLogin OK, uin=${loginResult.uin}, uid=${loginResult.uid}`);
1309
- const sessionConfig = kernel.buildSessionConfig({
1310
- appid: Appid,
1311
- fullVersion: bootEnv.qqVersion || "",
1312
- selfUin: loginResult.uin,
1313
- selfUid: loginResult.uid,
1314
- accountPath: bootEnv.dataDir || ".",
1315
- downloadPath: (0, node_path.join)(bootEnv.dataDir || ".", "temp")
1316
- });
1317
- const listener = kernel.createLifecycleSessionListener();
1318
- await kernel.initAndStartSession(ctx, sessionConfig, listener, { timeoutMs: 2e4 });
1319
- log("bootstrap: session init + startNT OK!");
1320
- } else log("bootstrap: kernel missing lifecycle fns (quickLogin/initAndStartSession)");
1321
- if (env.NAPUTO_PROBE === "1" && typeof kernel.probeRuntime === "function") try {
1322
- const probe = kernel.probeRuntime(ctx);
1323
- log(`bootstrap: probe done, session=${probe.session ? "ok" : "null"}, services=${Object.keys(probe.services ?? {}).length}`);
1324
- } catch (e) {
1325
- log(`bootstrap: probe error: ${errMsg(e)}`);
1326
- }
1476
+ await quickLoginAndStartSession(kernel, ctx, Appid, bootEnv);
1477
+ await probeRuntime(kernel, ctx);
1327
1478
  }
1328
1479
  /** 核心引导:import kernel → 装配 → 登录 → 替换 session → 等待就绪 → 协议装配。 */
1329
1480
  async function bootstrap(state) {