@napuketto/kernel 0.0.9 → 0.0.12
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/index.cjs +165 -53
- package/dist/index.d.cts +54 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +54 -13
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +166 -55
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { parse, stringify } from "smol-toml";
|
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import pino from "pino";
|
|
7
7
|
import pinoPretty from "pino-pretty";
|
|
8
|
-
import { homedir, hostname } from "node:os";
|
|
8
|
+
import { homedir, hostname, platform, release, type, version } from "node:os";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
11
|
import { get } from "node:https";
|
|
@@ -1315,9 +1315,9 @@ var MsgApi = class {
|
|
|
1315
1315
|
* 且发送后进程崩溃重启(supervisor 自动拉起)。完整字段(NapCat 同款,
|
|
1316
1316
|
* formatType/voiceType/canConvert2Text/waveAmplitudes 等)消除该错误。
|
|
1317
1317
|
*
|
|
1318
|
-
* ⚠️ silk 格式:QQ 语音协议为 silk v3
|
|
1319
|
-
*
|
|
1320
|
-
*
|
|
1318
|
+
* ⚠️ silk 格式:QQ 语音协议为 silk v3。非 silk → silk 的转码在协议层
|
|
1319
|
+
* (adapter,@napuketto/media 的 encodePcmToSilk,2026-08-12 接线)完成,
|
|
1320
|
+
* 此处接收的已是 silk 路径,仅做放置。kernel 不 import media(解耦红线)。
|
|
1321
1321
|
*/
|
|
1322
1322
|
async preparePttElement(path) {
|
|
1323
1323
|
const file = await statFile(path);
|
|
@@ -2590,6 +2590,8 @@ function startupSessionStart(ctx) {
|
|
|
2590
2590
|
/** 二维码过期错误码(errType=1 errCode=3,实测)。 */
|
|
2591
2591
|
const QR_EXPIRED_ERR_TYPE = 1;
|
|
2592
2592
|
const QR_EXPIRED_ERR_CODE = 3;
|
|
2593
|
+
/** 登录超时提示(参考项目文案,前端 failed 态展示)。 */
|
|
2594
|
+
const QR_LOGIN_TIMEOUT_MESSAGE = "登录超时,请刷新页面重试";
|
|
2593
2595
|
/** 二维码过期判定。 */
|
|
2594
2596
|
function isQrCodeExpired(errType, errCode) {
|
|
2595
2597
|
return errType === QR_EXPIRED_ERR_TYPE && errCode === QR_EXPIRED_ERR_CODE;
|
|
@@ -2604,10 +2606,17 @@ var QrLoginSession = class {
|
|
|
2604
2606
|
self = null;
|
|
2605
2607
|
qrCodeHandlers = /* @__PURE__ */ new Set();
|
|
2606
2608
|
stateHandlers = /* @__PURE__ */ new Set();
|
|
2607
|
-
|
|
2609
|
+
/** 超时定时器(refresh 后启动,登录成功/失败/stop 清理)。 */
|
|
2610
|
+
timeoutTimer = null;
|
|
2611
|
+
/** 失败原因(超时等,failed 态透出给上层提示文案)。 */
|
|
2612
|
+
failureReasonValue = null;
|
|
2613
|
+
/** 超时上限(毫秒,默认 QR_LOGIN_TIMEOUT_MS)。 */
|
|
2614
|
+
timeoutMs;
|
|
2615
|
+
constructor(loginService, options = {}) {
|
|
2608
2616
|
const svc = loginService;
|
|
2609
2617
|
if (svc === null || typeof svc.addKernelLoginListener !== "function") throw kernelError("loginService 无效(缺 addKernelLoginListener)", "INVALID_STATE");
|
|
2610
2618
|
this.loginService = svc;
|
|
2619
|
+
this.timeoutMs = options.timeoutMs ?? 12e4;
|
|
2611
2620
|
}
|
|
2612
2621
|
/** 当前状态。 */
|
|
2613
2622
|
get currentState() {
|
|
@@ -2617,6 +2626,10 @@ var QrLoginSession = class {
|
|
|
2617
2626
|
get selfInfo() {
|
|
2618
2627
|
return this.self;
|
|
2619
2628
|
}
|
|
2629
|
+
/** 失败原因(超时等;failed 态透出给上层提示文案)。 */
|
|
2630
|
+
get failureReason() {
|
|
2631
|
+
return this.failureReasonValue;
|
|
2632
|
+
}
|
|
2620
2633
|
/** 订阅二维码(png base64 + url)。返回退订函数。 */
|
|
2621
2634
|
onQrCode(cb) {
|
|
2622
2635
|
this.qrCodeHandlers.add(cb);
|
|
@@ -2639,10 +2652,12 @@ var QrLoginSession = class {
|
|
|
2639
2652
|
});
|
|
2640
2653
|
else this.refresh();
|
|
2641
2654
|
}
|
|
2642
|
-
/** 刷新二维码(手动 /
|
|
2655
|
+
/** 刷新二维码(手动 / 过期自动)。每次出码重启超时计时。 */
|
|
2643
2656
|
refresh() {
|
|
2657
|
+
this.failureReasonValue = null;
|
|
2644
2658
|
this.setState("waiting_scan");
|
|
2645
2659
|
this.loginService.getQRCodePicture();
|
|
2660
|
+
this.restartTimeout();
|
|
2646
2661
|
}
|
|
2647
2662
|
/** 构建登录监听(普通 JS 对象,NAPI 反射)。 */
|
|
2648
2663
|
buildListener() {
|
|
@@ -2661,6 +2676,7 @@ var QrLoginSession = class {
|
|
|
2661
2676
|
this.setState("scanned");
|
|
2662
2677
|
};
|
|
2663
2678
|
listener.onQRCodeLoginSucceed = (result) => {
|
|
2679
|
+
this.clearTimeoutTimer();
|
|
2664
2680
|
this.self = {
|
|
2665
2681
|
uin: result.uin,
|
|
2666
2682
|
uid: result.uid,
|
|
@@ -2673,20 +2689,41 @@ var QrLoginSession = class {
|
|
|
2673
2689
|
this.refresh();
|
|
2674
2690
|
return;
|
|
2675
2691
|
}
|
|
2692
|
+
this.clearTimeoutTimer();
|
|
2693
|
+
this.failureReasonValue = null;
|
|
2676
2694
|
this.setState("failed");
|
|
2677
2695
|
};
|
|
2678
2696
|
listener.onLoginFailed = () => {
|
|
2697
|
+
this.clearTimeoutTimer();
|
|
2698
|
+
this.failureReasonValue = null;
|
|
2679
2699
|
this.setState("failed");
|
|
2680
2700
|
};
|
|
2681
2701
|
return listener;
|
|
2682
2702
|
}
|
|
2683
2703
|
/** 停止:注销监听(登录成功后调用,防重复回调)。 */
|
|
2684
2704
|
stop() {
|
|
2705
|
+
this.clearTimeoutTimer();
|
|
2685
2706
|
if (this.listenerId !== null) {
|
|
2686
2707
|
this.loginService.removeKernelLoginListener(this.listenerId);
|
|
2687
2708
|
this.listenerId = null;
|
|
2688
2709
|
}
|
|
2689
2710
|
}
|
|
2711
|
+
/** 重启超时计时器:超时未登录 → failed + 超时提示。 */
|
|
2712
|
+
restartTimeout() {
|
|
2713
|
+
this.clearTimeoutTimer();
|
|
2714
|
+
this.timeoutTimer = setTimeout(() => {
|
|
2715
|
+
this.timeoutTimer = null;
|
|
2716
|
+
this.failureReasonValue = QR_LOGIN_TIMEOUT_MESSAGE;
|
|
2717
|
+
this.setState("failed");
|
|
2718
|
+
}, this.timeoutMs);
|
|
2719
|
+
}
|
|
2720
|
+
/** 清理超时计时器(幂等)。 */
|
|
2721
|
+
clearTimeoutTimer() {
|
|
2722
|
+
if (this.timeoutTimer !== null) {
|
|
2723
|
+
clearTimeout(this.timeoutTimer);
|
|
2724
|
+
this.timeoutTimer = null;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2690
2727
|
setState(next) {
|
|
2691
2728
|
this.state = next;
|
|
2692
2729
|
for (const cb of this.stateHandlers) cb(next);
|
|
@@ -2768,13 +2805,59 @@ function findInObject(value) {
|
|
|
2768
2805
|
*
|
|
2769
2806
|
* 与 lifecycle.ts(流程编排)解耦:本模块只回答「配置长什么样」,不管流程怎么走。
|
|
2770
2807
|
* 字段为 wrapper.node 外部契约(appid/qua/版本),运行时实测确认,自研描述。
|
|
2808
|
+
* appid 唯一来源是 major.node 动态解析(每版本不同);解析失败显式抛 KernelError,
|
|
2809
|
+
* 不再静默回退过期 appid(2026-08 硬编码审计:537237765 是 9.9.31 的 appid,该版本
|
|
2810
|
+
* 登录服务已被腾讯下线,静默用它会导致扫码「请下载最新版」且极难排查)。
|
|
2811
|
+
*/
|
|
2812
|
+
/** app_type=4:桌面端应用类型(engine.initWithDeskTopConfig 契约;移动端为其它值)。 */
|
|
2813
|
+
const APP_TYPE_DESKTOP = 4;
|
|
2814
|
+
/**
|
|
2815
|
+
* 缩略图生成参数(thumb_config,engine 契约):
|
|
2816
|
+
* maxSide 最长边像素上限 / minSide 最短边像素下限 / longLimit 长图最长短边比例上限 /
|
|
2817
|
+
* density 像素密度倍数。
|
|
2818
|
+
*/
|
|
2819
|
+
const THUMB_CONFIG = {
|
|
2820
|
+
maxSide: 324,
|
|
2821
|
+
minSide: 48,
|
|
2822
|
+
longLimit: 6,
|
|
2823
|
+
density: 2
|
|
2824
|
+
};
|
|
2825
|
+
/**
|
|
2826
|
+
* session.init 的 deviceConfig(wrapper 契约,JSON 字符串字面量):
|
|
2827
|
+
* appearance.isSplitViewMode 分屏模式开关,msg 空对象为占位。自建宿主无 UI,保持最小可用。
|
|
2828
|
+
*/
|
|
2829
|
+
const DEVICE_CONFIG_JSON = "{\"appearance\":{\"isSplitViewMode\":true},\"msg\":{}}";
|
|
2830
|
+
/** localId=2052:zh-CN(简体中文)locale 码(Windows LCID,deviceInfo.localId 契约)。 */
|
|
2831
|
+
const ZH_CN_LOCALE_ID = 2052;
|
|
2832
|
+
/** 平台名映射:os.platform() → wrapper devType 契约值(不写死具体构建号)。 */
|
|
2833
|
+
const DEV_TYPE_BY_PLATFORM = {
|
|
2834
|
+
win32: "Windows",
|
|
2835
|
+
darwin: "Mac",
|
|
2836
|
+
linux: "Linux",
|
|
2837
|
+
android: "Android",
|
|
2838
|
+
freebsd: "FreeBSD",
|
|
2839
|
+
openbsd: "OpenBSD",
|
|
2840
|
+
sunos: "SunOS",
|
|
2841
|
+
aix: "AIX"
|
|
2842
|
+
};
|
|
2843
|
+
/**
|
|
2844
|
+
* 系统信息运行时探测(wrapper 契约:platVer / osVersion / devType)。
|
|
2845
|
+
* 旧实现写死 "Windows 10.0.22631"/"Windows 10 Pro"——换机即失真(2026-08 硬编码审计整改)。
|
|
2846
|
+
* 探测失败时给通用值兜底,但不写死具体 Windows 构建号。
|
|
2771
2847
|
*/
|
|
2772
|
-
/** 系统信息(先用 fixed 值,真实环境探测后补)。 */
|
|
2773
2848
|
function systemInfo() {
|
|
2849
|
+
let devType = "Unknown";
|
|
2850
|
+
let platVer = "Unknown";
|
|
2851
|
+
let osVersion = "Unknown";
|
|
2852
|
+
try {
|
|
2853
|
+
devType = DEV_TYPE_BY_PLATFORM[platform()] ?? platform();
|
|
2854
|
+
platVer = `${devType} ${release()}`;
|
|
2855
|
+
osVersion = version() || type();
|
|
2856
|
+
} catch {}
|
|
2774
2857
|
return {
|
|
2775
|
-
platVer
|
|
2776
|
-
osVersion
|
|
2777
|
-
devType
|
|
2858
|
+
platVer,
|
|
2859
|
+
osVersion,
|
|
2860
|
+
devType
|
|
2778
2861
|
};
|
|
2779
2862
|
}
|
|
2780
2863
|
/** 纯数字串(major.node 提取的 appid 判定)。 */
|
|
@@ -2782,7 +2865,7 @@ const DIGITS_ONLY_RE = /^\d+$/;
|
|
|
2782
2865
|
/**
|
|
2783
2866
|
* 从 major.node 提取 appid(NapCat parseAppidFromMajorV2 的自研等价实现)。
|
|
2784
2867
|
* major.node 含 `QQAppId/` 标记后跟数字(腾讯工具链产物,实测确认)。
|
|
2785
|
-
* 返回 null
|
|
2868
|
+
* 返回 null 表示解析失败(调用方应显式报错,不再回退硬编码 appid)。
|
|
2786
2869
|
*/
|
|
2787
2870
|
function parseAppidFromMajor(majorPath) {
|
|
2788
2871
|
if (!existsSync(majorPath)) return null;
|
|
@@ -2813,48 +2896,34 @@ function readMarkerValue(buf, start) {
|
|
|
2813
2896
|
while (end < buf.length && buf[end] !== 0) end += 1;
|
|
2814
2897
|
return buf.subarray(start, end).toString("utf-8");
|
|
2815
2898
|
}
|
|
2816
|
-
/** Windows 兜底 appid / qua(major.node 解析失败时)。 */
|
|
2817
|
-
function fallbackAppidQua(fullVersion) {
|
|
2818
|
-
return {
|
|
2819
|
-
appid: "537237765",
|
|
2820
|
-
qua: `V1_WIN_NQ_${fullVersion}_${fullVersion.split("-")[1] ?? ""}_GW_B`
|
|
2821
|
-
};
|
|
2822
|
-
}
|
|
2823
2899
|
/**
|
|
2824
2900
|
* 解析 appid / qua(自研,参考 NapCat QQBasicInfoWrapper 思路但独立实现):
|
|
2825
|
-
*
|
|
2826
|
-
*
|
|
2827
|
-
* majorPath 传 wrapper.node 同目录 major.node
|
|
2901
|
+
* appid 唯一来源 = major.node 提取(实测 9.9.33-51802 = 537376818)。
|
|
2902
|
+
* majorPath 缺省或解析失败时抛 KernelError——不再静默回退过期 appid。
|
|
2903
|
+
* majorPath 传 wrapper.node 同目录 major.node。
|
|
2828
2904
|
*/
|
|
2829
2905
|
function resolveAppidQua(fullVersion, majorPath) {
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
}
|
|
2837
|
-
return fallbackAppidQua(fullVersion);
|
|
2906
|
+
const appid = majorPath !== void 0 ? parseAppidFromMajor(majorPath) : null;
|
|
2907
|
+
if (appid === null) throw kernelError("无法从 major.node 解析 appid,请确认 wrapper.node/major.node 完整,或更新 qq-releases.json", "INVALID_STATE");
|
|
2908
|
+
return {
|
|
2909
|
+
appid,
|
|
2910
|
+
qua: `V1_WIN_NQ_${fullVersion}_${fullVersion.split("-")[1] ?? ""}_GW_B`
|
|
2911
|
+
};
|
|
2838
2912
|
}
|
|
2839
|
-
/** 生成 engine 桌面配置(wrapper 契约字段)。majorPath
|
|
2913
|
+
/** 生成 engine 桌面配置(wrapper 契约字段)。majorPath 可选(缺失/解析失败会抛错)。 */
|
|
2840
2914
|
function buildEngineConfig(fullVersion, dataPathGlobal, majorPath) {
|
|
2841
2915
|
const { osVersion } = systemInfo();
|
|
2842
2916
|
const { qua } = resolveAppidQua(fullVersion, majorPath);
|
|
2843
2917
|
return {
|
|
2844
2918
|
base_path_prefix: "",
|
|
2845
2919
|
platform_type: PlatformType.KWINDOWS,
|
|
2846
|
-
app_type:
|
|
2920
|
+
app_type: APP_TYPE_DESKTOP,
|
|
2847
2921
|
app_version: fullVersion,
|
|
2848
2922
|
os_version: osVersion,
|
|
2849
2923
|
use_xlog: true,
|
|
2850
2924
|
qua,
|
|
2851
2925
|
global_path_config: { desktopGlobalPath: dataPathGlobal },
|
|
2852
|
-
thumb_config:
|
|
2853
|
-
maxSide: 324,
|
|
2854
|
-
minSide: 48,
|
|
2855
|
-
longLimit: 6,
|
|
2856
|
-
density: 2
|
|
2857
|
-
}
|
|
2926
|
+
thumb_config: THUMB_CONFIG
|
|
2858
2927
|
};
|
|
2859
2928
|
}
|
|
2860
2929
|
/** 生成登录初始化配置(externalVersion: false 与 NapCat 同款,扫码兼容关键)。 */
|
|
@@ -2872,12 +2941,12 @@ function buildLoginConfig(appid, fullVersion, commonPath) {
|
|
|
2872
2941
|
}
|
|
2873
2942
|
/** 生成 session 初始化配置(登录成功后调用)。 */
|
|
2874
2943
|
function buildSessionConfig(options) {
|
|
2875
|
-
const { appid, fullVersion, selfUin, selfUid, accountPath, downloadPath } = options;
|
|
2944
|
+
const { appid, fullVersion, selfUin, selfUid, accountPath, downloadPath, machineGuid } = options;
|
|
2876
2945
|
const { platVer, osVersion, devType } = systemInfo();
|
|
2877
2946
|
const deviceInfo = {
|
|
2878
|
-
guid: "",
|
|
2947
|
+
guid: machineGuid ?? "",
|
|
2879
2948
|
buildVer: fullVersion,
|
|
2880
|
-
localId:
|
|
2949
|
+
localId: ZH_CN_LOCALE_ID,
|
|
2881
2950
|
devName: hostname(),
|
|
2882
2951
|
devType,
|
|
2883
2952
|
vendorName: "",
|
|
@@ -2915,9 +2984,25 @@ function buildSessionConfig(options) {
|
|
|
2915
2984
|
},
|
|
2916
2985
|
defaultFileDownloadPath: downloadPath,
|
|
2917
2986
|
deviceInfo,
|
|
2918
|
-
deviceConfig:
|
|
2987
|
+
deviceConfig: DEVICE_CONFIG_JSON
|
|
2919
2988
|
};
|
|
2920
2989
|
}
|
|
2990
|
+
/**
|
|
2991
|
+
* 读取设备指纹 guid(wrapper.node 字符串分析实证,2026-08-12):
|
|
2992
|
+
* NodeIKernelLoginService::getMachineGuid(0 参,返回机器 GUID 字符串)。
|
|
2993
|
+
* 无 getMachineId 方法——TODO 里的方法名不存在,等价方法是 getMachineGuid。
|
|
2994
|
+
* 失败(无该方法 / 调用抛错 / 返回值非字符串)兜底空串。
|
|
2995
|
+
*/
|
|
2996
|
+
function readMachineGuid(loginService) {
|
|
2997
|
+
try {
|
|
2998
|
+
const fn = loginService?.["getMachineGuid"];
|
|
2999
|
+
if (typeof fn !== "function") return "";
|
|
3000
|
+
const value = fn.call(loginService);
|
|
3001
|
+
return typeof value === "string" ? value : "";
|
|
3002
|
+
} catch {
|
|
3003
|
+
return "";
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
2921
3006
|
//#endregion
|
|
2922
3007
|
//#region src/wrapper/wrapper-loader.ts
|
|
2923
3008
|
/**
|
|
@@ -3073,7 +3158,7 @@ function startSession(ctx) {
|
|
|
3073
3158
|
* 2. createSession(`new NodeIQQNTWrapperSession()`)——实测可用,默认路径
|
|
3074
3159
|
* (getMainSession 实测为空壳:service 全 null + 缺 startNT,仅 probe 探测参考)
|
|
3075
3160
|
*
|
|
3076
|
-
* 由
|
|
3161
|
+
* 由 kernel NapukettoCore(core.ts)装配调用;loader 自建宿主 bootstrap 也直接调用作回退。
|
|
3077
3162
|
* 返回 WrapperContext;失败抛 KernelError。
|
|
3078
3163
|
*/
|
|
3079
3164
|
function startNapuketto(options) {
|
|
@@ -3159,7 +3244,8 @@ function getOrCreateLoginService(exports) {
|
|
|
3159
3244
|
* 典型用法(boot.cjs 截获 exports 后):
|
|
3160
3245
|
* const core = await NapukettoCore.create({ dataRoot, account });
|
|
3161
3246
|
* core.attachWrapper(wrapperExports, { qqVersion, dataDir });
|
|
3162
|
-
*
|
|
3247
|
+
* // appid 由 resolveAppidQua 从 major.node 动态解析(版本绑定,不写死)
|
|
3248
|
+
* await core.login({ appid });
|
|
3163
3249
|
*
|
|
3164
3250
|
* 与手动拼 startNapuketto/quickLogin/initAndStartSession 等价,但把装配状态
|
|
3165
3251
|
* (ctx)集中管理,协议层直接消费 core.ctx。
|
|
@@ -3171,6 +3257,8 @@ function getOrCreateLoginService(exports) {
|
|
|
3171
3257
|
var NapukettoCore = class NapukettoCore {
|
|
3172
3258
|
/** 装配根(协议层 / apis / cache 的消费入口)。 */
|
|
3173
3259
|
ctx;
|
|
3260
|
+
/** QR 登录会话句柄(登录期间非空;登录终态后清空)。refreshQr() 据此可达。 */
|
|
3261
|
+
qrSession = null;
|
|
3174
3262
|
/** 由 create() 装配,不直接 new。 */
|
|
3175
3263
|
constructor(ctx) {
|
|
3176
3264
|
this.ctx = ctx;
|
|
@@ -3226,6 +3314,11 @@ var NapukettoCore = class NapukettoCore {
|
|
|
3226
3314
|
const { wrapper } = this.ctx;
|
|
3227
3315
|
if (wrapper === null) throw kernelError("wrapper 未装配,无法登录", "INVALID_STATE");
|
|
3228
3316
|
this.initLoginConfig(wrapper, opts.appid);
|
|
3317
|
+
if (opts.qrOnly === true) {
|
|
3318
|
+
const loginResult = await this.loginByQr(opts);
|
|
3319
|
+
this.ctx.login = loginResult;
|
|
3320
|
+
return loginResult;
|
|
3321
|
+
}
|
|
3229
3322
|
let loginResult;
|
|
3230
3323
|
try {
|
|
3231
3324
|
const quickOpts = {};
|
|
@@ -3269,23 +3362,37 @@ var NapukettoCore = class NapukettoCore {
|
|
|
3269
3362
|
if (this.ctx.wrapper === null) throw kernelError("wrapper 未装配,无法设置 session", "INVALID_STATE");
|
|
3270
3363
|
this.ctx.wrapper.session = session;
|
|
3271
3364
|
}
|
|
3365
|
+
/**
|
|
3366
|
+
* 手动刷新二维码(QR 登录期间可用)。返回是否成功触发(false = 当前不在扫码态)。
|
|
3367
|
+
* koishi 前端「刷新二维码」按钮经 loader IPC `login.refreshQr` 动作直达。
|
|
3368
|
+
*/
|
|
3369
|
+
refreshQr() {
|
|
3370
|
+
if (this.qrSession === null) return false;
|
|
3371
|
+
this.qrSession.refresh();
|
|
3372
|
+
return true;
|
|
3373
|
+
}
|
|
3272
3374
|
/** QR 登录:QrLoginSession 状态机 + 二维码写缓存目录(无 UI,cli 可打印路径)。 */
|
|
3273
3375
|
async loginByQr(opts) {
|
|
3274
3376
|
const { wrapper } = this.ctx;
|
|
3275
3377
|
if (wrapper === null || wrapper.loginService === null) throw kernelError("wrapper/loginService 不可用,无法 QR 登录", "INVALID_STATE");
|
|
3276
3378
|
const session = new QrLoginSession(wrapper.loginService);
|
|
3379
|
+
this.qrSession = session;
|
|
3277
3380
|
const qrPath = opts.qrCodePath ?? this.ctx.paths.file("cache", "qrcode.png");
|
|
3278
3381
|
this.subscribeQrProgress(session, opts, qrPath);
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3382
|
+
try {
|
|
3383
|
+
session.start();
|
|
3384
|
+
await waitQrLoggedIn(session);
|
|
3385
|
+
const self = session.selfInfo;
|
|
3386
|
+
if (self === null) throw kernelError("QR 登录成功但 selfInfo 为空", "INVALID_STATE");
|
|
3387
|
+
return {
|
|
3388
|
+
uin: self.uin,
|
|
3389
|
+
uid: self.uid,
|
|
3390
|
+
nick: self.nick
|
|
3391
|
+
};
|
|
3392
|
+
} finally {
|
|
3393
|
+
session.stop();
|
|
3394
|
+
this.qrSession = null;
|
|
3395
|
+
}
|
|
3289
3396
|
}
|
|
3290
3397
|
/** 订阅 QR 进度:二维码写盘 + 状态机经 onLoginProgress 转发。 */
|
|
3291
3398
|
subscribeQrProgress(session, opts, qrPath) {
|
|
@@ -3299,7 +3406,11 @@ var NapukettoCore = class NapukettoCore {
|
|
|
3299
3406
|
});
|
|
3300
3407
|
session.onStateChange((state) => {
|
|
3301
3408
|
this.ctx.logger.info({ state }, "QR 登录状态");
|
|
3302
|
-
|
|
3409
|
+
const message = state === "failed" ? session.failureReason ?? void 0 : void 0;
|
|
3410
|
+
opts.onLoginProgress?.({
|
|
3411
|
+
state,
|
|
3412
|
+
...message !== void 0 ? { message } : {}
|
|
3413
|
+
});
|
|
3303
3414
|
});
|
|
3304
3415
|
}
|
|
3305
3416
|
/** 停止:日志收尾(资源清理留给 P2 常驻管理)。 */
|
|
@@ -4023,6 +4134,6 @@ function listQQVersions(installDir) {
|
|
|
4023
4134
|
}).sort().reverse();
|
|
4024
4135
|
}
|
|
4025
4136
|
//#endregion
|
|
4026
|
-
export { ChatType, ConfigBase, DEFAULT_DATA_ROOT_NAME, ElementType, FriendApi, GrayTipSubType, GroupApi, GroupBridge, GroupCache, GroupListUpdateType, GroupNotifyApi, GroupNotifyMsgStatus, GroupNotifyMsgType, KERNEL_ERROR_CODES, KernelError, MAIN_CONFIG_FILE, MsgApi, MsgBridge, NTEventChannel, NTGroupMemberRole, NTGroupRequestOperateTypes, NapukettoCore, PathWrapper, ProfileApi, ProfileLikeApi, QrLoginSession, RichMediaApi, TicketApi, TipGroupElementType, WebApi, WebHonorType, buildEngineConfig, buildLoginConfig, buildSessionConfig, createCoreContext, createSessionListener as createLifecycleSessionListener, createSessionListener, createLogger, createLoginListener, createSession, createWrapper, electronProcessType, getExistingSession, getMainSession, initAndStartSession, initEngine, initSession, isKernelError, kernelError, listLoginAccounts, listQQVersions, parseAppidFromMajor, parseToml, probeRuntime, quickLogin, resolveAppidQua, resolveConfigPath, resolveDataRoot, resolveQQVersion, resolveQqGlobalPath, resolveQqUserDataRoot, resolveWrapperPath, startNapuketto, startSession, stringifyToml, toCanonicalElements, toSendElements, waitForNetworkConnection, waitSessionReady };
|
|
4137
|
+
export { ChatType, ConfigBase, DEFAULT_DATA_ROOT_NAME, ElementType, FriendApi, GrayTipSubType, GroupApi, GroupBridge, GroupCache, GroupListUpdateType, GroupNotifyApi, GroupNotifyMsgStatus, GroupNotifyMsgType, KERNEL_ERROR_CODES, KernelError, MAIN_CONFIG_FILE, MsgApi, MsgBridge, NTEventChannel, NTGroupMemberRole, NTGroupRequestOperateTypes, NapukettoCore, PathWrapper, ProfileApi, ProfileLikeApi, QrLoginSession, RichMediaApi, TicketApi, TipGroupElementType, WebApi, WebHonorType, buildEngineConfig, buildLoginConfig, buildSessionConfig, createCoreContext, createSessionListener as createLifecycleSessionListener, createSessionListener, createLogger, createLoginListener, createSession, createWrapper, electronProcessType, getExistingSession, getMainSession, initAndStartSession, initEngine, initSession, isKernelError, kernelError, listLoginAccounts, listQQVersions, parseAppidFromMajor, parseToml, probeRuntime, quickLogin, readMachineGuid, resolveAppidQua, resolveConfigPath, resolveDataRoot, resolveQQVersion, resolveQqGlobalPath, resolveQqUserDataRoot, resolveWrapperPath, startNapuketto, startSession, stringifyToml, toCanonicalElements, toSendElements, waitForNetworkConnection, waitSessionReady };
|
|
4027
4138
|
|
|
4028
4139
|
//# sourceMappingURL=index.mjs.map
|