@napuketto/loader 0.0.8 → 0.0.10

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.
@@ -0,0 +1,365 @@
1
+ import { StdioOptions, spawn } from "node:child_process";
2
+ //#region src/instance-lock.d.ts
3
+ /**
4
+ * instance-lock.ts:单实例锁(2026-08-07 根治「多实例抢数据目录锁挂起」)
5
+ *
6
+ * 背景(实测):同一账号数据目录(~/.napuketto/<uin>)只能被一个实例持有——
7
+ * QQ 原生层(MMKV / 登录单例 / 数据库)有锁,第二个实例抢不到锁会卡在
8
+ * loginService.initConfig 之后无响应(挂起)。此前 create-napukettoqq 生成的
9
+ * 副本实例常驻占锁,导致用户后续启动全部卡死。
10
+ *
11
+ * 方案:以数据目录为粒度加锁文件 `<dataDir>/instance.lock`(JSON 内容:
12
+ * pid / startedAt / cmdline 摘要)。启动前 checkInstanceLock:
13
+ * - 锁不存在 → 可启动(然后 acquireInstanceLock 写入)
14
+ * - 锁存在但 PID 已死 → 崩溃残留,自动清理(rm + 重新获取)
15
+ * - 锁存在且 PID 存活 → 已被占用,拒绝启动(返回占用 PID 供提示)
16
+ *
17
+ * 退出清理:进程正常退出时 releaseInstanceLock 删除锁文件;崩溃/强杀残留
18
+ * 由下次启动的「PID 存活检测」兜底(PID 已死 → 残留视为过期)。
19
+ *
20
+ * 放置说明:放 loader 而非 kernel——self-host.cjs 是 rolldown 内联 bundle
21
+ * (只内联 src/host/ 依赖树),静态 import kernel 会留 external 缺口;
22
+ * 放 loader 则 self-host(内联)与 cli(@napuketto/loader 导入)双入口可用。
23
+ */
24
+ /** 锁文件名(置于数据目录内)。 */
25
+ declare const INSTANCE_LOCK_FILE = "instance.lock";
26
+ /** 锁文件内容。 */
27
+ interface InstanceLockInfo {
28
+ /** 持有实例的进程 PID。 */
29
+ pid: number;
30
+ /** 获取锁的时间(epoch ms)。 */
31
+ startedAt: number;
32
+ /** 命令行摘要(诊断用,可选)。 */
33
+ cmdline?: string;
34
+ }
35
+ /** 数据目录锁检查结果。 */
36
+ interface InstanceLockCheck {
37
+ /** 是否被其他实例占用。 */
38
+ occupied: boolean;
39
+ /** 占用进程 PID(occupied=true 时有效)。 */
40
+ pid: number | null;
41
+ /** 锁文件完整路径(提示/清理用)。 */
42
+ lockPath: string;
43
+ }
44
+ /**
45
+ * 检测指定数据目录是否已被其他实例占用。
46
+ *
47
+ * @param dataDir 数据目录(账号级,如 ~/.napuketto/3567141148)
48
+ */
49
+ declare function checkInstanceLock(dataDir: string): InstanceLockCheck;
50
+ /**
51
+ * 获取数据目录锁。占用(其他实例存活)返回 false;成功或清理残留后返回 true。
52
+ * 幂等:本进程已持有则直接返回 true(不重复写)。
53
+ */
54
+ declare function acquireInstanceLock(dataDir: string, cmdline?: string): boolean;
55
+ /**
56
+ * 释放数据目录锁(正常退出时调用)。仅当持有者是本进程时才删除,
57
+ * 避免误删新持有者的锁(PID 复用 / 快速重启场景)。
58
+ */
59
+ declare function releaseInstanceLock(dataDir: string): void;
60
+ /** 注册进程退出清理(SIGINT/SIGTERM/exit 均触发;崩溃强杀由残留检测兜底)。 */
61
+ declare function registerLockCleanup(dataDir: string): void;
62
+ //#endregion
63
+ //#region src/locate-qq.d.ts
64
+ /** 数据根下 QQ 文件缓存目录名(<数据根>/qq-files/<版本>/,P1 下载解包产物)。 */
65
+ declare const QQ_FILES_DIR_NAME = "qq-files";
66
+ /** QQ 文件来源。 */
67
+ type QqFileSource = "local" | "cached";
68
+ /** QQ 安装目录探测结果。 */
69
+ interface QqInstallInfo {
70
+ /** QQ.exe 绝对路径(cached 来源为语义占位——缓存目录无 QQ.exe,当前无消费方)。 */
71
+ qqPath: string;
72
+ /** 安装根目录 / 文件根目录。 */
73
+ installDir: string;
74
+ /** 当前版本目录名(如 9.9.31-49919)。 */
75
+ version: string;
76
+ /** wrapper.node 绝对路径。 */
77
+ wrapperPath: string;
78
+ /** 来源:local = 本机安装;cached = NAPUTO_QQ_FILES / 数据根缓存。 */
79
+ source: QqFileSource;
80
+ }
81
+ /** resolveQqFiles 选项。 */
82
+ interface ResolveQqFilesOptions {
83
+ /** 显式文件根(L0;缺省读 NAPUTO_QQ_FILES)。 */
84
+ qqFilesDir?: string;
85
+ /** 显式 QQ.exe 路径(L1;缺省读 NAPUTO_QQ_PATH)。 */
86
+ qqPath?: string;
87
+ /** 数据根(L2 缓存扫描;缺省轻量解析 NAPKETTO_DATA ?? 项目根/.napuketto)。 */
88
+ dataRoot?: string;
89
+ /** L0/L1/L2 全部失败时自动进入下载流程(P1;默认 true,测试可关)。 */
90
+ autoDownload?: boolean;
91
+ /** 7z 可执行文件路径(解包用;缺省自动探测)。 */
92
+ sevenZipPath?: string;
93
+ }
94
+ /** 找 QQ.exe 路径。 */
95
+ declare function locateQqPath(): string;
96
+ /** 从 QQ.exe 路径推导安装目录,并探测当前版本(L1:本机安装)。 */
97
+ declare function resolveQqInstall(qqPath?: string): QqInstallInfo;
98
+ /**
99
+ * 多级来源定位 QQ 原生文件:
100
+ * L0 → L1 → L2 依次尝试,全部失败且 autoDownload 时自动进入下载流程(ensureQqFiles)。
101
+ * ⚠️ async:自动下载为异步流程(P1 起);纯定位场景用 resolveQqInstall(同步)。
102
+ */
103
+ declare function resolveQqFiles(options?: ResolveQqFilesOptions): Promise<QqInstallInfo>;
104
+ /**
105
+ * 确保 QQ 原生文件就绪(P1):幂等缓存检查 → 下载官方安装包 → sha256 校验
106
+ * → 7z 解包 → 提取 wrapper.node/QQNT.dll → 缓存。返回缓存版本 QqInstallInfo。
107
+ */
108
+ declare function ensureQqFiles(options?: {
109
+ /** 数据根(缓存 <数据根>/qq-files/)。 */
110
+ dataRoot?: string;
111
+ /** 7z 路径(缺省自动探测)。 */
112
+ sevenZipPath?: string;
113
+ }): Promise<QqInstallInfo>;
114
+ //#endregion
115
+ //#region src/launcher.d.ts
116
+ interface LaunchOptions {
117
+ /** QQ 安装信息(qqPath/version/wrapperPath)。 */
118
+ qq: QqInstallInfo;
119
+ /** kernel 入口(.mjs,self-host.cjs 里 import)。 */
120
+ kernelEntry: string;
121
+ /** 配置目录(账号数据目录,NAPUTO_CFG_DIR)。 */
122
+ cfgDir: string;
123
+ /** 全局配置文件路径(项目根 napuketto.toml,注入 NAPKETTO_CONFIG 供装配链读取)。 */
124
+ configPath?: string;
125
+ /** adapter 入口(.mjs,协议装配用)。 */
126
+ adapterEntry?: string;
127
+ /** network 入口(.mjs,协议装配用)。 */
128
+ networkEntry?: string;
129
+ /** 自建宿主:标准 node + stub QQNT.dll 直接引导,不拉起 QQ(2026-08-07 唯一路线)。 */
130
+ selfHost?: boolean;
131
+ /** stub QQNT.dll 目录(PATH 前置,转发 napi_*、uv_* 符号到 node.exe)。 */
132
+ stubDir?: string;
133
+ /** 强制指定快速登录账号(注入 NAPUTO_QUICK_UIN;cli `-q <uin>` 透传,2026-08-07)。 */
134
+ quickUin?: string;
135
+ /** IPC 子进程模式(=1:stdout 走 JSON 行协议 + stdin 收 action/control,koishi 插件驱动)。 */
136
+ ipc?: boolean;
137
+ /** 自建宿主入口(默认 dist/host/self-host.cjs)。 */
138
+ selfHostEntry?: string;
139
+ /** Windows 版 node.exe 路径(linux 场景覆盖;缺省 ensureWinNode 下载)。 */
140
+ winNodePath?: string;
141
+ /**
142
+ * 子进程工作目录(缺省继承父进程 cwd)。
143
+ *
144
+ * ⚠️ 必须指向数据根(~/.napuketto):QQ 原生层在账号上下文就绪前会
145
+ * fallback 到进程 cwd 落盘(如频道库 guild1.db),不指定则污染调用方
146
+ * 目录(实测写入项目根,guild1.db 08-07)。cli 传 dataRoot 根治。
147
+ */
148
+ cwd?: string;
149
+ /**
150
+ * 子进程 stdio(默认 "inherit")。cli 传 ["inherit","pipe","pipe"] 并接管
151
+ * 子进程 stdout/stderr:逐行过滤 wrapper 原生噪音(MMKV 刷屏、Node 符号
152
+ * 查找失败警告),其余转发——原生 printf 直写 fd 的字节无法从 JS 层拦截。
153
+ */
154
+ stdio?: StdioOptions;
155
+ }
156
+ interface LaunchResult {
157
+ /** 子进程句柄(自建宿主 node 进程)。 */
158
+ child: ReturnType<typeof spawn>;
159
+ /** 自建宿主入口路径(self-host.cjs)。 */
160
+ bootJsPath: string;
161
+ }
162
+ /**
163
+ * 启动自建宿主(路线 A):spawn 标准 node 直接跑 self-host.cjs,不拉起 QQ / 不注入。
164
+ *
165
+ * 关键(HANDOVER-V7 技术数据):wrapper.node 从 QQNT.dll 导入 99 符号
166
+ * (napi_*×40 + uv_*×56 + qq_magic×1 + v8/node mangled×2)——标准 node 无这些宿主
167
+ * 符号,必须 PATH 前置 stub QQNT.dll 目录(转发到 node.exe)+ QQ resources\app
168
+ * (wrapper.node 同目录依赖 DLL)才能 dlopen 成功。
169
+ *
170
+ * P2 平台分支:win32 用本机 node;linux 用 wine + Windows 版 node.exe
171
+ * (ensureWinNode 自动下载,路径过 toWinePath)。async(win-node 可能需下载)。
172
+ */
173
+ declare function launchSelfHost(options: LaunchOptions): Promise<LaunchResult>;
174
+ /**
175
+ * 默认 stub QQNT.dll 目录(loader 包内闭源 native/build/stub-test-env,开发机默认)。
176
+ * src 与 native 同层,构建后 dist 与 native 同层——`../native` 恒正确。
177
+ */
178
+ declare function defaultStubDir(): string;
179
+ /** 环境变量名(self-host.cjs 与 kernel 引导读取)。 */
180
+ declare const ENV: {
181
+ readonly QQ_PATH: "NAPUTO_QQ_PATH";
182
+ readonly KERNEL_ENTRY: "NAPUTO_KERNEL_ENTRY";
183
+ readonly CFG_DIR: "NAPUTO_CFG_DIR";
184
+ readonly QQ_VERSION: "NAPUTO_QQ_VERSION";
185
+ readonly WRAPPER_PATH: "NAPUTO_WRAPPER_PATH";
186
+ /** 自建宿主:标准 node + stub QQNT.dll 引导(self-host.cjs 分支)。 */
187
+ readonly SELF_HOST: "NAPUTO_SELF_HOST";
188
+ /** stub QQNT.dll 目录(launchSelfHost 注入,self-host.cjs 诊断用)。 */
189
+ readonly STUB_DIR: "NAPUTO_STUB_DIR";
190
+ /** adapter 包入口(协议装配用)。 */
191
+ readonly ADAPTER_ENTRY: "NAPUTO_ADAPTER_ENTRY";
192
+ /** network 包入口(协议装配用)。 */
193
+ readonly NETWORK_ENTRY: "NAPUTO_NETWORK_ENTRY";
194
+ /** 全局配置文件路径(项目根 napuketto.toml,boot-protocols 读取)。 */
195
+ readonly CONFIG_PATH: "NAPKETTO_CONFIG";
196
+ /** 强制指定快速登录账号(cli `-q <uin>` 透传,bootstrap 登录用)。 */
197
+ readonly QUICK_UIN: "NAPUTO_QUICK_UIN";
198
+ /** IPC 子进程模式(koishi 插件驱动:stdout JSON 行 + stdin action/control)。 */
199
+ readonly IPC: "NAPUTO_IPC";
200
+ };
201
+ //#endregion
202
+ //#region src/qq-download.d.ts
203
+ /** 下载选项。 */
204
+ interface DownloadOptions {
205
+ /** 目标文件绝对路径。 */
206
+ dest: string;
207
+ /** 期望 sha256(十六进制小写);缺省/空串 = 不校验(首次下载尚无参考值)。 */
208
+ expectedSha256?: string;
209
+ /** 覆盖下载地址(缺省读 NAPUTO_QQ_URL)。 */
210
+ url?: string;
211
+ /** 超时(毫秒,默认 60s 无数据则中断)。 */
212
+ timeoutMs?: number;
213
+ }
214
+ /** 下载结果。 */
215
+ interface DownloadResult {
216
+ /** 实际 sha256(十六进制小写)。 */
217
+ sha256: string;
218
+ /** 是否通过期望值校验(无期望值时恒 true)。 */
219
+ verified: boolean;
220
+ }
221
+ /** 下载中断(超时 / 非 2xx / 网络错误)抛出的错误。 */
222
+ declare class DownloadError extends Error {
223
+ constructor(message: string);
224
+ }
225
+ /**
226
+ * 下载文件并(可选)校验 sha256。失败时清理半成品文件。
227
+ * 返回实际 sha256(无论是否校验),便于调用方回填清单。
228
+ */
229
+ declare function downloadFile(options: DownloadOptions): Promise<DownloadResult>;
230
+ //#endregion
231
+ //#region src/qq-extract.d.ts
232
+ /** 7-Zip 可执行文件来源。 */
233
+ interface SevenZipResult {
234
+ /** 7z 可执行文件绝对路径。 */
235
+ exe: string;
236
+ /** 来源说明(内置资产 / 系统 / 环境变量)。 */
237
+ source: string;
238
+ }
239
+ /** 提取选项。 */
240
+ interface ExtractOptions {
241
+ /** 安装包绝对路径。 */
242
+ installerPath: string;
243
+ /** 目标版本目录(如 9.9.33-51802)。 */
244
+ version: string;
245
+ /** 缓存根目录(<数据根>/qq-files;版本子目录将建在其下)。 */
246
+ cacheRoot: string;
247
+ /** 7z 可执行文件路径(缺省自动探测)。 */
248
+ sevenZipPath?: string;
249
+ }
250
+ /**
251
+ * 探测 7z 可执行文件:
252
+ * NAPUTO_7Z_PATH 环境变量 > 内置资产(assets/7zip/7z.exe,Windows)> 系统 PATH 7z。
253
+ * Linux 上用 p7zip 提供的 7z 命令(系统 PATH)。
254
+ */
255
+ declare function findSevenZip(): SevenZipResult;
256
+ /** 解包安装包到指定目录(spawn 7z x,失败抛错)。 */
257
+ declare function extractInstaller(installerPath: string, destDir: string, sevenZipPath?: string): Promise<void>;
258
+ /**
259
+ * 从解包目录提取运行所需文件到缓存:
260
+ * resources/app 顶层全部 *.node + *.dll(保持同目录结构,PATH 前置即可解析依赖)。
261
+ * 幂等:目标 wrapper.node 已存在则跳过。
262
+ */
263
+ declare function extractWrapperFiles(extractedDir: string, version: string, cacheRoot: string): Promise<QqInstallInfo>;
264
+ /** 清理缓存版本目录(下载/解包失败残留,供调用方兜底)。 */
265
+ declare function clearCacheVersion(cacheRoot: string, version: string): Promise<void>;
266
+ //#endregion
267
+ //#region src/qq-releases.d.ts
268
+ /** 清单中单个版本条目。 */
269
+ interface QqReleaseEntry {
270
+ /** 版本目录名(如 9.9.33-51802)。 */
271
+ version: string;
272
+ /** 官方安装包下载 URL。 */
273
+ url: string;
274
+ /** sha256(十六进制小写);空串 = 尚未校验(首次录入时 CI 计算后填入)。 */
275
+ sha256: string;
276
+ /** appid(预知信息,运行时仍从 major.node 解析)。 */
277
+ appid: number;
278
+ /** 来源标记(official = 官方)。 */
279
+ source: string;
280
+ /** 构建日期(YYYY-MM-DD,可选)。 */
281
+ buildDate?: string;
282
+ }
283
+ /** 清单文件结构。 */
284
+ interface QqReleasesFile {
285
+ schema: number;
286
+ known: QqReleaseEntry[];
287
+ }
288
+ /** 清单解析失败。 */
289
+ declare class QqReleasesError extends Error {
290
+ constructor(message: string);
291
+ }
292
+ /** 读版本清单(同步,启动期调用一次即可)。 */
293
+ declare function loadQqReleases(): QqReleasesFile;
294
+ /** 取最新已知可用版本(known 数组按录入顺序,取最后一项 = 最新)。 */
295
+ declare function latestRelease(releases: QqReleasesFile): QqReleaseEntry;
296
+ /**
297
+ * 解析实际下载 URL:NAPUTO_QQ_URL 环境变量覆盖 > 清单 URL。
298
+ * 供 downloadFile 使用(也支持按版本查询,当前固定取最新)。
299
+ */
300
+ declare function resolveDownloadUrl(release: QqReleaseEntry): string;
301
+ //#endregion
302
+ //#region src/win-node.d.ts
303
+ /** 运行时缓存子目录名(<数据根>/runtime/win-node/)。 */
304
+ declare const WIN_NODE_DIR_NAME = "win-node";
305
+ /** Windows node.exe 定位结果。 */
306
+ interface WinNodeInfo {
307
+ /** node.exe 绝对路径(Windows 视角,供 wine 调用)。 */
308
+ exePath: string;
309
+ /** 已下载的完整版 node zip 版本号(如 v24.16.0)。 */
310
+ version: string;
311
+ }
312
+ /** 解析 nodejs.org zip URL(与 dist 目录命名一致)。 */
313
+ declare function nodeZipUrl(version: string): string;
314
+ /**
315
+ * 确保 Windows 版 node.exe 就绪(下载 + 解压 + 缓存)。
316
+ * 幂等:<数据根>/runtime/win-node/<version>/node.exe 已存在则直接返回。
317
+ */
318
+ declare function ensureWinNode(options?: {
319
+ /** 数据根(缓存 <数据根>/runtime/win-node/)。 */
320
+ dataRoot?: string;
321
+ /** node 版本(缺省读 NAPUTO_WIN_NODE_VERSION,再兜底 latest v24)。 */
322
+ version?: string;
323
+ /** 显式 node.exe 路径(覆盖缓存逻辑,读 NAPUTO_WIN_NODE_PATH)。 */
324
+ exePath?: string;
325
+ }): Promise<WinNodeInfo>;
326
+ //#endregion
327
+ //#region src/wine.d.ts
328
+ /**
329
+ * wine.ts:Linux 上经 wine 运行 Windows 版 node.exe 的路径与 spawn 组装(P2)。
330
+ *
331
+ * 设计文档 §3.2(2026-08-12 拍板确认)。核心认知:
332
+ * - Windows 版 node.exe 在 wine 内把 Linux 根目录挂载在 `Z:\`
333
+ * - process.dlopen / 环境变量路径都是 wine 视角的 Windows 路径
334
+ * - 传给 wine 子进程的**所有路径**(wrapperPath / cfgDir / stubDir / self-host.cjs / PATH)
335
+ * 都要过 toWinePath 转成 `Z:\...`
336
+ *
337
+ * 纯函数设计:不 import node:child_process,方便 Windows 上单测断言(P2 纯逻辑层)。
338
+ */
339
+ /** Linux 绝对路径 → wine `Z:\` 路径(Windows 版 node.exe 视角)。幂等:已是 Windows 路径(含盘符)原样返回。 */
340
+ declare function toWinePath(linuxPath: string): string;
341
+ /** 判断当前是否 Linux(需 wine 的场景)。 */
342
+ declare function isLinux(): boolean;
343
+ /** wine 可执行文件(Linux 上 spawn 用;可被 NAPUTO_WINE 覆盖)。 */
344
+ declare function wineBinary(): string;
345
+ /** spawn 命令与参数(平台分支;纯函数,可单测)。 */
346
+ interface SpawnCommand {
347
+ /** 命令(win32 = node.exe;linux = wine)。 */
348
+ command: string;
349
+ /** 参数(win32 = [selfHostPath];linux = [winNodePath, selfHostPath])。 */
350
+ args: string[];
351
+ }
352
+ /** 组装 spawn 命令(win32 vs linux 分支)。 */
353
+ declare function buildSpawnCommand(options: {
354
+ /** 平台(缺省 process.platform,测试注入)。 */
355
+ platform?: NodeJS.Platform;
356
+ /** Windows 本机 node 路径(win32 用,缺省 process.execPath)。 */
357
+ winNodePath?: string;
358
+ /** wine 命令(linux 用,缺省 wineBinary())。 */
359
+ wine?: string;
360
+ /** 自建宿主入口(self-host.cjs,传给 wine/win node)。 */
361
+ selfHostPath: string;
362
+ }): SpawnCommand;
363
+ //#endregion
364
+ export { DownloadError, type DownloadOptions, type DownloadResult, ENV, type ExtractOptions, INSTANCE_LOCK_FILE, type InstanceLockCheck, type InstanceLockInfo, 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, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, latestRelease, launchSelfHost, loadQqReleases, locateQqPath, nodeZipUrl, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
365
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/instance-lock.ts","../src/locate-qq.ts","../src/launcher.ts","../src/qq-download.ts","../src/qq-extract.ts","../src/qq-releases.ts","../src/win-node.ts","../src/wine.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cA2Ba;;UAGI;;EAEb;;EAEA;;EAEA;;;UAIa;;EAEb;;EAEA;;EAEA;;;;;;;iBAQY,kBAAkB,kBAAkB;;;;;iBA+BpC,oBAAoB,iBAAiB;;;;;iBA4BrC,oBAAoB;;iBAapB,oBAAoB;;;;cC5GvB;;KAiDD;;UAGK;;EAEb;;EAEA;;EAEA;;EAEA;;EAEA,QAAQ;;;UAIK;;EAEb;;EAEA;;EAEA;;EAEA;;EAEA;;;iBAIY;;iBAwCA,iBAAiB,kBAAkB;;;;;;iBAY7B,eAAe,UAAS,wBAA6B,QAAQ;;;;;iBA0D7D,cAClB;;EAEI;;EAEA;IAEL,QAAQ;;;UChMM;;EAEb,IAAI;;EAEJ;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;;EAQA;;;;;;EAMA,QAAQ;;UAGK;;EAEb,OAAO,kBAAkB;;EAEzB;;;;;;;;;;;;;iBAckB,eAAe,SAAS,gBAAgB,QAAQ;;;;;iBAoFtD;;cA4CH;WACT;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;;;;UCnNa;;EAEb;;EAEA;;EAEA;;EAEA;;;UAIa;;EAEb;;EAEA;;;cAIS,sBAAsB;EAC/B,YAAY;;;;;;iBA8CM,aAAa,SAAS,kBAAkB,QAAQ;;;;UCrDrD;;EAEb;;EAEA;;;UAIa;;EAEb;;EAEA;;EAEA;;EAEA;;;;;;;iBAQY,gBAAgB;;iBAuBV,iBAClB,uBACA,iBACA,wBACD;;;;;;iBA8BmB,oBAClB,sBACA,iBACA,oBACD,QAAQ;;iBAkDW,kBAAkB,mBAAmB,kBAAkB;;;;UC1J5D;;EAEb;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;UAIa;EACb;EACA,OAAO;;;cAIE,wBAAwB;EACjC,YAAY;;;iBAYA,kBAAkB;;iBAoBlB,cAAc,UAAU,iBAAiB;;;;;iBAYzC,mBAAmB,SAAS;;;;cC3D/B;;UAGI;;EAEb;;EAEA;;;iBAIY,WAAW;;;;;iBAQL,cAClB;;EAEI;;EAEA;;EAEA;IAEL,QAAQ;;;;;;;;;;;;;;;iBCpCK,WAAW;;iBAiBX;;iBAKA;;UAKC;;EAEb;;EAEA;;;iBAIY,kBAAkB;;EAE9B,WAAW,OAAO;;EAElB;;EAEA;;EAEA;IACA"}
package/dist/index.d.mts CHANGED
@@ -61,21 +61,56 @@ declare function releaseInstanceLock(dataDir: string): void;
61
61
  declare function registerLockCleanup(dataDir: string): void;
62
62
  //#endregion
63
63
  //#region src/locate-qq.d.ts
64
+ /** 数据根下 QQ 文件缓存目录名(<数据根>/qq-files/<版本>/,P1 下载解包产物)。 */
65
+ declare const QQ_FILES_DIR_NAME = "qq-files";
66
+ /** QQ 文件来源。 */
67
+ type QqFileSource = "local" | "cached";
64
68
  /** QQ 安装目录探测结果。 */
65
69
  interface QqInstallInfo {
66
- /** QQ.exe 绝对路径。 */
70
+ /** QQ.exe 绝对路径(cached 来源为语义占位——缓存目录无 QQ.exe,当前无消费方)。 */
67
71
  qqPath: string;
68
- /** 安装根目录。 */
72
+ /** 安装根目录 / 文件根目录。 */
69
73
  installDir: string;
70
74
  /** 当前版本目录名(如 9.9.31-49919)。 */
71
75
  version: string;
72
76
  /** wrapper.node 绝对路径。 */
73
77
  wrapperPath: string;
78
+ /** 来源:local = 本机安装;cached = NAPUTO_QQ_FILES / 数据根缓存。 */
79
+ source: QqFileSource;
80
+ }
81
+ /** resolveQqFiles 选项。 */
82
+ interface ResolveQqFilesOptions {
83
+ /** 显式文件根(L0;缺省读 NAPUTO_QQ_FILES)。 */
84
+ qqFilesDir?: string;
85
+ /** 显式 QQ.exe 路径(L1;缺省读 NAPUTO_QQ_PATH)。 */
86
+ qqPath?: string;
87
+ /** 数据根(L2 缓存扫描;缺省轻量解析 NAPKETTO_DATA ?? 项目根/.napuketto)。 */
88
+ dataRoot?: string;
89
+ /** L0/L1/L2 全部失败时自动进入下载流程(P1;默认 true,测试可关)。 */
90
+ autoDownload?: boolean;
91
+ /** 7z 可执行文件路径(解包用;缺省自动探测)。 */
92
+ sevenZipPath?: string;
74
93
  }
75
94
  /** 找 QQ.exe 路径。 */
76
95
  declare function locateQqPath(): string;
77
- /** 从 QQ.exe 路径推导安装目录,并探测当前版本。 */
96
+ /** 从 QQ.exe 路径推导安装目录,并探测当前版本(L1:本机安装)。 */
78
97
  declare function resolveQqInstall(qqPath?: string): QqInstallInfo;
98
+ /**
99
+ * 多级来源定位 QQ 原生文件:
100
+ * L0 → L1 → L2 依次尝试,全部失败且 autoDownload 时自动进入下载流程(ensureQqFiles)。
101
+ * ⚠️ async:自动下载为异步流程(P1 起);纯定位场景用 resolveQqInstall(同步)。
102
+ */
103
+ declare function resolveQqFiles(options?: ResolveQqFilesOptions): Promise<QqInstallInfo>;
104
+ /**
105
+ * 确保 QQ 原生文件就绪(P1):幂等缓存检查 → 下载官方安装包 → sha256 校验
106
+ * → 7z 解包 → 提取 wrapper.node/QQNT.dll → 缓存。返回缓存版本 QqInstallInfo。
107
+ */
108
+ declare function ensureQqFiles(options?: {
109
+ /** 数据根(缓存 <数据根>/qq-files/)。 */
110
+ dataRoot?: string;
111
+ /** 7z 路径(缺省自动探测)。 */
112
+ sevenZipPath?: string;
113
+ }): Promise<QqInstallInfo>;
79
114
  //#endregion
80
115
  //#region src/launcher.d.ts
81
116
  interface LaunchOptions {
@@ -101,6 +136,8 @@ interface LaunchOptions {
101
136
  ipc?: boolean;
102
137
  /** 自建宿主入口(默认 dist/host/self-host.cjs)。 */
103
138
  selfHostEntry?: string;
139
+ /** Windows 版 node.exe 路径(linux 场景覆盖;缺省 ensureWinNode 下载)。 */
140
+ winNodePath?: string;
104
141
  /**
105
142
  * 子进程工作目录(缺省继承父进程 cwd)。
106
143
  *
@@ -123,15 +160,17 @@ interface LaunchResult {
123
160
  bootJsPath: string;
124
161
  }
125
162
  /**
126
- * 启动自建宿主(路线 A,2026-08-07 产品化):spawn 标准 node 直接跑 self-host.cjs,
127
- * 不拉起 QQ / 不注入。
163
+ * 启动自建宿主(路线 A):spawn 标准 node 直接跑 self-host.cjs,不拉起 QQ / 不注入。
128
164
  *
129
165
  * 关键(HANDOVER-V7 技术数据):wrapper.node 从 QQNT.dll 导入 99 符号
130
166
  * (napi_*×40 + uv_*×56 + qq_magic×1 + v8/node mangled×2)——标准 node 无这些宿主
131
167
  * 符号,必须 PATH 前置 stub QQNT.dll 目录(转发到 node.exe)+ QQ resources\app
132
168
  * (wrapper.node 同目录依赖 DLL)才能 dlopen 成功。
169
+ *
170
+ * P2 平台分支:win32 用本机 node;linux 用 wine + Windows 版 node.exe
171
+ * (ensureWinNode 自动下载,路径过 toWinePath)。async(win-node 可能需下载)。
133
172
  */
134
- declare function launchSelfHost(options: LaunchOptions): LaunchResult;
173
+ declare function launchSelfHost(options: LaunchOptions): Promise<LaunchResult>;
135
174
  /**
136
175
  * 默认 stub QQNT.dll 目录(loader 包内闭源 native/build/stub-test-env,开发机默认)。
137
176
  * src 与 native 同层,构建后 dist 与 native 同层——`../native` 恒正确。
@@ -160,5 +199,167 @@ declare const ENV: {
160
199
  readonly IPC: "NAPUTO_IPC";
161
200
  };
162
201
  //#endregion
163
- export { ENV, INSTANCE_LOCK_FILE, type InstanceLockCheck, type InstanceLockInfo, type LaunchOptions, type LaunchResult, type QqInstallInfo, acquireInstanceLock, checkInstanceLock, defaultStubDir, launchSelfHost, locateQqPath, registerLockCleanup, releaseInstanceLock, resolveQqInstall };
202
+ //#region src/qq-download.d.ts
203
+ /** 下载选项。 */
204
+ interface DownloadOptions {
205
+ /** 目标文件绝对路径。 */
206
+ dest: string;
207
+ /** 期望 sha256(十六进制小写);缺省/空串 = 不校验(首次下载尚无参考值)。 */
208
+ expectedSha256?: string;
209
+ /** 覆盖下载地址(缺省读 NAPUTO_QQ_URL)。 */
210
+ url?: string;
211
+ /** 超时(毫秒,默认 60s 无数据则中断)。 */
212
+ timeoutMs?: number;
213
+ }
214
+ /** 下载结果。 */
215
+ interface DownloadResult {
216
+ /** 实际 sha256(十六进制小写)。 */
217
+ sha256: string;
218
+ /** 是否通过期望值校验(无期望值时恒 true)。 */
219
+ verified: boolean;
220
+ }
221
+ /** 下载中断(超时 / 非 2xx / 网络错误)抛出的错误。 */
222
+ declare class DownloadError extends Error {
223
+ constructor(message: string);
224
+ }
225
+ /**
226
+ * 下载文件并(可选)校验 sha256。失败时清理半成品文件。
227
+ * 返回实际 sha256(无论是否校验),便于调用方回填清单。
228
+ */
229
+ declare function downloadFile(options: DownloadOptions): Promise<DownloadResult>;
230
+ //#endregion
231
+ //#region src/qq-extract.d.ts
232
+ /** 7-Zip 可执行文件来源。 */
233
+ interface SevenZipResult {
234
+ /** 7z 可执行文件绝对路径。 */
235
+ exe: string;
236
+ /** 来源说明(内置资产 / 系统 / 环境变量)。 */
237
+ source: string;
238
+ }
239
+ /** 提取选项。 */
240
+ interface ExtractOptions {
241
+ /** 安装包绝对路径。 */
242
+ installerPath: string;
243
+ /** 目标版本目录(如 9.9.33-51802)。 */
244
+ version: string;
245
+ /** 缓存根目录(<数据根>/qq-files;版本子目录将建在其下)。 */
246
+ cacheRoot: string;
247
+ /** 7z 可执行文件路径(缺省自动探测)。 */
248
+ sevenZipPath?: string;
249
+ }
250
+ /**
251
+ * 探测 7z 可执行文件:
252
+ * NAPUTO_7Z_PATH 环境变量 > 内置资产(assets/7zip/7z.exe,Windows)> 系统 PATH 7z。
253
+ * Linux 上用 p7zip 提供的 7z 命令(系统 PATH)。
254
+ */
255
+ declare function findSevenZip(): SevenZipResult;
256
+ /** 解包安装包到指定目录(spawn 7z x,失败抛错)。 */
257
+ declare function extractInstaller(installerPath: string, destDir: string, sevenZipPath?: string): Promise<void>;
258
+ /**
259
+ * 从解包目录提取运行所需文件到缓存:
260
+ * resources/app 顶层全部 *.node + *.dll(保持同目录结构,PATH 前置即可解析依赖)。
261
+ * 幂等:目标 wrapper.node 已存在则跳过。
262
+ */
263
+ declare function extractWrapperFiles(extractedDir: string, version: string, cacheRoot: string): Promise<QqInstallInfo>;
264
+ /** 清理缓存版本目录(下载/解包失败残留,供调用方兜底)。 */
265
+ declare function clearCacheVersion(cacheRoot: string, version: string): Promise<void>;
266
+ //#endregion
267
+ //#region src/qq-releases.d.ts
268
+ /** 清单中单个版本条目。 */
269
+ interface QqReleaseEntry {
270
+ /** 版本目录名(如 9.9.33-51802)。 */
271
+ version: string;
272
+ /** 官方安装包下载 URL。 */
273
+ url: string;
274
+ /** sha256(十六进制小写);空串 = 尚未校验(首次录入时 CI 计算后填入)。 */
275
+ sha256: string;
276
+ /** appid(预知信息,运行时仍从 major.node 解析)。 */
277
+ appid: number;
278
+ /** 来源标记(official = 官方)。 */
279
+ source: string;
280
+ /** 构建日期(YYYY-MM-DD,可选)。 */
281
+ buildDate?: string;
282
+ }
283
+ /** 清单文件结构。 */
284
+ interface QqReleasesFile {
285
+ schema: number;
286
+ known: QqReleaseEntry[];
287
+ }
288
+ /** 清单解析失败。 */
289
+ declare class QqReleasesError extends Error {
290
+ constructor(message: string);
291
+ }
292
+ /** 读版本清单(同步,启动期调用一次即可)。 */
293
+ declare function loadQqReleases(): QqReleasesFile;
294
+ /** 取最新已知可用版本(known 数组按录入顺序,取最后一项 = 最新)。 */
295
+ declare function latestRelease(releases: QqReleasesFile): QqReleaseEntry;
296
+ /**
297
+ * 解析实际下载 URL:NAPUTO_QQ_URL 环境变量覆盖 > 清单 URL。
298
+ * 供 downloadFile 使用(也支持按版本查询,当前固定取最新)。
299
+ */
300
+ declare function resolveDownloadUrl(release: QqReleaseEntry): string;
301
+ //#endregion
302
+ //#region src/win-node.d.ts
303
+ /** 运行时缓存子目录名(<数据根>/runtime/win-node/)。 */
304
+ declare const WIN_NODE_DIR_NAME = "win-node";
305
+ /** Windows node.exe 定位结果。 */
306
+ interface WinNodeInfo {
307
+ /** node.exe 绝对路径(Windows 视角,供 wine 调用)。 */
308
+ exePath: string;
309
+ /** 已下载的完整版 node zip 版本号(如 v24.16.0)。 */
310
+ version: string;
311
+ }
312
+ /** 解析 nodejs.org zip URL(与 dist 目录命名一致)。 */
313
+ declare function nodeZipUrl(version: string): string;
314
+ /**
315
+ * 确保 Windows 版 node.exe 就绪(下载 + 解压 + 缓存)。
316
+ * 幂等:<数据根>/runtime/win-node/<version>/node.exe 已存在则直接返回。
317
+ */
318
+ declare function ensureWinNode(options?: {
319
+ /** 数据根(缓存 <数据根>/runtime/win-node/)。 */
320
+ dataRoot?: string;
321
+ /** node 版本(缺省读 NAPUTO_WIN_NODE_VERSION,再兜底 latest v24)。 */
322
+ version?: string;
323
+ /** 显式 node.exe 路径(覆盖缓存逻辑,读 NAPUTO_WIN_NODE_PATH)。 */
324
+ exePath?: string;
325
+ }): Promise<WinNodeInfo>;
326
+ //#endregion
327
+ //#region src/wine.d.ts
328
+ /**
329
+ * wine.ts:Linux 上经 wine 运行 Windows 版 node.exe 的路径与 spawn 组装(P2)。
330
+ *
331
+ * 设计文档 §3.2(2026-08-12 拍板确认)。核心认知:
332
+ * - Windows 版 node.exe 在 wine 内把 Linux 根目录挂载在 `Z:\`
333
+ * - process.dlopen / 环境变量路径都是 wine 视角的 Windows 路径
334
+ * - 传给 wine 子进程的**所有路径**(wrapperPath / cfgDir / stubDir / self-host.cjs / PATH)
335
+ * 都要过 toWinePath 转成 `Z:\...`
336
+ *
337
+ * 纯函数设计:不 import node:child_process,方便 Windows 上单测断言(P2 纯逻辑层)。
338
+ */
339
+ /** Linux 绝对路径 → wine `Z:\` 路径(Windows 版 node.exe 视角)。幂等:已是 Windows 路径(含盘符)原样返回。 */
340
+ declare function toWinePath(linuxPath: string): string;
341
+ /** 判断当前是否 Linux(需 wine 的场景)。 */
342
+ declare function isLinux(): boolean;
343
+ /** wine 可执行文件(Linux 上 spawn 用;可被 NAPUTO_WINE 覆盖)。 */
344
+ declare function wineBinary(): string;
345
+ /** spawn 命令与参数(平台分支;纯函数,可单测)。 */
346
+ interface SpawnCommand {
347
+ /** 命令(win32 = node.exe;linux = wine)。 */
348
+ command: string;
349
+ /** 参数(win32 = [selfHostPath];linux = [winNodePath, selfHostPath])。 */
350
+ args: string[];
351
+ }
352
+ /** 组装 spawn 命令(win32 vs linux 分支)。 */
353
+ declare function buildSpawnCommand(options: {
354
+ /** 平台(缺省 process.platform,测试注入)。 */
355
+ platform?: NodeJS.Platform;
356
+ /** Windows 本机 node 路径(win32 用,缺省 process.execPath)。 */
357
+ winNodePath?: string;
358
+ /** wine 命令(linux 用,缺省 wineBinary())。 */
359
+ wine?: string;
360
+ /** 自建宿主入口(self-host.cjs,传给 wine/win node)。 */
361
+ selfHostPath: string;
362
+ }): SpawnCommand;
363
+ //#endregion
364
+ export { DownloadError, type DownloadOptions, type DownloadResult, ENV, type ExtractOptions, INSTANCE_LOCK_FILE, type InstanceLockCheck, type InstanceLockInfo, 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, ensureQqFiles, ensureWinNode, extractInstaller, extractWrapperFiles, findSevenZip, isLinux, latestRelease, launchSelfHost, loadQqReleases, locateQqPath, nodeZipUrl, registerLockCleanup, releaseInstanceLock, resolveDownloadUrl, resolveQqFiles, resolveQqInstall, toWinePath, wineBinary };
164
365
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/instance-lock.ts","../src/locate-qq.ts","../src/launcher.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cA2Ba;;UAGI;;EAEb;;EAEA;;EAEA;;;UAIa;;EAEb;;EAEA;;EAEA;;;;;;;iBAQY,kBAAkB,kBAAkB;;;;;iBA+BpC,oBAAoB,iBAAiB;;;;;iBA4BrC,oBAAoB;;iBAapB,oBAAoB;;;;UCvEnB;;EAEb;;EAEA;;EAEA;;EAEA;;;iBAIY;;iBAaA,iBAAiB,kBAAkB;;;UC9DlC;;EAEb,IAAI;;EAEJ;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;;EAQA;;;;;;EAMA,QAAQ;;UAGK;;EAEb,OAAO,kBAAkB;;EAEzB;;;;;;;;;;;iBAYY,eAAe,SAAS,gBAAgB;;;;;iBAiDxC;;cAwCH;WACT;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/instance-lock.ts","../src/locate-qq.ts","../src/launcher.ts","../src/qq-download.ts","../src/qq-extract.ts","../src/qq-releases.ts","../src/win-node.ts","../src/wine.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cA2Ba;;UAGI;;EAEb;;EAEA;;EAEA;;;UAIa;;EAEb;;EAEA;;EAEA;;;;;;;iBAQY,kBAAkB,kBAAkB;;;;;iBA+BpC,oBAAoB,iBAAiB;;;;;iBA4BrC,oBAAoB;;iBAapB,oBAAoB;;;;cC5GvB;;KAiDD;;UAGK;;EAEb;;EAEA;;EAEA;;EAEA;;EAEA,QAAQ;;;UAIK;;EAEb;;EAEA;;EAEA;;EAEA;;EAEA;;;iBAIY;;iBAwCA,iBAAiB,kBAAkB;;;;;;iBAY7B,eAAe,UAAS,wBAA6B,QAAQ;;;;;iBA0D7D,cAClB;;EAEI;;EAEA;IAEL,QAAQ;;;UChMM;;EAEb,IAAI;;EAEJ;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;;EAQA;;;;;;EAMA,QAAQ;;UAGK;;EAEb,OAAO,kBAAkB;;EAEzB;;;;;;;;;;;;;iBAckB,eAAe,SAAS,gBAAgB,QAAQ;;;;;iBAoFtD;;cA4CH;WACT;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;;;;UCnNa;;EAEb;;EAEA;;EAEA;;EAEA;;;UAIa;;EAEb;;EAEA;;;cAIS,sBAAsB;EAC/B,YAAY;;;;;;iBA8CM,aAAa,SAAS,kBAAkB,QAAQ;;;;UCrDrD;;EAEb;;EAEA;;;UAIa;;EAEb;;EAEA;;EAEA;;EAEA;;;;;;;iBAQY,gBAAgB;;iBAuBV,iBAClB,uBACA,iBACA,wBACD;;;;;;iBA8BmB,oBAClB,sBACA,iBACA,oBACD,QAAQ;;iBAkDW,kBAAkB,mBAAmB,kBAAkB;;;;UC1J5D;;EAEb;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;UAIa;EACb;EACA,OAAO;;;cAIE,wBAAwB;EACjC,YAAY;;;iBAYA,kBAAkB;;iBAoBlB,cAAc,UAAU,iBAAiB;;;;;iBAYzC,mBAAmB,SAAS;;;;cC3D/B;;UAGI;;EAEb;;EAEA;;;iBAIY,WAAW;;;;;iBAQL,cAClB;;EAEI;;EAEA;;EAEA;IAEL,QAAQ;;;;;;;;;;;;;;;iBCpCK,WAAW;;iBAiBX;;iBAKA;;UAKC;;EAEb;;EAEA;;;iBAIY,kBAAkB;;EAE9B,WAAW,OAAO;;EAElB;;EAEA;;EAEA;IACA"}