@napuketto/kernel 0.1.0 → 0.1.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["stringifyTomlText","parseTomlText","sleep","httpsGet","osVersionString","osType","PlatformTypeValue"],"sources":["../src/infra/errors.ts","../src/infra/config.ts","../src/infra/logger.ts","../src/infra/paths.ts","../src/apis/result.ts","../src/apis/friend.ts","../src/types/entities.ts","../src/types/listeners/group.ts","../src/types/services/msg-service.ts","../src/types/message-element.ts","../src/types/services/group-service.ts","../src/types/wrapper.ts","../src/apis/group.ts","../src/apis/group-notify.ts","../src/apis/msg.ts","../src/apis/stranger-info.ts","../src/apis/profile.ts","../src/apis/profile-like.ts","../src/apis/richmedia.ts","../src/apis/ticket.ts","../src/apis/webapi.ts","../src/bridge/friend-bridge.ts","../src/bridge/group-bridge.ts","../src/bridge/msg-bridge.ts","../src/event-channel.ts","../src/cache/buddy-cache.ts","../src/login/wait.ts","../src/cache/group-cache.ts","../src/context.ts","../src/wrapper/wrapper-adapters.ts","../src/login/login-connect.ts","../src/login/lifecycle.ts","../src/login/login.ts","../src/wrapper/qq-data-path.ts","../src/wrapper/wrapper-config.ts","../src/wrapper/wrapper-loader.ts","../src/core.ts","../src/wrapper/session-resolver.ts","../src/wrapper/probe/probe-serialize.ts","../src/wrapper/probe/probe-utils.ts","../src/wrapper/probe/probe-services.ts","../src/wrapper/probe/probe.ts","../src/wrapper/wrapper-version.ts"],"sourcesContent":["/**\n * kernel 类型化错误(ADR-009 / ADR-017)\n *\n * apis 层内部解包原生 `{ result, errMsg }`:成功返回纯业务值,失败抛 KernelError。\n * 协议层只需维护 `KernelErrorCode → 协议错误码` 映射表,不解析错误逻辑。\n */\n\n/**\n * 错误码分类。P1 探测真实错误返回后再增删。\n * 当前 INVALID_PARAM 也承担基础设施层(配置校验失败等)的错误语义。\n */\nexport type KernelErrorCode =\n | \"SEND_FAILED\" // 发送失败(原生拒绝)\n | \"PERMISSION_DENIED\" // 无权限(禁言、非管理员等)\n | \"NOT_FOUND\" // 目标不存在(群/成员/消息)\n | \"TIMEOUT\" // 操作超时(含文件预测超时)\n | \"NOT_LOGIN\" // 未登录或已掉线\n | \"INVALID_PARAM\" // 参数非法(原生拒绝 / 配置校验失败)\n | \"INVALID_STATE\" // 状态非法(如 session 未创建即 startNT)\n | \"UNKNOWN\"; // 兜底\n\n/**\n * 全部错误码集合。协议层映射表可据此推导:\n * `Record<(typeof KERNEL_ERROR_CODES)[number], number>`。\n */\nexport const KERNEL_ERROR_CODES = [\n \"SEND_FAILED\",\n \"PERMISSION_DENIED\",\n \"NOT_FOUND\",\n \"TIMEOUT\",\n \"NOT_LOGIN\",\n \"INVALID_PARAM\",\n \"INVALID_STATE\",\n \"UNKNOWN\",\n] as const;\n\n/** kernel 类型化错误:apis / 基础设施层抛出的统一错误形态。 */\nexport class KernelError extends Error {\n readonly code: KernelErrorCode;\n\n constructor(message: string, code: KernelErrorCode = \"UNKNOWN\", options?: ErrorOptions) {\n super(message, options);\n this.name = \"KernelError\";\n this.code = code;\n }\n}\n\n/** 便捷构造:`kernelError('消息不存在', 'NOT_FOUND', { cause })`。 */\nexport function kernelError(\n message: string,\n code: KernelErrorCode = \"UNKNOWN\",\n options?: ErrorOptions,\n): KernelError {\n return new KernelError(message, code, options);\n}\n\n/** 类型守卫:协议层 catch 统一映射时区分 KernelError 与意外错误。 */\nexport function isKernelError(err: unknown): err is KernelError {\n return err instanceof KernelError;\n}\n","/**\n * 配置基类(ADR-012)\n *\n * 读文件 → 校验 → 内存对象 → 变更写回。支持 **JSON / TOML** 两种文本格式\n * (按扩展名推断:.toml → TOML(smol-toml),其余 → JSON)。\n *\n * kernel 不依赖 zod:只依赖校验器的最小 `parse` 形状(`ConfigSchema`),\n * 协议包的 zod schema 天然满足;kernel 主配置用手写校验器包装。\n * 协议配置 schema 在各自协议包,见各包 docs/design.md。\n */\nimport { mkdirSync } from \"node:fs\";\nimport { readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, extname } from \"node:path\";\nimport { parse as parseTomlText, stringify as stringifyTomlText } from \"smol-toml\";\nimport { KernelError } from \"./errors.js\";\n\n/** 配置文本格式。 */\nexport type ConfigFormat = \"json\" | \"toml\";\n\n/** 按扩展名推断格式(.toml → toml,其余 → json)。 */\nfunction inferFormat(path: string): ConfigFormat {\n if (extname(path).toLowerCase() === \".toml\") {\n return \"toml\";\n }\n return \"json\";\n}\n\nfunction isMissingFileError(err: unknown): boolean {\n if (err instanceof Error) {\n return (err as NodeJS.ErrnoException).code === \"ENOENT\";\n }\n return false;\n}\n\n/**\n * 校验器最小接口。zod 的 `ZodType<T>`(`parse(data: unknown): T`)天然满足;\n * 手写校验器包装成该形状即可。\n */\nexport interface ConfigSchema<T> {\n parse: (input: unknown) => T;\n}\n\nexport interface ConfigOptions<T> {\n /** 配置文件绝对路径。 */\n path: string;\n /** 校验器(协议 schema 用 zod;kernel 主配置用手写校验器包装)。 */\n schema: ConfigSchema<T>;\n /** 默认值:文件缺失时生成并落盘;校验以它为兜底。 */\n defaults: T;\n /** 内存初值(已校验对象):load() 时直接使用,跳过文件读写(全局 TOML 分段的装配方式)。 */\n seed?: T | undefined;\n /** 文本格式(缺省按扩展名推断)。 */\n format?: ConfigFormat | undefined;\n}\n\n/**\n * 配置基类。每次 `load()` / `save()` 都经过 schema 校验,\n * 校验失败抛 `KernelError('INVALID_PARAM')`,不静默吞掉。\n * - `load()`:seed 存在 → 直接用 seed;否则读文件(缺失落默认值)。\n * - `save()`:按格式序列化(TOML/JSON)原子写。\n */\nexport class ConfigBase<T> {\n readonly path: string;\n readonly defaults: T;\n\n private readonly schema: ConfigSchema<T>;\n private readonly format: ConfigFormat;\n private readonly seed: T | undefined;\n private value: T;\n\n constructor(opts: ConfigOptions<T>) {\n this.path = opts.path;\n this.defaults = opts.defaults;\n this.schema = opts.schema;\n this.format = opts.format ?? inferFormat(opts.path);\n this.seed = opts.seed;\n this.value = opts.seed ?? opts.defaults;\n }\n\n /** 当前内存值(只读消费;外部不得直接修改返回对象)。 */\n get(): T {\n return this.value;\n }\n\n /**\n * 加载配置:\n * - seed 存在 → 直接用 seed(已校验,不读文件;全局 TOML 分段装配方式);\n * - 文件缺失 → 写入默认值并落盘(首次运行自动生成),返回默认值;\n * - 文件存在 → 读取 + 校验,更新内存,返回。\n */\n async load(): Promise<T> {\n if (this.seed !== undefined) {\n this.value = this.seed;\n return this.value;\n }\n let raw: string;\n try {\n raw = await readFile(this.path, \"utf8\");\n } catch (err) {\n if (isMissingFileError(err)) {\n await this.save(this.defaults);\n return this.value;\n }\n throw new KernelError(`读取配置文件失败: ${this.path}`, \"UNKNOWN\", { cause: err });\n }\n this.value = this.parse(raw);\n return this.value;\n }\n\n /** 重新从磁盘加载(外部变更后的热更新入口,P2 配置热更新使用)。 */\n reload(): Promise<T> {\n return this.load();\n }\n\n /** 校验并落盘(原子写:临时文件 + rename),随后更新内存。 */\n async save(next: T): Promise<void> {\n const parsed = this.parse(this.serialize(next));\n mkdirSync(dirname(this.path), { recursive: true });\n const tmpPath = `${this.path}.tmp`;\n await writeFile(tmpPath, `${this.serialize(parsed)}\\n`, \"utf8\");\n await rename(tmpPath, this.path);\n this.value = parsed;\n }\n\n /** 对象 → 文本(TOML/JSON)。 */\n private serialize(value: T): string {\n if (this.format === \"toml\") {\n return stringifyTomlText(value as Record<string, unknown>);\n }\n return JSON.stringify(value, null, 4);\n }\n\n /** 文本 → 对象(TOML/JSON)。 */\n private deserialize(raw: string): unknown {\n if (this.format === \"toml\") {\n return parseTomlText(raw);\n }\n return JSON.parse(raw) as unknown;\n }\n\n /** 反序列化 + schema 校验,错误统一包装为 KernelError。 */\n private parse(raw: string): T {\n let input: unknown;\n try {\n input = this.deserialize(raw);\n } catch (err) {\n throw new KernelError(\n `配置文件 ${this.format.toUpperCase()} 解析失败: ${this.path}`,\n \"INVALID_PARAM\",\n { cause: err },\n );\n }\n try {\n return this.schema.parse(input);\n } catch (err) {\n throw new KernelError(`配置文件校验失败: ${this.path}`, \"INVALID_PARAM\", {\n cause: err,\n });\n }\n }\n}\n\n/** 解析 TOML 文本 → 对象(boot.cjs / 探测脚本等无 ConfigBase 场景复用)。 */\nexport function parseToml(text: string): Record<string, unknown> {\n return parseTomlText(text);\n}\n\n/** 对象 → TOML 文本(config init 生成全局配置文件等场景)。 */\nexport function stringifyToml(value: Record<string, unknown>): string {\n return stringifyTomlText(value);\n}\n","/**\n * pino 日志封装(ADR-007)\n *\n * - console(pino-pretty 可读输出)+ 可选文件双目的地\n * - 统一 level 控制\n * - 内置 redact(token / 票据等敏感字段不打日志),可追加\n * - 无全局单例(ADR-015 推论):每进程实例化一份,由装配层持有\n */\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport process from \"node:process\";\nimport pino from \"pino\";\nimport pinoPretty from \"pino-pretty\";\nimport { KernelError } from \"./errors.js\";\n\n/** 默认 redact 路径:顶层 token/票据 + 任意深度的密码/密钥。 */\nconst DEFAULT_REDACT_PATHS = [\"token\", \"ticket\", \"cookie\", \"password\", \"secret\"] as const;\n\nfunction toSingle(streams: Array<{ stream: pino.DestinationStream }>): pino.DestinationStream {\n const [only] = streams;\n if (only === undefined) {\n // 不可达:调用方已保证 streams 非空\n throw new KernelError(\"logger 内部错误:目的地列表为空\", \"UNKNOWN\");\n }\n return only.stream;\n}\n\n/** 日志级别(pino 标准级别 + silent)。 */\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\" | \"silent\";\n\nexport interface LoggerOptions {\n /** 日志级别,默认 `info`。 */\n level?: LogLevel;\n /** 是否输出到 console(pretty 模式),默认 true。 */\n console?: boolean;\n /** 文件日志路径;不传则不写文件(父目录自动创建)。 */\n file?: string;\n /** 附加基础字段(如 `{ name: 'kernel' }`),自动保留 pid;name 用 pino 保留字段\n * (logger name),pino-pretty 渲染为 `(name/pid)` 元数据头。 */\n base?: Record<string, unknown>;\n /** 追加的敏感字段路径(默认内置 token/票据等,见 DEFAULT_REDACT_PATHS)。 */\n redact?: string[];\n}\n\n/**\n * 创建 pino logger:console(pretty)+ 可选文件,统一 level / redact。\n *\n * 文件目的地用同步写入(`sync: true`):日志量级下性能可接受,且进程退出不丢日志,\n * 无需额外的 flush 步骤(对比异步 SonicBoom 的 flushSync 时序陷阱)。\n */\nexport function createLogger(opts: LoggerOptions = {}): pino.Logger {\n const level = opts.level ?? \"info\";\n const consoleEnabled = opts.console ?? true;\n const { file, base } = opts;\n\n const streams: Array<{ stream: pino.DestinationStream }> = [];\n if (consoleEnabled) {\n streams.push({\n stream: pinoPretty({\n colorize: true,\n // 短格式时间(2026-08-07 用户定稿):完整日期+时区前缀近 60 字符,\n // 每条日志重复占满横向视线;缩短为时分秒+毫秒,文件日志(JSON)\n // 仍保留完整时间戳可精确追溯。\n // ⚠️ 必须带 SYS: 前缀:无前缀按 UTC 渲染(北京 = UTC+8,时间会差 8 小时),\n // SYS: 才用本地系统时间。\n translateTime: \"SYS:HH:MM:ss.l\",\n // 原生多行展开(默认值,显式声明):属性按 key/value 逐行渲染,\n // 由 pino-pretty 自带颜色高亮(key 紫色 / 字符串青色),不手写格式化\n // 函数对抗原生渲染——高频业务日志(如收到消息)在调用点直接传\n // 纯字符串,不传对象,天然单行。\n singleLine: false,\n // ⚠️ 必须显式传 destination=process.stdout:pino-pretty 默认用\n // sonic-boom 直写 fd 1(UTF-8 字节流),在 pnpm start(cmd.exe +\n // 管道 936 转码)链路下中文被按 GBK 解码成乱码、ANSI 残留;\n // 走 process.stdout 的 TTY 路径(WriteConsoleW UTF-16)编码正确。\n destination: process.stdout,\n }),\n });\n }\n if (file) {\n mkdirSync(dirname(file), { recursive: true });\n streams.push({ stream: pino.destination({ dest: file, sync: true }) });\n }\n if (streams.length === 0) {\n throw new KernelError(\"logger 至少需要一个输出目标:console 或 file\", \"INVALID_PARAM\");\n }\n\n const pinoOptions: pino.LoggerOptions = {\n level,\n redact: [...DEFAULT_REDACT_PATHS, ...(opts.redact ?? [])],\n };\n if (base) {\n pinoOptions.base = { pid: process.pid, ...base };\n }\n\n let stream: pino.DestinationStream | ReturnType<typeof pino.multistream>;\n if (streams.length === 1) {\n stream = toSingle(streams);\n } else {\n stream = pino.multistream(streams);\n }\n\n return pino(pinoOptions, stream);\n}\n","/**\n * 数据目录布局(ADR-016,2026-08-08 修订:数据根默认放项目根)\n *\n * ```\n * <数据根>/<qq号>/ # 每账号独立目录(ADR-015 多账号前提)\n * ├── config/ # 各账号独立配置(历史产物,协议段已走 seed)\n * ├── logs/ # pino 文件日志\n * └── cache/ # 临时文件、媒体缓存\n *\n * <项目根>/napuketto.toml # 全局配置文件(放项目根目录,不入库)\n * <项目根>/.napuketto/ # 数据根(默认值,2026-08-08 用户拍板:直接放项目主目录)\n * ```\n *\n * 数据根优先级:cli `--data-dir`(显式参数)> `NAPKETTO_DATA` 环境变量 >\n * `<项目根>/.napuketto`(默认,部署原因不放用户目录)。\n * 配置文件路径见 `resolveConfigPath`(项目根探测,与数据根解耦)。\n */\nimport { existsSync, mkdirSync, readdirSync, rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\n/** 默认数据根目录名(项目根下,2026-08-08 用户拍板:不放用户目录)。 */\nexport const DEFAULT_DATA_ROOT_NAME = \".napuketto\";\n\n/** 全局配置文件名。 */\nexport const MAIN_CONFIG_FILE = \"napuketto.toml\";\n\nexport interface PathOptions {\n /** 显式数据根(cli `--data-dir` 传入,优先级最高)。 */\n dataRoot?: string;\n /** QQ 号,用于多账号隔离;缺省时直接使用数据根本身。 */\n account?: string;\n}\n\n/** `~` 前缀展开为用户主目录(跨平台配置文件写法 `dataDir = \"~/.napuketto\"`)。 */\nfunction expandTilde(p: string): string {\n if (p === \"~\") {\n return homedir();\n }\n if (p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n return join(homedir(), p.slice(2));\n }\n return p;\n}\n\n/**\n * 项目根查找:入口模块向上找 `pnpm-workspace.yaml`(monorepo 开发场景,即仓库根)→\n * cwd 向上找 `pnpm-workspace.yaml` / `package.json`(发布后用户项目)→ undefined(兜底)。\n */\nfunction resolveProjectRoot(): string | undefined {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const moduleRoot = findProjectRoot(moduleDir, \"pnpm-workspace.yaml\");\n if (moduleRoot !== undefined) {\n return moduleRoot;\n }\n return (\n findProjectRoot(process.cwd(), \"pnpm-workspace.yaml\") ??\n findProjectRoot(process.cwd(), \"package.json\")\n );\n}\n\n/**\n * 解析数据根:显式参数 > 环境变量 > `<项目根>/.napuketto`(默认)> 用户目录兜底(裸脚本)。\n * 支持 `~` 前缀(展开为用户主目录)与相对路径(相对 cwd 解析)。\n * 返回绝对路径。独立导出便于 cli / 探测脚本复用。\n */\nexport function resolveDataRoot(dataRoot?: string): string {\n const explicit = dataRoot ?? process.env[\"NAPKETTO_DATA\"];\n if (explicit !== undefined && explicit !== \"\") {\n return resolve(expandTilde(explicit));\n }\n const projectRoot = resolveProjectRoot();\n if (projectRoot !== undefined) {\n return join(projectRoot, DEFAULT_DATA_ROOT_NAME);\n }\n return join(homedir(), DEFAULT_DATA_ROOT_NAME);\n}\n\n/** 向上查找含标志文件的目录(项目根探测:monorepo 根 / 用户项目根)。 */\nfunction findProjectRoot(startDir: string, marker: string): string | undefined {\n let dir = startDir;\n for (;;) {\n if (existsSync(join(dir, marker))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return undefined;\n }\n dir = parent;\n }\n}\n\nexport interface ConfigPathOptions {\n /** 兜底数据根(探测全部失败时用 `<dataRoot>/napuketto.toml`,保持旧行为)。 */\n dataRoot?: string;\n}\n\n/**\n * 全局配置文件路径解析(2026-08-07 用户拍板:**配置文件放项目根目录,\n * 数据仍按数据根组织**——数据根只承载账号目录/日志/缓存/QQ 数据)。\n *\n * 优先级:\n * 1. `NAPKETTO_CONFIG` 环境变量(显式指定完整路径,任意场景可用)\n * 2. 项目根探测(入口模块向上找 pnpm-workspace.yaml → cwd 向上找,见 resolveProjectRoot)\n * 3. 数据根兜底 `<dataRoot>/napuketto.toml`(旧行为兼容)\n *\n * 返回绝对路径。独立导出便于 cli / loader 装配链复用。\n */\nexport function resolveConfigPath(opts: ConfigPathOptions = {}): string {\n const explicit = process.env[\"NAPKETTO_CONFIG\"];\n if (explicit !== undefined && explicit !== \"\") {\n return resolve(explicit);\n }\n const projectRoot = resolveProjectRoot();\n if (projectRoot !== undefined) {\n return join(projectRoot, MAIN_CONFIG_FILE);\n }\n return join(opts.dataRoot ?? resolveDataRoot(), MAIN_CONFIG_FILE);\n}\n\n/**\n * 路径装配器:持有账号数据目录下的全部子目录。\n * 无全局单例(ADR-015 推论)——每账号每进程实例化一份。\n */\nexport class PathWrapper {\n /** 数据根(绝对路径)。 */\n readonly root: string;\n /** 账号数据目录(绝对路径),缺省 account 时等于 root。 */\n readonly accountDir: string;\n /** config 目录(napuketto.json + 各协议配置)。 */\n readonly configDir: string;\n /** logs 目录(pino 文件日志)。 */\n readonly logsDir: string;\n /** cache 目录(临时文件、媒体缓存)。 */\n readonly cacheDir: string;\n\n constructor(opts: PathOptions = {}) {\n const root = resolveDataRoot(opts.dataRoot);\n this.root = root;\n if (opts.account) {\n this.accountDir = join(root, opts.account);\n } else {\n this.accountDir = root;\n }\n this.configDir = join(this.accountDir, \"config\");\n this.logsDir = join(this.accountDir, \"logs\");\n this.cacheDir = join(this.accountDir, \"cache\");\n }\n\n /** 递归创建全部目录,启动装配时调用一次。 */\n ensure(): void {\n for (const dir of [this.configDir, this.logsDir, this.cacheDir]) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n /** 账号目录内的文件路径(如 `file('config', 'napuketto.json')`)。 */\n file(...segments: string[]): string {\n return join(this.accountDir, ...segments);\n }\n\n /**\n * 清空缓存目录(clean_cache 动作经注入回调消费)。\n * 只删除 cacheDir 下的条目,保留目录本身。\n */\n clearCache(): void {\n let entries: string[];\n try {\n entries = readdirSync(this.cacheDir);\n } catch {\n return; // 目录不存在视为空\n }\n for (const entry of entries) {\n rmSync(join(this.cacheDir, entry), { recursive: true, force: true });\n }\n }\n}\n","/**\n * 原生调用结果解包(ADR-009 统一错误语义)\n *\n * wrapper.node 的 service 方法大多返回 `{ result, errMsg }`:\n * - unwrap:result 为 number 且非 0 → 抛 KernelError\n * - checkLooseResult:宽松形状(result 可能缺失/非数字)→ 仅当 result 为\n * 数字且非 0 时抛错(探测期兼容:某些 API 成功时 result 缺省或为对象)\n * - unwrapResult:result !== 0 → 按 errMsg 内容映射语义错误码(msg 系专用)\n */\n\nimport { type KernelErrorCode, kernelError } from \"../infra/index.js\";\nimport type { GeneralCallResult } from \"../types/index.js\";\n\n/** errMsg 关键词 → 错误码规则(顺序即优先级,命中任一组关键词即映射)。 */\nconst RESULT_CODE_RULES: ReadonlyArray<{ code: KernelErrorCode; keywords: readonly string[] }> = [\n { code: \"NOT_LOGIN\", keywords: [\"未登录\", \"login\"] },\n { code: \"PERMISSION_DENIED\", keywords: [\"无权限\", \"permission\"] },\n { code: \"NOT_FOUND\", keywords: [\"不存在\", \"not found\"] },\n];\n\n/** 按 errMsg 关键词映射错误码(未命中 → UNKNOWN)。 */\nfunction mapResultCode(msg: string): KernelErrorCode {\n for (const rule of RESULT_CODE_RULES) {\n if (rule.keywords.some((k) => msg.includes(k))) {\n return rule.code;\n }\n }\n return \"UNKNOWN\";\n}\n\n/** 原生 result 解包(result 字段非 0 抛 KernelError)。 */\nexport function unwrap(label: string, result: number, errMsg?: string): void {\n if (result === 0) {\n return;\n }\n throw kernelError(`${label} 失败: ${errMsg ?? \"无错误详情\"}`, \"UNKNOWN\");\n}\n\n/** 宽松 result 校验(result 为数字非 0 视为失败;缺失/非数字视为成功)。 */\nexport function checkLooseResult(\n label: string,\n res: { result?: unknown; errMsg?: unknown } | null | undefined,\n): void {\n if (res === undefined || res === null || typeof res.result !== \"number\" || res.result === 0) {\n return;\n }\n throw kernelError(`${label} 失败: ${String(res.errMsg ?? \"\")}`, \"UNKNOWN\");\n}\n\n/** 原生结果解包:result !== 0 抛 KernelError(errMsg 语义映射错误码)。 */\nexport function unwrapResult<T extends GeneralCallResult>(label: string, raw: T): void {\n if (raw.result === 0) {\n return;\n }\n const msg = raw.errMsg || \"无错误详情\";\n // 语义码优先(未登录/无权限/不存在),未命中且为 sendMsg 时兜底 SEND_FAILED\n let code = mapResultCode(msg);\n if (code === \"UNKNOWN\" && label === \"sendMsg\") {\n code = \"SEND_FAILED\";\n }\n throw kernelError(`${label} 失败: ${msg}`, code);\n}\n","/**\n * FriendApi:好友语义化 API(ADR-009 统一错误语义)\n *\n * 内部解包原生返回:成功返回纯业务值,失败抛 KernelError。\n * 方法面(P2-4 + P2-11):好友列表(uid + 昵称/备注)+ 好友请求\n * (getBuddyReq / approvalFriendRequest / setBuddyRemark / delBuddy /\n * getDoubtBuddyReq / approvalDoubtBuddyReq)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n BuddyCategory,\n BuddyReq,\n DoubtBuddyReq,\n NodeIKernelBuddyService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\nimport { checkLooseResult, unwrap } from \"./result.js\";\n\n/** 好友条目(uid + 昵称/备注,昵称由 buddy service 缓存补全)。 */\nexport interface Friend {\n uid: string;\n uin: string;\n nickname: string;\n remark: string;\n}\n\n/** 带分类的好友列表项(get_friends_with_category 返回)。 */\nexport interface FriendCategory {\n categoryId: number;\n categorySortId: number;\n categoryName: string;\n categoryMbCount: number;\n onlineCount: number;\n buddyList: Friend[];\n}\n\n/** 可疑好友申请(OB11 结构,get_doubt_friends_add_request 返回)。 */\nexport interface DoubtFriendRequestInfo {\n flag: string;\n uin: number;\n nick?: string;\n source?: string;\n reason?: string;\n msg?: string;\n group_code?: string;\n time?: string;\n type: \"doubt\";\n}\n\n/** FriendApi 构造选项。 */\nexport interface FriendApiOptions {\n /** uid→uin 转换器(通常注入 GroupApi.uidToUin);缺省 uin 退化为 uid。 */\n uidToUin?: (uids: string[]) => Promise<Map<string, string>>;\n}\n\n/** 好友 API:从 session 拿 buddy service,包装成语义化方法。 */\nexport class FriendApi {\n private readonly service: NodeIKernelBuddyService;\n private readonly uidToUin: ((uids: string[]) => Promise<Map<string, string>>) | undefined;\n\n constructor(session: NodeIQQNTWrapperSession, opts: FriendApiOptions = {}) {\n const service = session.getBuddyService() as unknown as NodeIKernelBuddyService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getBuddyService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.uidToUin = opts.uidToUin;\n }\n\n /** 好友列表(分类拍平为 Friend[];昵称/备注从 service 缓存读取)。 */\n async getFriendList(): Promise<Friend[]> {\n let categories: BuddyCategory[];\n try {\n categories = await this.service.getBuddyListFromCache(0);\n } catch {\n const raw = await this.service.getBuddyListV2(\"cli\", true, 0);\n if (raw.result !== 0) {\n throw kernelError(`getBuddyListV2 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n categories = raw.data;\n }\n // buddyList(明细,T10 实证)优先,buddyUids(旧描述字段)兜底\n const uids = categories.flatMap(\n (cat) => cat.buddyList?.map((b) => b.uid) ?? cat.buddyUids ?? [],\n );\n let uinMap = new Map<string, string>();\n if (this.uidToUin !== undefined) {\n uinMap = await this.uidToUin(uids);\n }\n const out: Friend[] = [];\n for (const uid of uids) {\n out.push({\n uid,\n uin: uinMap.get(uid) ?? uid,\n nickname: this.service.getBuddyNick(uid),\n remark: this.service.getBuddyRemark(uid),\n });\n }\n return out;\n }\n\n /** 带分类的好友列表(get_friends_with_category,分类保留)。 */\n async getFriendCategories(): Promise<FriendCategory[]> {\n const categories = await this.getBuddyCategories();\n const friendMap = new Map<string, Friend>();\n for (const friend of await this.getFriendList()) {\n friendMap.set(friend.uid, friend);\n }\n return categories.map((cat) => {\n const buddies: Friend[] = [];\n for (const uid of cat.buddyList?.map((b) => b.uid) ?? cat.buddyUids ?? []) {\n const friend = friendMap.get(uid);\n if (friend !== undefined) {\n buddies.push(friend);\n }\n }\n return {\n categoryId: cat.categoryId,\n categorySortId: cat.categorySortId,\n categoryName: cat.categroyName,\n categoryMbCount: cat.categroyMbCount,\n onlineCount: cat.onlineCount,\n buddyList: buddies,\n };\n });\n }\n\n /** 原始分类(getBuddyListV2 / 缓存)。 */\n private async getBuddyCategories(): Promise<BuddyCategory[]> {\n try {\n return await this.service.getBuddyListFromCache(0);\n } catch {\n const raw = await this.service.getBuddyListV2(\"cli\", true, 0);\n if (raw.result !== 0) {\n throw kernelError(`getBuddyListV2 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw.data;\n }\n }\n\n /** 判断是否好友。 */\n isBuddy(uid: string): boolean {\n return this.service.isBuddy(uid);\n }\n\n /** 好友申请列表(set_friend_add_request 应答前查找匹配项)。 */\n async getBuddyReqList(): Promise<BuddyReq[]> {\n const raw = await this.service.getBuddyReq();\n unwrap(\"getBuddyReq\", raw.result, raw.errMsg);\n return raw.buddyReqs ?? [];\n }\n\n /** 应答好友申请(accept=true 同意;remark 由调用方另行 setFriendRemark)。 */\n async handleFriendRequest(notify: BuddyReq, accept: boolean): Promise<void> {\n await this.service.approvalFriendRequest({\n friendUid: notify.friendUid,\n reqTime: notify.reqTime,\n accept,\n });\n }\n\n /** 设置好友备注(void 语义乐观处理)。 */\n setFriendRemark(uid: string, remark: string): void {\n this.service.setBuddyRemark({ uid, remark });\n }\n\n /** 删除好友(tempBlock=拉黑,tempBothDel=双向删除)。 */\n async deleteFriend(uid: string, tempBlock = false, tempBothDel = false): Promise<void> {\n const res = (await this.service.delBuddy({\n friendUid: uid,\n tempBlock,\n tempBothDel,\n })) as { result?: unknown; errMsg?: unknown } | undefined;\n checkLooseResult(\"delBuddy\", res);\n }\n\n /** 可疑好友申请列表(get_doubt_friends_add_request;uin 经 uidToUin 转换)。 */\n async getDoubtFriendRequest(count: number): Promise<DoubtFriendRequestInfo[]> {\n const reqId = String(Date.now());\n const raw = await this.service.getDoubtBuddyReq(reqId, count, \"\");\n unwrap(\"getDoubtBuddyReq\", raw.result, raw.errMsg);\n const list: DoubtBuddyReq[] = raw.doubtList ?? [];\n let uinMap = new Map<string, string>();\n if (this.uidToUin !== undefined && list.length > 0) {\n uinMap = await this.uidToUin(list.map((item) => item.uid));\n }\n const out: DoubtFriendRequestInfo[] = [];\n for (const item of list) {\n out.push(this.toDoubtFriendRequestInfo(item, uinMap));\n }\n return out;\n }\n\n /** 单项转换:DoubtBuddyReq → OB11 结构(可选字段条件赋值,exactOptionalPropertyTypes)。 */\n private toDoubtFriendRequestInfo(\n item: DoubtBuddyReq,\n uinMap: Map<string, string>,\n ): DoubtFriendRequestInfo {\n return toDoubtFriendRequestInfo(item, uinMap);\n }\n\n /** 处理可疑好友申请(set_doubt_friends_add_request;uid=flag)。 */\n handleDoubtFriendRequest(uid: string): void {\n this.service.approvalDoubtBuddyReq(uid, \"\", \"\");\n }\n}\n\n/** 可选字段条件赋值(undefined 跳过,兼容 exactOptionalPropertyTypes)。 */\nfunction assignOptional<K extends keyof DoubtFriendRequestInfo>(\n info: DoubtFriendRequestInfo,\n key: K,\n value: DoubtFriendRequestInfo[K] | undefined,\n): void {\n if (value !== undefined) {\n info[key] = value;\n }\n}\n\n/** 单项转换:DoubtBuddyReq → OB11 结构(导出供单测,friend.test.ts)。 */\nexport function toDoubtFriendRequestInfo(\n item: DoubtBuddyReq,\n uinMap: Map<string, string>,\n): DoubtFriendRequestInfo {\n const info: DoubtFriendRequestInfo = {\n flag: item.uid,\n uin: Number(uinMap.get(item.uid) ?? item.uid),\n type: \"doubt\",\n };\n assignOptional(info, \"nick\", item.nick);\n assignOptional(info, \"source\", item.source);\n assignOptional(info, \"reason\", item.reason);\n assignOptional(info, \"msg\", item.msg);\n assignOptional(info, \"group_code\", item.groupCode);\n assignOptional(info, \"time\", item.reqTime);\n return info;\n}\n","/**\n * 实体类型层(运行时探测产物 + 公开资料作说明书,ADR-006)\n *\n * ⚠️ 部分字段按公开资料(wrapper 外部契约)自研描述,待下一次进程内\n * 探测(probe.ts)校准——接口签名是外部系统事实,自研描述其形状。\n */\n\n/** 会话类型(QQ wrapper 外部契约)。 */\nexport const ChatType = {\n C2C: 1, // 私聊\n GROUP: 2, // 群聊\n DISC: 3, // 讨论组\n GUILD: 4, // 频道\n C2C_TEMP: 100, // 临时会话(群内私聊)\n} as const;\nexport type ChatType = (typeof ChatType)[keyof typeof ChatType];\n\n/** 会话对象(peer 目标:群号 / 用户 uid)。 */\nexport interface Peer {\n chatType: ChatType;\n peerUid: string;\n guildId?: string;\n}\n\n/** 文本元素(at 用 atType + atUid 表达)。 */\nexport interface TextElement {\n content: string;\n atType?: number; // 1=全体 2=单人 4=我\n atUid?: string;\n}\n\n/** 图片元素(发送时 picPath 必须;接收含 url)。 */\nexport interface PicElement {\n picPath?: string;\n picUrl?: string;\n picType?: number;\n picWidth?: number;\n picHeight?: number;\n md5?: string;\n fileName?: string;\n sourcePath?: string;\n thumbPath?: string;\n // 发送用完整字段(NapCat 式,2026-08-11 实证:缺任一发送失败)\n md5HexStr?: string;\n fileSize?: string;\n original?: boolean;\n picSubType?: number;\n fileUuid?: string;\n fileSubId?: string;\n thumbFileSize?: number;\n summary?: string;\n}\n\n/** 语音元素(silk)。 */\nexport interface PttElement {\n filePath?: string;\n md5?: string;\n fileName?: string;\n duration?: number;\n /** 语音转文字结果(translatePtt2Text 异步写回)。 */\n text?: string;\n // 发送用完整字段(NapCat 式,2026-08-12 实证:只给 filePath 会触发 wrapper\n // 内部 \"Cannot convert undefined or null to object\"(缺字段转换失败))\n md5HexStr?: string;\n fileSize?: string;\n formatType?: number;\n voiceType?: number;\n voiceChangeType?: number;\n canConvert2Text?: boolean;\n waveAmplitudes?: number[];\n fileSubId?: string;\n playState?: number;\n autoConvertText?: number;\n storeID?: number;\n otherBusinessInfo?: { aiVoiceType: number };\n // 收向完整字段(2026-09-08 B1 实测快照,getLatestDbMsgs 历史消息提取)\n fileId?: number;\n fileUuid?: string;\n translateStatus?: number;\n /** 传输状态:实测 2=收到已下载(数据库态,此时挪走本地文件调 downloadRichMedia 不重下)、4=自己发送已上传。 */\n transferStatus?: number;\n progress?: number;\n invalidState?: number;\n fileBizId?: unknown;\n import_rich_media_context?: unknown;\n isInApplicationDataPath?: boolean | null;\n}\n\n/** 表情元素。 */\nexport interface FaceElement {\n faceIndex: number;\n faceType?: number;\n}\n\n/** 回复元素(引用消息)。 */\nexport interface ReplyElement {\n replayMsgId: string;\n replayMsgSeq?: string;\n replayMsgTime?: string;\n senderUid?: string;\n senderUin?: string;\n}\n\n/** 视频元素。 */\nexport interface VideoElement {\n filePath?: string;\n videoUrl?: string;\n fileName?: string;\n fileSize?: string;\n}\n\n/** 文件元素。 */\nexport interface FileElement {\n filePath?: string;\n fileName?: string;\n fileSize?: string;\n fileUuid?: string;\n}\n\n/** 灰色提示子类型(系统事件类型,QQ wrapper 契约)。 */\nexport const GrayTipSubType = {\n REVOKE: 1, // 撤回\n PROCLAMATION: 2, // 群公告\n GROUP: 4, // 群成员变动(TipGroupElement)\n BUDDY: 5, // 好友变动\n FEED: 6,\n ESSENCE: 7, // 精华消息\n GROUP_NOTIFY: 8, // 群通知\n BUDDY_NOTIFY: 9,\n FILE: 10,\n JSON: 17,\n} as const;\nexport type GrayTipSubType = (typeof GrayTipSubType)[keyof typeof GrayTipSubType];\n\n/** 群成员变动类型(TipGroupElement.type)。 */\nexport const TipGroupElementType = {\n MEMBER_ADD: 1, // 入群\n DISBANDED: 2, // 群解散\n QUIT: 3, // 退群\n CREATED: 4, // 建群\n GROUP_NAME_MODIFIED: 5, // 群名修改\n BLOCK: 6,\n UNBLOCK: 7,\n SHUT_UP: 8, // 禁言\n} as const;\nexport type TipGroupElementType = (typeof TipGroupElementType)[keyof typeof TipGroupElementType];\n\n/** 撤回元素(revokeElement)。 */\nexport interface GrayTipRevokeElement {\n operatorUid: string;\n operatorNick?: string;\n operatorRole?: string;\n isSelfOperate?: boolean;\n wording?: string;\n}\n\n/** AIOP 操作元素(戳一戳等,aioOpGrayTipElement)。 */\nexport interface TipAioOpGrayTipElement {\n operateType?: number;\n peerUid?: string;\n fromGrpCodeOfTmpChat?: string;\n}\n\n/** 群成员变动元素(groupElement 内的 TipGroupElement)。 */\nexport interface TipGroupElement {\n type?: TipGroupElementType;\n groupName?: string;\n memberUid?: string;\n memberNick?: string;\n memberRemark?: string;\n adminUid?: string;\n adminNick?: string;\n adminRemark?: string;\n shutUp?: {\n curTime?: string;\n duration?: string; // 禁言秒数\n admin?: { uid?: string; card?: string; name?: string; role?: number };\n member?: { uid?: string; card?: string; name?: string; role?: number };\n };\n}\n\n/** 灰色提示元素(elementType=8,系统事件载体)。 */\nexport interface GrayTipElement {\n subElementType?: GrayTipSubType;\n revokeElement?: GrayTipRevokeElement;\n groupElement?: TipGroupElement;\n aioOpGrayTipElement?: TipAioOpGrayTipElement;\n xmlElement?: { busiId?: string; content?: string; templId?: string };\n jsonGrayTipElement?: { busiId?: string; jsonStr?: string };\n}\n\n/** 消息元素(RawMessage.elements 成员,elementType + 子元素互斥)。 */\nexport interface RawElement {\n elementType: number;\n elementId?: string;\n textElement?: TextElement;\n picElement?: PicElement;\n pttElement?: PttElement;\n faceElement?: FaceElement;\n replyElement?: ReplyElement;\n videoElement?: VideoElement;\n fileElement?: FileElement;\n grayTipElement?: GrayTipElement;\n /** 合并转发元素(elementType=16 MULTI_FORWARD)。 */\n multiForwardMsgElement?: {\n resId: string;\n fileName: string;\n xmlContent: string;\n };\n}\n\n/** QQ 消息(RawMessage,说明书参考字段,探测后校准)。 */\nexport interface RawMessage {\n msgId: string; // 雪花 ID\n msgSeq: string; // 消息序列号\n msgTime: string; // 时间戳\n msgRandom?: string;\n msgType: number;\n chatType: ChatType;\n peerUid: string; // 群号 / 用户 uid\n peerUin: string; // 群号 / 用户 QQ 号\n senderUid: string;\n senderUin: string;\n peerName: string;\n sendNickName: string;\n sendMemberName?: string;\n /** 发送状态(0=失败 1=发送中 2=成功 3=成功无seq)。onMsgInfoListUpdate 事件填充。 */\n sendStatus?: number;\n elements: RawElement[];\n records?: RawMessage[];\n guildId?: string;\n [key: string]: unknown;\n}\n","/**\n * Listener 接口层(运行时探测产物 + 公开资料作说明书,ADR-003 / ADR-006)\n *\n * GroupListener:群服务(GroupService)的原生回调监听接口。\n * 签名依据 wrapper 外部契约自研描述(零复制)——事件名约定 `${Service}/${method}`,由 event-channel 编译期推导。\n * 用 type 别名(非 interface):满足 ListenerShape(Record<string, unknown>)约束。\n */\nimport type {\n Group,\n GroupDetailInfo,\n GroupMember,\n GroupNotify,\n ShutUpGroupMember,\n} from \"../services/group-service.js\";\n\n/** 群列表更新类型(说明书参考,值待探测校准)。 */\nexport const GroupListUpdateType = {\n /** 全量更新(groupList 为完整列表)。 */\n ALL: 0,\n /** 增量更新(合并到现有缓存)。 */\n PARTIAL: 1,\n} as const;\nexport type GroupListUpdateType = (typeof GroupListUpdateType)[keyof typeof GroupListUpdateType];\n\n/** 成员数据来源(onMemberInfoChange 第二参,说明书参考)。 */\nexport const GroupMemberDataSource = {\n /** 本地缓存。 */\n CACHE: 0,\n /** 网络拉取。 */\n NETWORK: 1,\n} as const;\nexport type GroupMemberDataSource =\n (typeof GroupMemberDataSource)[keyof typeof GroupMemberDataSource];\n\n/** onMemberListChange 参数(sceneId 为群号 groupCode)。 */\nexport interface GroupMemberListChange {\n sceneId: string;\n ids: string[];\n /** uid → GroupMember。 */\n infos: Map<string, GroupMember>;\n hasPrev: boolean;\n hasNext: boolean;\n hasRobot: boolean;\n}\n\n/** 群服务(GroupService)的原生回调监听接口。 */\nexport type GroupListener = {\n /** 群列表初始化完成(listEmpty:是否无群)。 */\n onGroupListInited: (listEmpty: boolean) => void;\n /** 群列表更新。 */\n onGroupListUpdate: (updateType: GroupListUpdateType, groupList: Group[]) => void;\n /** 群详情变化。 */\n onGroupDetailInfoChange: (detailInfo: GroupDetailInfo) => void;\n /** 群成员列表变化(sceneId=groupCode)。 */\n onMemberListChange: (arg: GroupMemberListChange) => void;\n /** 群成员信息变化(dataSource 区分本地缓存/网络)。 */\n onMemberInfoChange: (\n groupCode: string,\n dataSource: GroupMemberDataSource,\n members: Map<string, GroupMember>,\n ) => void;\n /** 群通知更新(doubt:是否可疑/忽略列表)。 */\n onGroupNotifiesUpdated: (doubt: boolean, notifies: GroupNotify[]) => void;\n /** 群通知单屏(分页 seq)。 */\n onGroupSingleScreenNotifies: (doubt: boolean, seq: string, notifies: GroupNotify[]) => void;\n /** 群禁言成员列表变化。 */\n onShutUpMemberListChanged: (groupCode: string, members: ShutUpGroupMember[]) => void;\n /**\n * 群精华列表变化(OB11 group_essence 候选源,2026-09-10 接线)。\n * 证据:wrapper.node 9.9.33-52230 字符串——小写名位于 listener 反射字符串簇,\n * 与已知 onMemberListChange/onBuddyReqChange 交错(同一字符串表页);\n * API 族旁证 addGroupEssence/getGroupLatestEssenceList。\n * ⚠️ 参数形状待真实事件校准(unknown 透传,订阅方 raw 日志观测)。\n */\n onGroupEssenceListChange: (arg: unknown) => void;\n};\n","/**\n * NodeIKernelMsgService:消息服务接口面(自研描述,非移植)\n *\n * 依据:getMsgService() 运行时反射 + wrapper 外部契约(接口签名是 QQ 的外部事实,\n * 自研描述其形状,零复制实现)。\n * 只收录 apis/msg 当前需要的方法;其余 200+ 方法按需探测后补齐。\n */\nimport type {\n FaceElement,\n Peer,\n PicElement,\n PttElement,\n RawMessage,\n ReplyElement,\n TextElement,\n} from \"../entities.js\";\n\n/** 原生通用返回:result 非 0 即失败(errMsg 附原因)。 */\nexport interface GeneralCallResult {\n result: number;\n errMsg: string;\n}\n\n/** 消息元素类型枚举(QQ wrapper 外部契约,自研描述)。 */\nexport const ElementType = {\n UNKNOWN: 0,\n TEXT: 1,\n PIC: 2,\n FILE: 3,\n PTT: 4,\n VIDEO: 5,\n FACE: 6,\n REPLY: 7,\n GRAY_TIP: 8,\n WALLET: 9,\n ARK: 10,\n MFACE: 11,\n LIVE_GIFT: 12,\n STRUCT_LONG_MSG: 13,\n MARKDOWN: 14,\n GIPHY: 15,\n MULTI_FORWARD: 16,\n INLINE_KEYBOARD: 17,\n INTEXT_GIFT: 18,\n CALENDAR: 19,\n} as const;\nexport type ElementType = (typeof ElementType)[keyof typeof ElementType];\n\n/** 发送消息元素(session 发送用,elementType + 各子元素互斥)。 */\nexport interface SendMessageElement {\n elementType: ElementType;\n elementId?: string;\n textElement?: TextElement;\n faceElement?: FaceElement;\n replyElement?: ReplyElement;\n picElement?: PicElement;\n pttElement?: PttElement;\n}\n\n/** 消息服务:apis/msg 用到的核心方法面。 */\nexport interface NodeIKernelMsgService {\n addKernelMsgListener(listener: unknown): number;\n removeKernelMsgListener(listenerId: number): void;\n generateMsgUniqueId(chatType: number, time: string): string;\n sendMsg(\n msgId: string,\n peer: Peer,\n elements: SendMessageElement[],\n map: Map<number, unknown>,\n ): Promise<GeneralCallResult>;\n recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;\n getMsgs(\n peer: Peer,\n msgId: string,\n count: number,\n queryOrder: boolean,\n ): Promise<GeneralCallResult & { msgList?: RawMessage[] }>;\n /** 按 msgId 批量拉取消息(get_msg / 精华消息 / ptt 转文字共用)。 */\n getMsgsByMsgId(\n peer: Peer,\n ids: string[],\n ): Promise<GeneralCallResult & { msgList?: RawMessage[] }>;\n /**\n * 富媒体主动下载(get_record 语音下载,2026-09-08 B1 实测:单参数对象,\n * resolve undefined——结果只能经重拉消息元素观察;参数残缺时静默 no-op 不抛错;\n * transferStatus=2 已下载态调用为 no-op,详见 kernel design.md §5)。\n */\n downloadRichMedia(param: {\n msgId: string;\n elemId: string;\n chatType: number;\n downloadType: number;\n thumbSize: number;\n }): Promise<void>;\n setMsgRead(peer: Peer): Promise<GeneralCallResult>;\n /** 消息表情表态(set_msg_emoji_like;setOrCancel=true 点赞,false 取消)。 */\n setMsgEmojiLikes(\n peer: Peer,\n msgSeq: string,\n emojiId: string,\n emojiType: string,\n setOrCancel: boolean,\n ): Promise<GeneralCallResult>;\n /** 语音转文字(fetch_ptt_text;异步转写,结果写回 pttElement.text)。 */\n translatePtt2Text(msgId: string, peer: Peer, msgElement: unknown): Promise<GeneralCallResult>;\n /** 输入状态(set_input_status;eventType=1 输入中,0 停止)。 */\n sendShowInputStatusReq(chatType: number, eventType: number, toUid: string): Promise<unknown>;\n /**\n * 富媒体文件目标路径(NapCat 式发送图片前置:md5+文件名 → QQ 内部 Ori/Thumb 路径)。\n * 返回纯文件名(相对 QQ 数据根),需配合 NodeQQNTWrapperUtil.copyFile 放置文件。\n */\n getRichMediaFilePathForGuild(req: {\n md5HexStr: string;\n fileName: string;\n elementType: number;\n elementSubType: number;\n thumbSize: number;\n needCreate: boolean;\n downloadType: number;\n file_uuid: string;\n }): string;\n /** 合并转发组装(send_group/private_forward_msg;返回 MULTI_FORWARD 元素)。 */\n buildMultiForwardMsg(req: {\n srcMsgIds: string[];\n srcContact: Peer;\n }): Promise<GeneralCallResult & { rspInfo?: { elements?: SendMessageElement[] } }>;\n /** 获取合并转发内容(get_forward_msg;resId 取自 multiForwardMsgElement)。 */\n getMultiMsg(\n peer: Peer,\n msgId: string,\n resId: string,\n ): Promise<GeneralCallResult & { msgList?: RawMessage[] }>;\n /** 单条转发(forward_group/friend_single_msg;msgIds 源、dstPeers 目标)。 */\n forwardMsg(\n msgIds: string[],\n peer: Peer,\n dstPeers: Peer[],\n commentElements: unknown,\n ): Promise<GeneralCallResult>;\n /** 设置在线状态(set_online_status;customStatus 为自定义状态)。 */\n setStatus(args: {\n status: number;\n extStatus: number;\n batteryStatus: number;\n customStatus?: {\n faceId: string;\n wording: string;\n faceType: string;\n };\n }): Promise<GeneralCallResult>;\n}\n","/**\n * canonical 消息元素模型(协议无关,ADR-008 延伸)\n *\n * 描述 QQ 消息的事实结构,是各协议翻译层的公共中间表示:\n * kernel 只做一次 NT ↔ canonical(toCanonicalElements / toSendElements),\n * 各协议(onebot11/satori)只写薄映射 canonical ↔ 协议格式。\n */\n\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n FaceElement,\n FileElement,\n PicElement,\n PttElement,\n RawElement,\n RawMessage,\n ReplyElement,\n TextElement,\n VideoElement,\n} from \"./entities.js\";\nimport { ElementType, type SendMessageElement } from \"./services/msg-service.js\";\n\n/** 文本 at 类型(QQ wrapper 契约,自研描述)。 */\nconst AtType = { ALL: 1, ONE: 2, ME: 4 } as const;\n\n/** TEXT 元素 → canonical(at 或纯文本)。 */\nfunction textToCanonical(textEl: TextElement | undefined): CanonicalElement {\n if (textEl === undefined) {\n return { type: \"text\", text: \"\" };\n }\n const { atType, atUid, content } = textEl;\n if (atType === AtType.ALL) {\n return { type: \"at\", target: \"all\" };\n }\n if (atType === AtType.ONE || atType === AtType.ME) {\n // 2026-08-07:at 段补 display(显示名)——QQ 的 content 通常是 \"@昵称\",\n // 剥掉 @ 作 display,供 OB11 at 段 name / 日志渲染使用(此前只有 target=uid)。\n const base = { type: \"at\" as const, target: atUid ?? content };\n const display = content.startsWith(\"@\") ? content.slice(1) : content;\n if (display === \"\") {\n return base;\n }\n return { ...base, display };\n }\n return { type: \"text\", text: content };\n}\n\n/** PIC 元素 → canonical image。 */\nfunction picToCanonical(pic: PicElement | undefined): CanonicalElement {\n if (pic === undefined) {\n return { type: \"image\", path: \"\" };\n }\n const path = pic.picPath ?? pic.picUrl ?? \"\";\n const url = pic.picUrl;\n if (url === undefined) {\n return { type: \"image\", path };\n }\n return { type: \"image\", path, url };\n}\n\n/** FACE 元素 → canonical face。 */\nfunction faceToCanonical(face: FaceElement | undefined): CanonicalElement {\n if (face === undefined) {\n return { type: \"face\", id: \"0\" };\n }\n return { type: \"face\", id: String(face.faceIndex) };\n}\n\n/** PTT 元素 → canonical voice。 */\nfunction pttToCanonical(ptt: PttElement | undefined): CanonicalElement {\n if (ptt === undefined) {\n return { type: \"voice\", path: \"\" };\n }\n return { type: \"voice\", path: ptt.filePath ?? \"\" };\n}\n\n/** VIDEO 元素 → canonical video。 */\nfunction videoToCanonical(video: VideoElement | undefined): CanonicalElement {\n if (video === undefined) {\n return { type: \"video\", path: \"\" };\n }\n const path = video.filePath ?? video.videoUrl ?? \"\";\n const url = video.videoUrl;\n if (url === undefined) {\n return { type: \"video\", path };\n }\n return { type: \"video\", path, url };\n}\n\n/** FILE 元素 → canonical file。 */\nfunction fileToCanonical(file: FileElement | undefined): CanonicalElement {\n if (file === undefined) {\n return { type: \"file\", path: \"\" };\n }\n const path = file.filePath ?? \"\";\n const name = file.fileName;\n if (name === undefined) {\n return { type: \"file\", path };\n }\n return { type: \"file\", path, name };\n}\n\n/** REPLY 元素 → canonical reply。 */\nfunction replyToCanonical(reply: ReplyElement | undefined): CanonicalElement {\n if (reply === undefined) {\n return { type: \"reply\", messageId: \"\" };\n }\n return { type: \"reply\", messageId: reply.replayMsgId };\n}\n\n/** 单个 NT 元素 → canonical(接收方向,宽容:无法表达回 unknown,不抛错)。 */\nfunction toCanonicalElement(el: RawElement): CanonicalElement {\n switch (el.elementType) {\n case ElementType.TEXT:\n return textToCanonical(el.textElement);\n case ElementType.PIC:\n return picToCanonical(el.picElement);\n case ElementType.FACE:\n return faceToCanonical(el.faceElement);\n case ElementType.PTT:\n return pttToCanonical(el.pttElement);\n case ElementType.VIDEO:\n return videoToCanonical(el.videoElement);\n case ElementType.FILE:\n return fileToCanonical(el.fileElement);\n case ElementType.REPLY:\n return replyToCanonical(el.replyElement);\n default:\n return { type: \"unknown\", raw: el };\n }\n}\n\n/** NT RawMessage → canonical 元素(与 toSendElements 对称,接收方向)。 */\nexport function toCanonicalElements(msg: RawMessage): CanonicalElement[] {\n return msg.elements.map(toCanonicalElement);\n}\n\n/** canonical 元素 → NT 发送元素(text/at/face/image/voice/reply 核心五类)。 */\nexport function toSendElements(elements: CanonicalElement[]): SendMessageElement[] {\n const out: SendMessageElement[] = [];\n for (const el of elements) {\n switch (el.type) {\n case \"text\":\n out.push({ elementType: ElementType.TEXT, textElement: { content: el.text } });\n break;\n case \"at\":\n if (el.target === \"all\") {\n out.push({\n elementType: ElementType.TEXT,\n textElement: { content: el.display ?? \"@\", atType: 1 },\n });\n } else {\n out.push({\n elementType: ElementType.TEXT,\n textElement: { content: el.display ?? \"@\", atType: 2, atUid: el.target },\n });\n }\n break;\n case \"face\":\n out.push({\n elementType: ElementType.FACE,\n faceElement: { faceIndex: Number(el.id) },\n });\n break;\n case \"image\":\n out.push({ elementType: ElementType.PIC, picElement: { picPath: el.path } });\n break;\n case \"voice\":\n out.push({ elementType: ElementType.PTT, pttElement: { filePath: el.path } });\n break;\n case \"reply\":\n out.push({\n elementType: ElementType.REPLY,\n replyElement: {\n replayMsgId: el.messageId,\n replayMsgSeq: el.messageId,\n replayMsgTime: \"0\",\n },\n });\n break;\n case \"video\":\n case \"file\":\n case \"forward\":\n case \"json\":\n case \"xml\":\n case \"unknown\":\n throw kernelError(`发送元素 ${el.type} 暂不支持(P2-2 探测后补)`, \"INVALID_PARAM\");\n default:\n // exhaustive:CanonicalElement 无其他成员\n break;\n }\n }\n return out;\n}\n\n/** 协议无关的规范消息元素。 */\nexport type CanonicalElement =\n | { type: \"text\"; text: string }\n | { type: \"at\"; target: string; display?: string }\n | { type: \"image\"; path: string; url?: string; size?: { width: number; height: number } }\n | { type: \"face\"; id: string }\n | { type: \"voice\"; path: string; durationMs?: number; url?: string }\n | { type: \"video\"; path: string; url?: string }\n | { type: \"file\"; path: string; name?: string; size?: number }\n | { type: \"reply\"; messageId: string }\n | { type: \"forward\"; messageIds: string[] }\n | { type: \"json\"; raw: unknown }\n | { type: \"xml\"; raw: unknown }\n | { type: \"unknown\"; raw: unknown };\n","/**\n * NodeIKernelGroupService:群服务接口面(自研描述,非移植)\n *\n * 依据:getGroupService() 运行时反射 + wrapper 外部契约(接口签名是 QQ 的外部事实,\n * 自研描述其形状,零复制实现)。\n * 只收录 apis/group 当前需要的方法;其余按需探测后补齐。\n */\nimport type { GeneralCallResult } from \"./msg-service.js\";\n\n/** 群成员角色(QQ wrapper 契约)。 */\nexport const NTGroupMemberRole = {\n UNSPECIFIED: 0,\n STRANGER: 1,\n MEMBER: 2,\n ADMIN: 3,\n OWNER: 4,\n} as const;\nexport type NTGroupMemberRole = (typeof NTGroupMemberRole)[keyof typeof NTGroupMemberRole];\n\n/** 踢人请求项(KickMemberV2Req.kickList 成员,说明书参考,字段值待探测校准)。 */\nexport interface KickMemberInfo {\n optFlag: number;\n optOperate: number;\n optMemberUid: string;\n optBytesMsg: string;\n}\n\n/** 踢人请求(set_group_kick 的 kickMemberV2 参数,说明书参考)。 */\nexport interface KickMemberV2Req {\n groupCode: string;\n kickFlag: number;\n kickList: KickMemberInfo[];\n kickListUids: string[];\n kickMsg: string;\n}\n\n/** @all 剩余次数(getGroupRemainAtTimes 返回)。 */\nexport interface GroupRemainAtTimes {\n canAtAll: boolean;\n remainAtAllCountForUin: number;\n remainAtAllCountForGroup: number;\n atTimesMsg: string;\n canNotAtAllMsg: string;\n}\n\n/** 群通知类型(GroupNotifyMsgType,说明书参考)。 */\nexport const GroupNotifyMsgType = {\n UN_SPECIFIED: 0,\n INVITED_BY_MEMBER: 1, // 邀请入群\n REFUSE_INVITED: 2,\n REFUSED_BY_ADMINI_STRATOR: 3,\n AGREED_TOJOIN_DIRECT: 4,\n INVITED_NEED_ADMINI_STRATOR_PASS: 5,\n AGREED_TO_JOIN_BY_ADMINI_STRATOR: 6,\n REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS: 7, // 申请入群\n SET_ADMIN: 8,\n KICK_MEMBER_NOTIFY_ADMIN: 9,\n KICK_MEMBER_NOTIFY_KICKED: 10,\n MEMBER_LEAVE_NOTIFY_ADMIN: 11,\n CANCEL_ADMIN_NOTIFY_CANCELED: 12,\n CANCEL_ADMIN_NOTIFY_ADMIN: 13,\n TRANSFER_GROUP_NOTIFY_OLDOWNER: 14,\n TRANSFER_GROUP_NOTIFY_ADMIN: 15,\n} as const;\nexport type GroupNotifyMsgType = (typeof GroupNotifyMsgType)[keyof typeof GroupNotifyMsgType];\n\n/** 群通知处理状态(GroupNotifyMsgStatus,说明书参考)。 */\nexport const GroupNotifyMsgStatus = {\n KINIT: 0,\n KUNHANDLE: 1, // 未处理\n KAGREED: 2,\n KREFUSED: 3,\n KIGNORED: 4,\n} as const;\nexport type GroupNotifyMsgStatus = (typeof GroupNotifyMsgStatus)[keyof typeof GroupNotifyMsgStatus];\n\n/** 群请求操作类型(NTGroupRequestOperateTypes,说明书参考)。 */\nexport const NTGroupRequestOperateTypes = {\n KUNSPECIFIED: 0,\n KAGREE: 1,\n KREFUSE: 2,\n KIGNORE: 3,\n KDELETE: 4,\n} as const;\nexport type NTGroupRequestOperateTypes =\n (typeof NTGroupRequestOperateTypes)[keyof typeof NTGroupRequestOperateTypes];\n\n/** 群通知(getSingleScreenNotifies 返回项,说明书参考,待探测校准)。 */\nexport interface GroupNotify {\n seq: string;\n type: GroupNotifyMsgType;\n status: GroupNotifyMsgStatus;\n group?: { groupCode: string; groupName: string };\n user1?: { uid: string; nickName: string };\n user2?: { uid: string; nickName: string };\n actionTime?: string;\n postscript?: string;\n warningTips?: string;\n}\n\n/** 禁言成员(getGroupShutUpMemberList 返回项,说明书参考,待探测校准)。 */\nexport interface ShutUpGroupMember {\n uid: string;\n qid: string;\n uin: string;\n nick: string;\n remark: string;\n cardName: string;\n shutUpTime: number;\n memberSpecialTitle?: string;\n}\n\n/** 群列表项(Group 实体,说明书参考)。 */\nexport interface Group {\n groupCode: string;\n groupName: string;\n memberCount: number;\n maxMember: number;\n remarkName?: string;\n groupOwnerId?: { memberUin: string; memberUid: string };\n memberRole?: number;\n isTop?: boolean;\n [key: string]: unknown;\n}\n\n/** 群详情(getGroupDetailInfo 返回,说明书参考)。 */\nexport interface GroupDetailInfo {\n groupCode: string;\n groupUin: string;\n ownerUin: string;\n ownerUid?: string;\n groupName: string;\n memberNum: number;\n maxMemberNum: number;\n remarkName?: string;\n isTop?: boolean;\n groupCreateTime?: number;\n [key: string]: unknown;\n}\n\n/** 群成员(getAllMemberList result.infos 的 value,说明书参考)。 */\nexport interface GroupMember {\n uid: string;\n uin: string;\n nick: string;\n cardName: string;\n remark: string;\n role: NTGroupMemberRole;\n shutUpTime: number; // 禁言秒数\n joinTime: string;\n lastSpeakTime: string;\n memberSpecialTitle?: string;\n sex?: number;\n age?: number;\n isRobot?: boolean;\n [key: string]: unknown;\n}\n\n/** 群服务:apis/group 用到的核心方法面。 */\nexport interface NodeIKernelGroupService {\n addKernelGroupListener(listener: unknown): number;\n removeKernelGroupListener(listenerId: number): void;\n getGroupList(force: boolean): Promise<GeneralCallResult>;\n getGroupDetailInfo(groupCode: string, groupInfoSource: number): Promise<GeneralCallResult>;\n getAllMemberList(\n groupCode: string,\n forceFetch: boolean,\n ): Promise<{\n errCode: number;\n errMsg: string;\n result: {\n ids: Array<{ uid: string; index: number }>;\n infos: Map<string, GroupMember>;\n finish: boolean;\n };\n }>;\n getMemberInfo(\n groupCode: string,\n uids: string[],\n forceFetch: boolean,\n ): Promise<GeneralCallResult>;\n /** uin → uid(私聊发送等需要,说明书参考)。 */\n getUidByUins(\n uins: string[],\n ): Promise<{ errCode: number; errMsg: string; uids: Map<string, string> }>;\n /** uid → uin。 */\n getUinByUids(\n uids: string[],\n ): Promise<{ errCode: number; errMsg: string; uins: Map<string, string> }>;\n /** 踢人(set_group_kick)。 */\n kickMemberV2(param: KickMemberV2Req): Promise<GeneralCallResult>;\n /** 成员禁言(set_group_ban;timeStamp=0 解除禁言)。 */\n setMemberShutUp(\n groupCode: string,\n memberTimes: Array<{ uid: string; timeStamp: number }>,\n ): Promise<GeneralCallResult>;\n /** 全员禁言(set_group_whole_ban)。 */\n setGroupShutUp(groupCode: string, shutUp: boolean): Promise<GeneralCallResult>;\n /** 设置管理员(set_group_admin;void 语义,乐观处理)。 */\n modifyMemberRole(groupCode: string, uid: string, role: NTGroupMemberRole): void;\n /** 设置群名片(set_group_card;void 语义)。 */\n modifyMemberCardName(groupCode: string, uid: string, cardName: string): void;\n /** 修改群名(set_group_name)。 */\n modifyGroupName(\n groupCode: string,\n groupName: string,\n isNormalMember: boolean,\n ): Promise<GeneralCallResult>;\n /** 退群(set_group_leave)。 */\n quitGroupV2(param: {\n groupCode: string;\n needDeleteLocalMsg: boolean;\n }): Promise<GeneralCallResult>;\n /** 设置精华消息(msgSeq/msgRandom 取自消息本体)。 */\n addGroupEssence(param: {\n groupCode: string;\n msgRandom: number;\n msgSeq: number;\n }): Promise<unknown>;\n /** 取消精华消息。 */\n removeGroupEssence(param: {\n groupCode: string;\n msgRandom: number;\n msgSeq: number;\n }): Promise<unknown>;\n /** @all 剩余次数(get_group_at_all_remain)。 */\n getGroupRemainAtTimes(groupCode: string): Promise<\n Omit<GeneralCallResult, \"result\"> & {\n errCode: number;\n atInfo: GroupRemainAtTimes;\n }\n >;\n /** 单屏通知列表(get_group_system_msg / set_group_add_request 共用;列表可能走 listener,宽松解包)。 */\n getSingleScreenNotifies(\n doubt: boolean,\n startSeq: string,\n count: number,\n ): Promise<GeneralCallResult & { result?: unknown }>;\n /** 禁言成员列表(get_group_shut_list;同 listener 风格,宽松解包)。 */\n getGroupShutUpMemberList(groupCode: string): Promise<GeneralCallResult & { result?: unknown }>;\n /** 处理群请求(set_group_add_request 应答;postscript 空值可能导致失败,默认空格)。 */\n operateSysNotify(\n doubt: boolean,\n operateMsg: {\n operateType: NTGroupRequestOperateTypes;\n targetMsg: {\n seq: string;\n type: GroupNotifyMsgType;\n groupCode: string;\n postscript: string;\n };\n },\n ): Promise<void>;\n}\n","/**\n * wrapper.node 的 NAPI 接口面(自研描述,非移植)\n *\n * 来源:wrapper.node 在 QQ 定制版 Electron 里通过 napi_define_class / napi_set_named_property\n * 注册的 JS 类(2026-08-05 Ghidra 反编译 FUN_180c6274a / FUN_180c80224 确认类名)。\n * 这些接口是 wrapper.node 对外暴露的外部系统契约——我们自研描述其形状,未复制任何第三方代码。\n *\n * 注意:这里所有对象由 QQ 运行时的 NAPI 层自动构建/转换,业务层禁止触碰 C++ ABI。\n */\n\n/** 平台类型(与 wrapper.node 的 platform 字段对应)。 */\nexport const PlatformType = {\n KUNKNOWN: 0,\n KANDROID: 1,\n KIOS: 2,\n KWINDOWS: 3,\n KMAC: 4,\n KLINUX: 5,\n} as const;\nexport type PlatformType = (typeof PlatformType)[keyof typeof PlatformType];\n\n/** 厂商类型(deviceInfo.vendorType)。 */\nexport const VendorType = {\n KNOSETONIOS: 0,\n KUNSUPPORTANDROIDPUSH: 1,\n KSUPPORTTPNS: 2,\n KSUPPORTHMS: 3,\n KSUPPORTOPPOPUSH: 4,\n KSUPPORTVIVOPUSH: 5,\n KSUPPORTGOOGLEPUSH: 99,\n} as const;\nexport type VendorType = (typeof VendorType)[keyof typeof VendorType];\n\n/** NodeQQNTWrapperUtil:通用工具类(静态服务)。 */\nexport interface NodeQQNTWrapperUtil {\n get(): NodeQQNTWrapperUtil;\n getNTUserDataInfoConfig(): string;\n // 文件/哈希工具(按需补齐,运行时反射枚举为准)\n getFileSize(path: string): Promise<number>;\n fileIsExist(path: string): unknown;\n makeDirByPath(path: string): unknown;\n}\n\n/** engine 初始化配置(initWithDeskTopConfig 第一参,JS 对象直接传)。 */\nexport interface EnginInitDesktopConfig {\n base_path_prefix: string;\n platform_type: PlatformType;\n /** 桌面端应用类型(engine.initWithDeskTopConfig 契约,桌面端固定 4;移动端为其它值)。 */\n app_type: 4;\n app_version: string;\n os_version: string;\n use_xlog: boolean;\n qua: string;\n global_path_config: {\n desktopGlobalPath: string;\n };\n /**\n * 缩略图生成参数(wrapper 契约):maxSide 最长边上限 / minSide 最短边下限 /\n * longLimit 长图最长短边比例上限 / density 像素密度倍数。\n */\n thumb_config: { maxSide: 324; minSide: 48; longLimit: 6; density: 2 };\n}\n\n/** NodeIQQNTWrapperEngine 实例方法。 */\nexport interface NodeIQQNTWrapperEngine {\n initWithDeskTopConfig(config: EnginInitDesktopConfig, adapter: NodeIGlobalAdapter): void;\n initWithMobileConfig(config: unknown, adapter: NodeIGlobalAdapter): void;\n initLog(arg: unknown): void;\n setLogLevel(arg: unknown): void;\n onSendSSOReply(a: unknown, b: unknown, c: unknown, d: unknown, e: unknown): void;\n}\n\n/** NodeIQQNTWrapperEngine 构造器(含静态 get())。 */\nexport type NodeIQQNTWrapperEngineCtor = new () => NodeIQQNTWrapperEngine & {\n get(): NodeIQQNTWrapperEngine;\n};\n\n/** desktopPathConfig 结构。 */\nexport interface DesktopPathConfig {\n account_path: string;\n}\n\n/** rdeliveryConfig 结构。 */\nexport interface RDeliveryConfig {\n appKey: string;\n systemId: number;\n appId: string;\n logicEnvironment: string;\n platform: PlatformType;\n language: string;\n sdkVersion: string;\n userId: string;\n appVersion: string;\n osVersion: string;\n bundleId: string;\n serverUrl: string;\n fixedAfterHitKeys: string[];\n}\n\n/** deviceInfo 结构。 */\nexport interface DeviceInfo {\n guid: string;\n buildVer: string;\n localId: number;\n devName: string;\n devType: string;\n vendorName: string;\n osVer: string;\n vendorOsName: string;\n setMute: boolean;\n vendorType: VendorType;\n}\n\n/** session.init 的完整配置(JS 对象,NAPI 自动转换)。 */\nexport interface WrapperSessionInitConfig {\n selfUin: string;\n selfUid: string;\n desktopPathConfig: DesktopPathConfig;\n clientVer: string;\n a2: string;\n d2: string;\n d2Key: string;\n machineId: string;\n platform: PlatformType;\n platVer: string;\n appid: string;\n rdeliveryConfig: RDeliveryConfig;\n defaultFileDownloadPath: string;\n deviceInfo: DeviceInfo;\n deviceConfig: string;\n}\n\n/** NodeIQQNTWrapperSession 构造器(含静态 get / getNTWrapperSession / create)。 */\nexport type NodeIQQNTWrapperSessionCtor = {\n new (): NodeIQQNTWrapperSession;\n get(): NodeIQQNTWrapperSession;\n getNTWrapperSession(name: string): NodeIQQNTWrapperSession;\n create(): NodeIQQNTWrapperSession;\n};\n\n/** NodeIQQNTWrapperSession:会话主入口(实例方法)。 */\nexport interface NodeIQQNTWrapperSession {\n init(\n config: WrapperSessionInitConfig,\n depends: NodeIDependsAdapter,\n dispatcher: NodeIDispatcherAdapter,\n listener: NodeIKernelSessionListener,\n ): void;\n startNT(sessionId?: number): void;\n close(arg: unknown): void;\n onLine(arg: unknown): void;\n offLine(arg: unknown): void;\n disableIpDirect(arg: unknown): void;\n getAccountPath(arg: unknown): string;\n updateTicket(arg: unknown): void;\n // 60+ getService:运行时反射枚举后补齐\n getMsgService(): unknown;\n getGroupService(): unknown;\n getBuddyService(): unknown;\n getTicketService(): unknown;\n getRichMediaService(): unknown;\n /** 闪传服务(诊断用:调查 rich media transfer failed 时验证初始化触发点)。 */\n getFlashTransferService(): unknown;\n getProfileService(): unknown;\n getProfileLikeService(): unknown;\n getTipOffService(): unknown;\n}\n\n/** NodeIQQNTStartupSessionWrapper:启动会话实例方法。 */\nexport interface NodeIQQNTStartupSessionWrapper {\n stop(): void;\n start(): void;\n createWithModuleList(uk: unknown): unknown;\n getSessionIdList(): Promise<Map<unknown, unknown>>;\n}\n\n/** NodeIQQNTStartupSessionWrapper 构造器(含静态 create)。 */\nexport type NodeIQQNTStartupSessionWrapperCtor = new () => NodeIQQNTStartupSessionWrapper & {\n create(): NodeIQQNTStartupSessionWrapper;\n};\n\n/** NodeIKernelLoginService 实例方法。 */\nexport interface NodeIKernelLoginService {\n addKernelLoginListener(listener: unknown): number;\n removeKernelLoginListener(listener: number): void;\n initConfig(config: unknown): void;\n connect(): void;\n getLoginList(): Promise<{ result: number; LocalLoginInfoList: unknown[] }>;\n quickLoginWithUin(uin: string): Promise<unknown>;\n getQRCodePicture(): boolean;\n getMsfStatus(): number;\n /** 设备指纹 guid(wrapper.node 字符串分析实证,2026-08-12;无 getMachineId)。 */\n getMachineGuid(): string;\n}\n\n/** NodeIKernelLoginService 构造器(含静态 get)。 */\nexport type NodeIKernelLoginServiceCtor = new () => NodeIKernelLoginService & {\n get(): NodeIKernelLoginService;\n};\n\n/** NodeIO3MiscService:O3 杂项服务。 */\nexport interface NodeIO3MiscService {\n get(): NodeIO3MiscService;\n addO3MiscListener(listener: unknown): number;\n}\n\n// adapter 类型(session.init 第二/三参、engine.initWithDeskTopConfig 第二参)\n// 注意:wrapper exports **不含** NodeI*Adapter / NodeI*Listener 构造器(实测 89 键无)。\n// 传普通 JS 对象即可——NAPI 反射读取对象上的方法回调。方法名是 QQ wrapper 的外部契约\n//(2026-08-05 运行时行为实测确认,接口为自研描述)。\nexport interface NodeIGlobalAdapter {\n onLog?(...args: unknown[]): void;\n onGetSrvCalTime?(...args: unknown[]): void;\n onShowErrUITips?(...args: unknown[]): void;\n fixPicImgType?(...args: unknown[]): unknown;\n getAppSetting?(...args: unknown[]): unknown;\n onInstallFinished?(...args: unknown[]): void;\n onUpdateGeneralFlag?(...args: unknown[]): void;\n onGetOfflineMsg?(...args: unknown[]): void;\n}\n\n/** session.init 第二参:依赖回调(普通 JS 对象)。 */\nexport interface NodeIDependsAdapter {\n onMSFStatusChange?(statusType: number, changeReasonType: number): void;\n onMSFSsoError?(code: number, desc: string): void;\n getGroupCode?(...args: unknown[]): unknown;\n}\n\n/** session.init 第三参:请求分发回调(普通 JS 对象)。 */\nexport interface NodeIDispatcherAdapter {\n dispatchRequest?(arg: unknown): void;\n dispatchCall?(arg: unknown): void;\n dispatchCallWithJson?(arg: unknown): void;\n}\n\n/** 登录监听器接口(普通 JS 对象,传给 loginService.addKernelLoginListener)。自研描述。 */\nexport interface IKernelLoginListener {\n onLoginConnected?(...args: unknown[]): void;\n onLoginDisConnected?(...args: unknown[]): void;\n onLoginConnecting?(...args: unknown[]): void;\n onQRCodeGetPicture?(arg: { pngBase64QrcodeData: string; qrcodeUrl: string }): void;\n onQRCodeLoginPollingStarted?(...args: unknown[]): void;\n onQRCodeSessionUserScaned?(...args: unknown[]): void;\n onQRCodeLoginSucceed?(loginResult: { uid: string; uin: string; nick?: string }): void;\n onQRCodeSessionFailed?(errType: number, errCode: number, errMsg: string): void;\n onLoginFailed?(args: unknown): void;\n onLogoutSucceed?(...args: unknown[]): void;\n onLogoutFailed?(...args: unknown[]): void;\n onUserLoggedIn?(userid: string): void;\n onQRCodeSessionQuickLoginFailed?(...args: unknown[]): void;\n onPasswordLoginFailed?(...args: unknown[]): void;\n onQQLoginNumLimited?(...args: unknown[]): void;\n onLoginState?(...args: unknown[]): void;\n}\n\n/**\n * NodeIKernelSessionListener:session.init 第四参监听器(普通 JS 对象)。\n * init 完成信号:以 onOpentelemetryInit(is_init===true) 为准(onSessionInitComplete 为辅)。\n */\nexport interface NodeIKernelSessionListener {\n onNTSessionCreate?(sessionId: string): void;\n onGProSessionCreate?(sessionId: string): void;\n onSessionInitComplete?(result: unknown): void;\n onOpentelemetryInit?(info: { is_init: boolean; is_report: boolean }): void;\n onUserOnlineResult?(result: unknown): void;\n onGetSelfTinyId?(result: unknown): void;\n}\n\n/** wrapper.node 的 NAPI 顶层导出(module.exports)。 */\nexport interface WrapperNodeApi {\n NodeIO3MiscService: NodeIO3MiscService;\n NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;\n /** 构造器:`new wrapper.NodeIQQNTWrapperSession()`。 */\n NodeIQQNTWrapperSession: NodeIQQNTWrapperSessionCtor;\n NodeIQQNTStartupSessionWrapper: NodeIQQNTStartupSessionWrapperCtor;\n /** 构造器:`new wrapper.NodeIQQNTWrapperEngine()`。 */\n NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngineCtor;\n /** 构造器:`new wrapper.NodeIKernelLoginService()`。 */\n NodeIKernelLoginService: NodeIKernelLoginServiceCtor;\n // 注:wrapper exports **不含** NodeI*Adapter / NodeI*Listener 构造器(实测 89 键无)——\n // adapter/listener 一律传普通 JS 对象(见 NodeIGlobalAdapter 等接口)。\n}\n","/**\n * GroupApi:群语义化 API(ADR-009 统一错误语义)\n *\n * 内部解包原生返回:成功返回纯业务值,失败抛 KernelError。\n * 方法面(P2-4 + P2-10):群列表 / 群详情 / 成员列表 / 成员详情 / uin↔uid +\n * 群管操作(踢人 / 禁言 / 管理员 / 名片 / 群名 / 退群 / 精华消息 / @all 剩余)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n Group,\n GroupDetailInfo,\n GroupMember,\n KickMemberV2Req,\n NodeIKernelGroupService,\n NodeIKernelMsgService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\nimport { ChatType, NTGroupMemberRole } from \"../types/index.js\";\nimport { checkLooseResult, unwrap } from \"./result.js\";\n\n/** 群 API:从 session 拿 group service,包装成语义化方法。 */\nexport class GroupApi {\n private readonly service: NodeIKernelGroupService;\n /** 精华消息需要 msg service(按 msgId 拉消息取 msgSeq/msgRandom)。 */\n private readonly msgService: NodeIKernelMsgService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getGroupService() as unknown as NodeIKernelGroupService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getGroupService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n const msgService = session.getMsgService() as unknown as NodeIKernelMsgService | null;\n if (msgService === null || msgService === undefined) {\n throw kernelError(\"getMsgService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.msgService = msgService;\n }\n\n /**\n * 群列表(force=true 触发原生刷新)。\n *\n * ⚠️ 实测校准(2026-08-08 端到端联调):原生 getGroupList 返回仅\n * `{ result, errMsg }`——**列表数据经 onGroupListUpdate 事件推送**,\n * 不走返回值。消费群列表一律从 `GroupCache.listGroups()` 读(缓存由\n * 事件主动维护);本方法仅作「触发刷新」用,返回值恒为 []。\n */\n async getGroupList(force = false): Promise<Group[]> {\n const raw = await this.service.getGroupList(force);\n if (raw.result !== 0) {\n unwrap(\"getGroupList\", raw.result, raw.errMsg);\n }\n return [];\n }\n\n /** 群详情(groupCode 为群号字符串)。\n * getGroupDetailInfo 返回形状:失败 result=错误码;成功详情可能直接在 result\n * 对象里,也可能在 groupInfo/detailInfo 字段——三种形状都兼容(探测校准)。\n * 2026-08-07 修复:成功(result=0)时不再把 {result:0,errMsg} 原样当详情返回,\n * 而是提取详情字段;形状异常抛 UNKNOWN 而非静默返回垃圾数据。 */\n async getGroupInfo(groupCode: string): Promise<GroupDetailInfo> {\n const raw = await this.service.getGroupDetailInfo(groupCode, 0);\n const { result, errMsg } = raw as { result?: unknown; errMsg: string };\n if (typeof result === \"number\" && result !== 0) {\n unwrap(\"getGroupDetailInfo\", result, errMsg);\n }\n const detail = extractGroupDetail(result, raw);\n if (detail === null) {\n throw kernelError(\n `getGroupDetailInfo 返回形状异常(groupCode=${groupCode}): ${errMsg}`,\n \"UNKNOWN\",\n );\n }\n return detail;\n }\n\n /** 群成员列表(forceFetch=true 强制拉取,默认缓存优先)。 */\n async getGroupMemberList(groupCode: string, forceFetch = false): Promise<GroupMember[]> {\n const raw = await this.service.getAllMemberList(groupCode, forceFetch);\n if (raw.errCode !== 0) {\n throw kernelError(`getAllMemberList 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return [...raw.result.infos.values()];\n }\n\n /** 群成员详情(uids 为空返回空数组)。 */\n async getGroupMemberInfo(groupCode: string, uids: string[]): Promise<GroupMember[]> {\n if (uids.length === 0) {\n return [];\n }\n const raw = await this.service.getMemberInfo(groupCode, uids, true);\n unwrap(\"getMemberInfo\", raw.result, raw.errMsg);\n // 返回形状待探测校准:infos Map<uid, GroupMember>\n const { infos } = raw as unknown as { infos?: Map<string, GroupMember> };\n if (infos instanceof Map) {\n return [...infos.values()];\n }\n return [];\n }\n\n /** uin → uid(私聊发送等需要)。 */\n async uinToUid(uins: string[]): Promise<Map<string, string>> {\n if (uins.length === 0) {\n return new Map();\n }\n const raw = await this.service.getUidByUins(uins);\n if (raw.errCode !== 0) {\n throw kernelError(`getUidByUins 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw.uids;\n }\n\n /** uid → uin。 */\n async uidToUin(uids: string[]): Promise<Map<string, string>> {\n if (uids.length === 0) {\n return new Map();\n }\n const raw = await this.service.getUinByUids(uids);\n if (raw.errCode !== 0) {\n throw kernelError(`getUinByUids 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw.uins;\n }\n\n /** 群成员角色 → OB11 role 字符串。 */\n static roleToString(role: NTGroupMemberRole): \"owner\" | \"admin\" | \"member\" {\n if (role === NTGroupMemberRole.OWNER) {\n return \"owner\";\n }\n if (role === NTGroupMemberRole.ADMIN) {\n return \"admin\";\n }\n return \"member\";\n }\n\n /**\n * 踢人(set_group_kick)。memberUids 为成员 uid;refuseForever=true 拉黑拒绝再次入群。\n * kickMemberV2 的 kickFlag/kickList 字段值待探测校准(TODO P3 联调)。\n */\n async kickMember(\n groupCode: string,\n memberUids: string[],\n refuseForever: boolean,\n reason = \"\",\n ): Promise<void> {\n if (memberUids.length === 0) {\n throw kernelError(\"kickMember 需要至少一个成员 uid\", \"INVALID_PARAM\");\n }\n // refuseForever=true 拒绝再次入群(optFlag 字段值待探测校准)\n let optFlag = 1;\n if (refuseForever) {\n optFlag = 0;\n }\n const req: KickMemberV2Req = {\n groupCode,\n kickFlag: 0,\n kickList: memberUids.map((uid) => ({\n optFlag,\n optOperate: 0,\n optMemberUid: uid,\n optBytesMsg: \"\",\n })),\n kickListUids: memberUids,\n kickMsg: reason,\n };\n const raw = await this.service.kickMemberV2(req);\n unwrap(\"kickMemberV2\", raw.result, raw.errMsg);\n }\n\n /** 成员禁言(duration 秒,0 解除禁言)。 */\n async setMemberShutUp(\n groupCode: string,\n members: Array<{ uid: string; duration: number }>,\n ): Promise<void> {\n if (members.length === 0) {\n throw kernelError(\"setMemberShutUp 需要至少一个成员\", \"INVALID_PARAM\");\n }\n const raw = await this.service.setMemberShutUp(\n groupCode,\n members.map((m) => ({ uid: m.uid, timeStamp: m.duration })),\n );\n unwrap(\"setMemberShutUp\", raw.result, raw.errMsg);\n }\n\n /** 全员禁言(enable=true 开启全群禁言)。 */\n async setGroupShutUp(groupCode: string, enable: boolean): Promise<void> {\n const raw = await this.service.setGroupShutUp(groupCode, enable);\n unwrap(\"setGroupShutUp\", raw.result, raw.errMsg);\n }\n\n /** 设置管理员(role=ADMIN 设管理,MEMBER 取消;void 语义乐观处理)。 */\n setMemberRole(groupCode: string, uid: string, role: NTGroupMemberRole): void {\n this.service.modifyMemberRole(groupCode, uid, role);\n }\n\n /** 设置群名片(void 语义乐观处理)。 */\n setMemberCardName(groupCode: string, uid: string, cardName: string): void {\n this.service.modifyMemberCardName(groupCode, uid, cardName);\n }\n\n /** 修改群名。 */\n async modifyGroupName(groupCode: string, groupName: string): Promise<void> {\n const raw = await this.service.modifyGroupName(groupCode, groupName, false);\n unwrap(\"modifyGroupName\", raw.result, raw.errMsg);\n }\n\n /** 退群(needDeleteLocalMsg=is_dismiss:是否解散群,仅群主)。 */\n async quitGroup(groupCode: string, needDeleteLocalMsg = false): Promise<void> {\n const raw = await this.service.quitGroupV2({ groupCode, needDeleteLocalMsg });\n unwrap(\"quitGroupV2\", raw.result, raw.errMsg);\n }\n\n /** 设置精华消息(msgId 经 msgService 取 msgSeq/msgRandom)。 */\n async addGroupEssence(groupCode: string, msgId: string): Promise<void> {\n await this.setEssence(groupCode, msgId, true);\n }\n\n /** 取消精华消息。 */\n async removeGroupEssence(groupCode: string, msgId: string): Promise<void> {\n await this.setEssence(groupCode, msgId, false);\n }\n\n /** 精华消息公共流程:按 msgId 拉消息 → add/removeGroupEssence。 */\n private async setEssence(groupCode: string, msgId: string, add: boolean): Promise<void> {\n const peer = { chatType: ChatType.GROUP, peerUid: groupCode };\n const raw = await this.msgService.getMsgsByMsgId(peer, [msgId]);\n unwrap(\"getMsgsByMsgId\", raw.result, raw.errMsg);\n const msg = raw.msgList?.[0];\n if (msg === undefined) {\n throw kernelError(`消息 ${msgId} 不存在`, \"NOT_FOUND\");\n }\n const param = {\n groupCode,\n msgRandom: Number(msg.msgRandom ?? 0),\n msgSeq: Number(msg.msgSeq),\n };\n let label = \"removeGroupEssence\";\n let res: { result?: unknown; errMsg?: unknown } | undefined;\n if (add) {\n label = \"addGroupEssence\";\n res = (await this.service.addGroupEssence(param)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n } else {\n res = (await this.service.removeGroupEssence(param)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n }\n checkLooseResult(label, res);\n }\n\n /** @all 剩余次数(get_group_at_all_remain)。 */\n async getGroupRemainAtTimes(groupCode: string): Promise<{\n canAtAll: boolean;\n remainAtAllCountForUin: number;\n remainAtAllCountForGroup: number;\n }> {\n const raw = await this.service.getGroupRemainAtTimes(groupCode);\n if (raw.errCode !== 0) {\n throw kernelError(`getGroupRemainAtTimes 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return {\n canAtAll: raw.atInfo.canAtAll,\n remainAtAllCountForUin: raw.atInfo.remainAtAllCountForUin,\n remainAtAllCountForGroup: raw.atInfo.remainAtAllCountForGroup,\n };\n }\n}\n\n/**\n * 从 getGroupDetailInfo 返回提取群详情(兼容三种形状):\n * - result 为对象 → 详情直接在其中(最常见)\n * - result 为 0/空 → 从 raw.groupInfo / detailInfo / info 提取\n * 提取不到返回 null(调用方抛 UNKNOWN)。导出供单测。\n */\nexport function extractGroupDetail(result: unknown, raw: unknown): GroupDetailInfo | null {\n if (result !== null && typeof result === \"object\") {\n return result as GroupDetailInfo;\n }\n const detail = raw as {\n groupInfo?: unknown;\n detailInfo?: unknown;\n info?: unknown;\n };\n const candidate = detail.groupInfo ?? detail.detailInfo ?? detail.info;\n if (candidate !== undefined && candidate !== null && typeof candidate === \"object\") {\n return candidate as GroupDetailInfo;\n }\n return null;\n}\n","/**\n * GroupNotifyApi:群通知/禁言列表语义化 API(ADR-009 统一错误语义,P2-13)\n *\n * get_group_system_msg / set_group_add_request / get_group_shut_list 共用。\n * getSingleScreenNotifies / getGroupShutUpMemberList 的列表可能走 listener 回调,\n * 直接调用返回形状待探测校准:兼容 result 数组 / 字段 / 直接数组。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n GroupMember,\n GroupNotify,\n NodeIKernelGroupService,\n NodeIQQNTWrapperSession,\n NTGroupRequestOperateTypes,\n} from \"../types/index.js\";\nimport { unwrap } from \"./result.js\";\n\n/** 群通知 API:从 session 拿 group service,包装成语义化方法。 */\nexport class GroupNotifyApi {\n private readonly service: NodeIKernelGroupService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getGroupService() as unknown as NodeIKernelGroupService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getGroupService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 单屏通知列表(get_group_system_msg / set_group_add_request 共用)。 */\n async getSingleScreenNotifies(doubt: boolean, count: number): Promise<GroupNotify[]> {\n const raw = await this.service.getSingleScreenNotifies(doubt, \"\", count);\n return extractNotifyList(raw.result);\n }\n\n /** 禁言成员列表(get_group_shut_list)。 */\n async getGroupShutUpMemberList(groupCode: string): Promise<GroupMember[]> {\n const raw = await this.service.getGroupShutUpMemberList(groupCode);\n if (raw.result !== 0) {\n unwrap(\"getGroupShutUpMemberList\", raw.result, raw.errMsg);\n }\n return extractMemberList(raw.result);\n }\n\n /** 处理群请求(set_group_add_request 应答;approve=true 同意 / false 拒绝)。 */\n async handleGroupRequest(\n doubt: boolean,\n notify: GroupNotify,\n operateType: NTGroupRequestOperateTypes,\n reason: string,\n ): Promise<void> {\n const { group, seq, type } = notify;\n let groupCode = \"\";\n if (group !== undefined) {\n ({ groupCode } = group);\n }\n let postscript = reason;\n if (postscript === \"\") {\n postscript = \" \";\n }\n await this.service.operateSysNotify(doubt, {\n operateType,\n targetMsg: {\n seq,\n type,\n groupCode,\n postscript,\n },\n });\n }\n}\n\n/** 从 getSingleScreenNotifies 返回提取通知数组(兼容 result 数组 / notifies 字段)。 */\nexport function extractNotifyList(result: unknown): GroupNotify[] {\n if (Array.isArray(result)) {\n return result as GroupNotify[];\n }\n if (result !== null && typeof result === \"object\") {\n const obj = result as { notifies?: unknown; groupNotifies?: unknown };\n if (Array.isArray(obj.notifies)) {\n return obj.notifies as GroupNotify[];\n }\n if (Array.isArray(obj.groupNotifies)) {\n return obj.groupNotifies as GroupNotify[];\n }\n }\n return [];\n}\n\n/** 从 getGroupShutUpMemberList 返回提取成员数组(兼容 result 数组 / memberList 字段)。 */\nexport function extractMemberList(result: unknown): GroupMember[] {\n if (Array.isArray(result)) {\n return result as unknown as GroupMember[];\n }\n if (result !== null && typeof result === \"object\") {\n const obj = result as { memberList?: unknown; list?: unknown };\n if (Array.isArray(obj.memberList)) {\n return obj.memberList as unknown as GroupMember[];\n }\n if (Array.isArray(obj.list)) {\n return obj.list as unknown as GroupMember[];\n }\n }\n return [];\n}\n","/**\n * MsgApi:消息语义化 API(ADR-009 统一错误语义)\n *\n * 内部解包原生 `{ result, errMsg }`:成功返回纯业务值,失败抛 KernelError。\n * 协议层只维护 `KernelErrorCode → 协议错误码` 映射表,不解析错误逻辑。\n *\n * 方法面(P2-1):发送 / 撤回 / 拉历史 / 标记已读。group/friend 等后续 apis 同构。\n */\nimport { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { basename } from \"node:path\";\nimport type { MsgEventChannel } from \"../bridge/msg-bridge.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n CanonicalElement,\n NodeIKernelMsgService,\n NodeIQQNTWrapperSession,\n Peer,\n PttElement,\n RawElement,\n RawMessage,\n SendMessageElement,\n} from \"../types/index.js\";\nimport { ElementType, toSendElements } from \"../types/index.js\";\nimport { unwrapResult } from \"./result.js\";\n\n/** 发送状态(onMsgInfoListUpdate 事件 msg.sendStatus)。 */\nconst SEND_STATUS = { FAILED: 0, SENDING: 1, SUCCESS: 2, SUCCESS_NO_SEQ: 3 } as const;\n\n/** 发送确认超时(毫秒)。 */\nconst SEND_CONFIRM_TIMEOUT_MS = 15_000;\n\n/**\n * PIC 元素发送契约值(NapCat 同款发送契约值,未逐字段校准)。\n * 下列固定值均为实测调通的 wrapper 发送契约(picType / picSubType /\n * picWidth / picHeight / original / thumbFileSize),只抽常量不改值——改值会破坏图片发送。\n */\nconst PIC_TYPE = 1000;\nconst PIC_SUB_TYPE = 0;\nconst PIC_WIDTH = 0;\nconst PIC_HEIGHT = 0;\nconst PIC_ORIGINAL = true;\nconst PIC_THUMB_FILE_SIZE = 0;\n\n/**\n * PTT 元素发送契约值(NapCat 同款发送契约值,未逐字段校准)。\n * silk v3 协议:下列固定值均为实测调通的 wrapper 发送契约(formatType / voiceType /\n * voiceChangeType / canConvert2Text / playState / autoConvertText / storeID /\n * otherBusinessInfo.aiVoiceType),只抽常量不改值——改值会破坏语音发送。\n */\nconst PTT_FORMAT_TYPE = 1;\nconst PTT_VOICE_TYPE = 1;\nconst PTT_VOICE_CHANGE_TYPE = 0;\nconst PTT_CAN_CONVERT_2_TEXT = true;\nconst PTT_PLAY_STATE = 1;\nconst PTT_AUTO_CONVERT_TEXT = 0;\nconst PTT_STORE_ID = 0;\nconst PTT_AI_VOICE_TYPE = 0;\n/** 假波形数组(占位波形):无真实振幅数据时发送的固定占位,silk v3 协议契约值。 */\nconst PTT_WAVE_AMPLITUDES = [0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17];\n\n/** downloadRichMedia 契约值(2026-09-08 B1 实测:downloadType=2 可用 / thumbSize=0)。 */\nconst DOWNLOAD_TYPE_COMMON = 2;\nconst THUMB_SIZE_NONE = 0;\n/** 语音下载轮询参数(300ms × 34 ≈ 10s;返回 void 只能经重拉元素观察完成)。 */\nconst PTT_POLL_INTERVAL_MS = 300;\nconst PTT_POLL_TIMES = 34;\n\n/** sleep(下载轮询间隔)。 */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/** 读文件 stat(富媒体发送预处理;文件不存在抛 INVALID_PARAM)。 */\nasync function statFile(path: string): Promise<{ size: number }> {\n try {\n return await stat(path);\n } catch {\n throw kernelError(`图片文件不存在: ${path}`, \"INVALID_PARAM\");\n }\n}\n\n/** 计算文件 md5(十六进制小写)。 */\nasync function hashFile(path: string): Promise<string> {\n try {\n const buf = await readFile(path);\n return createHash(\"md5\").update(buf).digest(\"hex\");\n } catch {\n throw kernelError(`图片文件读取失败: ${path}`, \"INVALID_PARAM\");\n }\n}\n\n/**\n * 清洗撤回消息 id(防御外部脏参数)。\n *\n * 调用方(IPC / 协议层 / koishi 适配器)可能传 null、非数组、空字符串等——\n * 直接透传会让裸 TypeError 逃逸,或把脏值一路送到 wrapper.node 才失败\n * (原生层只回「无错误详情」,排查困难)。规则:\n * - 非数组 → INVALID_PARAM\n * - 过滤空字符串 / 纯空白 / 非字符串\n * - 清洗后为空 → INVALID_PARAM\n */\nfunction sanitizeRecallMsgIds(msgIds: string[]): string[] {\n if (!Array.isArray(msgIds)) {\n throw kernelError(\"recallMessage 的 msgIds 必须是数组\", \"INVALID_PARAM\");\n }\n const ids = msgIds.filter((id) => typeof id === \"string\" && id.trim() !== \"\");\n if (ids.length === 0) {\n throw kernelError(\"recallMessage 需要至少一个非空 msgId\", \"INVALID_PARAM\");\n }\n return ids;\n}\n\n/** 消息 API:从 session 拿 msg service,包装成语义化方法。 */\nexport class MsgApi {\n private readonly service: NodeIKernelMsgService;\n /** 消息事件通道(sendMsg 后等 onMsgInfoListUpdate 确认发送结果)。 */\n private readonly channel: MsgEventChannel | null;\n /** NodeQQNTWrapperUtil(富媒体发送 copyFile 用;session 拿不到时可为 null)。 */\n private readonly util: { get(): unknown } | null;\n /** 上次生成 msgId 的时间(单调递增,2026-08-07 防同毫秒并发碰撞)。 */\n private lastMsgTime = 0;\n\n constructor(session: NodeIQQNTWrapperSession, channel?: MsgEventChannel, util?: unknown) {\n const service = session.getMsgService() as unknown as NodeIKernelMsgService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getMsgService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel ?? null;\n // util 传入形态:构造器(NodeQQNTWrapperUtil,含 get())或实例,宽松兼容\n this.util = util !== null && util !== undefined ? (util as { get(): unknown }) : null;\n }\n\n /**\n * 生成 msgId 时间戳(单调递增)。\n * generateMsgUniqueId(chatType, time) 以 time 区分消息——同毫秒并发发送\n * (机器人群发/多会话同时回复)Date.now() 会碰撞,msgId 相同导致 wrapper\n * 拒绝或覆盖。严格单调递增保证进程内唯一。\n */\n private nextMsgTime(): string {\n const now = Date.now();\n if (now > this.lastMsgTime) {\n this.lastMsgTime = now;\n } else {\n this.lastMsgTime += 1;\n }\n return String(this.lastMsgTime);\n }\n\n /**\n * 发送消息:canonical 元素 → NT 发送元素 → sendMsg(NapCat 式)。\n * 返回 NT msgId(雪花 ID)。\n *\n * 2026-08-11 修复(NapCat 式,实测):\n * - sendMsg 第一参传 '0'(固定),msgId 塞 peer.guildId —— 传 msgId 作第一参\n * 时 wrapper 返回 result=5(失败),NapCat 同款调用返回 result=0。\n * - 发送结果以 onMsgInfoListUpdate 事件确认(sendStatus===2 成功):sendMsg\n * 返回值 result 可能为 5 但事件仍成功(异步确认),反之亦然。\n * - ⚠️ 必须先注册事件监听再调 sendMsg(事件可能在 sendMsg 返回前就触发)。\n */\n async sendMessage(target: Peer, elements: CanonicalElement[]): Promise<{ msgId: string }> {\n // NapCat 式富媒体预处理:PIC 元素补 md5/fileName/sourcePath 并放置文件\n // (getRichMediaFilePathForGuild → util.copyFile),否则发送器走未初始化\n // 的 FlashFileUploadService 导致 rich media transfer failed。\n const sendElements = await this.prepareSendElements(elements);\n const msgId = this.service.generateMsgUniqueId(target.chatType, this.nextMsgTime());\n // NapCat 同款:msgId 塞 guildId,第一参 '0'\n const sendPeer: Peer = { ...target, guildId: msgId };\n // 无事件通道(老用法):退化为看返回值\n if (this.channel === null) {\n const raw = await this.service.sendMsg(\"0\", sendPeer, sendElements, new Map());\n unwrapResult(\"sendMsg\", raw);\n return { msgId };\n }\n // 有事件通道:先注册确认监听(NapCat 式,事件可能早于 sendMsg 返回触发),\n // 再调 sendMsg。最终结果以事件 sendStatus 为准(raw.result 非 0 不判失败)。\n const confirm = this.confirmSend(msgId, target);\n // ⚠️ 预消费 confirm 的 rejection(2026-08-22 崩溃根因修复):若下方\n // service.sendMsg 抛错(wrapper 内部异常),confirm 确认监听已注册却无人\n // await,事件回调里 reject 会变成 unhandledRejection,Node 默认抛错退出\n // 直接拖垮整个子进程(实测:sendStatus=0 确认事件触发后进程崩溃、IPC\n // 通道关闭)。先挂 no-op 消费兜底,`await confirm` 仍能正常收到结果\n // (Promise 允许多个消费者),失败语义不变。\n confirm.catch(() => undefined);\n await this.service.sendMsg(\"0\", sendPeer, sendElements, new Map());\n await confirm;\n return { msgId };\n }\n\n /**\n * canonical 元素 → 发送元素,富媒体(PIC/PTT)做 NapCat 式预处理。\n * 2026-08-11 修复(实测):图片发送必须 elementType=2(PIC)+ 完整 picElement\n * (md5HexStr/fileSize/fileName/sourcePath),且文件须经 getRichMediaFilePathForGuild\n * 计算目标路径 + util.copyFile 放置——缺任一环节 sendMsg 返回 rich media transfer failed。\n * 2026-08-12(语音):PTT 同理——只给 filePath 触发 wrapper 内部\n * \"Cannot convert undefined or null to object\"(缺字段转换失败)。\n */\n private async prepareSendElements(elements: CanonicalElement[]): Promise<SendMessageElement[]> {\n const out: SendMessageElement[] = [];\n for (const el of elements) {\n switch (el.type) {\n case \"image\":\n out.push(await this.prepareImageElement(el.path));\n break;\n case \"voice\":\n out.push(await this.preparePttElement(el.path));\n break;\n default:\n out.push(...toSendElements([el]));\n break;\n }\n }\n return out;\n }\n\n /** PIC 元素 NapCat 式预处理:md5 → 目标路径 → copyFile → 完整 picElement。 */\n private async prepareImageElement(path: string): Promise<SendMessageElement> {\n const file = await statFile(path);\n const md5 = await hashFile(path);\n const fileName = basename(path);\n const relPath = await this.placeMediaFile(path, ElementType.PIC, md5, fileName);\n return {\n elementType: ElementType.PIC,\n elementId: \"\",\n picElement: {\n md5HexStr: md5,\n fileSize: String(file.size),\n picWidth: PIC_WIDTH,\n picHeight: PIC_HEIGHT,\n fileName,\n sourcePath: relPath,\n original: PIC_ORIGINAL,\n picType: PIC_TYPE,\n picSubType: PIC_SUB_TYPE,\n fileUuid: \"\",\n fileSubId: \"\",\n thumbFileSize: PIC_THUMB_FILE_SIZE,\n summary: \"\",\n },\n };\n }\n\n /**\n * PTT 元素 NapCat 式预处理:md5 → 目标路径 → copyFile → 完整 pttElement。\n *\n * 2026-08-12(实测依据):只给 filePath 时 wrapper 内部抛\n * \"Cannot convert undefined or null to object\"(缺 md5HexStr/fileSize 等字段),\n * 且发送后进程崩溃重启(supervisor 自动拉起)。完整字段(NapCat 同款,\n * formatType/voiceType/canConvert2Text/waveAmplitudes 等)消除该错误。\n *\n * ⚠️ silk 格式:QQ 语音协议为 silk v3。非 silk → silk 的转码在协议层\n * (adapter,@napuketto/media 的 encodePcmToSilk,2026-08-12 接线)完成,\n * 此处接收的已是 silk 路径,仅做放置。kernel 不 import media(解耦红线)。\n */\n private async preparePttElement(path: string): Promise<SendMessageElement> {\n const file = await statFile(path);\n const md5 = await hashFile(path);\n const fileName = basename(path);\n const relPath = await this.placeMediaFile(path, ElementType.PTT, md5, fileName);\n // 时长估算(NapCat 同款规则:~3KB/s 语音码率,缺 ffprobe 时兜底)\n const duration = Math.max(1, Math.floor(file.size / 1024 / 3));\n return {\n elementType: ElementType.PTT,\n elementId: \"\",\n pttElement: {\n fileName,\n filePath: relPath,\n md5HexStr: md5,\n fileSize: String(file.size),\n duration,\n formatType: PTT_FORMAT_TYPE,\n voiceType: PTT_VOICE_TYPE,\n voiceChangeType: PTT_VOICE_CHANGE_TYPE,\n canConvert2Text: PTT_CAN_CONVERT_2_TEXT,\n waveAmplitudes: PTT_WAVE_AMPLITUDES,\n fileSubId: \"\",\n playState: PTT_PLAY_STATE,\n autoConvertText: PTT_AUTO_CONVERT_TEXT,\n storeID: PTT_STORE_ID,\n otherBusinessInfo: { aiVoiceType: PTT_AI_VOICE_TYPE },\n },\n };\n }\n\n /**\n * 富媒体文件放置(PIC/PTT 共用):md5 + 文件名 → QQ 内部目标路径\n * (getRichMediaFilePathForGuild)→ util.copyFile 放置 → 返回相对数据根路径。\n */\n private async placeMediaFile(\n path: string,\n elementType: ElementType,\n md5: string,\n fileName: string,\n ): Promise<string> {\n const service = this.service;\n const util = this.util;\n // getRichMediaFilePathForGuild:QQ 内部目标路径(纯文件名,相对数据根)。\n // 固定入参 elementSubType / thumbSize / downloadType 为 NapCat 同款发送契约值\n // (未逐字段校准),发送方向固定,改值会破坏富媒体路径计算。\n const relPath = service.getRichMediaFilePathForGuild({\n md5HexStr: md5,\n fileName,\n elementType,\n elementSubType: 0, // 元素子类型(发送固定 0)\n thumbSize: 0, // 缩略图尺寸(发送固定 0,不预生成缩略图)\n needCreate: true,\n downloadType: 1, // 下载类型(发送固定 1)\n file_uuid: \"\",\n });\n if (util !== null) {\n const instance = typeof util.get === \"function\" ? util.get() : util;\n const copy = (instance as Record<string, unknown>)[\"copyFile\"];\n if (typeof copy === \"function\") {\n await (copy as (a: string, b: string) => Promise<unknown>).call(\n instance,\n path,\n relPath,\n );\n }\n }\n return relPath;\n }\n\n /** 注册 onMsgInfoListUpdate 确认监听(返回 Promise,resolve 时发送成功)。 */\n private confirmSend(msgId: string, target: Peer): Promise<void> {\n const channel = this.channel;\n if (channel === null) {\n return Promise.resolve();\n }\n return new Promise((resolve, reject) => {\n const off = channel.on(\"Msg/onMsgInfoListUpdate\", (list) => {\n const mine = list.find((m) => m.guildId === msgId);\n if (mine === undefined) {\n return; // 不是本次消息\n }\n if (\n mine.sendStatus === SEND_STATUS.SUCCESS ||\n mine.sendStatus === SEND_STATUS.SUCCESS_NO_SEQ\n ) {\n off();\n resolve();\n } else if (mine.sendStatus === SEND_STATUS.FAILED) {\n off();\n reject(\n kernelError(`sendMsg 失败: sendStatus=${mine.sendStatus}`, \"SEND_FAILED\"),\n );\n }\n // SENDING:继续等\n });\n // 超时兜底\n setTimeout(() => {\n off();\n reject(\n kernelError(\n `sendMsg 等待确认超时(msgId=${msgId}, target=${JSON.stringify(target)})`,\n \"SEND_FAILED\",\n ),\n );\n }, SEND_CONFIRM_TIMEOUT_MS);\n });\n }\n\n /** 撤回消息(群聊管理员 / 私聊 2 分钟内)。 */\n async recallMessage(target: Peer, msgIds: string[]): Promise<void> {\n const raw = await this.service.recallMsg(target, sanitizeRecallMsgIds(msgIds));\n unwrapResult(\"recallMsg\", raw);\n }\n\n /** 拉取历史消息(msgId 为空从最新拉;count 条,时间倒序)。 */\n async fetchMessages(\n target: Peer,\n opts: { count: number; msgId?: string },\n ): Promise<RawMessage[]> {\n const raw = await this.service.getMsgs(target, opts.msgId ?? \"\", opts.count, false);\n unwrapResult(\"getMsgs\", raw);\n return raw.msgList ?? [];\n }\n\n /** 按 msgId 批量拉取消息(get_msg / 精华消息 / ptt 转文字共用)。 */\n async fetchMsgsByMsgId(target: Peer, ids: string[]): Promise<RawMessage[]> {\n if (ids.length === 0) {\n return [];\n }\n const raw = await this.service.getMsgsByMsgId(target, ids);\n unwrapResult(\"getMsgsByMsgId\", raw);\n return raw.msgList ?? [];\n }\n\n /** 消息表情表态(set_msg_emoji_like;like=true 点赞,false 取消)。 */\n async setMsgEmojiLike(\n target: Peer,\n opts: { msgSeq: string; emojiId: string; emojiType: string; like: boolean },\n ): Promise<void> {\n const raw = await this.service.setMsgEmojiLikes(\n target,\n opts.msgSeq,\n opts.emojiId,\n opts.emojiType,\n opts.like,\n );\n unwrapResult(\"setMsgEmojiLikes\", raw);\n }\n\n /**\n * 语音转文字(fetch_ptt_text)。\n * 流程:按 msgId 拉消息 → 找 PTT 元素 → translatePtt2Text(异步转写)\n * → 再拉一次消息读 pttElement.text。\n */\n async fetchPttText(msgId: string, target: Peer): Promise<string> {\n const msgs = await this.fetchMsgsByMsgId(target, [msgId]);\n const ptt = findPttElement(msgs);\n if (ptt === null) {\n throw kernelError(\"消息中不包含语音\", \"NOT_FOUND\");\n }\n const raw = await this.service.translatePtt2Text(msgId, target, ptt);\n unwrapResult(\"translatePtt2Text\", raw);\n // 转写异步完成:再拉一次拿 text\n const after = await this.fetchMsgsByMsgId(target, [msgId]);\n const text = findPttElement(after)?.pttElement?.text;\n if (text === undefined || text === \"\") {\n throw kernelError(\"获取语音转文字结果失败\", \"UNKNOWN\");\n }\n return text;\n }\n\n /** 标记会话已读。 */\n async markRead(target: Peer): Promise<void> {\n const raw = await this.service.setMsgRead(target);\n unwrapResult(\"setMsgRead\", raw);\n }\n\n /**\n * 语音主动下载(get_record 本地未命中兜底,2026-09-08 B1)。\n * downloadRichMedia 返回 void(实测),完成信号 = 重拉消息元素:\n * 轮询(300ms × 34,约 10s)直到 filePath 非空且 transferStatus 进入\n * 已下载态(2/4);超时但 filePath 已有值则尽力返回(磁盘命中判断留给\n * 调用方),完全无路径抛 NOT_FOUND。\n */\n async downloadPtt(msgId: string, target: Peer): Promise<PttElement> {\n const msgs = await this.fetchMsgsByMsgId(target, [msgId]);\n const el = findPttElement(msgs);\n const ptt = el?.pttElement;\n if (el === null || ptt === undefined) {\n throw kernelError(\"消息中不包含语音\", \"NOT_FOUND\");\n }\n const elemId = el.elementId ?? \"\";\n await this.service.downloadRichMedia({\n msgId,\n elemId,\n chatType: target.chatType,\n downloadType: DOWNLOAD_TYPE_COMMON,\n thumbSize: THUMB_SIZE_NONE,\n });\n for (let i = 0; i < PTT_POLL_TIMES; i++) {\n await sleep(PTT_POLL_INTERVAL_MS);\n const again = await this.fetchMsgsByMsgId(target, [msgId]);\n const latest = findPttElement(again)?.pttElement;\n if (latest === undefined) {\n continue;\n }\n const done = latest.transferStatus === 2 || latest.transferStatus === 4;\n if (latest.filePath !== undefined && latest.filePath !== \"\" && done) {\n return latest;\n }\n if (\n i === PTT_POLL_TIMES - 1 &&\n latest.filePath !== undefined &&\n latest.filePath !== \"\"\n ) {\n return latest;\n }\n }\n throw kernelError(\"语音下载未完成(超时)\", \"TIMEOUT\");\n }\n\n /** 发送输入状态(set_input_status;eventType=1 输入中,0 停止)。 */\n async setInputStatus(target: Peer, eventType: number): Promise<void> {\n await this.service.sendShowInputStatusReq(target.chatType, eventType, target.peerUid);\n }\n\n /**\n * 发送合并转发(send_group/private_forward_msg)。\n * buildMultiForwardMsg 组装 MULTI_FORWARD 元素 → 直接作 sendMsg 元素发送。\n */\n async sendForwardMessage(\n target: Peer,\n sourcePeer: Peer,\n srcMsgIds: string[],\n ): Promise<{ msgId: string }> {\n if (srcMsgIds.length === 0) {\n throw kernelError(\"sendForwardMessage 需要至少一条源消息\", \"INVALID_PARAM\");\n }\n const built = await this.service.buildMultiForwardMsg({\n srcMsgIds,\n srcContact: sourcePeer,\n });\n unwrapResult(\"buildMultiForwardMsg\", built);\n const elements = built.rspInfo?.elements;\n if (elements === undefined || elements.length === 0) {\n throw kernelError(\"合并转发组装失败:无元素\", \"UNKNOWN\");\n }\n const msgId = this.service.generateMsgUniqueId(target.chatType, String(Date.now()));\n const raw = await this.service.sendMsg(msgId, target, elements, new Map());\n unwrapResult(\"sendMsg\", raw);\n return { msgId };\n }\n\n /** 获取合并转发内容(get_forward_msg;resId 取自 multiForwardMsgElement)。 */\n async fetchForwardMessage(peer: Peer, msgId: string): Promise<RawMessage[]> {\n const msgs = await this.fetchMsgsByMsgId(peer, [msgId]);\n const forward = findForwardElement(msgs);\n if (forward === null || forward.resId === \"\") {\n throw kernelError(\"消息不包含合并转发内容\", \"NOT_FOUND\");\n }\n const raw = await this.service.getMultiMsg(peer, msgId, forward.resId);\n unwrapResult(\"getMultiMsg\", raw);\n return raw.msgList ?? [];\n }\n\n /** 单条转发(forward_group/friend_single_msg;srcMsgIds 源、dstPeer 目标)。 */\n async forwardSingleMessage(\n sourcePeer: Peer,\n srcMsgIds: string[],\n dstPeer: Peer,\n ): Promise<void> {\n if (srcMsgIds.length === 0) {\n throw kernelError(\"forwardSingleMessage 需要至少一条源消息\", \"INVALID_PARAM\");\n }\n const raw = await this.service.forwardMsg(srcMsgIds, sourcePeer, [dstPeer], undefined);\n unwrapResult(\"forwardMsg\", raw);\n }\n\n /** 设置在线状态(set_online_status;customStatus 为自定义状态)。 */\n async setOnlineStatus(opts: {\n status: number;\n extStatus: number;\n batteryStatus: number;\n customStatus?: { faceId: string; wording: string; faceType: string };\n }): Promise<void> {\n const raw = await this.service.setStatus(opts);\n unwrapResult(\"setStatus\", raw);\n }\n}\n\n/** 在消息列表中找含 pttElement 的元素(找不到返回 null)。 */\nfunction findPttElement(msgs: RawMessage[]): RawElement | null {\n const [first] = msgs;\n if (first === undefined) {\n return null;\n }\n return first.elements.find((el) => el.pttElement !== undefined) ?? null;\n}\n\n/** 在消息列表中找含 multiForwardMsgElement 的元素(找不到返回 null)。 */\nfunction findForwardElement(\n msgs: RawMessage[],\n): { resId: string; fileName: string; xmlContent: string } | null {\n const [first] = msgs;\n if (first === undefined) {\n return null;\n }\n return (\n first.elements.find((el) => el.multiForwardMsgElement !== undefined)\n ?.multiForwardMsgElement ?? null\n );\n}\n","/**\n * 陌生人信息扁平化纯函数(get_stranger_info 的数据映射部分)\n *\n * 2026-08-08 从 profile.ts 拆出:NAPI 会话依赖(ProfileApi)与纯映射分离,\n * 纯函数可独立单测(stranger-info.test.ts 基线锁定宽松取值行为)。\n *\n * 宽松取值:优先 info(uid 完整详情),字段缺失回退 byUin(uin 初查详情),\n * 两份都缺给默认值——字段缺失是 QQ 详情接口的常态,不能抛错。\n */\nimport type { UserDetailInfoByUin } from \"../types/index.js\";\n\n/** 陌生人信息(get_stranger_info 返回)。 */\nexport interface StrangerInfo {\n user_id: number;\n uid: string;\n nickname: string;\n age: number;\n qid: string;\n qq_level: number;\n sex: \"male\" | \"female\" | \"unknown\";\n long_nick: string;\n reg_time: number;\n is_vip: boolean;\n is_years_vip: boolean;\n vip_level: number;\n remark: string;\n status: number;\n login_days: number;\n}\n\n/** 扁平化后的详情视图(双来源字段级回退的产物,仅含 StrangerInfo 需要的字段)。 */\ninterface MergedDetail {\n coreInfo?: { nick?: string; remark?: string } | undefined;\n baseInfo?: { age?: number; qid?: string; sex?: number; longNick?: string } | undefined;\n vasInfo?: { svipFlag?: boolean; yearVipFlag?: boolean; vipLevel?: number } | undefined;\n commonExt?: { qqLevel?: number; regTime?: number } | undefined;\n status?: number | undefined;\n}\n\n/** 宽松取值:undefined 时给默认值(null 也归默认)。 */\nfunction orDefault<T>(value: T | undefined, fallback: T): T {\n return value ?? fallback;\n}\n\n/** 合并两份详情:优先 info(uid 完整详情),字段缺失回退 byUin(uin 初查详情)。 */\nfunction mergeDetail(info: UserDetailInfoByUin, byUin: UserDetailInfoByUin): MergedDetail {\n const infoSimple = info.detail?.simpleInfo;\n const byUinSimple = byUin.detail?.simpleInfo;\n return {\n coreInfo: pickField(infoSimple, byUinSimple, (s) => s?.coreInfo),\n baseInfo: pickField(infoSimple, byUinSimple, (s) => s?.baseInfo),\n vasInfo: pickField(infoSimple, byUinSimple, (s) => s?.vasInfo),\n commonExt: pickField(info.detail, byUin.detail, (d) => d?.commonExt),\n status: pickField(infoSimple, byUinSimple, (s) => s?.status?.status),\n };\n}\n\n/** 字段级回退:primary 的字段取不到回退 fallback 的同名字段(宽松取值)。 */\nfunction pickField<T, R>(\n primary: T | undefined,\n fallback: T | undefined,\n get: (src: T) => R | undefined,\n): R | undefined {\n const fromPrimary = primary !== undefined ? get(primary) : undefined;\n if (fromPrimary !== undefined) {\n return fromPrimary;\n }\n return fallback !== undefined ? get(fallback) : undefined;\n}\n\n/** 扁平化两份详情 → StrangerInfo(宽松取值,字段缺失给默认)。 */\nexport function flattenStrangerInfo(\n uin: string,\n uid: string,\n byUin: UserDetailInfoByUin,\n info: UserDetailInfoByUin,\n): StrangerInfo {\n const d = mergeDetail(info, byUin);\n return {\n user_id: Number(uin),\n uid,\n nickname: orDefault(d.coreInfo?.nick, \"\"),\n age: orDefault(d.baseInfo?.age, 0),\n qid: orDefault(d.baseInfo?.qid, \"\"),\n qq_level: orDefault(d.commonExt?.qqLevel, 0),\n sex: mapSex(d.baseInfo?.sex),\n long_nick: orDefault(d.baseInfo?.longNick, \"\"),\n reg_time: orDefault(d.commonExt?.regTime, 0),\n is_vip: orDefault(d.vasInfo?.svipFlag, false),\n is_years_vip: orDefault(d.vasInfo?.yearVipFlag, false),\n vip_level: orDefault(d.vasInfo?.vipLevel, 0),\n remark: orDefault(d.coreInfo?.remark, \"\"),\n status: orDefault(d.status, 0),\n login_days: 0,\n };\n}\n\n/** 性别数值 → OB11 字符串(1=男 2=女 其余 unknown)。 */\nfunction mapSex(raw: number | undefined): \"male\" | \"female\" | \"unknown\" {\n if (raw === 1) {\n return \"male\";\n }\n if (raw === 2) {\n return \"female\";\n }\n return \"unknown\";\n}\n","/**\n * ProfileApi:资料语义化 API(ADR-009 统一错误语义,P2-14/P2-15)\n *\n * - setLongNick:set_self_longnick\n * - setNickName:set_qq_profile\n * - setHeader:set_qq_avatar\n * - getStrangerInfo:get_stranger_info(uin → uid → 详情扁平化)\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type { NodeIKernelProfileService, NodeIQQNTWrapperSession } from \"../types/index.js\";\nimport { checkLooseResult } from \"./result.js\";\nimport type { StrangerInfo } from \"./stranger-info.js\";\nimport { flattenStrangerInfo } from \"./stranger-info.js\";\n\n/** 资料 API:从 session 拿 profile service,包装成语义化方法。 */\nexport class ProfileApi {\n private readonly service: NodeIKernelProfileService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getProfileService() as unknown as NodeIKernelProfileService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getProfileService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 设置个性签名(set_self_longnick)。 */\n async setLongNick(longNick: string): Promise<void> {\n const res = (await this.service.setLongNick(longNick)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n checkLooseResult(\"setLongNick\", res);\n }\n\n /** 设置昵称(set_qq_profile)。 */\n async setNickName(nickName: string): Promise<void> {\n const res = (await this.service.setNickName(nickName)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n checkLooseResult(\"setNickName\", res);\n }\n\n /** 设置头像(set_qq_avatar;filePath 为本地路径)。 */\n async setHeader(filePath: string): Promise<void> {\n const raw = await this.service.setHeader(filePath);\n if (raw.result !== 0) {\n throw kernelError(`setHeader 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n }\n\n /** 获取陌生人信息(get_stranger_info;uin 为用户 QQ 号)。 */\n async getStrangerInfo(uin: string): Promise<StrangerInfo> {\n const byUin = await this.service.getUserDetailInfoByUin(uin);\n const detailUid = byUin.detail?.uid;\n let uid = uin;\n if (detailUid !== undefined && detailUid !== \"\") {\n uid = detailUid;\n }\n const info = await this.service.getUserDetailInfo(uid);\n return flattenStrangerInfo(uin, uid, byUin, info);\n }\n}\n","/**\n * ProfileLikeApi:资料点赞语义化 API(ADR-009 统一错误语义,P2-14)\n *\n * sendLike:send_like(sourceId=71 赞来源,doLikeCount 次数)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type { NodeIKernelProfileLikeService, NodeIQQNTWrapperSession } from \"../types/index.js\";\n\n/** 点赞 API:从 session 拿 profile like service,包装成语义化方法。 */\nexport class ProfileLikeApi {\n private readonly service: NodeIKernelProfileLikeService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service =\n session.getProfileLikeService() as unknown as NodeIKernelProfileLikeService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getProfileLikeService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 点赞(send_like;uid 为目标 uid,count 次数)。 */\n async sendLike(uid: string, count = 1): Promise<void> {\n const raw = await this.service.setBuddyProfileLike({\n friendUid: uid,\n sourceId: 71,\n doLikeCount: count,\n doLikeTollCount: 0,\n });\n if (raw.result !== 0) {\n throw kernelError(`点赞失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n }\n}\n","/**\n * RichMediaApi:富媒体语义化 API(ADR-009 统一错误语义,P2-14)\n *\n * - translateWords:translate_en2zh\n * - 群文件:列表 / 空间 / 建夹 / 删除文件 / 删除夹 / 重命名 / 移动 / 转存\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n GetFileListParam,\n GroupFileItemInfo,\n GroupFolderInfo,\n NodeIKernelRichMediaService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\nimport { unwrap } from \"./result.js\";\n\n/** 群文件业务类型(deleteGroupFile/moveGroupFile/renameGroupFile 用,说明书参考)。 */\nconst GROUP_FILE_BIZ_TYPE = 102;\n/** 排序方式(按时间)。 */\nconst SORT_TYPE_TIME = 1;\n/** 排序方向(倒序)。 */\nconst SORT_ORDER_DESC = 2;\n/** 起始下标(根目录)。 */\nconst START_INDEX_ROOT = 0;\n/** 不显示在线文档文件夹。 */\nconst HIDE_ONLINEDOC_FOLDER = 0;\n/** 默认文件数量。 */\nconst DEFAULT_FILE_COUNT = 50;\n/** 空间查询文件数量(单条即可)。 */\nconst SPACE_QUERY_FILE_COUNT = 1;\n\n/** 群文件列表条目(fileInfo/folderInfo 取其一)。 */\nexport interface GroupFileListItem {\n fileInfo?: GroupFileItemInfo;\n folderInfo?: GroupFolderInfo;\n}\n\n/** 群文件系统信息。 */\nexport interface GroupFileSystemInfo {\n fileCount: number;\n limitCount: number;\n usedSpace: number;\n totalSpace: number;\n}\n\n/** 富媒体 API:从 session 拿 rich media service,包装成语义化方法。 */\nexport class RichMediaApi {\n private readonly service: NodeIKernelRichMediaService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service =\n session.getRichMediaService() as unknown as NodeIKernelRichMediaService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getRichMediaService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 英文单词翻译(translate_en2zh)。 */\n async translateWords(words: string[]): Promise<string[]> {\n const raw = await this.service.translateEnWordToZn(words);\n unwrap(\"translateEnWordToZn\", raw.result, raw.errMsg);\n return raw.words ?? [];\n }\n\n /** 群文件列表(get_group_root_files;parent 缺省根目录)。 */\n async getGroupFileList(\n groupCode: string,\n parentFolderId = \"\",\n fileCount = DEFAULT_FILE_COUNT,\n ): Promise<GroupFileListItem[]> {\n const params: GetFileListParam = {\n sortType: SORT_TYPE_TIME,\n fileCount,\n startIndex: START_INDEX_ROOT,\n sortOrder: SORT_ORDER_DESC,\n showOnlinedocFolder: HIDE_ONLINEDOC_FOLDER,\n };\n const raw = await this.service.getGroupFileList(groupCode, params);\n if (raw.result !== 0) {\n unwrap(\"getGroupFileList\", raw.result, raw.errMsg);\n }\n const list = extractFileList(raw.result);\n if (parentFolderId === \"\") {\n return list;\n }\n return list.filter(\n (item) =>\n (item.fileInfo !== undefined && item.fileInfo.parent === parentFolderId) ||\n (item.folderInfo !== undefined && item.folderInfo.folderUid === parentFolderId),\n );\n }\n\n /** 群文件系统信息(get_group_file_system_info;getGroupFileList 的 groupSpaceResult)。 */\n async getGroupFileSystemInfo(groupCode: string): Promise<GroupFileSystemInfo> {\n const params: GetFileListParam = {\n sortType: SORT_TYPE_TIME,\n fileCount: SPACE_QUERY_FILE_COUNT,\n startIndex: START_INDEX_ROOT,\n sortOrder: SORT_ORDER_DESC,\n showOnlinedocFolder: HIDE_ONLINEDOC_FOLDER,\n };\n const raw = await this.service.getGroupFileList(groupCode, params);\n if (raw.result !== 0) {\n unwrap(\"getGroupFileList\", raw.result, raw.errMsg);\n }\n const space = raw.groupSpaceResult;\n if (space === undefined) {\n throw kernelError(\"getGroupFileList 无 groupSpaceResult\", \"UNKNOWN\");\n }\n return {\n fileCount: countGroupFiles(raw.result),\n limitCount: 0,\n usedSpace: space.usedSpace,\n totalSpace: space.totalSpace,\n };\n }\n\n /** 创建群文件夹(create_group_file_folder)。 */\n async createGroupFolder(groupCode: string, folderName: string): Promise<string> {\n const raw = await this.service.createGroupFolder(groupCode, folderName);\n if (raw.result !== 0) {\n unwrap(\"createGroupFolder\", raw.result, raw.errMsg);\n }\n // 返回形状待探测:兼容 folderId / resultWithGroupItem\n const { result } = raw as { result?: unknown };\n if (typeof result === \"string\") {\n return result;\n }\n return \"\";\n }\n\n /** 删除群文件(delete_group_file)。 */\n async deleteGroupFile(groupCode: string, fileIds: string[]): Promise<void> {\n const raw = await this.service.deleteGroupFile(groupCode, [GROUP_FILE_BIZ_TYPE], fileIds);\n if (raw.result !== 0) {\n unwrap(\"deleteGroupFile\", raw.result, raw.errMsg);\n }\n }\n\n /** 删除群文件夹(delete_group_folder)。 */\n async deleteGroupFolder(groupCode: string, folderId: string): Promise<void> {\n const raw = await this.service.deleteGroupFolder(groupCode, folderId);\n if (raw.result !== 0) {\n unwrap(\"deleteGroupFolder\", raw.result, raw.errMsg);\n }\n }\n\n /** 重命名群文件(rename_group_file;参数语义待探测校准)。 */\n async renameGroupFile(groupCode: string, fileId: string, newName: string): Promise<void> {\n await this.service.renameGroupFile(groupCode, GROUP_FILE_BIZ_TYPE, fileId, newName, \"\");\n }\n\n /** 移动群文件(move_group_file;targetFolder 空为根目录)。 */\n async moveGroupFile(\n groupCode: string,\n fileIds: string[],\n targetFolderId: string,\n ): Promise<void> {\n const raw = await this.service.moveGroupFile(\n groupCode,\n [GROUP_FILE_BIZ_TYPE],\n fileIds,\n \"\",\n targetFolderId,\n );\n if (raw.result !== 0) {\n unwrap(\"moveGroupFile\", raw.result, raw.errMsg);\n }\n }\n\n /** 转存群文件(trans_group_file)。 */\n async transGroupFile(groupCode: string, fileId: string): Promise<void> {\n const raw = await this.service.transGroupFile(groupCode, fileId);\n if (raw.result !== 0) {\n unwrap(\"transGroupFile\", raw.result, raw.errMsg);\n }\n }\n}\n\n/** 从 getGroupFileList 返回提取文件列表(兼容数组 / { items } / { fileList })。导出供单测。 */\nexport function extractFileList(result: unknown): GroupFileListItem[] {\n if (Array.isArray(result)) {\n return result as GroupFileListItem[];\n }\n if (result === null || typeof result !== \"object\") {\n return [];\n }\n const obj = result as { items?: unknown; fileList?: unknown; list?: unknown };\n for (const key of [\"items\", \"fileList\", \"list\"] as const) {\n const value = obj[key];\n if (Array.isArray(value)) {\n return value as GroupFileListItem[];\n }\n }\n return [];\n}\n\n/** 统计文件数量(兼容数组 / { items })。 */\nfunction countGroupFiles(result: unknown): number {\n if (Array.isArray(result)) {\n return (result as GroupFileListItem[]).filter((item) => item.fileInfo !== undefined).length;\n }\n return 0;\n}\n","/**\n * TicketApi:票据语义化 API(ADR-009 统一错误语义,P2-13)\n *\n * - getClientKey:TicketService.forceFetchClientKey 解包(get_clientkey)\n * - getCookies:clientKey → ssl.ptlogin2.qq.com/jump 跳转 → 解析 set-cookie 头\n * (fetch 会吞掉 Set-Cookie 头,故用 node:https 手动请求)\n */\nimport { get as httpsGet } from \"node:https\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n ForceFetchClientKeyRetType,\n NodeIKernelTicketService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 跳转请求超时(毫秒)。 */\nconst JUMP_TIMEOUT_MS = 10_000;\n\n/** 票据 API:从 session 拿 ticket service,包装成语义化方法。 */\nexport class TicketApi {\n private readonly service: NodeIKernelTicketService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getTicketService() as unknown as NodeIKernelTicketService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getTicketService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 获取 clientKey(get_clientkey)。 */\n async getClientKey(): Promise<ForceFetchClientKeyRetType> {\n const raw = await this.service.forceFetchClientKey(\"\");\n if (raw.result !== 0 || raw.clientKey === \"\") {\n throw kernelError(`forceFetchClientKey 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw;\n }\n\n /** 获取指定域名的 cookies(get_cookies;uin 为机器人 QQ 号)。 */\n async getCookies(domain: string, uin: string): Promise<Record<string, string>> {\n const { clientKey } = await this.getClientKey();\n const u1 = `https://${domain}/${uin}/infocenter`;\n const jumpUrl =\n \"https://ssl.ptlogin2.qq.com/jump?ptlang=1033\" +\n `&clientuin=${encodeURIComponent(uin)}` +\n `&clientkey=${encodeURIComponent(clientKey)}` +\n `&u1=${encodeURIComponent(u1)}` +\n \"&keyindex=19\";\n return fetchJumpCookies(jumpUrl);\n }\n\n /** 计算 bkn(get_csrf_token 与 web 接口共用;skey → bkn 哈希)。 */\n static getBkn(skey: string): string {\n let hash = 5381;\n for (let i = 0; i < skey.length; i += 1) {\n const code = skey.charCodeAt(i);\n hash = hash + (hash << 5) + code;\n }\n return (hash & 0x7f_ff_ff_ff).toString();\n }\n}\n\n/** 请求 jump 地址并解析 set-cookie → dict(fetch 吞 Set-Cookie,故用 node:https)。 */\nfunction fetchJumpCookies(url: string): Promise<Record<string, string>> {\n return new Promise((resolve, reject) => {\n const req = httpsGet(\n url,\n {\n headers: {\n \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\",\n },\n },\n (res) => {\n const cookies = parseSetCookieHeaders(res.headers[\"set-cookie\"]);\n res.resume();\n res.on(\"end\", () => resolve(cookies));\n },\n );\n req.setTimeout(JUMP_TIMEOUT_MS, () => {\n req.destroy();\n reject(new Error(\"获取 cookies 超时\"));\n });\n req.on(\"error\", (err) => reject(err));\n });\n}\n\n/** 解析 set-cookie 头数组 → { name: value } dict。 */\nfunction parseSetCookieHeaders(raw: string[] | undefined): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (raw === undefined) {\n return cookies;\n }\n for (const line of raw) {\n const [pair] = line.split(\";\");\n if (pair !== undefined) {\n const eq = pair.indexOf(\"=\");\n if (eq > 0) {\n const name = pair.slice(0, eq).trim();\n const value = pair.slice(eq + 1);\n if (name !== \"\") {\n cookies[name] = value;\n }\n }\n }\n }\n return cookies;\n}\n","/**\n * WebApi:群空间 web 接口(P2-15)\n *\n * qun.qq.com 群空间接口需 Cookie(p_skey/skey)+ bkn(skey 哈希)。\n * 复用 TicketApi.getCookies('qun.qq.com') 取 Cookie,TicketApi.getBkn 算 bkn。\n * 零 NapCat 代码:接口契约(URL/参数/返回形状)是 qun.qq.com 外部系统的事实,\n * 自研描述。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport { TicketApi } from \"./ticket.js\";\n\n/** 群空间域名(Cookie 归属)。 */\nconst QUN_DOMAIN = \"qun.qq.com\";\n\n/** 精华消息页大小(分页拉取)。 */\nconst ESSENCE_PAGE_LIMIT = 50;\n\n/** 精华消息最大分页数。 */\nconst ESSENCE_MAX_PAGES = 20;\n\n/** 精华消息项(digest_list 返回 data.msg_list 成员,说明书参考)。 */\nexport interface EssenceMsgItem {\n msg_seq: number;\n msg_random: number;\n sender_uin: string;\n sender_nick: string;\n add_digest_uin: string;\n add_digest_nick: string;\n add_digest_time: number;\n msg_content: Array<{\n msg_type?: number;\n text?: string;\n image_url?: string;\n }>;\n}\n\n/** 群荣誉项(honorlist 返回列表项,说明书参考)。 */\nexport interface HonorListItem {\n uin: string;\n name: string;\n avatar: string;\n desc: string;\n}\n\n/** 群荣誉信息(getGroupHonorInfo 返回)。 */\nexport interface GroupHonorWebInfo {\n group_id: number;\n current_talkative: HonorListItem | null;\n talkative_list: HonorListItem[];\n performer_list: HonorListItem[];\n legend_list: HonorListItem[];\n emotion_list: HonorListItem[];\n strong_newbie_list: HonorListItem[];\n}\n\n/** 群荣誉类型(1=龙王 2=群聊之火 3=群聊炽热 6=快乐源泉)。 */\nexport const WebHonorType = {\n ALL: 0,\n TALKATIVE: 1,\n PERFORMER: 2,\n LEGEND: 3,\n EMOTION: 6,\n} as const;\nexport type WebHonorType = (typeof WebHonorType)[keyof typeof WebHonorType];\n\n/** 群空间 web API(Cookie 经 TicketApi 注入)。 */\nexport class WebApi {\n private readonly getCookies: (domain: string) => Promise<Record<string, string>>;\n\n constructor(opts: { getCookies: (domain: string) => Promise<Record<string, string>> }) {\n this.getCookies = opts.getCookies;\n }\n\n /** 拉取单页精华消息(失败/retcode 非 0 返回 null)。 */\n private async fetchEssencePage(\n groupCode: string,\n cookie: Record<string, string>,\n bkn: string,\n page: number,\n ): Promise<{ items: EssenceMsgItem[]; isEnd: boolean } | null> {\n const url =\n \"https://qun.qq.com/cgi-bin/group_digest/digest_list?\" +\n new URLSearchParams({\n bkn,\n page_start: String(page * ESSENCE_PAGE_LIMIT),\n page_limit: String(ESSENCE_PAGE_LIMIT),\n group_code: groupCode,\n }).toString();\n const ret = await fetchQunJson(url, cookie);\n if (ret === null || ret.retcode !== 0) {\n return null;\n }\n const list = ret.data?.msg_list;\n return {\n items: Array.isArray(list) ? list.filter((item) => item !== null) : [],\n isEnd: ret.data?.is_end === true,\n };\n }\n\n /** 获取群精华消息列表(get_essence_msg_list;分页拉取至 is_end)。 */\n async getEssenceMsgList(groupCode: string): Promise<EssenceMsgItem[]> {\n const cookie = await this.getCookies(QUN_DOMAIN);\n const bkn = TicketApi.getBkn(cookie[\"skey\"] ?? \"\");\n const out: EssenceMsgItem[] = [];\n for (let page = 0; page < ESSENCE_MAX_PAGES; page += 1) {\n const result = await this.fetchEssencePage(groupCode, cookie, bkn, page);\n if (result === null) {\n break;\n }\n out.push(...result.items);\n if (result.isEnd) {\n break;\n }\n }\n return out;\n }\n\n /** 获取群荣誉信息(get_group_honor_info;type 0=全部)。 */\n async getGroupHonorInfo(groupCode: string, type: WebHonorType): Promise<GroupHonorWebInfo> {\n const cookie = await this.getCookies(QUN_DOMAIN);\n const result: GroupHonorWebInfo = {\n group_id: Number(groupCode),\n current_talkative: null,\n talkative_list: [],\n performer_list: [],\n legend_list: [],\n emotion_list: [],\n strong_newbie_list: [],\n };\n await this.fillHonorLists(result, groupCode, cookie, type);\n return result;\n }\n\n /** 按 type 填充荣誉列表(ALL 拉全部,单类型只拉对应类)。 */\n private async fillHonorLists(\n result: GroupHonorWebInfo,\n groupCode: string,\n cookie: Record<string, string>,\n type: WebHonorType,\n ): Promise<void> {\n for (const target of honorTargets(type)) {\n const list = await this.fetchHonorList(groupCode, cookie, target.kind);\n result[target.listKey] = list;\n if (target.kind === WebHonorType.TALKATIVE) {\n const [first] = list;\n if (first !== undefined) {\n result.current_talkative = first;\n }\n }\n }\n }\n\n /** 拉取单类荣誉列表(honorlist 接口,正则提取 __INITIAL_STATE__)。 */\n private async fetchHonorList(\n groupCode: string,\n cookie: Record<string, string>,\n type: number,\n ): Promise<HonorListItem[]> {\n const url =\n \"https://qun.qq.com/interactive/honorlist?\" +\n new URLSearchParams({ gc: groupCode, type: String(type) }).toString();\n const text = await fetchQunText(url, cookie);\n if (text === null) {\n return [];\n }\n return parseHonorList(text, type);\n }\n}\n\n/** honorlist 页面 __INITIAL_STATE__ 提取。 */\nconst INITIAL_STATE_RE = /window\\.__INITIAL_STATE__=(.*?);/;\n\n/** 从 honorlist 页面提取 __INITIAL_STATE__ → 荣誉列表(解析失败返回空)。 */\nexport function parseHonorList(text: string, type: number): HonorListItem[] {\n const match = INITIAL_STATE_RE.exec(text);\n if (match === null || match[1] === undefined) {\n return [];\n }\n let state: { talkativeList?: unknown; actorList?: unknown };\n try {\n state = JSON.parse(match[1]);\n } catch {\n return [];\n }\n const raw = type === WebHonorType.TALKATIVE ? state.talkativeList : state.actorList;\n if (!Array.isArray(raw)) {\n return [];\n }\n const out: HonorListItem[] = [];\n for (const item of raw) {\n if (item !== null && typeof item === \"object\") {\n const obj = item as {\n uin?: unknown;\n name?: unknown;\n avatar?: unknown;\n desc?: unknown;\n };\n out.push({\n uin: String(obj.uin ?? \"\"),\n name: String(obj.name ?? \"\"),\n avatar: String(obj.avatar ?? \"\"),\n desc: String(obj.desc ?? \"\"),\n });\n }\n }\n return out;\n}\n\n/** 请求 qun.qq.com JSON 接口(带 Cookie;失败返回 null)。 */\nasync function fetchQunJson(\n url: string,\n cookie: Record<string, string>,\n): Promise<{ retcode: number; data?: { msg_list?: EssenceMsgItem[]; is_end?: boolean } } | null> {\n try {\n const res = await fetch(url, { headers: { Cookie: cookieToString(cookie) } });\n if (!res.ok) {\n return null;\n }\n const json = (await res.json()) as {\n retcode?: number;\n data?: { msg_list?: unknown; is_end?: unknown };\n };\n return {\n retcode: json.retcode ?? -1,\n data: json.data as { msg_list?: EssenceMsgItem[]; is_end?: boolean },\n };\n } catch (err) {\n throw kernelError(`qun.qq.com 请求失败: ${String(err)}`, \"UNKNOWN\");\n }\n}\n\n/** 请求 qun.qq.com 文本接口(带 Cookie;失败返回 null)。 */\nasync function fetchQunText(url: string, cookie: Record<string, string>): Promise<string | null> {\n try {\n const res = await fetch(url, { headers: { Cookie: cookieToString(cookie) } });\n if (!res.ok) {\n return null;\n }\n return await res.text();\n } catch (err) {\n throw kernelError(`qun.qq.com 请求失败: ${String(err)}`, \"UNKNOWN\");\n }\n}\n\n/** Cookie dict → 头字符串。 */\nfunction cookieToString(cookie: Record<string, string>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(cookie)) {\n parts.push(`${key}=${value}`);\n }\n return parts.join(\"; \");\n}\n\n/** 荣誉类型 → 目标列表(ALL 拉全部,单类型只拉对应类)。 */\nfunction honorTargets(type: WebHonorType): Array<{\n kind: WebHonorType;\n listKey: \"talkative_list\" | \"performer_list\" | \"legend_list\" | \"emotion_list\";\n}> {\n const all = [\n { kind: WebHonorType.TALKATIVE, listKey: \"talkative_list\" },\n { kind: WebHonorType.PERFORMER, listKey: \"performer_list\" },\n { kind: WebHonorType.LEGEND, listKey: \"legend_list\" },\n { kind: WebHonorType.EMOTION, listKey: \"emotion_list\" },\n ] as const;\n if (type === WebHonorType.ALL) {\n return [...all];\n }\n return all.filter((target) => target.kind === type);\n}\n","/**\n * FriendBridge:好友事件桥(2026-09-08)\n *\n * 职责:把 buddy service 的原生回调(onBuddyReqChange / onBuddyListChange 等)\n * 推入类型化事件通道 `NTEventChannel<BuddyListener, \"Buddy\">`。与 MsgBridge/\n * GroupBridge 同构(每个 Service 只注册一次原生监听,ADR-003)。\n *\n * 方法名证据:wrapper.node(9.9.33-52230)二进制字符串提取;参数形状\n * 待真实事件校准(透传 unknown,订阅方防御性收窄)。\n *\n * 用法:\n * const channel = new NTEventChannel<BuddyListener, \"Buddy\">(\"Buddy\");\n * const bridge = new FriendBridge(session, channel);\n * bridge.register(); // addKernelBuddyListener(普通 JS 对象,NAPI 反射)\n * bridge.unregister(); // 停止时清理\n */\n\nimport type { NTEventChannel } from \"../event-channel.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n BuddyListener,\n NodeIKernelBuddyService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 好友事件通道的固定类型(事件名前缀 \"Buddy\")。 */\nexport type BuddyEventChannel = NTEventChannel<BuddyListener, \"Buddy\">;\n\n/**\n * 好友事件桥:注册原生监听 → 回调 emit 到 channel。\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份。\n */\nexport class FriendBridge {\n private readonly service: NodeIKernelBuddyService;\n private readonly channel: BuddyEventChannel;\n private listenerId: number | null = null;\n\n constructor(session: NodeIQQNTWrapperSession, channel: BuddyEventChannel) {\n const service = session.getBuddyService() as unknown as NodeIKernelBuddyService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getBuddyService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel;\n }\n\n /** 注册原生监听(幂等)。 */\n register(): void {\n if (this.listenerId !== null) {\n return;\n }\n // listener 为普通 JS 对象(NAPI 反射读取方法回调)\n const listener: BuddyListener = {\n onBuddyReqChange: (arg) => this.channel.emit(\"Buddy/onBuddyReqChange\", arg),\n onBuddyListChange: (arg) => this.channel.emit(\"Buddy/onBuddyListChange\", arg),\n onBuddyListChangedV2: (arg) => this.channel.emit(\"Buddy/onBuddyListChangedV2\", arg),\n onBuddyDeleted: (arg) => this.channel.emit(\"Buddy/onBuddyDeleted\", arg),\n };\n this.listenerId = this.service.addKernelBuddyListener(listener);\n }\n\n /** 注销原生监听(幂等)。 */\n unregister(): void {\n if (this.listenerId === null) {\n return;\n }\n this.service.removeKernelBuddyListener(this.listenerId);\n this.listenerId = null;\n }\n}\n","/**\n * GroupBridge:群事件桥(P2-17,2026-08-05)\n *\n * 职责:把 group service 的原生回调(onGroupListUpdate / onMemberListChange 等)\n * 推入类型化事件通道 `NTEventChannel<GroupListener, \"Group\">`。每个 Service 只\n * 注册一次原生监听(ADR-003),缓存维护(GroupCache)与协议层都订阅 channel。\n *\n * 用法:\n * const channel = new NTEventChannel<GroupListener, \"Group\">(\"Group\");\n * const bridge = new GroupBridge(session, channel);\n * bridge.register(); // addKernelGroupListener(普通 JS 对象,NAPI 反射)\n * const cache = new GroupCache({ channel, groupApi });\n * cache.register(); // 订阅 channel 主动维护\n * bridge.unregister(); // 停止时清理\n */\n\nimport type { NTEventChannel } from \"../event-channel.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n GroupListener,\n NodeIKernelGroupService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 群事件通道的固定类型(事件名前缀 \"Group\")。 */\nexport type GroupEventChannel = NTEventChannel<GroupListener, \"Group\">;\n\n/**\n * 群事件桥:注册原生监听 → 回调 emit 到 channel。\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份。\n */\nexport class GroupBridge {\n private readonly service: NodeIKernelGroupService;\n private readonly channel: GroupEventChannel;\n private listenerId: number | null = null;\n\n constructor(session: NodeIQQNTWrapperSession, channel: GroupEventChannel) {\n const service = session.getGroupService() as unknown as NodeIKernelGroupService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getGroupService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel;\n }\n\n /** 注册原生监听(幂等)。 */\n register(): void {\n if (this.listenerId !== null) {\n return;\n }\n // listener 为普通 JS 对象(NAPI 反射读取方法回调)\n const listener: GroupListener = {\n onGroupListInited: (listEmpty) =>\n this.channel.emit(\"Group/onGroupListInited\", listEmpty),\n onGroupListUpdate: (updateType, groupList) =>\n this.channel.emit(\"Group/onGroupListUpdate\", updateType, groupList),\n onGroupDetailInfoChange: (detailInfo) =>\n this.channel.emit(\"Group/onGroupDetailInfoChange\", detailInfo),\n onMemberListChange: (arg) => this.channel.emit(\"Group/onMemberListChange\", arg),\n onMemberInfoChange: (groupCode, dataSource, members) =>\n this.channel.emit(\"Group/onMemberInfoChange\", groupCode, dataSource, members),\n onGroupNotifiesUpdated: (doubt, notifies) =>\n this.channel.emit(\"Group/onGroupNotifiesUpdated\", doubt, notifies),\n onGroupSingleScreenNotifies: (doubt, seq, notifies) =>\n this.channel.emit(\"Group/onGroupSingleScreenNotifies\", doubt, seq, notifies),\n onShutUpMemberListChanged: (groupCode, members) =>\n this.channel.emit(\"Group/onShutUpMemberListChanged\", groupCode, members),\n // 2026-09-10 接线:群精华列表变化(方法名证据见 listeners/group.ts 注释)\n onGroupEssenceListChange: (arg) =>\n this.channel.emit(\"Group/onGroupEssenceListChange\", arg),\n };\n this.listenerId = this.service.addKernelGroupListener(listener);\n }\n\n /** 注销原生监听(幂等)。 */\n unregister(): void {\n if (this.listenerId === null) {\n return;\n }\n this.service.removeKernelGroupListener(this.listenerId);\n this.listenerId = null;\n }\n}\n","/**\n * MsgBridge:消息事件桥(P2-2,2026-08-05)\n *\n * 职责:把 msg service 的原生回调(onRecvMsg 等)推入类型化事件通道\n * `NTEventChannel<MsgListener, \"Msg\">`。每个 Service 只注册一次原生监听\n * (ADR-003),缓存维护与协议翻译都订阅 channel。\n *\n * 用法:\n * const channel = new NTEventChannel<MsgListener, \"Msg\">(\"Msg\");\n * const bridge = new MsgBridge(session, channel);\n * bridge.register(); // addKernelMsgListener(普通 JS 对象,NAPI 反射)\n * // 协议层订阅:channel.on(\"Msg/onRecvMsg\", handler)\n * bridge.unregister(); // 停止时清理\n */\n\nimport type { NTEventChannel } from \"../event-channel.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n MsgListener,\n NodeIKernelMsgService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 消息事件通道的固定类型(事件名前缀 \"Msg\")。 */\nexport type MsgEventChannel = NTEventChannel<MsgListener, \"Msg\">;\n\n/**\n * 消息事件桥:注册原生监听 → 回调 emit 到 channel。\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份。\n */\nexport class MsgBridge {\n private readonly service: NodeIKernelMsgService;\n private readonly channel: MsgEventChannel;\n private listenerId: number | null = null;\n\n constructor(session: NodeIQQNTWrapperSession, channel: MsgEventChannel) {\n const service = session.getMsgService() as unknown as NodeIKernelMsgService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getMsgService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel;\n }\n\n /** 注册原生监听(幂等)。 */\n register(): void {\n if (this.listenerId !== null) {\n return;\n }\n // listener 为普通 JS 对象(NAPI 反射读取方法回调)\n // onRecvMsg 透传原生回调参数(运行时实证为消息数组,2026-08-07)。\n const listener: MsgListener = {\n onRecvMsg: (msgs) => this.channel.emit(\"Msg/onRecvMsg\", msgs),\n onRecvMsgReadReport: (reports) => this.channel.emit(\"Msg/onRecvMsgReadReport\", reports),\n onRecvMsgReceipt: (receipts) => this.channel.emit(\"Msg/onRecvMsgReceipt\", receipts),\n // 2026-08-11 补齐:发送状态更新(sendMsg 结果以此为准,NapCat 同款)\n onMsgInfoListUpdate: (msgs) => this.channel.emit(\"Msg/onMsgInfoListUpdate\", msgs),\n // 2026-09-10 接线:离线文件 / sys msg(方法名证据见 listeners/msg.ts 头注释)\n onRecvOfflineFileMsg: (arg) => this.channel.emit(\"Msg/onRecvOfflineFileMsg\", arg),\n onRecvOnlineFileMsg: (arg) => this.channel.emit(\"Msg/onRecvOnlineFileMsg\", arg),\n onRecvSysMsg: (arg) => this.channel.emit(\"Msg/onRecvSysMsg\", arg),\n };\n this.listenerId = this.service.addKernelMsgListener(listener);\n }\n\n /** 注销原生监听(幂等)。 */\n unregister(): void {\n if (this.listenerId === null) {\n return;\n }\n this.service.removeKernelMsgListener(this.listenerId);\n this.listenerId = null;\n }\n}\n","/**\n * 类型化事件通道(ADR-003)\n *\n * - 事件名约定 `${Service}/${method}`(如 \"Msg/onRecvMsg\"),签名从 Listener 接口编译期推导\n * - 事件名写错 → 编译期报错(类型推导)\n * - `on` 返回 unsubscribe;`waitFor` 注册临时监听 → filter → 超时自动清理(替代魔法字符串方案)\n * - error 兜底:监听器抛异常不打断派发,统一经 `onError` 通知\n *\n * 组合实现而非 extends EventEmitter:EventEmitter.on 返回 `this`,与设计要求的\n * 返回 unsubscribe 冲突;组合更干净,且内核无全局单例(ADR-015 推论)。\n *\n * 类型推导说明:不直接 `ListenerEvents<L, Name>[E]` 索引(泛型下会扩宽到\n * string|number|symbol 索引,索引出 unknown),而是用 Extract 按事件名从\n * `{ name, fn }` 联合精确提取 handler / 参数类型,保证实例化后签名精确。\n */\nimport { EventEmitter } from \"node:events\";\nimport { KernelError } from \"./infra/index.js\";\n\n/** waitFor 默认超时(毫秒)。 */\nconst DEFAULT_WAIT_TIMEOUT_MS = 30_000;\n\n/** Listener 接口形状(宽约束;精确签名由实例化时的具体接口保证)。 */\ntype ListenerShape = Record<string, unknown>;\n\n/** 事件名:`${Service}/${method}`。 */\ntype EventName<L extends ListenerShape, Name extends string> = `${Name}/${keyof L & string}`;\n\n/** 按事件名从 Listener 精确提取 handler 类型(条件类型 infer,泛型安全)。 */\ntype EventHandler<\n L extends ListenerShape,\n Name extends string,\n E extends EventName<L, Name>,\n> = Extract<\n {\n [K in keyof L]: L[K] extends (...args: infer A) => infer R\n ? { name: `${Name}/${K & string}`; fn: (...args: A) => R }\n : never;\n }[keyof L],\n { name: E }\n>[\"fn\"];\n\n/** 按事件名从 Listener 精确提取参数类型。 */\ntype EventArgs<\n L extends ListenerShape,\n Name extends string,\n E extends EventName<L, Name>,\n> = Extract<\n {\n [K in keyof L]: L[K] extends (...args: infer A) => unknown\n ? { name: `${Name}/${K & string}`; args: A }\n : never;\n }[keyof L],\n { name: E }\n>[\"args\"];\n\n/** 从 Listener 接口推导事件映射(文档/调试用):`{ \"Msg/onRecvMsg\": fn }`。 */\nexport type ListenerEvents<L extends ListenerShape, Name extends string> = {\n [K in keyof L as `${Name}/${K & string}`]: L[K];\n};\n\n/**\n * 类型化事件通道。\n *\n * @typeParam L - Listener 接口(探测产物,如 MsgListener)\n * @typeParam Name - Service 名(事件名前缀,如 \"Msg\")\n */\nexport class NTEventChannel<L extends ListenerShape, Name extends string> {\n readonly serviceName: Name;\n\n private readonly emitter = new EventEmitter();\n private readonly errorHandlers = new Set<(err: unknown) => void>();\n private readonly anyHandlers = new Set<(event: string, ...args: unknown[]) => void>();\n\n constructor(serviceName: Name) {\n this.serviceName = serviceName;\n // 单个 Service 多订阅者(缓存维护 + 协议翻译),关闭默认 10 个监听器上限\n this.emitter.setMaxListeners(0);\n }\n\n /**\n * 订阅事件:返回 unsubscribe(只注销该 handler,不影响其他订阅者)。\n * 每个 Service 只注册一次原生监听,缓存维护与协议翻译都订阅这里。\n */\n on<E extends EventName<L, Name>>(event: E, handler: EventHandler<L, Name, E>): () => void {\n this.emitter.addListener(event as string, handler as (...args: unknown[]) => void);\n return () => {\n this.emitter.removeListener(event as string, handler as (...args: unknown[]) => void);\n };\n }\n\n /**\n * 请求-响应桥:注册临时监听 → filter 命中 → resolve;超时自动清理并 reject。\n * 替代魔法字符串事件名方案。\n */\n waitFor<E extends EventName<L, Name>>(\n event: E,\n opts: {\n filter?: (...args: EventArgs<L, Name, E>) => boolean;\n timeout?: number;\n } = {},\n ): Promise<EventArgs<L, Name, E>> {\n const timeoutMs = opts.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;\n return new Promise((resolve, reject) => {\n const handler = (...args: EventArgs<L, Name, E>) => {\n if (opts.filter && !opts.filter(...args)) {\n return; // 不匹配:保持监听,等待下一条\n }\n cleanup();\n resolve(args);\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(new KernelError(`等待事件超时: ${event as string}`, \"TIMEOUT\"));\n }, timeoutMs);\n const cleanup = (): void => {\n clearTimeout(timer);\n this.emitter.removeListener(\n event as string,\n handler as (...args: unknown[]) => void,\n );\n };\n this.emitter.addListener(event as string, handler as (...args: unknown[]) => void);\n });\n }\n\n /**\n * 类型化派发:wrapper-loader 的原生回调经此推入通道。\n * 2026-08-07 修复:原生 EventEmitter.emit 中一个 listener 抛异常会中断后续\n * listener 派发(Node 语义)——改为快照数组逐个 try/catch,保证「单个订阅者\n * 异常不打断派发」(协议翻译层异常不饿死缓存维护等其他订阅者)。\n */\n /**\n * 订阅所有事件(IPC 子进程模式整通道转发用:事件名 `\"${service}/${method}\"`\n * 经 handler 首参透出,args 原样透传)。返回退订函数。\n */\n onAny(handler: (event: string, ...args: unknown[]) => void): () => void {\n this.anyHandlers.add(handler);\n return () => {\n this.anyHandlers.delete(handler);\n };\n }\n\n emit<E extends EventName<L, Name>>(event: E, ...args: EventArgs<L, Name, E>): void {\n // 全事件订阅(IPC 转发):单个订阅者异常不打断派发,统一走 error 兜底\n for (const anyHandler of this.anyHandlers) {\n try {\n anyHandler(event as string, ...args);\n } catch (err) {\n this.notifyError(err);\n }\n }\n const listeners = this.emitter.listeners(event as string);\n for (const listener of listeners) {\n try {\n (listener as (...a: unknown[]) => void)(...args);\n } catch (err) {\n // error 兜底:单个订阅者异常不打断派发,统一通知 onError\n this.notifyError(err);\n }\n }\n }\n\n /** 通知所有错误处理器(单个异常不打断其他处理器)。 */\n private notifyError(err: unknown): void {\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n /** 订阅错误通知(监听器抛出的异常 / 其他内部错误)。 */\n onError(handler: (err: unknown) => void): () => void {\n this.errorHandlers.add(handler);\n return () => {\n this.errorHandlers.delete(handler);\n };\n }\n}\n","/**\n * BuddyCache:好友缓存(B2,2026-09-08;仿 GroupCache 的 ADR-008 模式)\n *\n * - 更新:订阅 Buddy/onBuddyListChange 全量快照(T10 实证 = BuddyCategory[],\n * 好友明细在 category.buddyList)维护 uid → BuddyListEntry 快照。\n * - diff:首帧只建 baseline 不发事件(防止启动时对每个好友发 friend_add);\n * 之后快照对比上一帧——新增发布 BuddyCache/onBuddyAdded、消失发布\n * BuddyCache/onBuddyRemoved(归一化事件,协议层翻译保持纯函数)。\n * - 消费:协议翻译层订阅 diff 通道(friend_add notice)或只读快照查询。\n *\n * 无全局单例(ADR-015 推论)——每进程每 session 一份,由装配层持有。\n */\n\nimport type { BuddyEventChannel } from \"../bridge/index.js\";\nimport { NTEventChannel } from \"../event-channel.js\";\nimport type { BuddyCategory, BuddyListEntry } from \"../types/index.js\";\n\n/** BuddyCache diff 事件 listener(归一化「新增/删除」事件)。 */\nexport type BuddyCacheListener = {\n onBuddyAdded: (entry: BuddyListEntry) => void;\n onBuddyRemoved: (entry: BuddyListEntry) => void;\n};\n\n/** BuddyCache diff 事件通道(事件名 BuddyCache/onBuddyAdded 等)。 */\nexport type BuddyCacheEventChannel = NTEventChannel<BuddyCacheListener, \"BuddyCache\">;\n\n/** BuddyCache 构造参数。 */\nexport interface BuddyCacheOptions {\n /** Buddy 事件通道(FriendBridge 注册原生监听后 emit 到这里)。 */\n channel: BuddyEventChannel;\n /** 校准日志(可选:快照形状异常 raw 记录;缺省静默)。 */\n logger?: { warn(obj: unknown, msg?: string): void };\n}\n\n/**\n * 防御性收窄 onBuddyListChange 载荷 → BuddyCategory[](T10 实证形状;\n * 非数组返回 null,调用方打 raw 日志积累校准数据)。\n */\nexport function narrowBuddyCategories(arg: unknown): BuddyCategory[] | null {\n if (!Array.isArray(arg)) {\n return null;\n }\n for (const item of arg) {\n if (item === null || typeof item !== \"object\") {\n return null;\n }\n }\n return arg as BuddyCategory[];\n}\n\n/** 从快照分类拍平好友明细(buddyList 缺失的分类跳过)。 */\nfunction flattenEntries(categories: BuddyCategory[]): BuddyListEntry[] {\n const out: BuddyListEntry[] = [];\n for (const cat of categories) {\n for (const entry of cat.buddyList ?? []) {\n if (typeof entry?.uid === \"string\" && entry.uid !== \"\") {\n out.push(entry);\n }\n }\n }\n return out;\n}\n\n/** 好友缓存:快照维护 + diff 归一化事件。 */\nexport class BuddyCache {\n private readonly channel: BuddyEventChannel;\n private readonly logger: { warn(obj: unknown, msg?: string): void } | undefined;\n\n /** diff 归一化事件通道(协议层订阅 friend_add / IPC 转发消费)。 */\n readonly events: BuddyCacheEventChannel = new NTEventChannel(\"BuddyCache\");\n\n /** 好友快照(uid → entry,onBuddyListChange 全量维护)。 */\n private readonly buddies = new Map<string, BuddyListEntry>();\n\n /** 首帧已建 baseline(此后快照才参与 diff)。 */\n private bootstrapped = false;\n\n private unsubscribes: (() => void) | null = null;\n\n constructor(opts: BuddyCacheOptions) {\n this.channel = opts.channel;\n this.logger = opts.logger;\n }\n\n /** 订阅 Buddy 事件通道(幂等)。 */\n register(): void {\n if (this.unsubscribes !== null) {\n return;\n }\n const unsub = this.channel.on(\"Buddy/onBuddyListChange\", (arg: unknown) => {\n const categories = narrowBuddyCategories(arg);\n if (categories === null) {\n // 未知形状:raw 日志积累校准数据(V2 事件载荷是 boolean,同样落这里)\n this.logger?.warn(\n { raw: JSON.stringify(arg)?.slice(0, 2000) },\n \"BuddyCache: onBuddyListChange 未知载荷形状\",\n );\n return;\n }\n this.applySnapshot(flattenEntries(categories));\n });\n this.unsubscribes = unsub;\n }\n\n /** 退订事件通道(幂等)。 */\n unregister(): void {\n this.unsubscribes?.();\n this.unsubscribes = null;\n this.buddies.clear();\n this.bootstrapped = false;\n }\n\n /** 应用全量快照:首帧建 baseline,之后 diff 发布归一化事件。 */\n private applySnapshot(entries: BuddyListEntry[]): void {\n if (!this.bootstrapped) {\n for (const entry of entries) {\n this.buddies.set(entry.uid, entry);\n }\n this.bootstrapped = true;\n return;\n }\n const next = new Map<string, BuddyListEntry>();\n for (const entry of entries) {\n next.set(entry.uid, entry);\n }\n for (const [uid, entry] of next) {\n if (!this.buddies.has(uid)) {\n this.events.emit(\"BuddyCache/onBuddyAdded\", entry);\n }\n }\n for (const [uid, entry] of this.buddies) {\n if (!next.has(uid)) {\n this.events.emit(\"BuddyCache/onBuddyRemoved\", entry);\n }\n }\n this.buddies.clear();\n for (const [uid, entry] of next) {\n this.buddies.set(uid, entry);\n }\n }\n\n /** 好友快照(只读拷贝)。 */\n listBuddies(): BuddyListEntry[] {\n return [...this.buddies.values()];\n }\n\n /** 单个好友(快照;undefined = 不在缓存)。 */\n getBuddy(uid: string): BuddyListEntry | undefined {\n return this.buddies.get(uid);\n }\n\n /** 快照是否已建(测试/诊断用)。 */\n get baselineReady(): boolean {\n return this.bootstrapped;\n }\n}\n","/**\n * 等待/延迟工具(login 模块共享,2026-08-08 克隆合并)\n */\n\n/** 等待条件满足(轮询,带超时)。 */\nexport function waitFor(\n predicate: () => boolean,\n timeoutMs: number,\n intervalMs = 500,\n): Promise<boolean> {\n return new Promise((resolve) => {\n const started = Date.now();\n const tick = (): void => {\n if (predicate()) {\n resolve(true);\n return;\n }\n if (Date.now() - started > timeoutMs) {\n resolve(false);\n return;\n }\n setTimeout(tick, intervalMs);\n };\n tick();\n });\n}\n\n/** 短延迟。 */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","/**\n * GroupCache:群/成员缓存(ADR-008,P2-17,2026-08-05)\n *\n * - 更新:订阅 GroupEventChannel 主动维护(群列表/群详情/成员事件 → 缓存 upsert)。\n * - 回填:只读接口查询缺失时经 GroupApi 惰性拉取(in-flight 去重防并发重复拉)。\n * - 消费:协议翻译层只读消费,禁止调 API(翻译 = 纯函数)。\n *\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份,由装配层持有。\n */\n\nimport type { GroupApi } from \"../apis/index.js\";\nimport type { GroupEventChannel } from \"../bridge/index.js\";\nimport { waitFor } from \"../login/wait.js\";\nimport type {\n Group,\n GroupDetailInfo,\n GroupMember,\n GroupMemberDataSource,\n GroupMemberListChange,\n} from \"../types/index.js\";\nimport { GroupListUpdateType } from \"../types/index.js\";\n\n/** GroupCache 构造参数。 */\nexport interface GroupCacheOptions {\n /** 群事件通道(GroupBridge 注册原生监听后 emit 到这里)。 */\n channel: GroupEventChannel;\n /** 群 API(惰性回填用)。 */\n groupApi: GroupApi;\n}\n\n/**\n * 群/成员缓存:事件主动维护 + 查询惰性回填。\n * 只读接口 getGroupDetail / getMembers / getMember / getGroup(列表项)。\n */\nexport class GroupCache {\n private readonly channel: GroupEventChannel;\n private readonly groupApi: GroupApi;\n\n /** 群列表项(onGroupListUpdate / getGroupList 回填)。 */\n private readonly groupList = new Map<string, Group>();\n /** 群详情(onGroupDetailInfoChange / getGroupInfo 回填)。 */\n private readonly groupDetails = new Map<string, GroupDetailInfo>();\n /** 群成员:groupCode → (uid → GroupMember)。 */\n private readonly members = new Map<string, Map<string, GroupMember>>();\n /** 惰性回填 in-flight 去重。 */\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n private unsubscribes: (() => void) | null = null;\n\n constructor(opts: GroupCacheOptions) {\n this.channel = opts.channel;\n this.groupApi = opts.groupApi;\n }\n\n /** 订阅群事件通道(幂等)。 */\n register(): void {\n if (this.unsubscribes !== null) {\n return;\n }\n const unsubs: Array<() => void> = [];\n unsubs.push(\n this.channel.on(\"Group/onGroupListInited\", (listEmpty: boolean) => {\n if (listEmpty) {\n this.groupList.clear();\n }\n }),\n );\n unsubs.push(\n this.channel.on(\n \"Group/onGroupListUpdate\",\n (updateType: GroupListUpdateType, groupList: Group[]) => {\n if (updateType === GroupListUpdateType.ALL) {\n this.groupList.clear();\n }\n for (const group of groupList) {\n this.groupList.set(group.groupCode, group);\n }\n },\n ),\n );\n unsubs.push(\n this.channel.on(\"Group/onGroupDetailInfoChange\", (detailInfo: GroupDetailInfo) => {\n this.groupDetails.set(detailInfo.groupCode, detailInfo);\n }),\n );\n unsubs.push(\n this.channel.on(\"Group/onMemberListChange\", (arg: GroupMemberListChange) => {\n this.upsertMembers(arg.sceneId, arg.infos);\n }),\n );\n unsubs.push(\n this.channel.on(\n \"Group/onMemberInfoChange\",\n (\n _groupCode: string,\n _dataSource: GroupMemberDataSource,\n members: Map<string, GroupMember>,\n ) => {\n this.upsertMembers(_groupCode, members);\n },\n ),\n );\n this.unsubscribes = () => {\n for (const unsub of unsubs) {\n unsub();\n }\n };\n }\n\n /** 退订事件通道(幂等)。 */\n unregister(): void {\n this.unsubscribes?.();\n this.unsubscribes = null;\n this.groupList.clear();\n this.groupDetails.clear();\n this.members.clear();\n this.inFlight.clear();\n }\n\n /** 合并成员到该群缓存(uid → GroupMember)。 */\n private upsertMembers(groupCode: string, infos: Map<string, GroupMember>): void {\n let map = this.members.get(groupCode);\n if (map === undefined) {\n map = new Map();\n this.members.set(groupCode, map);\n }\n for (const [uid, member] of infos) {\n map.set(uid, member);\n }\n }\n\n /** 群列表是否已有缓存(同步判断)。 */\n hasGroup(groupCode: string): boolean {\n return this.groupList.has(groupCode) || this.groupDetails.has(groupCode);\n }\n\n /**\n * 群列表快照(只读,缓存由 onGroupListUpdate 事件主动维护)。\n * 群列表数据不走原生 getGroupList 返回值(实测仅 { result, errMsg },\n * 列表经事件推送)——协议/IPC 层消费列表一律经此方法。\n */\n listGroups(): Group[] {\n return [...this.groupList.values()];\n }\n\n /**\n * 群列表快照(缓存为空时主动触发原生刷新,等待事件回填后返回)。\n *\n * 实测(2026-08-08 端到端联调):onGroupListUpdate 仅在登录后首轮同步\n * 推送(MMKV 已有缓存后不再重复推送)——进程内缓存为空时列表读不到。\n * 触发 getGroupList(true) 会重新拉取并推送事件回填缓存。\n * 返回空数组 = 刷新后仍无数据(超时 5s)。\n */\n async listGroupsRefreshed(): Promise<Group[]> {\n if (this.groupList.size > 0) {\n return [...this.groupList.values()];\n }\n await this.groupApi.getGroupList(true);\n await waitFor(() => this.groupList.size > 0, 5_000, 200);\n return [...this.groupList.values()];\n }\n\n /**\n * 群列表项(只读;缺失惰性回填 getGroupList)。\n * 返回 undefined 表示群不存在。\n */\n async getGroup(groupCode: string): Promise<Group | undefined> {\n const cached = this.groupList.get(groupCode);\n if (cached !== undefined) {\n return cached;\n }\n await this.loadGroupList();\n return this.groupList.get(groupCode);\n }\n\n /**\n * 群详情(只读;缺失惰性回填 getGroupInfo)。\n * getGroupInfo 恒返回对象(失败抛 KernelError),直接缓存。\n */\n async getGroupDetail(groupCode: string): Promise<GroupDetailInfo> {\n const cached = this.groupDetails.get(groupCode);\n if (cached !== undefined) {\n return cached;\n }\n const detail = await this.dedupe<GroupDetailInfo>(`detail:${groupCode}`, () =>\n this.groupApi.getGroupInfo(groupCode),\n );\n this.groupDetails.set(groupCode, detail);\n return detail;\n }\n\n /**\n * 群成员列表(只读;缺失惰性回填 getAllMemberList(forceFetch=true))。\n * 返回空数组表示该群无成员(或拉取失败已由 GroupApi 抛 KernelError)。\n */\n async getMembers(groupCode: string): Promise<GroupMember[]> {\n const map = this.members.get(groupCode);\n if (map !== undefined && map.size > 0) {\n return [...map.values()];\n }\n const members = await this.dedupe<GroupMember[]>(`members:${groupCode}`, () =>\n this.groupApi.getGroupMemberList(groupCode, true),\n );\n const merged = new Map<string, GroupMember>();\n for (const member of members) {\n merged.set(member.uid, member);\n }\n this.members.set(groupCode, merged);\n return members;\n }\n\n /**\n * 群成员(只读;缺失回填:先整群拉取,再按 uid 取;仍无则 getMemberInfo 单查)。\n * 返回 undefined 表示成员不在群。\n */\n async getMember(groupCode: string, uid: string): Promise<GroupMember | undefined> {\n const cached = await this.getCachedMember(groupCode, uid);\n if (cached !== undefined) {\n return cached;\n }\n const members = await this.dedupe<GroupMember[]>(`member:${groupCode}:${uid}`, () =>\n this.groupApi.getGroupMemberInfo(groupCode, [uid]),\n );\n const [member] = members;\n if (member === undefined) {\n return;\n }\n const target = this.members.get(groupCode);\n if (target !== undefined) {\n target.set(member.uid, member);\n }\n return member;\n }\n\n /** 读缓存成员(缺失先整群回填一次)。 */\n private async getCachedMember(\n groupCode: string,\n uid: string,\n ): Promise<GroupMember | undefined> {\n let map = this.members.get(groupCode);\n if (map === undefined || map.size === 0) {\n await this.getMembers(groupCode);\n map = this.members.get(groupCode);\n }\n return map?.get(uid);\n }\n\n /** 惰性回填全量群列表(getGroup 缺失时)。 */\n private async loadGroupList(): Promise<void> {\n const list = await this.dedupe<Group[]>(\"group-list\", () =>\n this.groupApi.getGroupList(false),\n );\n for (const group of list) {\n this.groupList.set(group.groupCode, group);\n }\n }\n\n /** 惰性回填并发去重:同 key 并发只拉一次。 */\n private async dedupe<T>(key: string, loader: () => Promise<T>): Promise<T> {\n const existing = this.inFlight.get(key);\n if (existing !== undefined) {\n return await (existing as Promise<T>);\n }\n const task = loader()\n .catch((err: unknown) => {\n throw err;\n })\n .finally(() => {\n this.inFlight.delete(key);\n });\n this.inFlight.set(key, task);\n return await task;\n }\n}\n","/**\n * CoreContext:只读装配根(ADR-015 推论,无全局单例)\n *\n * 把 logger / paths / wrapper 上下文聚合成单一装配根,由装配层(NapukettoCore)\n * 创建并传递给消费方(协议层 adapter、apis、cache)。\n *\n * - 无全局单例:每账号每进程一份,多账号多进程场景天然隔离(ADR-015)。\n * - 只读消费:字段由装配层填充,消费方只读,不直接修改。\n */\nimport type pino from \"pino\";\nimport type { PathWrapper } from \"./infra/index.js\";\nimport type { LoginResult } from \"./login/index.js\";\nimport type { WrapperContext } from \"./wrapper/wrapper-loader.js\";\n\nexport interface CoreContextOptions {\n /** pino logger(console + 可选文件,由装配层创建)。 */\n logger: pino.Logger;\n /** 账号数据目录布局(config/logs/cache)。 */\n paths: PathWrapper;\n}\n\n/**\n * 装配根:持有进程级共享对象。\n * wrapper / login 由 NapukettoCore 装配后填充,未装配前为 null。\n */\nexport interface CoreContext {\n readonly logger: pino.Logger;\n readonly paths: PathWrapper;\n /** QQ wrapper 上下文(attachWrapper 后填充)。 */\n wrapper: WrapperContext | null;\n /** 登录结果(login 成功后填充)。 */\n login: LoginResult | null;\n}\n\n/** 创建装配根(初始 wrapper/login 为 null)。 */\nexport function createCoreContext(opts: CoreContextOptions): CoreContext {\n return {\n logger: opts.logger,\n paths: opts.paths,\n wrapper: null,\n login: null,\n };\n}\n","/**\n * wrapper NAPI 回调适配器实现(普通 JS 对象,2026-08-05 确认)\n *\n * 事实(实测):wrapper.node 的 exports 89 键里没有 NodeI*Adapter/Listener 构造器,\n * engine.initWithDeskTopConfig / session.init 的 adapter 与 listener 参数一律接受\n * **普通 JS 对象**——NAPI 按方法名反射读取回调(自研实现)。\n *\n * 职责:engine / session 交互所需的全部回调占位与监听器工厂。\n * - GlobalAdapter → engine.initWithDeskTopConfig 第二参(onLog / getAppSetting / ...)\n * - DependsAdapter → session.init 第二参(onMSFStatusChange / onMSFSsoError / getGroupCode)\n * - DispatcherAdapter → session.init 第三参(dispatchRequest / dispatchCall / dispatchCallWithJson)\n * - createSessionListener → 日志版 session 监听器(boot 阶段默认,login 模块可覆盖)\n * - createLoginListener → 登录监听器占位(登录器接入时填充)\n *\n * 这些类的实例本身就是「普通 JS 对象」,可被 NAPI 反射——直接 new 传入即可。\n */\n\nimport type {\n IKernelLoginListener,\n NodeIDependsAdapter,\n NodeIDispatcherAdapter,\n NodeIGlobalAdapter,\n NodeIKernelSessionListener,\n} from \"../types/index.js\";\n\n/** NAPI 回调占位(方法存在即可被反射调用,无副作用)。 */\nconst noop = (): void => undefined;\n\n/** NodeIGlobalAdapter 实现(engine.initWithDeskTopConfig 第二参)。 */\nexport class GlobalAdapter implements NodeIGlobalAdapter {\n onLog = noop;\n onGetSrvCalTime = noop;\n onShowErrUITips = noop;\n fixPicImgType = noop;\n getAppSetting = noop;\n onInstallFinished = noop;\n onUpdateGeneralFlag = noop;\n onGetOfflineMsg = noop;\n}\n\n/** NodeIDependsAdapter 实现(session.init 第二参)。 */\nexport class DependsAdapter implements NodeIDependsAdapter {\n onMSFStatusChange = noop;\n onMSFSsoError = noop;\n getGroupCode = noop;\n}\n\n/** NodeIDispatcherAdapter 实现(session.init 第三参)。 */\nexport class DispatcherAdapter implements NodeIDispatcherAdapter {\n dispatchRequest = noop;\n dispatchCall = noop;\n dispatchCallWithJson = noop;\n}\n\n/** 会话监听器选项。 */\nexport interface SessionListenerOptions {\n /** 事件日志回调(默认静默——boot 阶段事件对用户是噪音;调试时可传回调观察)。 */\n onEvent?: (name: string, ...args: unknown[]) => void;\n}\n\n/**\n * 创建会话监听器(默认静默,boot 阶段默认;login 模块接入后可覆盖)。\n * 回调方法必须存在(NAPI 按方法名反射),但输出与否由调用方决定——\n * onNTSessionCreate / onOpentelemetryInit 等对用户无信息量,默认不打 stdout。\n */\nexport function createSessionListener(\n opts: SessionListenerOptions = {},\n): NodeIKernelSessionListener {\n const { onEvent } = opts;\n const emit = (name: string, ...args: unknown[]): void => {\n onEvent?.(name, ...args);\n };\n return {\n onNTSessionCreate: (sessionId) => emit(\"onNTSessionCreate\", sessionId),\n onGProSessionCreate: (sessionId) => emit(\"onGProSessionCreate\", sessionId),\n onSessionInitComplete: (sessionId) => emit(\"onSessionInitComplete\", sessionId),\n onOpentelemetryInit: (info) => emit(\"onOpentelemetryInit\", info),\n onUserOnlineResult: (result) => emit(\"onUserOnlineResult\", result),\n onGetSelfTinyId: (result) => emit(\"onGetSelfTinyId\", result),\n };\n}\n\n/** 创建登录监听器(占位,登录器接入时填充)。 */\nexport function createLoginListener(): IKernelLoginListener {\n return {};\n}\n","/**\n * 登录连接与快速登录(从 lifecycle.ts 拆分,2026-08-08 FTA 优化)\n *\n * - waitForNetworkConnection:等 MSF 网络连接就绪(getMsfStatus() === 3)\n * - ensureLoginConnected:connect() → 等 onLoginConnected(自建宿主必需)\n * - quickLogin:历史账号快速登录(网络异常 1006511 等就绪后重试)\n */\n\nimport { kernelError } from \"../infra/index.js\";\nimport { createLoginListener } from \"../wrapper/wrapper-adapters.js\";\nimport type { WrapperContext } from \"../wrapper/wrapper-loader.js\";\nimport { sleep, waitFor } from \"./wait.js\";\n\n/** 登录结果(QR 或快速登录)。 */\nexport interface LoginResult {\n uin: string;\n uid: string;\n nick: string;\n}\n\n/** 登录列表项(getLoginList 返回,说明书参考)。 */\nexport interface LoginAccountInfo {\n uin: string;\n uid?: string;\n nickName?: string;\n isQuickLogin?: boolean;\n}\n\n/** 登录服务形状(getLoginList / quickLoginWithUin / getMsfStatus,自研描述)。 */\ntype LoginServiceShape = {\n getLoginList(): Promise<{ result: number; LocalLoginInfoList: LoginAccountInfo[] }>;\n quickLoginWithUin(uin: string): Promise<{ result: string; loginErrorInfo: { errMsg: string } }>;\n getMsfStatus(): number;\n};\n\n/**\n * 网络状态(MSF)常量。\n * 3 = 已连接(NapCat waitForNetworkConnection 语义,自研描述)。\n */\nconst MSF_STATUS_CONNECTED = 3;\n\n/** 快速登录网络异常错误码(1006511,P2-1 实测:登录前网络未就绪时报此错)。 */\nconst NETWORK_ERROR_CODE = \"1006511\";\n\n/** 登录连接异常错误提示(自建宿主实测:connect 后未缓冲即 quickLogin 报此错)。 */\nconst CONNECTION_ERROR_HINT = \"登录系统连接异常\";\n\n/** 网络重试最大次数(每次重试前等网络就绪)。 */\nconst NETWORK_RETRY_MAX = 3;\n\n/** 网络就绪等待超时(毫秒)。 */\nconst NETWORK_READY_TIMEOUT_MS = 15_000;\n\n/** 网络就绪轮询间隔(毫秒)。 */\nconst NETWORK_READY_POLL_MS = 1000;\n\n/** 连接稳定缓冲(毫秒,p0-kernel-flow 实证值)。 */\nconst CONNECTION_SETTLE_MS = 3000;\n\n/**\n * 等待网络连接就绪(loginService.getMsfStatus() === 3)。\n * 参考 NapCat waitForNetworkConnection 思路(自研实现):快速登录在 QQ 刚拉起、\n * 网络栈未初始化时报 1006511 网络异常——等待 MSF 连接后再重试。\n * @returns 是否在超时前就绪。\n */\nexport function waitForNetworkConnection(\n ctx: WrapperContext,\n opts: { timeoutMs?: number } = {},\n): Promise<boolean> {\n const raw = ctx.loginService as unknown as LoginServiceShape | null;\n if (raw === null || typeof raw.getMsfStatus !== \"function\") {\n return Promise.resolve(false);\n }\n return waitFor(\n () => {\n try {\n return raw.getMsfStatus() === MSF_STATUS_CONNECTED;\n } catch {\n return false;\n }\n },\n opts.timeoutMs ?? NETWORK_READY_TIMEOUT_MS,\n NETWORK_READY_POLL_MS,\n );\n}\n\n/** 列出历史登录账号(boot.cjs 启动横幅用,「可用快速登录 of QQ」)。 */\nexport async function listLoginAccounts(ctx: WrapperContext): Promise<LoginAccountInfo[]> {\n const raw = ctx.loginService as unknown as LoginServiceShape | null;\n if (raw === null) {\n throw kernelError(\"loginService 无效(缺 getLoginList)\", \"INVALID_STATE\");\n }\n const list = await raw.getLoginList();\n return list.LocalLoginInfoList;\n}\n\n/**\n * 登录服务连接(自建宿主必需,HANDOVER-V6/V10 p0-kernel-flow 实证顺序):\n * connect() → 等 onLoginConnected。不 connect 则快速登录报「登录系统连接异常」。\n *\n * 对齐 p0-kernel-flow:initConfig → getLoginList → connect + onLoginConnected →\n * quickLoginWithUin。QQ 环境(worker/主进程)下可能已连接——connect 幂等,\n * 监听器超时兜底,不影响原流程。\n */\nasync function ensureLoginConnected(\n ctx: WrapperContext,\n opts: { timeoutMs?: number },\n): Promise<void> {\n const raw = ctx.loginService as unknown as {\n connect?: () => void;\n addKernelLoginListener?: (listener: unknown) => number;\n } | null;\n if (raw === null || typeof raw.connect !== \"function\") {\n return; // 无 connect(老版本 / QQ 主进程自连),跳过\n }\n await waitLoginConnected(raw, opts.timeoutMs);\n // onLoginConnected 后网络栈仍在初始化(p0-kernel-flow 实证:connect → onLoginConnected\n // → 3s 缓冲 → quickLoginWithUin 成功;无缓冲则「登录系统连接异常」)。\n await sleep(CONNECTION_SETTLE_MS);\n}\n\n/** 等 onLoginConnected(注册临时监听 + connect + 超时兜底)。 */\nfunction waitLoginConnected(\n raw: {\n connect?: () => void;\n addKernelLoginListener?: (listener: unknown) => number;\n },\n timeoutMs?: number,\n): Promise<void> {\n return new Promise<void>((resolve) => {\n let settled = false;\n const finish = (): void => {\n if (!settled) {\n settled = true;\n resolve();\n }\n };\n tryRegisterConnectListener(raw, finish);\n setTimeout(finish, timeoutMs ?? NETWORK_READY_TIMEOUT_MS);\n try {\n raw.connect?.();\n } catch {\n finish();\n }\n });\n}\n\n/** 注册临时登录连接监听(失败不阻塞连接)。 */\nfunction tryRegisterConnectListener(\n raw: {\n connect?: () => void;\n addKernelLoginListener?: (listener: unknown) => number;\n },\n finish: () => void,\n): void {\n try {\n // 临时监听等 onLoginConnected;保留不 remove——登录后续回调\n // (onQRCodeLoginSucceed 等)仍可能触发,且进程内只登录一次,无泄漏风险。\n const listener = createLoginListener();\n listener.onLoginConnected = () => {\n finish();\n };\n if (typeof raw.addKernelLoginListener === \"function\") {\n raw.addKernelLoginListener(listener);\n }\n } catch {\n // 监听注册失败不阻塞连接\n }\n}\n\n/**\n * 目标账号选择(2026-08-07 修复:显式 uin 不在列表时**报错**而非静默 fallback——\n * 之前会退到第一个可快速登录账号,导致 `-q <uin>` 实际登成别的号)。\n * 导出供单测(login-connect.test.ts)。\n */\nexport function pickLoginTarget(\n items: LoginAccountInfo[],\n uin: string | undefined,\n): LoginAccountInfo {\n if (uin !== undefined) {\n const target = items.find((i) => i.uin === uin);\n if (target === undefined) {\n const available = items.map((i) => i.uin).join(\"、\");\n throw kernelError(`账号 ${uin} 不在登录列表(可用:${available})`, \"NOT_FOUND\");\n }\n return target;\n }\n // 未指定 uin:优先第一个可快速登录账号,否则列表第一个\n const quick = items.find((i) => i.isQuickLogin);\n if (quick !== undefined) {\n return quick;\n }\n const [first] = items;\n if (first === undefined) {\n throw kernelError(\"无可用登录账号\", \"NOT_LOGIN\");\n }\n return first;\n}\n\n/**\n * 带网络重试的快速登录(P2-1)。\n * 重试语义:仅当失败为网络异常(1006511)且未达上限时,等网络就绪后重试。\n */\nasync function loginWithNetworkRetry(\n ctx: WrapperContext,\n loginService: LoginServiceShape,\n target: LoginAccountInfo,\n opts: { uin?: string; timeoutMs?: number },\n): Promise<LoginResult> {\n let lastErrMsg = \"\";\n for (let attempt = 1; attempt <= NETWORK_RETRY_MAX; attempt += 1) {\n const attemptResult = await attemptQuickLogin(loginService, target);\n if (attemptResult.ok) {\n return attemptResult.result;\n }\n lastErrMsg = attemptResult.errMsg;\n const shouldRetry = isNetworkError(lastErrMsg) && attempt < NETWORK_RETRY_MAX;\n if (!shouldRetry) {\n break;\n }\n const ready = await waitForNetworkConnection(ctx, {\n timeoutMs: opts.timeoutMs ?? NETWORK_READY_TIMEOUT_MS,\n });\n if (!ready) {\n break;\n }\n }\n throw kernelError(`快速登录失败: ${lastErrMsg}`, \"NOT_LOGIN\");\n}\n\n/** 单次快速登录尝试(成功带结果,失败带错误消息)。 */\nasync function attemptQuickLogin(\n loginService: LoginServiceShape,\n target: LoginAccountInfo,\n): Promise<{ ok: true; result: LoginResult } | { ok: false; errMsg: string }> {\n const result = await loginService.quickLoginWithUin(target.uin);\n const { errMsg } = result.loginErrorInfo;\n if (errMsg) {\n return { ok: false, errMsg };\n }\n return {\n ok: true,\n result: {\n uin: target.uin,\n uid: target.uid ?? \"\",\n nick: target.nickName ?? \"\",\n },\n };\n}\n\n/** 判定快速登录失败是否为网络异常(重试语义)。导出供单测。 */\nexport function isNetworkError(errMsg: string): boolean {\n return errMsg.includes(NETWORK_ERROR_CODE) || errMsg.includes(CONNECTION_ERROR_HINT);\n}\n\n/**\n * 快速登录:遍历历史登录列表尝试。\n * P2-1:失败且错误为网络异常(1006511)时,等网络就绪后重试(最多 NETWORK_RETRY_MAX 次)。\n */\nexport async function quickLogin(\n ctx: WrapperContext,\n opts: { uin?: string; timeoutMs?: number },\n): Promise<LoginResult> {\n const raw = ctx.loginService as unknown as LoginServiceShape | null;\n if (raw === null) {\n throw kernelError(\"loginService 无效(缺 getLoginList)\", \"INVALID_STATE\");\n }\n const loginService = raw;\n // 连接登录服务(自建宿主必需:不 connect 则「登录系统连接异常」)\n await ensureLoginConnected(ctx, {\n ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n });\n const list = await loginService.getLoginList();\n const items = list.LocalLoginInfoList;\n if (items.length === 0) {\n throw kernelError(\"无历史登录账号\", \"NOT_LOGIN\");\n }\n const target = pickLoginTarget(items, opts.uin);\n return loginWithNetworkRetry(ctx, loginService, target, opts);\n}\n","/**\n * 完整启动生命周期(wrapper.node 契约流程,2026-08-05 实测确认,自研实现)\n *\n * 启动顺序由 wrapper.node 外部契约决定(engine→login→session→startNT),\n * 运行时实测确认,自研实现:\n * 1. engine.initWithDeskTopConfig(appid/qua/版本)\n * 2. loginService.initConfig + addKernelLoginListener\n * 3. getLoginList() → quickLoginWithUin(uin)(或 QR 登录)\n * 4. 登录成功 → genSessionConfig → session.init(config, 3 adapter, listener) → startNT(0)\n * 5. 等 init 完成信号(onOpentelemetryInit(is_init) 为主,onSessionInitComplete 为辅)\n *\n * 关键(2026-08-05 实测修正):\n * - session 用 `new wrapper.NodeIQQNTWrapperSession()` 创建;exports 不含 NodeI*Adapter/Listener\n * 构造器(89 键实测无)——adapter 与 listener 一律传普通 JS 对象(NAPI 反射读取方法回调)。\n *\n * 模块边界(2026-08-05 解耦;2026-08-08 FTA 优化再拆):\n * - 配置装配(buildEngineConfig / buildLoginConfig / buildSessionConfig)→ wrapper-config.ts\n * - NAPI 回调适配器(GlobalAdapter / DependsAdapter / DispatcherAdapter / listener 工厂)→ wrapper-adapters.ts\n * - 登录连接 + 快速登录(waitForNetworkConnection / quickLogin 等)→ login-connect.ts\n * - 本文件只保留 session 流程编排:就绪等待(waitSessionReady)+ session 初始化(initAndStartSession)。\n */\n\nimport { kernelError } from \"../infra/index.js\";\nimport type { NodeIKernelSessionListener, WrapperSessionInitConfig } from \"../types/index.js\";\nimport { DependsAdapter, DispatcherAdapter } from \"../wrapper/wrapper-adapters.js\";\nimport type { WrapperContext } from \"../wrapper/wrapper-loader.js\";\nimport { waitFor } from \"./wait.js\";\n\nexport type { LoginAccountInfo, LoginResult } from \"./login-connect.js\";\nexport { listLoginAccounts, quickLogin, waitForNetworkConnection } from \"./login-connect.js\";\n\n/** session init 默认超时(毫秒)。 */\nconst DEFAULT_INIT_TIMEOUT_MS = 15_000;\n\n/** session 就绪等待默认超时(毫秒)。 */\nconst SESSION_READY_TIMEOUT_MS = 30_000;\n\n/** session 就绪轮询间隔(毫秒)。 */\nconst SESSION_READY_POLL_MS = 1000;\n\n/**\n * 等待 QQ 自己完成 session 初始化(framework 复用模式,2026-08-05 修正)。\n *\n * 背景:QQ 9.9.31 实测——session 由 QQ 主进程自己创建并 init(登录后)。\n * 我们不自己 init(重复 init 断言 \"implementation of IQQNTWrapperSession is not valid\"),\n * 也不自己 create(登录前 create() 干扰 QQ,实测 QQ 退出 code=0)。\n * 正确做法:等 QQ 的 session 就绪(getMsgService 非 null = 核心 service 已挂载)。\n */\nexport async function waitSessionReady(\n ctx: WrapperContext,\n opts: { timeoutMs?: number },\n): Promise<void> {\n const { session } = ctx;\n if (session === null || session === undefined) {\n throw kernelError(\"session 未创建\", \"INVALID_STATE\");\n }\n const ok = await waitFor(\n () => {\n try {\n const svc = session.getMsgService();\n return svc !== null && svc !== undefined;\n } catch {\n return false;\n }\n },\n opts.timeoutMs ?? SESSION_READY_TIMEOUT_MS,\n SESSION_READY_POLL_MS,\n );\n if (!ok) {\n throw kernelError(\"等待 QQ session 就绪超时(QQ 未完成 init)\", \"TIMEOUT\");\n }\n}\n\n/** 登录连接 + 快速登录(quickLogin 等)已拆到 login-connect.ts(见文件头 re-export)。 */\n\n/** session 初始化(4 参全为普通 JS 对象,等 init 完成信号)。 */\nexport async function initAndStartSession(\n ctx: WrapperContext,\n config: WrapperSessionInitConfig,\n listener: NodeIKernelSessionListener,\n opts: { timeoutMs?: number },\n): Promise<void> {\n const { session } = ctx;\n if (session === null || session === undefined) {\n throw kernelError(\"session 未创建\", \"INVALID_STATE\");\n }\n\n // adapter / listener 全部用普通 JS 对象(实测 exports 89 键无 NodeI*Adapter/Listener\n // 构造器;NAPI 反射读取对象方法回调,自研实现)。\n const depends = new DependsAdapter();\n const dispatcher = new DispatcherAdapter();\n\n const initComplete = watchInitSignal(listener);\n session.init(config, depends, dispatcher, listener);\n startSessionBestEffort(ctx, session);\n\n const ok = await Promise.race([\n initComplete.then(() => true),\n waitFor(() => false, opts.timeoutMs ?? DEFAULT_INIT_TIMEOUT_MS).then(() => false),\n ]);\n if (!ok) {\n throw kernelError(\"session init 超时\", \"TIMEOUT\");\n }\n}\n\n/** 监听 init 完成信号:onOpentelemetryInit(is_init) 为主,onSessionInitComplete(0) 为辅。 */\nfunction watchInitSignal(listener: NodeIKernelSessionListener): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const onOpentelemetry = listener.onOpentelemetryInit;\n listener.onOpentelemetryInit = (info) => {\n if (info.is_init) {\n resolve();\n }\n if (typeof onOpentelemetry === \"function\") {\n onOpentelemetry(info);\n }\n };\n const onInitComplete = listener.onSessionInitComplete;\n listener.onSessionInitComplete = (r) => {\n if (r === 0 || r === \"0\") {\n resolve();\n } else {\n reject(kernelError(`session init 失败: ${String(r)}`, \"UNKNOWN\"));\n }\n if (typeof onInitComplete === \"function\") {\n onInitComplete(r);\n }\n };\n });\n}\n\n/**\n * 启动会话(best effort,失败不致命):\n * **先 init 后 start(2026-08-07 V9 决定性修正)**。\n * 自建宿主实测(HANDOVER-V9,p0-napcat-min):必须先 session.init 再\n * startupSession.start()——顺序颠倒(先 start 后 init)业务 service 不挂载\n * (getMsgService null);init 后用 startNT(非 startupSession.start)也失败。\n * NapCat initializeSession 同款:有 startupSession 用 start(),否则 startNT(0)。\n */\nfunction startSessionBestEffort(\n ctx: WrapperContext,\n session: NonNullable<WrapperContext[\"session\"]>,\n): void {\n if (startupSessionStart(ctx)) {\n return;\n }\n try {\n session.startNT(0);\n } catch {\n try {\n session.startNT();\n } catch {\n // 无 startNT(9.9.31):忽略,等 init 完成信号\n }\n }\n}\n\n/** 有 startupSession 则 start()(失败不致命),返回是否已处理。 */\nfunction startupSessionStart(ctx: WrapperContext): boolean {\n const { startupSession } = ctx;\n if (startupSession === null || typeof startupSession.start !== \"function\") {\n return false;\n }\n try {\n startupSession.start();\n } catch {\n // start 失败不致命,靠 onOpentelemetryInit/onSessionInitComplete 信号判断\n }\n return true;\n}\n","/**\n * login.ts:QR 登录流程编排 + 状态机 + selfInfo(ADR-010,2026-08-05)\n *\n * 流程(QQ wrapper 登录契约,自研实现):\n * loginService.initConfig(core.login 已做)→ addKernelLoginListener → connect()\n * → 指定账号:quickLoginWithUin;否则 getQRCodePicture() 触发二维码\n * → 回调驱动状态机:未登录 → 扫码中 → 已扫码 → 已登录(onQRCodeLoginSucceed)\n * → 二维码过期(errType=1 errCode=3)→ refresh()\n *\n * 不含 UI 渲染(cli 只做二维码渲染/URL 打印)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type { IKernelLoginListener } from \"../types/index.js\";\nimport { createLoginListener } from \"../wrapper/wrapper-adapters.js\";\n\n/** 登录状态。 */\nexport type LoginState = \"idle\" | \"waiting_scan\" | \"scanned\" | \"logged_in\" | \"failed\";\n\n/** 二维码数据(png base64 + 解码 url)。 */\nexport interface QrCodeData {\n pngBase64: string;\n qrcodeUrl: string;\n}\n\n/** selfInfo(登录成功后填写)。 */\nexport interface SelfInfo {\n uin: string;\n uid: string;\n nick: string;\n}\n\n/** 快速登录项。 */\nexport interface LoginListItem {\n uin: string;\n uid?: string;\n nickName?: string;\n isQuickLogin?: boolean;\n}\n\n/** loginService 接口(从 WrapperContext.loginService 获取)。 */\ninterface LoginServiceLike {\n addKernelLoginListener(listener: unknown): number;\n removeKernelLoginListener(listenerId: number): void;\n connect(): void;\n getLoginList(): Promise<{ result: number; LocalLoginInfoList: LoginListItem[] }>;\n quickLoginWithUin(uin: string): Promise<{ result: string; loginErrorInfo: { errMsg: string } }>;\n getQRCodePicture(): boolean;\n}\n\n/** 二维码过期错误码(errType=1 errCode=3,实测)。 */\nconst QR_EXPIRED_ERR_TYPE = 1;\nconst QR_EXPIRED_ERR_CODE = 3;\n\n/** 登录超时上限(毫秒,参考项目 60 次 × 2s = 120s,2026-08-13 用户拍板照搬)。 */\nexport const QR_LOGIN_TIMEOUT_MS = 120_000;\n\n/** 登录超时提示(参考项目文案,前端 failed 态展示)。 */\nexport const QR_LOGIN_TIMEOUT_MESSAGE = \"登录超时,请刷新页面重试\";\n\n/** 二维码过期判定。 */\nfunction isQrCodeExpired(errType: number, errCode: number): boolean {\n return errType === QR_EXPIRED_ERR_TYPE && errCode === QR_EXPIRED_ERR_CODE;\n}\n\n/** 二维码 png data URI 前缀剥离正则。 */\nconst DATA_URI_PREFIX_RE = /^data:image\\/\\w+;base64,/;\n\n/** QR 登录会话(状态机 + 回调订阅)。 */\nexport class QrLoginSession {\n private readonly loginService: LoginServiceLike;\n private state: LoginState = \"idle\";\n private listenerId: number | null = null;\n private self: SelfInfo | null = null;\n private readonly qrCodeHandlers = new Set<(qr: QrCodeData) => void>();\n private readonly stateHandlers = new Set<(state: LoginState) => void>();\n /** 超时定时器(refresh 后启动,登录成功/失败/stop 清理)。 */\n private timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n /** 失败原因(超时等,failed 态透出给上层提示文案)。 */\n private failureReasonValue: string | null = null;\n /** 超时上限(毫秒,默认 QR_LOGIN_TIMEOUT_MS)。 */\n private readonly timeoutMs: number;\n\n constructor(loginService: unknown, options: { timeoutMs?: number } = {}) {\n const svc = loginService as LoginServiceLike | null;\n if (svc === null || typeof svc.addKernelLoginListener !== \"function\") {\n throw kernelError(\"loginService 无效(缺 addKernelLoginListener)\", \"INVALID_STATE\");\n }\n this.loginService = svc;\n this.timeoutMs = options.timeoutMs ?? QR_LOGIN_TIMEOUT_MS;\n }\n\n /** 当前状态。 */\n get currentState(): LoginState {\n return this.state;\n }\n\n /** selfInfo(登录成功后有值)。 */\n get selfInfo(): SelfInfo | null {\n return this.self;\n }\n\n /** 失败原因(超时等;failed 态透出给上层提示文案)。 */\n get failureReason(): string | null {\n return this.failureReasonValue;\n }\n\n /** 订阅二维码(png base64 + url)。返回退订函数。 */\n onQrCode(cb: (qr: QrCodeData) => void): () => void {\n this.qrCodeHandlers.add(cb);\n return () => this.qrCodeHandlers.delete(cb);\n }\n\n /** 订阅状态变化。返回退订函数。 */\n onStateChange(cb: (state: LoginState) => void): () => void {\n this.stateHandlers.add(cb);\n return () => this.stateHandlers.delete(cb);\n }\n\n /** 开始登录:注册监听 → connect → 快速登录(指定账号)或触发二维码。 */\n start(opts: { quickUin?: string } = {}): void {\n if (this.state !== \"idle\") {\n return;\n }\n this.listenerId = this.loginService.addKernelLoginListener(this.buildListener());\n this.loginService.connect();\n if (opts.quickUin !== undefined && opts.quickUin !== \"\") {\n this.loginService\n .quickLoginWithUin(opts.quickUin)\n .then((result) => {\n // ⚠️ wrapper 契约(attemptQuickLogin 已实证):快速登录失败是\n // resolve 带 loginErrorInfo.errMsg,**不是 reject**——只挂 .catch\n // 会永不触发 → 无凭据环境(WSL 扫码)永不 refresh → 二维码永不\n // 产生,完全静默阻塞(2026-08-13 WSL 扫码卡死根因)。\n if (result.loginErrorInfo.errMsg !== \"\") {\n this.refresh();\n }\n })\n .catch(() => {\n // reject(异常路径)同样回退二维码\n this.refresh();\n });\n } else {\n this.refresh();\n }\n }\n\n /** 刷新二维码(手动 / 过期自动)。每次出码重启超时计时。 */\n refresh(): void {\n this.failureReasonValue = null;\n this.setState(\"waiting_scan\");\n this.loginService.getQRCodePicture();\n this.restartTimeout();\n }\n\n /** 构建登录监听(普通 JS 对象,NAPI 反射)。 */\n private buildListener(): IKernelLoginListener {\n const listener = createLoginListener();\n listener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {\n const realBase64 = pngBase64QrcodeData.replace(DATA_URI_PREFIX_RE, \"\");\n this.emitQrCode({ pngBase64: realBase64, qrcodeUrl });\n };\n listener.onQRCodeLoginPollingStarted = () => {\n this.setState(\"waiting_scan\");\n };\n listener.onQRCodeSessionUserScaned = () => {\n this.setState(\"scanned\");\n };\n listener.onQRCodeLoginSucceed = (result) => {\n this.clearTimeoutTimer();\n this.self = { uin: result.uin, uid: result.uid, nick: result.nick ?? \"\" };\n this.setState(\"logged_in\");\n };\n listener.onQRCodeSessionFailed = (errType, errCode) => {\n if (isQrCodeExpired(errType, errCode)) {\n // 二维码过期 → 自动刷新(2026-08-13 用户拍板保留自动刷新 + 手动刷新按钮)\n this.refresh();\n return;\n }\n this.clearTimeoutTimer();\n this.failureReasonValue = null;\n this.setState(\"failed\");\n };\n listener.onLoginFailed = () => {\n this.clearTimeoutTimer();\n this.failureReasonValue = null;\n this.setState(\"failed\");\n };\n return listener;\n }\n\n /** 停止:注销监听(登录成功后调用,防重复回调)。 */\n stop(): void {\n this.clearTimeoutTimer();\n if (this.listenerId !== null) {\n this.loginService.removeKernelLoginListener(this.listenerId);\n this.listenerId = null;\n }\n }\n\n /** 重启超时计时器:超时未登录 → failed + 超时提示。 */\n private restartTimeout(): void {\n this.clearTimeoutTimer();\n this.timeoutTimer = setTimeout(() => {\n this.timeoutTimer = null;\n this.failureReasonValue = QR_LOGIN_TIMEOUT_MESSAGE;\n this.setState(\"failed\");\n }, this.timeoutMs);\n }\n\n /** 清理超时计时器(幂等)。 */\n private clearTimeoutTimer(): void {\n if (this.timeoutTimer !== null) {\n clearTimeout(this.timeoutTimer);\n this.timeoutTimer = null;\n }\n }\n\n private setState(next: LoginState): void {\n this.state = next;\n for (const cb of this.stateHandlers) {\n cb(next);\n }\n }\n\n private emitQrCode(qr: QrCodeData): void {\n for (const cb of this.qrCodeHandlers) {\n cb(qr);\n }\n }\n}\n","/**\n * QQ 数据根路径解析(P2-1,2026-08-06)\n *\n * 从 `getNTUserDataInfoConfig()` 的返回值中提取 QQ 真实数据根路径:\n * worker(utilityProcess)模式下 loginService 是 new 的,commonPath 必须指向\n * QQ 真实数据路径才能读到历史账号(cli 的 `.napuketto\\default` 读不到,\n * getLoginList 空,P2-1 实测)。\n *\n * 返回值形态可能是 JSON 字符串(对象/数组)或纯路径字符串,此处做宽容解析。\n */\n\nimport { join } from \"node:path\";\n\n/**\n * QQ 全局数据目录(commonPath / desktopGlobalPath = 数据根/nt_qq/global,\n * HANDOVER-V6 自建宿主三要素之三,p0-kernel-flow 实证)。\n *\n * 防御:已含 nt_qq/global 后缀(worker 环境下 getNTUserDataInfoConfig 可能\n * 返回完整路径)则不重复拼接,避免 `nt_qq/global/nt_qq/global` 错位。\n */\nexport function resolveQqGlobalPath(root: string): string {\n const norm = root.replace(/\\\\/g, \"/\");\n if (norm.endsWith(\"/nt_qq/global\")) {\n return root;\n }\n if (norm.endsWith(\"/nt_qq\")) {\n return join(root, \"global\");\n }\n return join(root, \"nt_qq\", \"global\");\n}\n\n/** 从 getNTUserDataInfoConfig 返回值提取数据根(JSON 字符串解析或纯路径)。 */\nexport function extractDataRoot(raw: string): string | null {\n if (raw.startsWith(\"{\") || raw.startsWith(\"[\")) {\n try {\n return findPathInValue(JSON.parse(raw));\n } catch {\n return null;\n }\n }\n return raw;\n}\n\n/** 递归在对象/数组里找含 QQ 数据路径特征的字符串值。 */\nfunction findPathInValue(value: unknown): string | null {\n if (typeof value === \"string\") {\n return isQqDataPath(value) ? value : null;\n }\n if (Array.isArray(value)) {\n return findInArray(value);\n }\n if (typeof value === \"object\" && value !== null) {\n return findInObject(value);\n }\n return null;\n}\n\n/** 判断字符串是否含 QQ 数据路径特征。 */\nfunction isQqDataPath(value: string): boolean {\n return value.includes(\"Tencent Files\") || value.includes(\"nt_qq\");\n}\n\n/** 在数组里递归查找。 */\nfunction findInArray(value: unknown[]): string | null {\n for (const item of value) {\n const found = findPathInValue(item);\n if (found !== null) {\n return found;\n }\n }\n return null;\n}\n\n/** 在对象值里递归查找。 */\nfunction findInObject(value: object): string | null {\n for (const v of Object.values(value)) {\n const found = findPathInValue(v);\n if (found !== null) {\n return found;\n }\n }\n return null;\n}\n","/**\n * wrapper 交互配置装配(纯函数,无状态)\n *\n * 生成 QQ wrapper 交互所需的三份配置:\n * - buildEngineConfig → engine.initWithDeskTopConfig(appid / qua / 版本 / 路径)\n * - buildLoginConfig → loginService.initConfig\n * - buildSessionConfig → session.init(登录成功后调用)\n *\n * 与 lifecycle.ts(流程编排)解耦:本模块只回答「配置长什么样」,不管流程怎么走。\n * 字段为 wrapper.node 外部契约(appid/qua/版本),运行时实测确认,自研描述。\n * appid 唯一来源是 major.node 动态解析(每版本不同);解析失败显式抛 KernelError,\n * 不再静默回退过期 appid(2026-08 硬编码审计:537237765 是 9.9.31 的 appid,该版本\n * 登录服务已被腾讯下线,静默用它会导致扫码「请下载最新版」且极难排查)。\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { hostname, type as osType, version as osVersionString, platform, release } from \"node:os\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n DeviceInfo,\n EnginInitDesktopConfig,\n WrapperSessionInitConfig,\n} from \"../types/index.js\";\nimport { PlatformType as PlatformTypeValue, VendorType } from \"../types/index.js\";\n\n/** app_type=4:桌面端应用类型(engine.initWithDeskTopConfig 契约;移动端为其它值)。 */\nconst APP_TYPE_DESKTOP = 4;\n\n/**\n * 缩略图生成参数(thumb_config,engine 契约):\n * maxSide 最长边像素上限 / minSide 最短边像素下限 / longLimit 长图最长短边比例上限 /\n * density 像素密度倍数。\n */\nconst THUMB_CONFIG = {\n maxSide: 324,\n minSide: 48,\n longLimit: 6,\n density: 2,\n} as const;\n\n/**\n * session.init 的 deviceConfig(wrapper 契约,JSON 字符串字面量):\n * appearance.isSplitViewMode 分屏模式开关,msg 空对象为占位。自建宿主无 UI,保持最小可用。\n */\nconst DEVICE_CONFIG_JSON = '{\"appearance\":{\"isSplitViewMode\":true},\"msg\":{}}';\n\n/** localId=2052:zh-CN(简体中文)locale 码(Windows LCID,deviceInfo.localId 契约)。 */\nconst ZH_CN_LOCALE_ID = 2052;\n\n/** 平台名映射:os.platform() → wrapper devType 契约值(不写死具体构建号)。 */\nconst DEV_TYPE_BY_PLATFORM: Record<string, string> = {\n win32: \"Windows\",\n darwin: \"Mac\",\n linux: \"Linux\",\n android: \"Android\",\n freebsd: \"FreeBSD\",\n openbsd: \"OpenBSD\",\n sunos: \"SunOS\",\n aix: \"AIX\",\n};\n\n/**\n * 系统信息运行时探测(wrapper 契约:platVer / osVersion / devType)。\n * 旧实现写死 \"Windows 10.0.22631\"/\"Windows 10 Pro\"——换机即失真(2026-08 硬编码审计整改)。\n * 探测失败时给通用值兜底,但不写死具体 Windows 构建号。\n */\nfunction systemInfo(): { platVer: string; osVersion: string; devType: string } {\n let devType = \"Unknown\";\n let platVer = \"Unknown\";\n let osVersion = \"Unknown\";\n try {\n devType = DEV_TYPE_BY_PLATFORM[platform()] ?? platform();\n // platVer 契约形如 \"Windows 10.0.22631\" = \"<平台名> <内核版本号>\"。\n platVer = `${devType} ${release()}`;\n // osVersion 优先 os.version()(Windows 返回 \"Windows 11 Pro\" 等),空则用 os.type()。\n osVersion = osVersionString() || osType();\n } catch {\n // 探测失败:保留通用兜底值。\n }\n return { platVer, osVersion, devType };\n}\n\n/** 纯数字串(major.node 提取的 appid 判定)。 */\nconst DIGITS_ONLY_RE = /^\\d+$/;\n\n/**\n * 从 major.node 提取 appid(NapCat parseAppidFromMajorV2 的自研等价实现)。\n * major.node 含 `QQAppId/` 标记后跟数字(腾讯工具链产物,实测确认)。\n * 返回 null 表示解析失败(调用方应显式报错,不再回退硬编码 appid)。\n */\nexport function parseAppidFromMajor(majorPath: string): string | null {\n if (!existsSync(majorPath)) {\n return null;\n }\n let buf: Buffer;\n try {\n buf = readFileSync(majorPath);\n } catch {\n return null;\n }\n return scanAppidMarkers(buf);\n}\n\n/** 扫描 buf 中所有 QQAppId/ 标记,返回第一个纯数字串(无则 null)。 */\nfunction scanAppidMarkers(buf: Buffer): string | null {\n const marker = Buffer.from(\"QQAppId/\", \"utf-8\");\n let pos = 0;\n while (pos < buf.length) {\n const idx = buf.indexOf(marker, pos);\n if (idx === -1) {\n return null;\n }\n const str = readMarkerValue(buf, idx + marker.length);\n if (DIGITS_ONLY_RE.test(str)) {\n return str;\n }\n pos = idx + marker.length + str.length + 1;\n }\n return null;\n}\n\n/** 读取标记后的 C 字符串(到首个 0 字节为止)。 */\nfunction readMarkerValue(buf: Buffer, start: number): string {\n let end = start;\n while (end < buf.length && buf[end] !== 0) {\n end += 1;\n }\n return buf.subarray(start, end).toString(\"utf-8\");\n}\n\n/**\n * 解析 appid / qua(自研,参考 NapCat QQBasicInfoWrapper 思路但独立实现):\n * appid 唯一来源 = major.node 提取(实测 9.9.33-51802 = 537376818)。\n * majorPath 缺省或解析失败时抛 KernelError——不再静默回退过期 appid。\n * majorPath 传 wrapper.node 同目录 major.node。\n */\nexport function resolveAppidQua(\n fullVersion: string,\n majorPath?: string,\n): { appid: string; qua: string } {\n const appid = majorPath !== undefined ? parseAppidFromMajor(majorPath) : null;\n if (appid === null) {\n throw kernelError(\n \"无法从 major.node 解析 appid,请确认 wrapper.node/major.node 完整,或更新 qq-releases.json\",\n \"INVALID_STATE\",\n );\n }\n return {\n appid,\n qua: `V1_WIN_NQ_${fullVersion}_${fullVersion.split(\"-\")[1] ?? \"\"}_GW_B`,\n };\n}\n\n/** 生成 engine 桌面配置(wrapper 契约字段)。majorPath 可选(缺失/解析失败会抛错)。 */\nexport function buildEngineConfig(\n fullVersion: string,\n dataPathGlobal: string,\n majorPath?: string,\n): EnginInitDesktopConfig {\n const { osVersion } = systemInfo();\n const { qua } = resolveAppidQua(fullVersion, majorPath);\n return {\n base_path_prefix: \"\",\n platform_type: PlatformTypeValue.KWINDOWS,\n app_type: APP_TYPE_DESKTOP,\n app_version: fullVersion,\n os_version: osVersion,\n use_xlog: true,\n qua,\n global_path_config: {\n desktopGlobalPath: dataPathGlobal,\n },\n thumb_config: THUMB_CONFIG,\n };\n}\n\n/** 生成登录初始化配置(externalVersion: false 与 NapCat 同款,扫码兼容关键)。 */\nexport function buildLoginConfig(\n appid: string,\n fullVersion: string,\n commonPath: string,\n): Record<string, unknown> {\n const { platVer } = systemInfo();\n return {\n machineId: \"\",\n appid,\n platVer,\n commonPath,\n clientVer: fullVersion,\n hostName: hostname(),\n externalVersion: false,\n };\n}\n\n/** buildSessionConfig 参数。 */\nexport interface SessionConfigOptions {\n appid: string;\n fullVersion: string;\n selfUin: string;\n selfUid: string;\n accountPath: string;\n downloadPath: string;\n /** 设备指纹 guid(来自 loginService.getMachineGuid();缺省空串)。 */\n machineGuid?: string;\n}\n\n/** 生成 session 初始化配置(登录成功后调用)。 */\nexport function buildSessionConfig(options: SessionConfigOptions): WrapperSessionInitConfig {\n const { appid, fullVersion, selfUin, selfUid, accountPath, downloadPath, machineGuid } =\n options;\n const { platVer, osVersion, devType } = systemInfo();\n const deviceInfo: DeviceInfo = {\n // 设备指纹 guid:loginService.getMachineGuid()(wrapper 探测确认,无 getMachineId 方法)。\n guid: machineGuid ?? \"\",\n buildVer: fullVersion,\n localId: ZH_CN_LOCALE_ID,\n devName: hostname(),\n devType,\n vendorName: \"\",\n osVer: osVersion,\n vendorOsName: devType,\n setMute: false,\n vendorType: VendorType.KNOSETONIOS,\n };\n return {\n selfUin,\n selfUid,\n desktopPathConfig: {\n account_path: accountPath,\n },\n clientVer: fullVersion,\n a2: \"\",\n d2: \"\",\n d2Key: \"\",\n machineId: \"\",\n platform: PlatformTypeValue.KWINDOWS,\n platVer,\n appid,\n rdeliveryConfig: {\n appKey: \"\",\n systemId: 0,\n appId: \"\",\n logicEnvironment: \"\",\n platform: PlatformTypeValue.KWINDOWS,\n language: \"\",\n sdkVersion: \"\",\n userId: \"\",\n appVersion: \"\",\n osVersion: \"\",\n bundleId: \"\",\n serverUrl: \"\",\n fixedAfterHitKeys: [\"\"],\n },\n defaultFileDownloadPath: downloadPath,\n deviceInfo,\n deviceConfig: DEVICE_CONFIG_JSON,\n };\n}\n\n/**\n * 读取设备指纹 guid(wrapper.node 字符串分析实证,2026-08-12):\n * NodeIKernelLoginService::getMachineGuid(0 参,返回机器 GUID 字符串)。\n * 无 getMachineId 方法——TODO 里的方法名不存在,等价方法是 getMachineGuid。\n * 失败(无该方法 / 调用抛错 / 返回值非字符串)兜底空串。\n */\nexport function readMachineGuid(loginService: unknown): string {\n try {\n const fn = (loginService as Record<string, unknown> | null | undefined)?.[\"getMachineGuid\"];\n if (typeof fn !== \"function\") {\n return \"\";\n }\n const value: unknown = fn.call(loginService);\n return typeof value === \"string\" ? value : \"\";\n } catch {\n return \"\";\n }\n}\n","/**\n * wrapper 引导(NAPI 范式,2026-08-05 重构,替代旧 koffi 方案)\n *\n * 事实(实测):wrapper.node 不是标准 NAPI self-register 模块,只能在 QQ 定制版\n * Electron 主进程里由 preload 注册。@napuketto/loader 注入 hook DLL 后,boot.cjs\n * 在 QQ 主进程内截获 `process.dlopen` 的 `module.exports`,然后调用本模块初始化。\n *\n * 本模块**不再**使用 koffi / vtable / 内存偏移——所有对象由 QQ 的 NAPI 层自动构建。\n * 业务层拿到的都是真实 JS 对象:engine.initWithDeskTopConfig(config, adapter) 等。\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n EnginInitDesktopConfig,\n NodeIKernelSessionListener,\n NodeIQQNTStartupSessionWrapper,\n NodeIQQNTWrapperEngine,\n NodeIQQNTWrapperSession,\n NodeIQQNTWrapperSessionCtor,\n WrapperNodeApi,\n WrapperSessionInitConfig,\n} from \"../types/index.js\";\nimport { PlatformType } from \"../types/index.js\";\nimport { extractDataRoot, resolveQqGlobalPath } from \"./qq-data-path.js\";\nimport {\n createSessionListener,\n DependsAdapter,\n DispatcherAdapter,\n GlobalAdapter,\n} from \"./wrapper-adapters.js\";\n\n// ---------------------------------------------------------------\n// startNapuketto 内部辅助(非 export,boot 装配用)\n// ---------------------------------------------------------------\n\n/** 默认 engine 配置(KWINDOWS + 版本号,字段待首个真实联调确认)。 */\nfunction defaultEngineConfig(env: BootEnv, qqDataPath?: string): EnginInitDesktopConfig {\n return {\n base_path_prefix: env.dataDir ?? \"\",\n platform_type: PlatformType.KWINDOWS,\n app_type: 4,\n app_version: env.qqVersion ?? \"\",\n os_version: process.platform,\n use_xlog: false,\n qua: \"\",\n global_path_config: {\n desktopGlobalPath: qqDataPath ?? env.dataDir ?? \"\",\n },\n thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },\n };\n}\n\n/**\n * Electron 进程类型(Node 的 process 类型无此字段)。\n * - \"browser\":QQ 主进程(V1 模式)\n * - \"utility\":utilityProcess Worker(路线 B,2026-08-06)\n */\nexport function electronProcessType(): string | undefined {\n return (process as unknown as { type?: string }).type;\n}\n\n/**\n * 解析 QQ 用户数据根(登录数据目录,如 `C:\\Users\\<user>\\Documents\\Tencent Files`)。\n * 优先 NodeQQNTWrapperUtil.getNTUserDataInfoConfig()(QQ 官方 API),回退默认位置。\n *\n * 背景(2026-08-06 P2-1 实测):worker(utilityProcess)模式下 loginService 是\n * `new NodeIKernelLoginService()` 新建的,其 initConfig 的 commonPath 必须指向\n * QQ 真实数据路径才能读到历史账号——cli 的 `.napuketto\\default` 数据目录读不到\n * (getLoginList 返回空 → 只能 QR 登录)。V1 主进程模式 QQ 自己 initConfig 过\n * 正确路径,无需此解析。\n */\nexport function resolveQqUserDataRoot(exports: WrapperNodeApi): string | null {\n return extractUserDataRoot(exports) ?? join(homedir(), \"Documents\", \"Tencent Files\");\n}\n\n/** 从 QQ 官方 API 提取数据根(不可用返回 null,调用方回退默认)。 */\nfunction extractUserDataRoot(exports: WrapperNodeApi): string | null {\n try {\n const util = exports.NodeQQNTWrapperUtil as unknown as {\n get?: () => { getNTUserDataInfoConfig?: () => unknown };\n } | null;\n const raw = util?.get?.().getNTUserDataInfoConfig?.();\n if (typeof raw === \"string\" && raw.trim() !== \"\") {\n return extractDataRoot(raw.trim());\n }\n } catch {\n // 回退默认位置\n }\n return null;\n}\n\n// ---------------------------------------------------------------\n// 导出区(useExportsLast:export 全部在文件末尾)\n// ---------------------------------------------------------------\n\n/** QQ 版本信息(登录握手用)。 */\nexport interface QQVersionContext {\n fullVersion: string;\n buildVersion: string;\n}\n\n/** 已引导的 wrapper 上下文(运行在 QQ Electron 主进程内)。 */\nexport interface WrapperContext {\n /** wrapper.node 的 NAPI 顶层导出。 */\n exports: WrapperNodeApi;\n /** engine 单例(get() 已调用)。 */\n engine: NodeIQQNTWrapperEngine;\n /** 版本信息(供登录握手等使用)。 */\n versionInfo: QQVersionContext;\n /** 会话(QQ 拦截捕获 / createSession 后填充)。 */\n session: NodeIQQNTWrapperSession | null;\n /** 启动会话包装(NapCat 同款:StartupSessionWrapper.create(),start() 替代 startNT)。 */\n startupSession: NodeIQQNTStartupSessionWrapper | null;\n /** QQ 的 loginService 实例(拦截 new 捕获,可为 null)。 */\n loginService: unknown;\n}\n\n/**\n * 从 loader 截获的 NAPI exports 创建 wrapper 上下文。\n * 在 QQ 主进程内调用(boot.cjs → kernel 入口)。\n */\nexport function createWrapper(\n exports: WrapperNodeApi,\n versionInfo: QQVersionContext,\n): WrapperContext {\n const engineMaybe = exports.NodeIQQNTWrapperEngine as unknown as {\n get?: () => NodeIQQNTWrapperEngine;\n } | null;\n if (engineMaybe === null || typeof engineMaybe.get !== \"function\") {\n throw kernelError(\n \"wrapper.node exports 无效(缺少 NodeIQQNTWrapperEngine)\",\n \"INVALID_PARAM\",\n );\n }\n const engine = engineMaybe.get();\n if (!engine || typeof engine.initWithDeskTopConfig !== \"function\") {\n throw kernelError(\"NodeIQQNTWrapperEngine.get() 返回对象无效\", \"INVALID_PARAM\");\n }\n return {\n exports,\n engine,\n versionInfo,\n session: null,\n startupSession: null,\n loginService: null,\n };\n}\n\n/** engine 初始化(wrapper 契约:先 engine 后 session)。config 为普通 JS 对象。 */\nexport function initEngine(ctx: WrapperContext, config: EnginInitDesktopConfig): void {\n ctx.engine.initWithDeskTopConfig(config, new GlobalAdapter());\n}\n\n/**\n * 创建会话(2026-08-06 P2-0 实测修正——NapCat 方式自研等价):\n * 1. StartupSessionWrapper.create()(启动会话包装)\n * 2. getNTWrapperSession(\"nt_1\")(QQ 主 session,带完整 cpp_impl)\n * 3. S.create() 回退\n *\n * 背景:`new NodeIQQNTWrapperSession()` 构造对象缺 cpp_impl,session.init 断言\n * \"implementation of IQQNTWrapperSession is not valid\"(P2-0 实测确认)。\n * NapCat 用 StartupSessionWrapper.create() + getNTWrapperSession('nt_1') 解决——\n * 返回带完整实现的实例。worker(utilityProcess)继承 QQ env 后同样有效。\n */\nexport function createSession(ctx: WrapperContext): NodeIQQNTWrapperSession {\n const S = ctx.exports.NodeIQQNTWrapperSession;\n const session =\n createStartupSession(ctx) ?? getNTWrapperSession(S) ?? createNewSession(S) ?? new S();\n ctx.session = session;\n return session;\n}\n\n/** 步骤 1:StartupSessionWrapper.create()(建立启动 session,失败忽略)。 */\nfunction createStartupSession(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n const Ssw = ctx.exports.NodeIQQNTStartupSessionWrapper as unknown as\n | {\n create?: () => NodeIQQNTStartupSessionWrapper;\n }\n | undefined;\n if (Ssw === undefined || typeof Ssw.create !== \"function\") {\n return null;\n }\n try {\n ctx.startupSession = Ssw.create();\n } catch {\n // 可选,失败继续\n }\n return null;\n}\n\n/** 步骤 2:getNTWrapperSession(\"nt_1\")(QQ 主 session,带 cpp_impl)。 */\nfunction getNTWrapperSession(S: NodeIQQNTWrapperSessionCtor): NodeIQQNTWrapperSession | null {\n if (typeof S.getNTWrapperSession !== \"function\") {\n return null;\n }\n try {\n return S.getNTWrapperSession(\"nt_1\");\n } catch {\n return null; // 回退\n }\n}\n\n/** 步骤 3:S.create() 回退。 */\nfunction createNewSession(S: NodeIQQNTWrapperSessionCtor): NodeIQQNTWrapperSession | null {\n if (typeof S.create !== \"function\") {\n return null;\n }\n try {\n return S.create();\n } catch {\n return null;\n }\n}\n\n/** session 初始化(4 参全为 JS 对象,NAPI 自动转换)。 */\nexport function initSession(\n ctx: WrapperContext,\n config: WrapperSessionInitConfig,\n listener: NodeIKernelSessionListener,\n): void {\n const session = ctx.session ?? createSession(ctx);\n session.init(config, new DependsAdapter(), new DispatcherAdapter(), listener);\n}\n\n/**\n * 启动会话(有 startupSession 用 start(),否则 startNT(0))。\n *\n * ⚠️ 2026-08-07 V9 修正:单纯 start() 只在「已 init」的 session 上有效。\n * 自建宿主必须走 initAndStartSession(先 session.init 再 startupSession.start())——\n * 先 start 后 init 业务 service 不挂载(HANDOVER-V9 实测)。本函数保留给\n * 已 init 的 session 补启动用;新路径请用 initAndStartSession。\n */\nexport function startSession(ctx: WrapperContext): void {\n const { session, startupSession } = ctx;\n if (session === null) {\n throw kernelError(\"session 未创建,无法启动\", \"INVALID_STATE\");\n }\n if (startupSession !== null && typeof startupSession.start === \"function\") {\n startupSession.start();\n return;\n }\n session.startNT(0);\n}\n\n/**\n * boot 装配入口:createWrapper → initEngine → session。\n *\n * **session 来源(2026-08-05 修正)**:\n * 1. qqSession(boot.cjs 拦截 `new` 窃取的 QQ 已 init session)——尚未落地\n * 2. createSession(`new NodeIQQNTWrapperSession()`)——实测可用,默认路径\n * (getMainSession 实测为空壳:service 全 null + 缺 startNT,仅 probe 探测参考)\n *\n * 由 kernel NapukettoCore(core.ts)装配调用;loader 自建宿主 bootstrap 也直接调用作回退。\n * 返回 WrapperContext;失败抛 KernelError。\n */\nexport function startNapuketto(options: StartNapukettoOptions): WrapperContext {\n const { env, engineConfig, sessionConfig, wrapperExports, qqSession, qqLoginService } = options;\n const versionInfo: QQVersionContext = {\n fullVersion: env?.qqVersion ?? \"\",\n buildVersion: env?.qqVersion ?? \"\",\n };\n const ctx = createWrapper(wrapperExports, versionInfo);\n const hasCapturedSession = captureSession(ctx, qqSession);\n // session 创建时机:**自建宿主(标准 node)必须在 engine init 之前创建**——\n // p0-kernel-flow 决定性顺序(HANDOVER-V6):SSW.create + getNTWrapperSession(\"nt_1\")\n // 在 engine.initWithDeskTopConfig 前;engine 先建 session 后建 → 登录后\n // session.init 的 onOpentelemetryInit 不触发(2026-08-07 自建宿主实测)。\n // 路线 B(utility)与 QQ 主进程(browser)保持 engine 先建原顺序(已验证)。\n const isSelfHost = electronProcessType() === undefined;\n prepareSession(ctx, hasCapturedSession, isSelfHost);\n initEngine(ctx, resolveEngineConfig(ctx, env, engineConfig));\n resolveLoginService(ctx, qqLoginService);\n // session:路线 B(utility)与 QQ 主进程(browser)在 engine 后创建;\n // 自建宿主已在 engine 前创建(isSelfHost 分支),此处跳过避免重复。\n finishSession(ctx, hasCapturedSession, isSelfHost, sessionConfig);\n return ctx;\n}\n\n/** engine 前 session 准备(自建宿主必须先建,顺序决定性)。 */\nfunction prepareSession(ctx: WrapperContext, hasCaptured: boolean, isSelfHost: boolean): void {\n if (!hasCaptured && isSelfHost) {\n createSession(ctx);\n }\n}\n\n/** engine 后收尾:非自建宿主补建 session;配置了 sessionConfig 则 init+start。 */\nfunction finishSession(\n ctx: WrapperContext,\n hasCaptured: boolean,\n isSelfHost: boolean,\n sessionConfig: WrapperSessionInitConfig | undefined,\n): void {\n if (!hasCaptured && !isSelfHost) {\n createSession(ctx);\n }\n if (sessionConfig !== undefined) {\n initSession(ctx, sessionConfig, createSessionListener());\n startSession(ctx);\n }\n}\n\n/** engine 配置:显式覆盖优先,否则默认(含 QQ 数据根 global 路径解析)。 */\nfunction resolveEngineConfig(\n ctx: WrapperContext,\n env: BootEnv | undefined,\n engineConfig: EnginInitDesktopConfig | undefined,\n): EnginInitDesktopConfig {\n if (engineConfig !== undefined) {\n return engineConfig;\n }\n return defaultEngineConfig(env ?? {}, resolveBootQqGlobalPath(ctx));\n}\n\n/** 捕获 QQ 已 init 的 session(boot.cjs 拦截 `new` 窃取),返回是否捕获成功。 */\nfunction captureSession(\n ctx: WrapperContext,\n qqSession: NodeIQQNTWrapperSession | null | undefined,\n): boolean {\n if (qqSession === undefined || qqSession === null) {\n return false;\n }\n ctx.session = qqSession;\n return true;\n}\n\n/**\n * 解析 QQ 真实数据目录的 global 路径(desktopGlobalPath 三要素之三)。\n * worker(utilityProcess)模式 + 自建宿主(标准 node)下必须指向 QQ 真实\n * 数据目录(数据根/nt_qq/global,HANDOVER-V6),否则登录数据读写错位\n * (P2-1 实测 2026-08-06;getLoginList 空)。QQ 主进程(V1)QQ 自己已配置\n * engine,无需解析。\n */\nfunction resolveBootQqGlobalPath(ctx: WrapperContext): string | undefined {\n if (electronProcessType() === \"browser\") {\n return undefined;\n }\n const root = resolveQqUserDataRoot(ctx.exports);\n if (root === null) {\n return undefined;\n }\n return resolveQqGlobalPath(root);\n}\n\n/**\n * 解析 loginService:优先捕获实例,其次单例 get()(自建宿主/worker 下 QQ 环境\n * 创建的实例才完整——`new` 自建实例读不到登录列表(getLoginList 空),\n * HANDOVER-V6 p0-login3 实证),最后 new 回退。\n */\nfunction resolveLoginService(ctx: WrapperContext, qqLoginService: unknown): void {\n if (qqLoginService !== undefined && qqLoginService !== null) {\n ctx.loginService = qqLoginService as WrapperContext[\"loginService\"];\n return;\n }\n ctx.loginService = getOrCreateLoginService(ctx.exports);\n}\n\n/** 单例 get() 优先,缺失回退 new(都失败返回 null)。 */\nfunction getOrCreateLoginService(exports: WrapperNodeApi): unknown {\n try {\n const loginSvcCtor = exports.NodeIKernelLoginService as unknown as {\n get?: () => unknown;\n } | null;\n if (loginSvcCtor !== null && typeof loginSvcCtor.get === \"function\") {\n return loginSvcCtor.get();\n }\n return new exports.NodeIKernelLoginService();\n } catch {\n return null;\n }\n}\n\n/** startNapuketto 的引导环境(由 loader launcher 注入环境变量,boot.cjs 透传)。 */\nexport interface BootEnv {\n /** QQ 版本(如 \"9.9.31-49919\")。 */\n qqVersion?: string;\n /** 用户数据目录(cfgDir 的父级,用于 desktopPathConfig)。 */\n dataDir?: string;\n /** 账号 uin(可选,提供则一步 init+start)。 */\n selfUin?: string;\n /** 账号 uid(可选)。 */\n selfUid?: string;\n}\n\n/** startNapuketto 选项。 */\nexport interface StartNapukettoOptions {\n /** 截获的 wrapper.node NAPI exports(boot.cjs 传入)。 */\n wrapperExports: WrapperNodeApi;\n /** 引导环境(版本/数据目录/账号)。 */\n env?: BootEnv;\n /** 覆盖 engine 配置(联调用)。 */\n engineConfig?: EnginInitDesktopConfig;\n /** 覆盖 session 配置(联调用,提供则自动 init+start)。 */\n sessionConfig?: WrapperSessionInitConfig;\n /** QQ 已 init 的 session(boot.cjs 拦截 new 捕获)。 */\n qqSession?: NodeIQQNTWrapperSession | null;\n /** QQ 的 loginService 实例(boot.cjs 拦截 new 捕获)。 */\n qqLoginService?: unknown;\n}\n","/**\n * NapukettoCore:装配 + 启动(§9 第 6 项,2026-08-05 实现)\n *\n * 装配:paths(建目录)→ logger → CoreContext。\n * 启动:attachWrapper(wrapperExports → startNapuketto)→ login(快速登录 → session.init)。\n *\n * 典型用法(boot.cjs 截获 exports 后):\n * const core = await NapukettoCore.create({ dataRoot, account });\n * core.attachWrapper(wrapperExports, { qqVersion, dataDir });\n * // appid 由 resolveAppidQua 从 major.node 动态解析(版本绑定,不写死)\n * await core.login({ appid });\n *\n * 与手动拼 startNapuketto/quickLogin/initAndStartSession 等价,但把装配状态\n * (ctx)集中管理,协议层直接消费 core.ctx。\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { type CoreContext, createCoreContext } from \"./context.js\";\nimport {\n createLogger,\n kernelError,\n type LogLevel,\n type PathOptions,\n PathWrapper,\n} from \"./infra/index.js\";\nimport {\n type LoginResult,\n type LoginState,\n type QrCodeData,\n QrLoginSession,\n quickLogin,\n type SelfInfo,\n} from \"./login/index.js\";\nimport type { NodeIQQNTWrapperSession, WrapperNodeApi } from \"./types/index.js\";\nimport { resolveQqGlobalPath } from \"./wrapper/qq-data-path.js\";\nimport { buildLoginConfig } from \"./wrapper/wrapper-config.js\";\nimport {\n type BootEnv,\n electronProcessType,\n resolveQqUserDataRoot,\n startNapuketto,\n type WrapperContext,\n} from \"./wrapper/wrapper-loader.js\";\n\nexport interface NapukettoCoreOptions {\n /** 数据根 / 账号(透传给 PathWrapper,见 ADR-016)。 */\n paths?: PathOptions;\n /** 日志级别,默认 info。 */\n logLevel?: LogLevel;\n /** 文件日志路径;不传则默认 `logs/napuketto.log`(父目录自动创建)。 */\n logFile?: string;\n /** 是否输出 console,默认 true。 */\n consoleLog?: boolean;\n}\n\n/** 登录进度(IPC 子进程模式经 onLoginProgress 转发;koishi 插件展示二维码/状态用)。 */\nexport interface LoginProgress {\n /** 登录状态(QR 状态机)。 */\n state: LoginState;\n /** 二维码数据(state=waiting_scan 时有)。 */\n qr?: QrCodeData;\n /** 登录成功 selfInfo(state=logged_in 时有)。 */\n selfInfo?: SelfInfo;\n /** 失败原因(state=failed 时有;如「登录超时,请刷新页面重试」)。 */\n message?: string;\n}\n\n/** login 参数。 */\nexport interface CoreLoginOptions {\n /** appid(登录握手,由 resolveAppidQua 从 major.node 动态解析,不写死)。 */\n appid: string;\n /** session.init 超时(毫秒),默认 15s。 */\n initTimeoutMs?: number;\n /** 指定快速登录账号(缺省遍历历史列表)。 */\n quickUin?: string;\n /** 快速登录失败时回退 QR 登录(二维码写缓存目录),默认 false。 */\n qrFallback?: boolean;\n /** 强制扫码:跳过快速登录,直接走 QR 登录(koishi 强制扫码 / 切账号)。默认 false。 */\n qrOnly?: boolean;\n /** QR 二维码图片保存路径(默认缓存目录 qrcode.png)。 */\n qrCodePath?: string;\n /** 登录进度回调(QR 阶段:二维码数据 + 状态变化)。子进程 IPC 模式转发用。 */\n onLoginProgress?: (progress: LoginProgress) => void;\n}\n\n/**\n * 核心装配器:持有 CoreContext,负责启动/停止编排。\n * 无全局单例(ADR-015 推论)——每账号每进程实例化一份。\n */\nexport class NapukettoCore {\n /** 装配根(协议层 / apis / cache 的消费入口)。 */\n readonly ctx: CoreContext;\n\n /** QR 登录会话句柄(登录期间非空;登录终态后清空)。refreshQr() 据此可达。 */\n private qrSession: QrLoginSession | null = null;\n\n /** 由 create() 装配,不直接 new。 */\n private constructor(ctx: CoreContext) {\n this.ctx = ctx;\n }\n\n /**\n * 装配:paths(建目录)→ logger → CoreContext。\n * 不加载 wrapper / 不登录——启动阶段由 attachWrapper + login 完成。\n */\n static create(opts: NapukettoCoreOptions = {}): NapukettoCore {\n const paths = new PathWrapper(opts.paths);\n paths.ensure();\n const logger = createLogger({\n level: opts.logLevel ?? \"info\",\n console: opts.consoleLog ?? true,\n file: opts.logFile ?? paths.file(\"logs\", \"napuketto.log\"),\n // base.name 用 pino 保留字段(logger name):pino-pretty 渲染为 (kernel/pid)\n // 元数据头,不占属性行;cli/loader 同款,三来源可区分\n base: { name: \"kernel\" },\n });\n return new NapukettoCore(createCoreContext({ logger, paths }));\n }\n\n /**\n * 装配 wrapper:wrapperExports → startNapuketto(engine.init + session 创建),\n * 结果挂到 ctx.wrapper。在 QQ 主进程内调用(boot.cjs 截获 exports 后)。\n * captured:boot.cjs 拦截 `new` 捕获的 QQ 实例(session/loginService,优先使用)。\n */\n attachWrapper(\n wrapperExports: WrapperNodeApi,\n env?: BootEnv,\n captured?: { qqSession?: NodeIQQNTWrapperSession | null; qqLoginService?: unknown },\n ): WrapperContext {\n let wrapper: WrapperContext;\n if (env === undefined) {\n wrapper = startNapuketto({ wrapperExports, ...captured });\n } else {\n wrapper = startNapuketto({ wrapperExports, env, ...captured });\n }\n this.ctx.wrapper = wrapper;\n this.ctx.logger.info({ version: env?.qqVersion }, \"wrapper 装配完成(engine + session)\");\n return wrapper;\n }\n\n /**\n * 登录 + session 初始化(wrapper 契约,2026-08-05 实测确认):\n * loginService.initConfig → 登录(快速/QR)→ **登录成功后** create session + init\n * → 等 onOpentelemetryInit(is_init) 完成信号。成功后填 ctx.login。\n *\n * 关键(实测):登录前 create()/init 均断言失败(session 未 init / 干扰 QQ);\n * 登录后 QQ 自己 init 并触发 listener.onOpentelemetryInit——我们只需在登录后\n * create + init 并监听该信号。\n */\n async login(opts: CoreLoginOptions): Promise<LoginResult> {\n const { wrapper } = this.ctx;\n if (wrapper === null) {\n throw kernelError(\"wrapper 未装配,无法登录\", \"INVALID_STATE\");\n }\n\n // 1. loginService.initConfig(wrapper 流程:addKernelLoginListener 前)\n this.initLoginConfig(wrapper, opts.appid);\n\n // 强制扫码(qrOnly):跳过快速登录,直接 QR 登录(koishi 强制扫码 / 切账号)。\n if (opts.qrOnly === true) {\n const loginResult = await this.loginByQr(opts);\n this.ctx.login = loginResult;\n return loginResult;\n }\n\n // 2. 登录:快速登录(优先)→ QR 回退。登录成功前不碰 session。\n let loginResult: LoginResult;\n try {\n const quickOpts: { uin?: string } = {};\n if (opts.quickUin !== undefined) {\n quickOpts.uin = opts.quickUin;\n }\n loginResult = await quickLogin(wrapper, quickOpts);\n this.ctx.logger.info({ uin: loginResult.uin, uid: loginResult.uid }, \"快速登录成功\");\n } catch (err) {\n if (opts.qrFallback !== true) {\n throw err;\n }\n loginResult = await this.loginByQr(opts);\n }\n\n // 3. 登录成功即返回。QQ 登录后重建 session(Proxy 捕获新实例),由 boot.cjs\n // 登录成功后 setSession 替换,再自行等待 session 就绪(waitSessionReady)。\n // 不在 login 内等待——替换发生在 login 返回之后,这里等不到。\n this.ctx.login = loginResult;\n return loginResult;\n }\n\n /**\n * loginService.initConfig(worker/自建宿主需指定 QQ 真实数据路径)。\n * worker(utilityProcess)模式 + 自建宿主(标准 node)下 loginService 是\n * new 的,commonPath 必须指向 QQ 真实数据目录(数据根/nt_qq/global,\n * HANDOVER-V6 三要素之三)才能读到历史账号——cli 的 `.napuketto\\default`\n * 读不到(getLoginList 空,P2-1 实测 2026-08-06)。\n * QQ 主进程(V1)模式 QQ 自己 initConfig 过正确路径,无需解析。\n */\n private initLoginConfig(wrapper: WrapperContext, appid: string): void {\n const loginService = wrapper.loginService as {\n initConfig?: (config: unknown) => void;\n } | null;\n if (loginService === null || typeof loginService.initConfig !== \"function\") {\n this.ctx.logger.warn(\"loginService 不可用,跳过 initConfig\");\n return;\n }\n const commonPath = resolveCommonPath(this.ctx, wrapper);\n const loginCfg = buildLoginConfig(appid, wrapper.versionInfo.fullVersion, commonPath);\n loginService.initConfig(loginCfg);\n this.ctx.logger.info({ commonPath }, \"loginService.initConfig OK\");\n }\n\n /**\n * 替换当前 session(登录后 Proxy 捕获的 QQ 新实例替换自己 create 的无效实例)。\n * 在 QQ 主进程内由 boot.cjs 调用(登录成功后)。\n */\n setSession(session: NodeIQQNTWrapperSession): void {\n if (this.ctx.wrapper === null) {\n throw kernelError(\"wrapper 未装配,无法设置 session\", \"INVALID_STATE\");\n }\n this.ctx.wrapper.session = session;\n }\n\n /**\n * 手动刷新二维码(QR 登录期间可用)。返回是否成功触发(false = 当前不在扫码态)。\n * koishi 前端「刷新二维码」按钮经 loader IPC `login.refreshQr` 动作直达。\n */\n refreshQr(): boolean {\n if (this.qrSession === null) {\n return false;\n }\n this.qrSession.refresh();\n return true;\n }\n\n /** QR 登录:QrLoginSession 状态机 + 二维码写缓存目录(无 UI,cli 可打印路径)。 */\n private async loginByQr(opts: CoreLoginOptions): Promise<LoginResult> {\n const { wrapper } = this.ctx;\n if (wrapper === null || wrapper.loginService === null) {\n throw kernelError(\"wrapper/loginService 不可用,无法 QR 登录\", \"INVALID_STATE\");\n }\n const session = new QrLoginSession(wrapper.loginService);\n // 暴露句柄:登录期间外部可经 refreshQr() 手动刷新二维码(koishi 前端刷新按钮)。\n this.qrSession = session;\n const qrPath = opts.qrCodePath ?? this.ctx.paths.file(\"cache\", \"qrcode.png\");\n this.subscribeQrProgress(session, opts, qrPath);\n\n try {\n // 启动 QR 登录(注册监听 → connect → getQRCodePicture)\n // ⚠️ 不传 quickUin(2026-08-13):QR 回退路径下快速登录已在 core.login\n // 失败过一次,这里再传会导致 QrLoginSession 二次快速登录——无凭据环境\n // 直接 resolve 带 errMsg,白等一个周期才出二维码。QR 登录应直接出码。\n session.start();\n\n await waitQrLoggedIn(session);\n const self = session.selfInfo;\n if (self === null) {\n throw kernelError(\"QR 登录成功但 selfInfo 为空\", \"INVALID_STATE\");\n }\n return { uin: self.uin, uid: self.uid, nick: self.nick };\n } finally {\n session.stop();\n this.qrSession = null;\n }\n }\n\n /** 订阅 QR 进度:二维码写盘 + 状态机经 onLoginProgress 转发。 */\n private subscribeQrProgress(\n session: QrLoginSession,\n opts: CoreLoginOptions,\n qrPath: string,\n ): void {\n session.onQrCode((qr) => {\n this.ctx.logger.warn(`请扫描二维码登录(保存: ${qrPath} | URL: ${qr.qrcodeUrl})`);\n if (qr.pngBase64 !== \"\") {\n writeQrCodePng(qrPath, qr.pngBase64);\n }\n opts.onLoginProgress?.({ state: \"waiting_scan\", qr });\n });\n session.onStateChange((state) => {\n this.ctx.logger.info({ state }, \"QR 登录状态\");\n const message = state === \"failed\" ? (session.failureReason ?? undefined) : undefined;\n opts.onLoginProgress?.({ state, ...(message !== undefined ? { message } : {}) });\n });\n }\n\n /** 停止:日志收尾(资源清理留给 P2 常驻管理)。 */\n stop(): void {\n this.ctx.logger.info(\"NapukettoCore 停止\");\n }\n}\n\n/** 写二维码 png 到磁盘(base64 解码,供 cli/用户扫码)。 */\nfunction writeQrCodePng(filePath: string, pngBase64: string): void {\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, Buffer.from(pngBase64, \"base64\"));\n } catch {\n // 写失败不阻塞登录(cli 仍可打印 URL)\n }\n}\n\n/** 等待 QR 登录终态:logged_in resolve,failed reject。 */\nfunction waitQrLoggedIn(session: QrLoginSession): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const offState = session.onStateChange((state) => {\n if (state === \"logged_in\") {\n offState();\n resolve();\n } else if (state === \"failed\") {\n offState();\n reject(kernelError(\"QR 登录失败\", \"NOT_LOGIN\"));\n }\n });\n });\n}\n\n/**\n * loginService.initConfig 的 commonPath:\n * worker/自建宿主(非 browser)优先 QQ 真实数据目录(数据根/nt_qq/global),\n * 否则回退账号目录。\n */\nfunction resolveCommonPath(ctx: CoreContext, wrapper: WrapperContext): string {\n if (electronProcessType() !== \"browser\") {\n const root = resolveQqUserDataRoot(wrapper.exports);\n if (root !== null) {\n return resolveQqGlobalPath(root);\n }\n }\n return ctx.paths.accountDir;\n}\n","/**\n * session 解析(2026-08-05,wrapper 机制实测确认后)\n *\n * - getMainSession:startup.create → getSessionIdList → getNTWrapperSession(nt_x)\n * (实测这些返回的 session 是空壳——核心 service 未 init,仅作参考)\n * - 真正可用的 session 是 `new wrapper.NodeIQQNTWrapperSession()` + init(见 lifecycle.ts)\n */\n\nimport type { NodeIQQNTWrapperSession } from \"../types/index.js\";\nimport type { WrapperContext } from \"./wrapper-loader.js\";\n\n/** 从 getSessionIdList 的 Map 提取主 sessionId(nt_ 前缀优先)。 */\nfunction findMainSessionId(ids: Map<unknown, unknown>): string | null {\n return firstStringId(ids, (s) => s.startsWith(\"nt_\")) ?? firstStringId(ids, () => true);\n}\n\n/** 遍历 Map 找第一个满足谓词的字符串 id(值优先,其次键)。 */\nfunction firstStringId(ids: Map<unknown, unknown>, pred: (s: string) => boolean): string | null {\n for (const [k, v] of ids) {\n if (typeof v === \"string\" && pred(v)) {\n return v;\n }\n if (typeof k === \"string\" && pred(k)) {\n return k;\n }\n }\n return null;\n}\n\n/** 通过 getNTWrapperSession 拿主 session(内部辅助)。 */\nfunction resolveMainSession(\n created: { start?: () => void; getSessionIdList?: () => unknown },\n getNTWrapperSession: (id: string) => NodeIQQNTWrapperSession,\n): NodeIQQNTWrapperSession | null {\n created.start?.();\n const ids = typeof created.getSessionIdList === \"function\" ? created.getSessionIdList() : null;\n if (!(ids instanceof Map)) {\n return null;\n }\n const mainId = findMainSessionId(ids);\n if (mainId === null) {\n return null;\n }\n const maybe = getNTWrapperSession(mainId) as NodeIQQNTWrapperSession | null | undefined;\n return maybe !== null && maybe !== undefined && typeof maybe.getMsgService === \"function\"\n ? maybe\n : null;\n}\n\n/**\n * 复用 QQ 已有 session(P1-4:QQ 已登录,直接拿单例避免重复 init)。\n * 优先 `NodeIQQNTWrapperSession.get()`;返回 null 时回退 createSession。\n */\nexport function getExistingSession(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n const got = trySessionGet(ctx);\n if (got !== null) {\n ctx.session = got;\n return got;\n }\n return null;\n}\n\n/** NodeIQQNTWrapperSession.get() 单例(无效/异常返回 null)。 */\nfunction trySessionGet(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n try {\n const S = ctx.exports.NodeIQQNTWrapperSession as unknown as {\n get?: () => NodeIQQNTWrapperSession;\n };\n if (typeof S.get !== \"function\") {\n return null;\n }\n const got = S.get();\n return got && typeof got.getMsgService === \"function\" ? got : null;\n } catch {\n return null; // 复用失败,回退 create\n }\n}\n\n/**\n * 复用 QQ 主 session(P1-4 实测链路):\n * `NodeIQQNTStartupSessionWrapper.create()` → `start()` → `getSessionIdList()`(Map\n * {nt:\"nt_3\", gpro:\"gpro_3\"})→ `NodeIQQNTWrapperSession.getNTWrapperSession(\"nt_3\")`。\n * 注:实测这些 session 是空壳(核心 service null),真正可用的是 new + init(lifecycle.ts)。\n */\nexport function getMainSession(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n try {\n return resolveMainFromStartup(ctx) ?? null;\n } catch {\n return null; // 复用失败,回退 create\n }\n}\n\n/** startup.create → start → getSessionIdList → getNTWrapperSession 链路。 */\nfunction resolveMainFromStartup(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n const startupRaw = ctx.exports.NodeIQQNTStartupSessionWrapper as unknown as {\n create?: () => { start?: () => void; getSessionIdList?: () => unknown } | null;\n };\n const sessionCtor = ctx.exports.NodeIQQNTWrapperSession as unknown as {\n getNTWrapperSession?: (id: string) => NodeIQQNTWrapperSession;\n };\n if (\n typeof startupRaw.create !== \"function\" ||\n typeof sessionCtor.getNTWrapperSession !== \"function\"\n ) {\n return null;\n }\n const created = startupRaw.create();\n if (!created) {\n return null;\n }\n const getSession = (id: string): NodeIQQNTWrapperSession =>\n (sessionCtor.getNTWrapperSession as (name: string) => NodeIQQNTWrapperSession)(id);\n const session = resolveMainSession(created, getSession);\n if (session !== null) {\n ctx.session = session;\n }\n return session;\n}\n","/**\n * 运行时反射探测的序列化工具(从 probe.ts 拆分,2026-08-08 FTA 优化)\n *\n * - serialize:深度受限序列化(BigInt 转字符串,Map/Set/Promise 展开,循环引用防护)\n * - tryShape:解析 service getter 返回形状\n * - shapeKeyGetters:探测 service 关键数据 getter 返回形状\n */\n\n/** 序列化深度上限(循环引用/深层对象防护)。 */\nconst MAX_SERIALIZE_DEPTH = 4;\n/** 数组序列化条数上限。 */\nconst MAX_ARRAY_ITEMS = 20;\n/** 对象键序列化条数上限。 */\nconst MAX_OBJECT_KEYS = 50;\n\n/** 序列化一个值(BigInt 转字符串,Map/Set/Promise 展开,循环引用防护)。 */\nexport function serialize(value: unknown, depth = 0): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === \"bigint\") return value.toString();\n if (typeof value === \"function\") return `[function ${value.name ?? \"anon\"}]`;\n if (typeof value !== \"object\") return value;\n if (depth > MAX_SERIALIZE_DEPTH) return \"[depth-limit]\";\n try {\n return serializeContainer(value, depth);\n } catch {\n return \"[unserializable]\";\n }\n}\n\n/** 序列化容器对象(数组 / Map / Set / Promise / 普通对象)。 */\nfunction serializeContainer(value: object, depth: number): unknown {\n if (Array.isArray(value)) {\n return serializeArray(value, depth);\n }\n if (value instanceof Map) {\n return serializeMap(value, depth);\n }\n if (value instanceof Set) {\n return serializeSet(value, depth);\n }\n if (typeof (value as Promise<unknown>).then === \"function\") {\n return \"[Promise]\";\n }\n return serializeObject(value, depth);\n}\n\n/** 数组序列化(条数截断)。 */\nfunction serializeArray(value: unknown[], depth: number): unknown[] {\n return value.slice(0, MAX_ARRAY_ITEMS).map((v) => serialize(v, depth + 1));\n}\n\n/** Map 序列化(键转字符串,条目截断)。 */\nfunction serializeMap(value: Map<unknown, unknown>, depth: number): unknown {\n const out: Record<string, unknown> = {};\n let i = 0;\n for (const [k, v] of value) {\n if (i >= MAX_OBJECT_KEYS) break;\n out[String(k)] = serialize(v, depth + 1);\n i += 1;\n }\n return { kind: \"Map\", size: value.size, entries: out };\n}\n\n/** Set 序列化(值截断)。 */\nfunction serializeSet(value: Set<unknown>, depth: number): unknown {\n return {\n kind: \"Set\",\n size: value.size,\n values: [...value].slice(0, MAX_ARRAY_ITEMS).map((v) => serialize(v, depth + 1)),\n };\n}\n\n/** 普通对象序列化(键截断)。 */\nfunction serializeObject(value: object, depth: number): unknown {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value).slice(0, MAX_OBJECT_KEYS)) {\n out[key] = serialize((value as Record<string, unknown>)[key], depth + 1);\n }\n return out;\n}\n\n/** 尝试解析一个 service getter 的返回值形状(深度受限)。 */\nexport function tryShape(session: unknown, getter: string): unknown {\n try {\n const fn = (session as Record<string, unknown>)[getter];\n if (typeof fn !== \"function\") return null;\n const value = fn.call(session);\n if (value === null || value === undefined) return null;\n return serialize(value);\n } catch {\n return \"[error]\";\n }\n}\n\n/** 数据 getter 方法名模式(探测返回形状用)。 */\nconst DATA_GETTER_RE = /^(get|query|fetch|load)/i;\n\n/** 探测 service 的关键数据 getter 返回形状(取前几个无参调用)。 */\nexport function shapeKeyGetters(\n session: unknown,\n serviceMethods: string[],\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const candidates = serviceMethods.filter(\n (m) => DATA_GETTER_RE.test(m) && !m.endsWith(\"Listener\"),\n );\n for (const m of candidates.slice(0, 8)) {\n out[m] = tryShape(session, m);\n }\n return out;\n}\n","/**\n * 运行时反射探测基础工具(从 probe.ts 拆分,2026-08-08 FTA 优化)\n *\n * - listMethods:反射枚举对象原型链上的方法名(去重、去构造器)\n * - tryCall:安全调用方法拿返回值(失败记录错误,不中断探测)\n */\n\n/** 反射枚举一个对象原型链上的方法名(去重、去构造器)。 */\nexport function listMethods(obj: unknown): string[] {\n if (obj === null || obj === undefined) {\n return [];\n }\n const names = new Set<string>();\n collectPrototypeNames(obj, names);\n collectOwnNames(obj, names);\n return [...names].sort();\n}\n\n/** 收集原型链上的属性名(到 Object/Function.prototype 为止,去构造器)。 */\nfunction collectPrototypeNames(obj: unknown, names: Set<string>): void {\n let proto = Object.getPrototypeOf(obj);\n while (isProbeProto(proto)) {\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (name !== \"constructor\") {\n names.add(name);\n }\n }\n proto = Object.getPrototypeOf(proto);\n }\n}\n\n/** 是否仍需遍历的原型(非 Object/Function.prototype 且非空)。 */\nfunction isProbeProto(proto: object | null): proto is object {\n return proto !== null && proto !== Object.prototype && proto !== Function.prototype;\n}\n\n/** 收集自有属性名(去构造器)。 */\nfunction collectOwnNames(obj: unknown, names: Set<string>): void {\n for (const name of Object.getOwnPropertyNames(obj)) {\n if (name !== \"constructor\") {\n names.add(name);\n }\n }\n}\n\n/** 安全调用:尝试调用方法拿返回值,失败记录错误(不中断探测)。 */\nexport function tryCall(\n obj: unknown,\n name: string,\n): { ok: boolean; value?: unknown; error?: string } {\n try {\n const fn = (obj as Record<string, unknown>)[name];\n if (typeof fn !== \"function\") {\n return { ok: false, error: `not a function (typeof=${typeof fn})` };\n }\n const value = fn.call(obj);\n return { ok: true, value };\n } catch (e) {\n let message = String(e);\n if (e instanceof Error) {\n message = e.message;\n }\n return { ok: false, error: message };\n }\n}\n","/**\n * wrapper exports 探测(从 probe.ts 拆分,2026-08-08 FTA 优化)\n *\n * - probeExportCtors:关键 export 构造器方法面\n * - probeEngineCalls:engine 关键调用\n * - probeLoginService:LoginService 实例(QQ 已登录凭据入口)\n */\n\nimport type { WrapperContext } from \"../wrapper-loader.js\";\nimport { serialize, tryShape } from \"./probe-serialize.js\";\nimport { listMethods, tryCall } from \"./probe-utils.js\";\n\n/** 探测关键 export 构造器方法(get/create/getNTWrapperSession 等)。 */\nexport function probeExportCtors(ctx: WrapperContext): Record<string, unknown> {\n const keys = [\n \"NodeIQQNTWrapperSession\",\n \"NodeIQQNTStartupSessionWrapper\",\n \"NodeQQNTWrapperUtil\",\n \"NodeIKernelLoginService\",\n \"NodeIQQNTWrapperEngine\",\n ] as const;\n const out: Record<string, unknown> = {};\n for (const key of keys) {\n const ctor = (ctx.exports as unknown as Record<string, unknown>)[key];\n if (ctor !== null && ctor !== undefined) {\n out[key] = { methods: listMethods(ctor), ownKeys: Object.getOwnPropertyNames(ctor) };\n }\n }\n return out;\n}\n\n/** 探测 engine 关键调用(getDeviceInfo / getECDHService / getThirdPartySigService)。 */\nexport function probeEngineCalls(ctx: WrapperContext): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const m of ENGINE_CALL_PROBES) {\n out[m] = probeEngineCall(ctx, m);\n }\n return out;\n}\n\n/** 探测单个 engine 方法调用(成功记录方法面/形状,失败记录错误)。 */\nfunction probeEngineCall(ctx: WrapperContext, m: string): unknown {\n const call = tryCall(ctx.engine, m);\n return call.ok\n ? { value: serialize(call.value), methods: call.value ? listMethods(call.value) : [] }\n : { error: call.error ?? \"null/undefined\" };\n}\n\n/** engine 关键调用候选。 */\nconst ENGINE_CALL_PROBES = [\n \"getDeviceInfo\",\n \"getECDHService\",\n \"getThirdPartySigService\",\n \"readyToShow\",\n] as const;\n\n/** 探测 LoginService 实例(QQ 已登录凭据入口)。 */\nexport function probeLoginService(ctx: WrapperContext): Record<string, unknown> | null {\n const ctor = ctx.exports.NodeIKernelLoginService as unknown as {\n get?: () => unknown;\n } | null;\n if (ctor === null || typeof ctor.get !== \"function\") {\n return null;\n }\n const inst = tryCall(ctor, \"get\");\n if (!(inst.ok && inst.value)) {\n return { get: inst.error ?? \"null/undefined\" };\n }\n return {\n methods: listMethods(inst.value),\n ownKeys: Object.getOwnPropertyNames(inst.value).slice(0, 30),\n ...probeLoginGetters(inst.value),\n };\n}\n\n/** 尝试关键登录信息 getter(无参调用,探测形状)。 */\nfunction probeLoginGetters(inst: unknown): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const m of LOGIN_GETTER_PROBES) {\n if (typeof (inst as Record<string, unknown>)[m] === \"function\") {\n out[m] = tryShape(inst, m);\n }\n }\n return out;\n}\n\n/** 登录信息 getter 候选(无参调用,探测形状)。 */\nconst LOGIN_GETTER_PROBES = [\n \"getAccountInfo\",\n \"getLoginInfo\",\n \"getA2\",\n \"getTicket\",\n \"getUin\",\n \"getUid\",\n \"getSelfUin\",\n] as const;\n","/**\n * 运行时反射探测(ADR-006 落地,P1-4)\n *\n * 注入链路打通后(2026-08-05),wrapper exports 是 QQ 已注册的单例。\n * 本模块在 **QQ 主进程内**反射枚举 session / service 的真实方法名与返回形状,\n * 产出 JSON 到 NAPUTO_CFG_DIR/napuketto-probe.json,作为 types/services/ 的权威来源。\n *\n * 用法:boot.cjs 在 NAPUTO_PROBE=1 时于 startNapuketto 后调用 probeRuntime(ctx)。\n * 产物是研究工具数据,类型层据此补全(自研描述,非移植)。\n *\n * 工具拆分(2026-08-08 FTA 优化):方法枚举/安全调用 → probe-utils.ts;\n * 序列化/形状探测 → probe-serialize.ts;exports/service 探测 → probe-services.ts;\n * 本文件只留 session 探测编排(probeStartup/probeSession/probeRuntime)。\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\nimport { getMainSession } from \"../session-resolver.js\";\nimport type { WrapperContext } from \"../wrapper-loader.js\";\nimport { serialize, shapeKeyGetters } from \"./probe-serialize.js\";\nimport { probeEngineCalls, probeExportCtors, probeLoginService } from \"./probe-services.js\";\nimport { listMethods, tryCall } from \"./probe-utils.js\";\n\n/** 枚举候选 sessionId(nt_0..nt_9 / gpro_0..gpro_9)找 QQ 已 init 的 session。 */\nfunction enumerateSessionIds(ctx: WrapperContext): Record<string, unknown> {\n const S = ctx.exports.NodeIQQNTWrapperSession;\n const enumFn = getNTWrapperSessionFn(ctx);\n if (enumFn === null) {\n return {};\n }\n const out: Record<string, unknown> = {};\n for (let i = 0; i < 10; i += 1) {\n probeSessionId(enumFn, S, `nt_${i}`, out);\n probeSessionId(enumFn, S, `gpro_${i}`, out);\n }\n return out;\n}\n\n/** 探测单个 sessionId(getter 存在性 + getMsgService 调用结果)。 */\nfunction probeSessionId(\n enumFn: (...args: unknown[]) => unknown,\n thisCtx: unknown,\n id: string,\n out: Record<string, unknown>,\n): void {\n try {\n const value = enumFn.call(thisCtx, id);\n if (!value) {\n return;\n }\n out[id] = probeSessionRecord(value);\n } catch {\n // 忽略单次失败\n }\n}\n\n/** 探测单个 session 记录(getter 存在性 + msgService 调用结果 + 方法面)。 */\nfunction probeSessionRecord(value: unknown): Record<string, unknown> {\n const rec = value as Record<string, unknown>;\n const msg = tryMsgService(rec, value);\n return {\n hasMsgServiceGetter: typeof rec[\"getMsgService\"] === \"function\",\n msgServiceOk: msg.service !== null,\n msgServiceMethods: msg.service ? listMethods(msg.service).length : 0,\n msgError: msg.error,\n methods: listMethods(value),\n };\n}\n\n/** 尝试调用 getMsgService(失败记录错误,成功返回实例)。 */\nfunction tryMsgService(\n rec: Record<string, unknown>,\n thisCtx: unknown,\n): { service: unknown; error: string | null } {\n try {\n const svc = (rec[\"getMsgService\"] as () => unknown).call(thisCtx);\n return { service: svc ?? null, error: null };\n } catch (e) {\n return { service: null, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** 取 NodeIQQNTWrapperSession.getNTWrapperSession 函数(无则 null)。 */\nfunction getNTWrapperSessionFn(ctx: WrapperContext): ((...args: unknown[]) => unknown) | null {\n const fn = (ctx.exports.NodeIQQNTWrapperSession as unknown as Record<string, unknown>)[\n \"getNTWrapperSession\"\n ];\n return typeof fn === \"function\" ? (fn as (...args: unknown[]) => unknown) : null;\n}\n\n/** 探测 startup 实例:start() + getSessionIdList(),提取 sessionIds(Map 值或键)。 */\nfunction probeStartupChain(created: unknown): {\n start: Record<string, unknown>;\n createdGetSessionIdList: Record<string, unknown>;\n sessionIds: string[];\n} {\n const started = tryCall(created, \"start\");\n const ids = tryCall(created, \"getSessionIdList\");\n return {\n start: started.ok ? { ok: true } : { error: started.error },\n createdGetSessionIdList: ids.ok ? { value: serialize(ids.value) } : { error: ids.error },\n sessionIds: extractSessionIds(ids),\n };\n}\n\n/** 从 getSessionIdList 结果提取 sessionIds(Map 值或键优先)。 */\nfunction extractSessionIds(ids: { ok: boolean; value?: unknown }): string[] {\n if (!(ids.ok && ids.value instanceof Map)) {\n return [];\n }\n const out: string[] = [];\n for (const [k, v] of ids.value) {\n out.push(typeof v === \"string\" ? v : String(k));\n }\n return out;\n}\n\n/** 用真实 sessionId 取主 session(getNTWrapperSession(nt_x))并记录方法面。 */\nfunction probeSessionById(ctx: WrapperContext, id: string): unknown {\n const fn = getNTWrapperSessionFn(ctx);\n if (fn === null) {\n return null;\n }\n try {\n const value = fn.call(ctx.exports.NodeIQQNTWrapperSession, id);\n return value\n ? { methods: listMethods(value), ownKeys: Object.getOwnPropertyNames(value) }\n : null;\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** 兜底:候选名字 getNTWrapperSession(无 Map 数据时)。 */\nfunction probeByNameFallback(ctx: WrapperContext): Record<string, unknown> {\n const candidates = [\"main\", \"primary\", \"session1\", \"default\", \"\"];\n const getResults: Record<string, unknown> = {};\n for (const name of candidates) {\n getResults[name === \"\" ? \"<empty>\" : name] = probeSessionByName(ctx, name);\n }\n return getResults;\n}\n\n/** 探测单个候选名(getNTWrapperSession(name),记录方法面或错误)。 */\nfunction probeSessionByName(ctx: WrapperContext, name: string): unknown {\n try {\n const fn = getNTWrapperSessionFn(ctx);\n if (fn === null) {\n return null;\n }\n const value = fn.call(ctx.exports.NodeIQQNTWrapperSession, name);\n return value\n ? { methods: listMethods(value), ownKeys: Object.getOwnPropertyNames(value) }\n : null;\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** 探测 startup session 链路:create() → start() → getSessionIdList → getNTWrapperSession。 */\nfunction probeStartup(ctx: WrapperContext): Record<string, unknown> | null {\n const startup = ctx.exports.NodeIQQNTStartupSessionWrapper as unknown as {\n create?: () => unknown;\n } | null;\n if (startup === null || typeof startup.create !== \"function\") {\n return null;\n }\n const out: Record<string, unknown> = { staticMethods: listMethods(startup) };\n const created = tryCall(startup, \"create\");\n out[\"create\"] = created.ok\n ? {\n methods: created.value ? listMethods(created.value) : [],\n value: serialize(created.value),\n }\n : { error: created.error };\n if (created.ok && created.value) {\n Object.assign(out, probeCreatedChain(ctx, created.value));\n }\n out[\"enumSessionIds\"] = enumerateSessionIds(ctx);\n // 无 Map 数据时兜底:候选名字\n if (out[\"sessionIds\"] === undefined) {\n out[\"getNTWrapperSession\"] = probeByNameFallback(ctx);\n }\n return out;\n}\n\n/** 探测 create 后的实例链:start / getSessionIdList / 主 session。 */\nfunction probeCreatedChain(ctx: WrapperContext, created: unknown): Record<string, unknown> {\n const chain = probeStartupChain(created);\n const out: Record<string, unknown> = {\n start: chain.start,\n createdGetSessionIdList: chain.createdGetSessionIdList,\n sessionIds: chain.sessionIds,\n };\n const mainId = findMainProbeId(chain.sessionIds);\n if (mainId !== undefined) {\n out[\"mainSessionId\"] = mainId;\n out[\"mainSession\"] = probeSessionById(ctx, mainId);\n }\n return out;\n}\n\n/** 选主 sessionId(nt_ 前缀优先,否则第一个)。 */\nfunction findMainProbeId(sessionIds: string[]): string | undefined {\n return sessionIds.find((id) => id.startsWith(\"nt_\")) ?? sessionIds[0];\n}\n\n/** 探测 session 与 service 方法(优先复用 QQ 主 session)。 */\nfunction probeSession(ctx: WrapperContext): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const session = pickProbeSession(ctx, out);\n out[\"session\"] = session\n ? { methods: listMethods(session), ownKeys: Object.getOwnPropertyNames(session) }\n : null;\n if (session !== null && session !== undefined) {\n out[\"services\"] = collectServiceGetters(session);\n }\n return out;\n}\n\n/** 选探测目标 session:优先 QQ 主 session(getMainSession),回退 ctx.session。 */\nfunction pickProbeSession(ctx: WrapperContext, out: Record<string, unknown>): unknown {\n try {\n const main = getMainSession(ctx);\n if (main !== null) {\n out[\"sessionSource\"] = \"mainSession(getNTWrapperSession)\";\n return main;\n }\n } catch {\n // fallthrough\n }\n if (ctx.session !== null) {\n out[\"sessionSource\"] = \"own-create\";\n return ctx.session;\n }\n return null;\n}\n\n/** 收集 session 的 get*Service getter 方法面与返回形状。 */\nfunction collectServiceGetters(session: unknown): Record<string, unknown> {\n const getters = listMethods(session).filter(\n (m) => m.startsWith(\"get\") && m.endsWith(\"Service\"),\n );\n const services: Record<string, unknown> = {};\n for (const getter of getters) {\n services[getter] = probeServiceGetter(session, getter);\n }\n return services;\n}\n\n/** 探测单个 getter:成功记录方法面/形状,失败记录错误。 */\nfunction probeServiceGetter(session: unknown, getter: string): unknown {\n const call = tryCall(session, getter);\n if (!(call.ok && call.value)) {\n return { error: call.error ?? \"null/undefined\", ok: call.ok };\n }\n const methods = listMethods(call.value);\n return {\n methods,\n ownKeys: Object.getOwnPropertyNames(call.value).slice(0, 20),\n shapes: shapeKeyGetters(call.value, methods),\n };\n}\n\n/**\n * 探测 wrapper 运行时:dump session 方法与各 service 方法到 JSON。\n * 返回探测结果对象;同时写文件(NAPUTO_CFG_DIR/<file>,默认 napuketto-probe.json)。\n */\nexport function probeRuntime(\n ctx: WrapperContext,\n file = \"napuketto-probe.json\",\n): Record<string, unknown> {\n const result: Record<string, unknown> = {\n timestamp: new Date().toISOString(),\n exports: listMethods(ctx.exports),\n engineMethods: listMethods(ctx.engine),\n exportCtors: probeExportCtors(ctx),\n engineCalls: probeEngineCalls(ctx),\n loginService: probeLoginService(ctx),\n startup: probeStartup(ctx),\n ...probeSession(ctx),\n };\n\n // 写文件\n try {\n const dir = process.env[\"NAPUTO_CFG_DIR\"] ?? process.cwd();\n mkdirSync(dir, { recursive: true });\n const full = join(dir, file);\n writeFileSync(full, JSON.stringify(result, null, 2), \"utf8\");\n result[\"probeFile\"] = full;\n } catch (e) {\n result[\"probeFileError\"] = e instanceof Error ? e.message : String(e);\n }\n return result;\n}\n","/**\n * QQ 安装目录探测(ADR-018)\n *\n * wrapper.node 路径随 QQ 版本变化:`<安装目录>/versions/<版本>/resources/app/wrapper.node`。\n * 加载前必须知道版本(登录握手参数 appid/qua 与版本强相关)。\n *\n * 已实测确认(2026-08-05,QQ 9.9.31-49919):\n * - 目录结构:versions/<9.9.31-49919>/resources/app/wrapper.node\n * - 版本信息在 versions/<版本>/resources/app/package.json(name=qq-chat / version / buildVersion)\n * - wrapper.node 是 C++ ABI 模块(非 N-API),导出 INTSessionShell 等符号\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { kernelError } from \"../infra/index.js\";\n\n/** 从 versions 目录读取版本 package.json(拿 name/version/buildVersion)。 */\nfunction readVersionPackage(\n installDir: string,\n version: string,\n): { name: string; version: string; buildVersion?: string } {\n const pkgPath = join(installDir, \"versions\", version, \"resources\", \"app\", \"package.json\");\n if (!existsSync(pkgPath)) {\n throw kernelError(`QQ 版本目录缺少 package.json: ${pkgPath}`, \"NOT_FOUND\");\n }\n try {\n const raw = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n name?: string;\n version?: string;\n buildVersion?: string;\n };\n return {\n name: raw.name ?? \"\",\n version: raw.version ?? \"\",\n ...(raw.buildVersion !== undefined ? { buildVersion: raw.buildVersion } : {}),\n };\n } catch (cause) {\n throw kernelError(`QQ 版本 package.json 解析失败: ${pkgPath}`, \"INVALID_PARAM\", { cause });\n }\n}\n\n/** 解析 wrapper.node 绝对路径(按 ADR-018 目录约定)。 */\nexport function resolveWrapperPath(installDir: string, version: string): string {\n return join(installDir, \"versions\", version, \"resources\", \"app\", \"wrapper.node\");\n}\n\n/**\n * 探测指定版本信息(校验 wrapper.node 存在)。\n * 版本号不匹配目录时抛 NOT_FOUND(明确失败而非静默)。\n */\nexport function resolveQQVersion(installDir: string, version: string): QQVersionInfo {\n const wrapperPath = resolveWrapperPath(installDir, version);\n if (!existsSync(wrapperPath)) {\n throw kernelError(`未找到 wrapper.node: ${wrapperPath}`, \"NOT_FOUND\");\n }\n const pkg = readVersionPackage(installDir, version);\n return {\n fullVersion: pkg.version || version,\n buildVersion: pkg.buildVersion ?? \"\",\n wrapperPath,\n appid: \"\",\n qua: \"\",\n };\n}\n\n/** 扫描 versions 目录,取可加载的版本列表(按目录名倒序,最新在前)。 */\nexport function listQQVersions(installDir: string): string[] {\n const versionsDir = join(installDir, \"versions\");\n if (!existsSync(versionsDir)) {\n return [];\n }\n const dirs = readdirSync(versionsDir).filter((name) => {\n const full = join(versionsDir, name);\n return (\n statSync(full).isDirectory() &&\n existsSync(join(full, \"resources\", \"app\", \"wrapper.node\"))\n );\n });\n return dirs.sort().reverse();\n}\n\n/** QQ 版本信息。 */\nexport interface QQVersionInfo {\n /** 完整版本号,如 \"9.9.31-49919\"。 */\n fullVersion: string;\n /** 构建号,如 \"49919\"。 */\n buildVersion: string;\n /** wrapper.node 绝对路径。 */\n wrapperPath: string;\n /** 登录握手参数(P1 探测脚本产出,暂为空串)。 */\n appid: string;\n /** 登录握手参数(P1 探测脚本产出,暂为空串)。 */\n qua: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,MAAa,qBAAqB;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;;AAGA,IAAa,cAAb,cAAiC,MAAM;CACnC;CAEA,YAAY,SAAiB,OAAwB,WAAW,SAAwB;EACpF,MAAM,SAAS,OAAO;EACtB,KAAK,OAAO;EACZ,KAAK,OAAO;CAChB;AACJ;;AAGA,SAAgB,YACZ,SACA,OAAwB,WACxB,SACW;CACX,OAAO,IAAI,YAAY,SAAS,MAAM,OAAO;AACjD;;AAGA,SAAgB,cAAc,KAAkC;CAC5D,OAAO,eAAe;AAC1B;;;;;;;;;;;;;;ACvCA,SAAS,YAAY,MAA4B;CAC7C,IAAI,QAAQ,IAAI,CAAC,CAAC,YAAY,MAAM,SAChC,OAAO;CAEX,OAAO;AACX;AAEA,SAAS,mBAAmB,KAAuB;CAC/C,IAAI,eAAe,OACf,OAAQ,IAA8B,SAAS;CAEnD,OAAO;AACX;;;;;;;AA6BA,IAAa,aAAb,MAA2B;CACvB;CACA;CAEA;CACA;CACA;CACA;CAEA,YAAY,MAAwB;EAChC,KAAK,OAAO,KAAK;EACjB,KAAK,WAAW,KAAK;EACrB,KAAK,SAAS,KAAK;EACnB,KAAK,SAAS,KAAK,UAAU,YAAY,KAAK,IAAI;EAClD,KAAK,OAAO,KAAK;EACjB,KAAK,QAAQ,KAAK,QAAQ,KAAK;CACnC;;CAGA,MAAS;EACL,OAAO,KAAK;CAChB;;;;;;;CAQA,MAAM,OAAmB;EACrB,IAAI,KAAK,SAAS,KAAA,GAAW;GACzB,KAAK,QAAQ,KAAK;GAClB,OAAO,KAAK;EAChB;EACA,IAAI;EACJ,IAAI;GACA,MAAM,MAAM,SAAS,KAAK,MAAM,MAAM;EAC1C,SAAS,KAAK;GACV,IAAI,mBAAmB,GAAG,GAAG;IACzB,MAAM,KAAK,KAAK,KAAK,QAAQ;IAC7B,OAAO,KAAK;GAChB;GACA,MAAM,IAAI,YAAY,aAAa,KAAK,QAAQ,WAAW,EAAE,OAAO,IAAI,CAAC;EAC7E;EACA,KAAK,QAAQ,KAAK,MAAM,GAAG;EAC3B,OAAO,KAAK;CAChB;;CAGA,SAAqB;EACjB,OAAO,KAAK,KAAK;CACrB;;CAGA,MAAM,KAAK,MAAwB;EAC/B,MAAM,SAAS,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC;EAC9C,UAAU,QAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;EACjD,MAAM,UAAU,GAAG,KAAK,KAAK;EAC7B,MAAM,UAAU,SAAS,GAAG,KAAK,UAAU,MAAM,EAAE,KAAK,MAAM;EAC9D,MAAM,OAAO,SAAS,KAAK,IAAI;EAC/B,KAAK,QAAQ;CACjB;;CAGA,UAAkB,OAAkB;EAChC,IAAI,KAAK,WAAW,QAChB,OAAOA,UAAkB,KAAgC;EAE7D,OAAO,KAAK,UAAU,OAAO,MAAM,CAAC;CACxC;;CAGA,YAAoB,KAAsB;EACtC,IAAI,KAAK,WAAW,QAChB,OAAOC,MAAc,GAAG;EAE5B,OAAO,KAAK,MAAM,GAAG;CACzB;;CAGA,MAAc,KAAgB;EAC1B,IAAI;EACJ,IAAI;GACA,QAAQ,KAAK,YAAY,GAAG;EAChC,SAAS,KAAK;GACV,MAAM,IAAI,YACN,QAAQ,KAAK,OAAO,YAAY,EAAE,SAAS,KAAK,QAChD,iBACA,EAAE,OAAO,IAAI,CACjB;EACJ;EACA,IAAI;GACA,OAAO,KAAK,OAAO,MAAM,KAAK;EAClC,SAAS,KAAK;GACV,MAAM,IAAI,YAAY,aAAa,KAAK,QAAQ,iBAAiB,EAC7D,OAAO,IACX,CAAC;EACL;CACJ;AACJ;;AAGA,SAAgB,UAAU,MAAuC;CAC7D,OAAOA,MAAc,IAAI;AAC7B;;AAGA,SAAgB,cAAc,OAAwC;CAClE,OAAOD,UAAkB,KAAK;AAClC;;;;;;;;;;;;AC1JA,MAAM,uBAAuB;CAAC;CAAS;CAAU;CAAU;CAAY;AAAQ;AAE/E,SAAS,SAAS,SAA4E;CAC1F,MAAM,CAAC,QAAQ;CACf,IAAI,SAAS,KAAA,GAET,MAAM,IAAI,YAAY,uBAAuB,SAAS;CAE1D,OAAO,KAAK;AAChB;;;;;;;AAyBA,SAAgB,aAAa,OAAsB,CAAC,GAAgB;CAChE,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,iBAAiB,KAAK,WAAW;CACvC,MAAM,EAAE,MAAM,SAAS;CAEvB,MAAM,UAAqD,CAAC;CAC5D,IAAI,gBACA,QAAQ,KAAK,EACT,QAAQ,WAAW;EACf,UAAU;EAMV,eAAe;EAKf,YAAY;EAKZ,aAAa,QAAQ;CACzB,CAAC,EACL,CAAC;CAEL,IAAI,MAAM;EACN,UAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;EAC5C,QAAQ,KAAK,EAAE,QAAQ,KAAK,YAAY;GAAE,MAAM;GAAM,MAAM;EAAK,CAAC,EAAE,CAAC;CACzE;CACA,IAAI,QAAQ,WAAW,GACnB,MAAM,IAAI,YAAY,oCAAoC,eAAe;CAG7E,MAAM,cAAkC;EACpC;EACA,QAAQ,CAAC,GAAG,sBAAsB,GAAI,KAAK,UAAU,CAAC,CAAE;CAC5D;CACA,IAAI,MACA,YAAY,OAAO;EAAE,KAAK,QAAQ;EAAK,GAAG;CAAK;CAGnD,IAAI;CACJ,IAAI,QAAQ,WAAW,GACnB,SAAS,SAAS,OAAO;MAEzB,SAAS,KAAK,YAAY,OAAO;CAGrC,OAAO,KAAK,aAAa,MAAM;AACnC;;;;;;;;;;;;;;;;;;;;;AC/EA,MAAa,yBAAyB;;AAGtC,MAAa,mBAAmB;;AAUhC,SAAS,YAAY,GAAmB;CACpC,IAAI,MAAM,KACN,OAAO,QAAQ;CAEnB,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE,WAAW,KAAK,GACxC,OAAO,KAAK,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;CAErC,OAAO;AACX;;;;;AAMA,SAAS,qBAAyC;CAE9C,MAAM,aAAa,gBADD,QAAQ,cAAc,YAAY,GAAG,CACZ,GAAG,qBAAqB;CACnE,IAAI,eAAe,KAAA,GACf,OAAO;CAEX,OACI,gBAAgB,QAAQ,IAAI,GAAG,qBAAqB,KACpD,gBAAgB,QAAQ,IAAI,GAAG,cAAc;AAErD;;;;;;AAOA,SAAgB,gBAAgB,UAA2B;CACvD,MAAM,WAAW,YAAY,QAAQ,IAAI;CACzC,IAAI,aAAa,KAAA,KAAa,aAAa,IACvC,OAAO,QAAQ,YAAY,QAAQ,CAAC;CAExC,MAAM,cAAc,mBAAmB;CACvC,IAAI,gBAAgB,KAAA,GAChB,OAAO,KAAK,aAAa,sBAAsB;CAEnD,OAAO,KAAK,QAAQ,GAAG,sBAAsB;AACjD;;AAGA,SAAS,gBAAgB,UAAkB,QAAoC;CAC3E,IAAI,MAAM;CACV,SAAS;EACL,IAAI,WAAW,KAAK,KAAK,MAAM,CAAC,GAC5B,OAAO;EAEX,MAAM,SAAS,QAAQ,GAAG;EAC1B,IAAI,WAAW,KACX;EAEJ,MAAM;CACV;AACJ;;;;;;;;;;;;AAkBA,SAAgB,kBAAkB,OAA0B,CAAC,GAAW;CACpE,MAAM,WAAW,QAAQ,IAAI;CAC7B,IAAI,aAAa,KAAA,KAAa,aAAa,IACvC,OAAO,QAAQ,QAAQ;CAE3B,MAAM,cAAc,mBAAmB;CACvC,IAAI,gBAAgB,KAAA,GAChB,OAAO,KAAK,aAAa,gBAAgB;CAE7C,OAAO,KAAK,KAAK,YAAY,gBAAgB,GAAG,gBAAgB;AACpE;;;;;AAMA,IAAa,cAAb,MAAyB;;CAErB;;CAEA;;CAEA;;CAEA;;CAEA;CAEA,YAAY,OAAoB,CAAC,GAAG;EAChC,MAAM,OAAO,gBAAgB,KAAK,QAAQ;EAC1C,KAAK,OAAO;EACZ,IAAI,KAAK,SACL,KAAK,aAAa,KAAK,MAAM,KAAK,OAAO;OAEzC,KAAK,aAAa;EAEtB,KAAK,YAAY,KAAK,KAAK,YAAY,QAAQ;EAC/C,KAAK,UAAU,KAAK,KAAK,YAAY,MAAM;EAC3C,KAAK,WAAW,KAAK,KAAK,YAAY,OAAO;CACjD;;CAGA,SAAe;EACX,KAAK,MAAM,OAAO;GAAC,KAAK;GAAW,KAAK;GAAS,KAAK;EAAQ,GAC1D,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;CAE1C;;CAGA,KAAK,GAAG,UAA4B;EAChC,OAAO,KAAK,KAAK,YAAY,GAAG,QAAQ;CAC5C;;;;;CAMA,aAAmB;EACf,IAAI;EACJ,IAAI;GACA,UAAU,YAAY,KAAK,QAAQ;EACvC,QAAQ;GACJ;EACJ;EACA,KAAK,MAAM,SAAS,SAChB,OAAO,KAAK,KAAK,UAAU,KAAK,GAAG;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CAE3E;AACJ;;;;;;;;;;;;;ACrKA,MAAM,oBAA2F;CAC7F;EAAE,MAAM;EAAa,UAAU,CAAC,OAAO,OAAO;CAAE;CAChD;EAAE,MAAM;EAAqB,UAAU,CAAC,OAAO,YAAY;CAAE;CAC7D;EAAE,MAAM;EAAa,UAAU,CAAC,OAAO,WAAW;CAAE;AACxD;;AAGA,SAAS,cAAc,KAA8B;CACjD,KAAK,MAAM,QAAQ,mBACf,IAAI,KAAK,SAAS,MAAM,MAAM,IAAI,SAAS,CAAC,CAAC,GACzC,OAAO,KAAK;CAGpB,OAAO;AACX;;AAGA,SAAgB,OAAO,OAAe,QAAgB,QAAuB;CACzE,IAAI,WAAW,GACX;CAEJ,MAAM,YAAY,GAAG,MAAM,OAAO,UAAU,WAAW,SAAS;AACpE;;AAGA,SAAgB,iBACZ,OACA,KACI;CACJ,IAAI,QAAQ,KAAA,KAAa,QAAQ,QAAQ,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,GACtF;CAEJ,MAAM,YAAY,GAAG,MAAM,OAAO,OAAO,IAAI,UAAU,EAAE,KAAK,SAAS;AAC3E;;AAGA,SAAgB,aAA0C,OAAe,KAAc;CACnF,IAAI,IAAI,WAAW,GACf;CAEJ,MAAM,MAAM,IAAI,UAAU;CAE1B,IAAI,OAAO,cAAc,GAAG;CAC5B,IAAI,SAAS,aAAa,UAAU,WAChC,OAAO;CAEX,MAAM,YAAY,GAAG,MAAM,OAAO,OAAO,IAAI;AACjD;;;;;;;;;;;;ACLA,IAAa,YAAb,MAAuB;CACnB;CACA;CAEA,YAAY,SAAkC,OAAyB,CAAC,GAAG;EACvE,MAAM,UAAU,QAAQ,gBAAgB;EACxC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,yCAAyC,eAAe;EAE9E,KAAK,UAAU;EACf,KAAK,WAAW,KAAK;CACzB;;CAGA,MAAM,gBAAmC;EACrC,IAAI;EACJ,IAAI;GACA,aAAa,MAAM,KAAK,QAAQ,sBAAsB,CAAC;EAC3D,QAAQ;GACJ,MAAM,MAAM,MAAM,KAAK,QAAQ,eAAe,OAAO,MAAM,CAAC;GAC5D,IAAI,IAAI,WAAW,GACf,MAAM,YAAY,sBAAsB,IAAI,UAAU,SAAS;GAEnE,aAAa,IAAI;EACrB;EAEA,MAAM,OAAO,WAAW,SACnB,QAAQ,IAAI,WAAW,KAAK,MAAM,EAAE,GAAG,KAAK,IAAI,aAAa,CAAC,CACnE;EACA,IAAI,yBAAS,IAAI,IAAoB;EACrC,IAAI,KAAK,aAAa,KAAA,GAClB,SAAS,MAAM,KAAK,SAAS,IAAI;EAErC,MAAM,MAAgB,CAAC;EACvB,KAAK,MAAM,OAAO,MACd,IAAI,KAAK;GACL;GACA,KAAK,OAAO,IAAI,GAAG,KAAK;GACxB,UAAU,KAAK,QAAQ,aAAa,GAAG;GACvC,QAAQ,KAAK,QAAQ,eAAe,GAAG;EAC3C,CAAC;EAEL,OAAO;CACX;;CAGA,MAAM,sBAAiD;EACnD,MAAM,aAAa,MAAM,KAAK,mBAAmB;EACjD,MAAM,4BAAY,IAAI,IAAoB;EAC1C,KAAK,MAAM,UAAU,MAAM,KAAK,cAAc,GAC1C,UAAU,IAAI,OAAO,KAAK,MAAM;EAEpC,OAAO,WAAW,KAAK,QAAQ;GAC3B,MAAM,UAAoB,CAAC;GAC3B,KAAK,MAAM,OAAO,IAAI,WAAW,KAAK,MAAM,EAAE,GAAG,KAAK,IAAI,aAAa,CAAC,GAAG;IACvE,MAAM,SAAS,UAAU,IAAI,GAAG;IAChC,IAAI,WAAW,KAAA,GACX,QAAQ,KAAK,MAAM;GAE3B;GACA,OAAO;IACH,YAAY,IAAI;IAChB,gBAAgB,IAAI;IACpB,cAAc,IAAI;IAClB,iBAAiB,IAAI;IACrB,aAAa,IAAI;IACjB,WAAW;GACf;EACJ,CAAC;CACL;;CAGA,MAAc,qBAA+C;EACzD,IAAI;GACA,OAAO,MAAM,KAAK,QAAQ,sBAAsB,CAAC;EACrD,QAAQ;GACJ,MAAM,MAAM,MAAM,KAAK,QAAQ,eAAe,OAAO,MAAM,CAAC;GAC5D,IAAI,IAAI,WAAW,GACf,MAAM,YAAY,sBAAsB,IAAI,UAAU,SAAS;GAEnE,OAAO,IAAI;EACf;CACJ;;CAGA,QAAQ,KAAsB;EAC1B,OAAO,KAAK,QAAQ,QAAQ,GAAG;CACnC;;CAGA,MAAM,kBAAuC;EACzC,MAAM,MAAM,MAAM,KAAK,QAAQ,YAAY;EAC3C,OAAO,eAAe,IAAI,QAAQ,IAAI,MAAM;EAC5C,OAAO,IAAI,aAAa,CAAC;CAC7B;;CAGA,MAAM,oBAAoB,QAAkB,QAAgC;EACxE,MAAM,KAAK,QAAQ,sBAAsB;GACrC,WAAW,OAAO;GAClB,SAAS,OAAO;GAChB;EACJ,CAAC;CACL;;CAGA,gBAAgB,KAAa,QAAsB;EAC/C,KAAK,QAAQ,eAAe;GAAE;GAAK;EAAO,CAAC;CAC/C;;CAGA,MAAM,aAAa,KAAa,YAAY,OAAO,cAAc,OAAsB;EAMnF,iBAAiB,YAAY,MALV,KAAK,QAAQ,SAAS;GACrC,WAAW;GACX;GACA;EACJ,CAAC,CAC+B;CACpC;;CAGA,MAAM,sBAAsB,OAAkD;EAC1E,MAAM,QAAQ,OAAO,KAAK,IAAI,CAAC;EAC/B,MAAM,MAAM,MAAM,KAAK,QAAQ,iBAAiB,OAAO,OAAO,EAAE;EAChE,OAAO,oBAAoB,IAAI,QAAQ,IAAI,MAAM;EACjD,MAAM,OAAwB,IAAI,aAAa,CAAC;EAChD,IAAI,yBAAS,IAAI,IAAoB;EACrC,IAAI,KAAK,aAAa,KAAA,KAAa,KAAK,SAAS,GAC7C,SAAS,MAAM,KAAK,SAAS,KAAK,KAAK,SAAS,KAAK,GAAG,CAAC;EAE7D,MAAM,MAAgC,CAAC;EACvC,KAAK,MAAM,QAAQ,MACf,IAAI,KAAK,KAAK,yBAAyB,MAAM,MAAM,CAAC;EAExD,OAAO;CACX;;CAGA,yBACI,MACA,QACsB;EACtB,OAAO,yBAAyB,MAAM,MAAM;CAChD;;CAGA,yBAAyB,KAAmB;EACxC,KAAK,QAAQ,sBAAsB,KAAK,IAAI,EAAE;CAClD;AACJ;;AAGA,SAAS,eACL,MACA,KACA,OACI;CACJ,IAAI,UAAU,KAAA,GACV,KAAK,OAAO;AAEpB;;AAGA,SAAgB,yBACZ,MACA,QACsB;CACtB,MAAM,OAA+B;EACjC,MAAM,KAAK;EACX,KAAK,OAAO,OAAO,IAAI,KAAK,GAAG,KAAK,KAAK,GAAG;EAC5C,MAAM;CACV;CACA,eAAe,MAAM,QAAQ,KAAK,IAAI;CACtC,eAAe,MAAM,UAAU,KAAK,MAAM;CAC1C,eAAe,MAAM,UAAU,KAAK,MAAM;CAC1C,eAAe,MAAM,OAAO,KAAK,GAAG;CACpC,eAAe,MAAM,cAAc,KAAK,SAAS;CACjD,eAAe,MAAM,QAAQ,KAAK,OAAO;CACzC,OAAO;AACX;;;;;;;;;;ACnOA,MAAa,WAAW;CACpB,KAAK;CACL,OAAO;CACP,MAAM;CACN,OAAO;CACP,UAAU;AACd;;AA0GA,MAAa,iBAAiB;CAC1B,QAAQ;CACR,cAAc;CACd,OAAO;CACP,OAAO;CACP,MAAM;CACN,SAAS;CACT,cAAc;CACd,cAAc;CACd,MAAM;CACN,MAAM;AACV;;AAIA,MAAa,sBAAsB;CAC/B,YAAY;CACZ,WAAW;CACX,MAAM;CACN,SAAS;CACT,qBAAqB;CACrB,OAAO;CACP,SAAS;CACT,SAAS;AACb;;;;AChIA,MAAa,sBAAsB;;CAE/B,KAAK;;CAEL,SAAS;AACb;;;;ACGA,MAAa,cAAc;CACvB,SAAS;CACT,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,OAAO;CACP,MAAM;CACN,OAAO;CACP,UAAU;CACV,QAAQ;CACR,KAAK;CACL,OAAO;CACP,WAAW;CACX,iBAAiB;CACjB,UAAU;CACV,OAAO;CACP,eAAe;CACf,iBAAiB;CACjB,aAAa;CACb,UAAU;AACd;;;;;;;;;;;ACtBA,MAAM,SAAS;CAAE,KAAK;CAAG,KAAK;CAAG,IAAI;AAAE;;AAGvC,SAAS,gBAAgB,QAAmD;CACxE,IAAI,WAAW,KAAA,GACX,OAAO;EAAE,MAAM;EAAQ,MAAM;CAAG;CAEpC,MAAM,EAAE,QAAQ,OAAO,YAAY;CACnC,IAAI,WAAW,OAAO,KAClB,OAAO;EAAE,MAAM;EAAM,QAAQ;CAAM;CAEvC,IAAI,WAAW,OAAO,OAAO,WAAW,OAAO,IAAI;EAG/C,MAAM,OAAO;GAAE,MAAM;GAAe,QAAQ,SAAS;EAAQ;EAC7D,MAAM,UAAU,QAAQ,WAAW,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;EAC7D,IAAI,YAAY,IACZ,OAAO;EAEX,OAAO;GAAE,GAAG;GAAM;EAAQ;CAC9B;CACA,OAAO;EAAE,MAAM;EAAQ,MAAM;CAAQ;AACzC;;AAGA,SAAS,eAAe,KAA+C;CACnE,IAAI,QAAQ,KAAA,GACR,OAAO;EAAE,MAAM;EAAS,MAAM;CAAG;CAErC,MAAM,OAAO,IAAI,WAAW,IAAI,UAAU;CAC1C,MAAM,MAAM,IAAI;CAChB,IAAI,QAAQ,KAAA,GACR,OAAO;EAAE,MAAM;EAAS;CAAK;CAEjC,OAAO;EAAE,MAAM;EAAS;EAAM;CAAI;AACtC;;AAGA,SAAS,gBAAgB,MAAiD;CACtE,IAAI,SAAS,KAAA,GACT,OAAO;EAAE,MAAM;EAAQ,IAAI;CAAI;CAEnC,OAAO;EAAE,MAAM;EAAQ,IAAI,OAAO,KAAK,SAAS;CAAE;AACtD;;AAGA,SAAS,eAAe,KAA+C;CACnE,IAAI,QAAQ,KAAA,GACR,OAAO;EAAE,MAAM;EAAS,MAAM;CAAG;CAErC,OAAO;EAAE,MAAM;EAAS,MAAM,IAAI,YAAY;CAAG;AACrD;;AAGA,SAAS,iBAAiB,OAAmD;CACzE,IAAI,UAAU,KAAA,GACV,OAAO;EAAE,MAAM;EAAS,MAAM;CAAG;CAErC,MAAM,OAAO,MAAM,YAAY,MAAM,YAAY;CACjD,MAAM,MAAM,MAAM;CAClB,IAAI,QAAQ,KAAA,GACR,OAAO;EAAE,MAAM;EAAS;CAAK;CAEjC,OAAO;EAAE,MAAM;EAAS;EAAM;CAAI;AACtC;;AAGA,SAAS,gBAAgB,MAAiD;CACtE,IAAI,SAAS,KAAA,GACT,OAAO;EAAE,MAAM;EAAQ,MAAM;CAAG;CAEpC,MAAM,OAAO,KAAK,YAAY;CAC9B,MAAM,OAAO,KAAK;CAClB,IAAI,SAAS,KAAA,GACT,OAAO;EAAE,MAAM;EAAQ;CAAK;CAEhC,OAAO;EAAE,MAAM;EAAQ;EAAM;CAAK;AACtC;;AAGA,SAAS,iBAAiB,OAAmD;CACzE,IAAI,UAAU,KAAA,GACV,OAAO;EAAE,MAAM;EAAS,WAAW;CAAG;CAE1C,OAAO;EAAE,MAAM;EAAS,WAAW,MAAM;CAAY;AACzD;;AAGA,SAAS,mBAAmB,IAAkC;CAC1D,QAAQ,GAAG,aAAX;EACI,KAAK,YAAY,MACb,OAAO,gBAAgB,GAAG,WAAW;EACzC,KAAK,YAAY,KACb,OAAO,eAAe,GAAG,UAAU;EACvC,KAAK,YAAY,MACb,OAAO,gBAAgB,GAAG,WAAW;EACzC,KAAK,YAAY,KACb,OAAO,eAAe,GAAG,UAAU;EACvC,KAAK,YAAY,OACb,OAAO,iBAAiB,GAAG,YAAY;EAC3C,KAAK,YAAY,MACb,OAAO,gBAAgB,GAAG,WAAW;EACzC,KAAK,YAAY,OACb,OAAO,iBAAiB,GAAG,YAAY;EAC3C,SACI,OAAO;GAAE,MAAM;GAAW,KAAK;EAAG;CAC1C;AACJ;;AAGA,SAAgB,oBAAoB,KAAqC;CACrE,OAAO,IAAI,SAAS,IAAI,kBAAkB;AAC9C;;AAGA,SAAgB,eAAe,UAAoD;CAC/E,MAAM,MAA4B,CAAC;CACnC,KAAK,MAAM,MAAM,UACb,QAAQ,GAAG,MAAX;EACI,KAAK;GACD,IAAI,KAAK;IAAE,aAAa,YAAY;IAAM,aAAa,EAAE,SAAS,GAAG,KAAK;GAAE,CAAC;GAC7E;EACJ,KAAK;GACD,IAAI,GAAG,WAAW,OACd,IAAI,KAAK;IACL,aAAa,YAAY;IACzB,aAAa;KAAE,SAAS,GAAG,WAAW;KAAK,QAAQ;IAAE;GACzD,CAAC;QAED,IAAI,KAAK;IACL,aAAa,YAAY;IACzB,aAAa;KAAE,SAAS,GAAG,WAAW;KAAK,QAAQ;KAAG,OAAO,GAAG;IAAO;GAC3E,CAAC;GAEL;EACJ,KAAK;GACD,IAAI,KAAK;IACL,aAAa,YAAY;IACzB,aAAa,EAAE,WAAW,OAAO,GAAG,EAAE,EAAE;GAC5C,CAAC;GACD;EACJ,KAAK;GACD,IAAI,KAAK;IAAE,aAAa,YAAY;IAAK,YAAY,EAAE,SAAS,GAAG,KAAK;GAAE,CAAC;GAC3E;EACJ,KAAK;GACD,IAAI,KAAK;IAAE,aAAa,YAAY;IAAK,YAAY,EAAE,UAAU,GAAG,KAAK;GAAE,CAAC;GAC5E;EACJ,KAAK;GACD,IAAI,KAAK;IACL,aAAa,YAAY;IACzB,cAAc;KACV,aAAa,GAAG;KAChB,cAAc,GAAG;KACjB,eAAe;IACnB;GACJ,CAAC;GACD;EACJ,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,WACD,MAAM,YAAY,QAAQ,GAAG,KAAK,mBAAmB,eAAe;CAI5E;CAEJ,OAAO;AACX;;;;ACvLA,MAAa,oBAAoB;CAC7B,aAAa;CACb,UAAU;CACV,QAAQ;CACR,OAAO;CACP,OAAO;AACX;;AA8BA,MAAa,qBAAqB;CAC9B,cAAc;CACd,mBAAmB;CACnB,gBAAgB;CAChB,2BAA2B;CAC3B,sBAAsB;CACtB,kCAAkC;CAClC,kCAAkC;CAClC,uCAAuC;CACvC,WAAW;CACX,0BAA0B;CAC1B,2BAA2B;CAC3B,2BAA2B;CAC3B,8BAA8B;CAC9B,2BAA2B;CAC3B,gCAAgC;CAChC,6BAA6B;AACjC;;AAIA,MAAa,uBAAuB;CAChC,OAAO;CACP,WAAW;CACX,SAAS;CACT,UAAU;CACV,UAAU;AACd;;AAIA,MAAa,6BAA6B;CACtC,cAAc;CACd,QAAQ;CACR,SAAS;CACT,SAAS;CACT,SAAS;AACb;;;;;;;;;;;;;ACxEA,MAAa,eAAe;CACxB,UAAU;CACV,UAAU;CACV,MAAM;CACN,UAAU;CACV,MAAM;CACN,QAAQ;AACZ;;AAIA,MAAa,aAAa;CACtB,aAAa;CACb,uBAAuB;CACvB,cAAc;CACd,aAAa;CACb,kBAAkB;CAClB,kBAAkB;CAClB,oBAAoB;AACxB;;;;;;;;;;;ACTA,IAAa,WAAb,MAAsB;CAClB;;CAEA;CAEA,YAAY,SAAkC;EAC1C,MAAM,UAAU,QAAQ,gBAAgB;EACxC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,yCAAyC,eAAe;EAE9E,KAAK,UAAU;EACf,MAAM,aAAa,QAAQ,cAAc;EACzC,IAAI,eAAe,QAAQ,eAAe,KAAA,GACtC,MAAM,YAAY,uCAAuC,eAAe;EAE5E,KAAK,aAAa;CACtB;;;;;;;;;CAUA,MAAM,aAAa,QAAQ,OAAyB;EAChD,MAAM,MAAM,MAAM,KAAK,QAAQ,aAAa,KAAK;EACjD,IAAI,IAAI,WAAW,GACf,OAAO,gBAAgB,IAAI,QAAQ,IAAI,MAAM;EAEjD,OAAO,CAAC;CACZ;;;;;;CAOA,MAAM,aAAa,WAA6C;EAC5D,MAAM,MAAM,MAAM,KAAK,QAAQ,mBAAmB,WAAW,CAAC;EAC9D,MAAM,EAAE,QAAQ,WAAW;EAC3B,IAAI,OAAO,WAAW,YAAY,WAAW,GACzC,OAAO,sBAAsB,QAAQ,MAAM;EAE/C,MAAM,SAAS,mBAAmB,QAAQ,GAAG;EAC7C,IAAI,WAAW,MACX,MAAM,YACF,uCAAuC,UAAU,KAAK,UACtD,SACJ;EAEJ,OAAO;CACX;;CAGA,MAAM,mBAAmB,WAAmB,aAAa,OAA+B;EACpF,MAAM,MAAM,MAAM,KAAK,QAAQ,iBAAiB,WAAW,UAAU;EACrE,IAAI,IAAI,YAAY,GAChB,MAAM,YAAY,wBAAwB,IAAI,UAAU,SAAS;EAErE,OAAO,CAAC,GAAG,IAAI,OAAO,MAAM,OAAO,CAAC;CACxC;;CAGA,MAAM,mBAAmB,WAAmB,MAAwC;EAChF,IAAI,KAAK,WAAW,GAChB,OAAO,CAAC;EAEZ,MAAM,MAAM,MAAM,KAAK,QAAQ,cAAc,WAAW,MAAM,IAAI;EAClE,OAAO,iBAAiB,IAAI,QAAQ,IAAI,MAAM;EAE9C,MAAM,EAAE,UAAU;EAClB,IAAI,iBAAiB,KACjB,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;EAE7B,OAAO,CAAC;CACZ;;CAGA,MAAM,SAAS,MAA8C;EACzD,IAAI,KAAK,WAAW,GAChB,uBAAO,IAAI,IAAI;EAEnB,MAAM,MAAM,MAAM,KAAK,QAAQ,aAAa,IAAI;EAChD,IAAI,IAAI,YAAY,GAChB,MAAM,YAAY,oBAAoB,IAAI,UAAU,SAAS;EAEjE,OAAO,IAAI;CACf;;CAGA,MAAM,SAAS,MAA8C;EACzD,IAAI,KAAK,WAAW,GAChB,uBAAO,IAAI,IAAI;EAEnB,MAAM,MAAM,MAAM,KAAK,QAAQ,aAAa,IAAI;EAChD,IAAI,IAAI,YAAY,GAChB,MAAM,YAAY,oBAAoB,IAAI,UAAU,SAAS;EAEjE,OAAO,IAAI;CACf;;CAGA,OAAO,aAAa,MAAuD;EACvE,IAAI,SAAS,kBAAkB,OAC3B,OAAO;EAEX,IAAI,SAAS,kBAAkB,OAC3B,OAAO;EAEX,OAAO;CACX;;;;;CAMA,MAAM,WACF,WACA,YACA,eACA,SAAS,IACI;EACb,IAAI,WAAW,WAAW,GACtB,MAAM,YAAY,2BAA2B,eAAe;EAGhE,IAAI,UAAU;EACd,IAAI,eACA,UAAU;EAEd,MAAM,MAAuB;GACzB;GACA,UAAU;GACV,UAAU,WAAW,KAAK,SAAS;IAC/B;IACA,YAAY;IACZ,cAAc;IACd,aAAa;GACjB,EAAE;GACF,cAAc;GACd,SAAS;EACb;EACA,MAAM,MAAM,MAAM,KAAK,QAAQ,aAAa,GAAG;EAC/C,OAAO,gBAAgB,IAAI,QAAQ,IAAI,MAAM;CACjD;;CAGA,MAAM,gBACF,WACA,SACa;EACb,IAAI,QAAQ,WAAW,GACnB,MAAM,YAAY,4BAA4B,eAAe;EAEjE,MAAM,MAAM,MAAM,KAAK,QAAQ,gBAC3B,WACA,QAAQ,KAAK,OAAO;GAAE,KAAK,EAAE;GAAK,WAAW,EAAE;EAAS,EAAE,CAC9D;EACA,OAAO,mBAAmB,IAAI,QAAQ,IAAI,MAAM;CACpD;;CAGA,MAAM,eAAe,WAAmB,QAAgC;EACpE,MAAM,MAAM,MAAM,KAAK,QAAQ,eAAe,WAAW,MAAM;EAC/D,OAAO,kBAAkB,IAAI,QAAQ,IAAI,MAAM;CACnD;;CAGA,cAAc,WAAmB,KAAa,MAA+B;EACzE,KAAK,QAAQ,iBAAiB,WAAW,KAAK,IAAI;CACtD;;CAGA,kBAAkB,WAAmB,KAAa,UAAwB;EACtE,KAAK,QAAQ,qBAAqB,WAAW,KAAK,QAAQ;CAC9D;;CAGA,MAAM,gBAAgB,WAAmB,WAAkC;EACvE,MAAM,MAAM,MAAM,KAAK,QAAQ,gBAAgB,WAAW,WAAW,KAAK;EAC1E,OAAO,mBAAmB,IAAI,QAAQ,IAAI,MAAM;CACpD;;CAGA,MAAM,UAAU,WAAmB,qBAAqB,OAAsB;EAC1E,MAAM,MAAM,MAAM,KAAK,QAAQ,YAAY;GAAE;GAAW;EAAmB,CAAC;EAC5E,OAAO,eAAe,IAAI,QAAQ,IAAI,MAAM;CAChD;;CAGA,MAAM,gBAAgB,WAAmB,OAA8B;EACnE,MAAM,KAAK,WAAW,WAAW,OAAO,IAAI;CAChD;;CAGA,MAAM,mBAAmB,WAAmB,OAA8B;EACtE,MAAM,KAAK,WAAW,WAAW,OAAO,KAAK;CACjD;;CAGA,MAAc,WAAW,WAAmB,OAAe,KAA6B;EACpF,MAAM,OAAO;GAAE,UAAU,SAAS;GAAO,SAAS;EAAU;EAC5D,MAAM,MAAM,MAAM,KAAK,WAAW,eAAe,MAAM,CAAC,KAAK,CAAC;EAC9D,OAAO,kBAAkB,IAAI,QAAQ,IAAI,MAAM;EAC/C,MAAM,MAAM,IAAI,UAAU;EAC1B,IAAI,QAAQ,KAAA,GACR,MAAM,YAAY,MAAM,MAAM,OAAO,WAAW;EAEpD,MAAM,QAAQ;GACV;GACA,WAAW,OAAO,IAAI,aAAa,CAAC;GACpC,QAAQ,OAAO,IAAI,MAAM;EAC7B;EACA,IAAI,QAAQ;EACZ,IAAI;EACJ,IAAI,KAAK;GACL,QAAQ;GACR,MAAO,MAAM,KAAK,QAAQ,gBAAgB,KAAK;EAGnD,OACI,MAAO,MAAM,KAAK,QAAQ,mBAAmB,KAAK;EAItD,iBAAiB,OAAO,GAAG;CAC/B;;CAGA,MAAM,sBAAsB,WAIzB;EACC,MAAM,MAAM,MAAM,KAAK,QAAQ,sBAAsB,SAAS;EAC9D,IAAI,IAAI,YAAY,GAChB,MAAM,YAAY,6BAA6B,IAAI,UAAU,SAAS;EAE1E,OAAO;GACH,UAAU,IAAI,OAAO;GACrB,wBAAwB,IAAI,OAAO;GACnC,0BAA0B,IAAI,OAAO;EACzC;CACJ;AACJ;;;;;;;AAQA,SAAgB,mBAAmB,QAAiB,KAAsC;CACtF,IAAI,WAAW,QAAQ,OAAO,WAAW,UACrC,OAAO;CAEX,MAAM,SAAS;CAKf,MAAM,YAAY,OAAO,aAAa,OAAO,cAAc,OAAO;CAClE,IAAI,cAAc,KAAA,KAAa,cAAc,QAAQ,OAAO,cAAc,UACtE,OAAO;CAEX,OAAO;AACX;;;;;;;;;;;AC/QA,IAAa,iBAAb,MAA4B;CACxB;CAEA,YAAY,SAAkC;EAC1C,MAAM,UAAU,QAAQ,gBAAgB;EACxC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,yCAAyC,eAAe;EAE9E,KAAK,UAAU;CACnB;;CAGA,MAAM,wBAAwB,OAAgB,OAAuC;EAEjF,OAAO,mBAAkB,MADP,KAAK,QAAQ,wBAAwB,OAAO,IAAI,KAAK,EAAA,CAC1C,MAAM;CACvC;;CAGA,MAAM,yBAAyB,WAA2C;EACtE,MAAM,MAAM,MAAM,KAAK,QAAQ,yBAAyB,SAAS;EACjE,IAAI,IAAI,WAAW,GACf,OAAO,4BAA4B,IAAI,QAAQ,IAAI,MAAM;EAE7D,OAAO,kBAAkB,IAAI,MAAM;CACvC;;CAGA,MAAM,mBACF,OACA,QACA,aACA,QACa;EACb,MAAM,EAAE,OAAO,KAAK,SAAS;EAC7B,IAAI,YAAY;EAChB,IAAI,UAAU,KAAA,GACV,CAAC,CAAE,aAAc;EAErB,IAAI,aAAa;EACjB,IAAI,eAAe,IACf,aAAa;EAEjB,MAAM,KAAK,QAAQ,iBAAiB,OAAO;GACvC;GACA,WAAW;IACP;IACA;IACA;IACA;GACJ;EACJ,CAAC;CACL;AACJ;;AAGA,SAAgB,kBAAkB,QAAgC;CAC9D,IAAI,MAAM,QAAQ,MAAM,GACpB,OAAO;CAEX,IAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;EAC/C,MAAM,MAAM;EACZ,IAAI,MAAM,QAAQ,IAAI,QAAQ,GAC1B,OAAO,IAAI;EAEf,IAAI,MAAM,QAAQ,IAAI,aAAa,GAC/B,OAAO,IAAI;CAEnB;CACA,OAAO,CAAC;AACZ;;AAGA,SAAgB,kBAAkB,QAAgC;CAC9D,IAAI,MAAM,QAAQ,MAAM,GACpB,OAAO;CAEX,IAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;EAC/C,MAAM,MAAM;EACZ,IAAI,MAAM,QAAQ,IAAI,UAAU,GAC5B,OAAO,IAAI;EAEf,IAAI,MAAM,QAAQ,IAAI,IAAI,GACtB,OAAO,IAAI;CAEnB;CACA,OAAO,CAAC;AACZ;;;;;;;;;;;;AC7EA,MAAM,cAAc;CAAE,QAAQ;CAAG,SAAS;CAAG,SAAS;CAAG,gBAAgB;AAAE;;AAG3E,MAAM,0BAA0B;;;;;;AAOhC,MAAM,WAAW;AACjB,MAAM,eAAe;AACrB,MAAM,YAAY;AAClB,MAAM,aAAa;AACnB,MAAM,eAAe;AACrB,MAAM,sBAAsB;;;;;;;AAQ5B,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;AACvB,MAAM,wBAAwB;AAC9B,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,wBAAwB;AAC9B,MAAM,eAAe;AACrB,MAAM,oBAAoB;;AAE1B,MAAM,sBAAsB;CAAC;CAAG;CAAI;CAAG;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;AAAE;;AAGrF,MAAM,uBAAuB;AAC7B,MAAM,kBAAkB;;AAExB,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB;;AAGvB,SAASE,QAAM,IAA2B;CACtC,OAAO,IAAI,SAAS,YAAY;EAC5B,WAAW,SAAS,EAAE;CAC1B,CAAC;AACL;;AAGA,eAAe,SAAS,MAAyC;CAC7D,IAAI;EACA,OAAO,MAAM,KAAK,IAAI;CAC1B,QAAQ;EACJ,MAAM,YAAY,YAAY,QAAQ,eAAe;CACzD;AACJ;;AAGA,eAAe,SAAS,MAA+B;CACnD,IAAI;EACA,MAAM,MAAM,MAAM,SAAS,IAAI;EAC/B,OAAO,WAAW,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,KAAK;CACrD,QAAQ;EACJ,MAAM,YAAY,aAAa,QAAQ,eAAe;CAC1D;AACJ;;;;;;;;;;;AAYA,SAAS,qBAAqB,QAA4B;CACtD,IAAI,CAAC,MAAM,QAAQ,MAAM,GACrB,MAAM,YAAY,gCAAgC,eAAe;CAErE,MAAM,MAAM,OAAO,QAAQ,OAAO,OAAO,OAAO,YAAY,GAAG,KAAK,MAAM,EAAE;CAC5E,IAAI,IAAI,WAAW,GACf,MAAM,YAAY,gCAAgC,eAAe;CAErE,OAAO;AACX;;AAGA,IAAa,SAAb,MAAoB;CAChB;;CAEA;;CAEA;;CAEA,cAAsB;CAEtB,YAAY,SAAkC,SAA2B,MAAgB;EACrF,MAAM,UAAU,QAAQ,cAAc;EACtC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,uCAAuC,eAAe;EAE5E,KAAK,UAAU;EACf,KAAK,UAAU,WAAW;EAE1B,KAAK,OAAO,SAAS,QAAQ,SAAS,KAAA,IAAa,OAA8B;CACrF;;;;;;;CAQA,cAA8B;EAC1B,MAAM,MAAM,KAAK,IAAI;EACrB,IAAI,MAAM,KAAK,aACX,KAAK,cAAc;OAEnB,KAAK,eAAe;EAExB,OAAO,OAAO,KAAK,WAAW;CAClC;;;;;;;;;;;;CAaA,MAAM,YAAY,QAAc,UAA0D;EAItF,MAAM,eAAe,MAAM,KAAK,oBAAoB,QAAQ;EAC5D,MAAM,QAAQ,KAAK,QAAQ,oBAAoB,OAAO,UAAU,KAAK,YAAY,CAAC;EAElF,MAAM,WAAiB;GAAE,GAAG;GAAQ,SAAS;EAAM;EAEnD,IAAI,KAAK,YAAY,MAAM;GAEvB,aAAa,WAAW,MADN,KAAK,QAAQ,QAAQ,KAAK,UAAU,8BAAc,IAAI,IAAI,CAAC,CAClD;GAC3B,OAAO,EAAE,MAAM;EACnB;EAGA,MAAM,UAAU,KAAK,YAAY,OAAO,MAAM;EAO9C,QAAQ,YAAY,KAAA,CAAS;EAC7B,MAAM,KAAK,QAAQ,QAAQ,KAAK,UAAU,8BAAc,IAAI,IAAI,CAAC;EACjE,MAAM;EACN,OAAO,EAAE,MAAM;CACnB;;;;;;;;;CAUA,MAAc,oBAAoB,UAA6D;EAC3F,MAAM,MAA4B,CAAC;EACnC,KAAK,MAAM,MAAM,UACb,QAAQ,GAAG,MAAX;GACI,KAAK;IACD,IAAI,KAAK,MAAM,KAAK,oBAAoB,GAAG,IAAI,CAAC;IAChD;GACJ,KAAK;IACD,IAAI,KAAK,MAAM,KAAK,kBAAkB,GAAG,IAAI,CAAC;IAC9C;GACJ,SACI,IAAI,KAAK,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;EAExC;EAEJ,OAAO;CACX;;CAGA,MAAc,oBAAoB,MAA2C;EACzE,MAAM,OAAO,MAAM,SAAS,IAAI;EAChC,MAAM,MAAM,MAAM,SAAS,IAAI;EAC/B,MAAM,WAAW,SAAS,IAAI;EAC9B,MAAM,UAAU,MAAM,KAAK,eAAe,MAAM,YAAY,KAAK,KAAK,QAAQ;EAC9E,OAAO;GACH,aAAa,YAAY;GACzB,WAAW;GACX,YAAY;IACR,WAAW;IACX,UAAU,OAAO,KAAK,IAAI;IAC1B,UAAU;IACV,WAAW;IACX;IACA,YAAY;IACZ,UAAU;IACV,SAAS;IACT,YAAY;IACZ,UAAU;IACV,WAAW;IACX,eAAe;IACf,SAAS;GACb;EACJ;CACJ;;;;;;;;;;;;;CAcA,MAAc,kBAAkB,MAA2C;EACvE,MAAM,OAAO,MAAM,SAAS,IAAI;EAChC,MAAM,MAAM,MAAM,SAAS,IAAI;EAC/B,MAAM,WAAW,SAAS,IAAI;EAC9B,MAAM,UAAU,MAAM,KAAK,eAAe,MAAM,YAAY,KAAK,KAAK,QAAQ;EAE9E,MAAM,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,OAAO,OAAO,CAAC,CAAC;EAC7D,OAAO;GACH,aAAa,YAAY;GACzB,WAAW;GACX,YAAY;IACR;IACA,UAAU;IACV,WAAW;IACX,UAAU,OAAO,KAAK,IAAI;IAC1B;IACA,YAAY;IACZ,WAAW;IACX,iBAAiB;IACjB,iBAAiB;IACjB,gBAAgB;IAChB,WAAW;IACX,WAAW;IACX,iBAAiB;IACjB,SAAS;IACT,mBAAmB,EAAE,aAAa,kBAAkB;GACxD;EACJ;CACJ;;;;;CAMA,MAAc,eACV,MACA,aACA,KACA,UACe;EACf,MAAM,UAAU,KAAK;EACrB,MAAM,OAAO,KAAK;EAIlB,MAAM,UAAU,QAAQ,6BAA6B;GACjD,WAAW;GACX;GACA;GACA,gBAAgB;GAChB,WAAW;GACX,YAAY;GACZ,cAAc;GACd,WAAW;EACf,CAAC;EACD,IAAI,SAAS,MAAM;GACf,MAAM,WAAW,OAAO,KAAK,QAAQ,aAAa,KAAK,IAAI,IAAI;GAC/D,MAAM,OAAQ,SAAqC;GACnD,IAAI,OAAO,SAAS,YAChB,MAAO,KAAoD,KACvD,UACA,MACA,OACJ;EAER;EACA,OAAO;CACX;;CAGA,YAAoB,OAAe,QAA6B;EAC5D,MAAM,UAAU,KAAK;EACrB,IAAI,YAAY,MACZ,OAAO,QAAQ,QAAQ;EAE3B,OAAO,IAAI,SAAS,SAAS,WAAW;GACpC,MAAM,MAAM,QAAQ,GAAG,4BAA4B,SAAS;IACxD,MAAM,OAAO,KAAK,MAAM,MAAM,EAAE,YAAY,KAAK;IACjD,IAAI,SAAS,KAAA,GACT;IAEJ,IACI,KAAK,eAAe,YAAY,WAChC,KAAK,eAAe,YAAY,gBAClC;KACE,IAAI;KACJ,QAAQ;IACZ,OAAO,IAAI,KAAK,eAAe,YAAY,QAAQ;KAC/C,IAAI;KACJ,OACI,YAAY,0BAA0B,KAAK,cAAc,aAAa,CAC1E;IACJ;GAEJ,CAAC;GAED,iBAAiB;IACb,IAAI;IACJ,OACI,YACI,wBAAwB,MAAM,WAAW,KAAK,UAAU,MAAM,EAAE,IAChE,aACJ,CACJ;GACJ,GAAG,uBAAuB;EAC9B,CAAC;CACL;;CAGA,MAAM,cAAc,QAAc,QAAiC;EAE/D,aAAa,aAAa,MADR,KAAK,QAAQ,UAAU,QAAQ,qBAAqB,MAAM,CAAC,CAChD;CACjC;;CAGA,MAAM,cACF,QACA,MACqB;EACrB,MAAM,MAAM,MAAM,KAAK,QAAQ,QAAQ,QAAQ,KAAK,SAAS,IAAI,KAAK,OAAO,KAAK;EAClF,aAAa,WAAW,GAAG;EAC3B,OAAO,IAAI,WAAW,CAAC;CAC3B;;CAGA,MAAM,iBAAiB,QAAc,KAAsC;EACvE,IAAI,IAAI,WAAW,GACf,OAAO,CAAC;EAEZ,MAAM,MAAM,MAAM,KAAK,QAAQ,eAAe,QAAQ,GAAG;EACzD,aAAa,kBAAkB,GAAG;EAClC,OAAO,IAAI,WAAW,CAAC;CAC3B;;CAGA,MAAM,gBACF,QACA,MACa;EAQb,aAAa,oBAAoB,MAPf,KAAK,QAAQ,iBAC3B,QACA,KAAK,QACL,KAAK,SACL,KAAK,WACL,KAAK,IACT,CACoC;CACxC;;;;;;CAOA,MAAM,aAAa,OAAe,QAA+B;EAE7D,MAAM,MAAM,eAAe,MADR,KAAK,iBAAiB,QAAQ,CAAC,KAAK,CAAC,CACzB;EAC/B,IAAI,QAAQ,MACR,MAAM,YAAY,YAAY,WAAW;EAG7C,aAAa,qBAAqB,MADhB,KAAK,QAAQ,kBAAkB,OAAO,QAAQ,GAAG,CAC9B;EAGrC,MAAM,OAAO,eAAe,MADR,KAAK,iBAAiB,QAAQ,CAAC,KAAK,CAAC,CACxB,CAAC,EAAE,YAAY;EAChD,IAAI,SAAS,KAAA,KAAa,SAAS,IAC/B,MAAM,YAAY,eAAe,SAAS;EAE9C,OAAO;CACX;;CAGA,MAAM,SAAS,QAA6B;EAExC,aAAa,cAAc,MADT,KAAK,QAAQ,WAAW,MAAM,CAClB;CAClC;;;;;;;;CASA,MAAM,YAAY,OAAe,QAAmC;EAEhE,MAAM,KAAK,eAAe,MADP,KAAK,iBAAiB,QAAQ,CAAC,KAAK,CAAC,CAC1B;EAC9B,MAAM,MAAM,IAAI;EAChB,IAAI,OAAO,QAAQ,QAAQ,KAAA,GACvB,MAAM,YAAY,YAAY,WAAW;EAE7C,MAAM,SAAS,GAAG,aAAa;EAC/B,MAAM,KAAK,QAAQ,kBAAkB;GACjC;GACA;GACA,UAAU,OAAO;GACjB,cAAc;GACd,WAAW;EACf,CAAC;EACD,KAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,KAAK;GACrC,MAAMA,QAAM,oBAAoB;GAEhC,MAAM,SAAS,eAAe,MADV,KAAK,iBAAiB,QAAQ,CAAC,KAAK,CAAC,CACtB,CAAC,EAAE;GACtC,IAAI,WAAW,KAAA,GACX;GAEJ,MAAM,OAAO,OAAO,mBAAmB,KAAK,OAAO,mBAAmB;GACtE,IAAI,OAAO,aAAa,KAAA,KAAa,OAAO,aAAa,MAAM,MAC3D,OAAO;GAEX,IACI,MAAM,MACN,OAAO,aAAa,KAAA,KACpB,OAAO,aAAa,IAEpB,OAAO;EAEf;EACA,MAAM,YAAY,eAAe,SAAS;CAC9C;;CAGA,MAAM,eAAe,QAAc,WAAkC;EACjE,MAAM,KAAK,QAAQ,uBAAuB,OAAO,UAAU,WAAW,OAAO,OAAO;CACxF;;;;;CAMA,MAAM,mBACF,QACA,YACA,WAC0B;EAC1B,IAAI,UAAU,WAAW,GACrB,MAAM,YAAY,gCAAgC,eAAe;EAErE,MAAM,QAAQ,MAAM,KAAK,QAAQ,qBAAqB;GAClD;GACA,YAAY;EAChB,CAAC;EACD,aAAa,wBAAwB,KAAK;EAC1C,MAAM,WAAW,MAAM,SAAS;EAChC,IAAI,aAAa,KAAA,KAAa,SAAS,WAAW,GAC9C,MAAM,YAAY,gBAAgB,SAAS;EAE/C,MAAM,QAAQ,KAAK,QAAQ,oBAAoB,OAAO,UAAU,OAAO,KAAK,IAAI,CAAC,CAAC;EAElF,aAAa,WAAW,MADN,KAAK,QAAQ,QAAQ,OAAO,QAAQ,0BAAU,IAAI,IAAI,CAAC,CAC9C;EAC3B,OAAO,EAAE,MAAM;CACnB;;CAGA,MAAM,oBAAoB,MAAY,OAAsC;EAExE,MAAM,UAAU,mBAAmB,MADhB,KAAK,iBAAiB,MAAM,CAAC,KAAK,CAAC,CACf;EACvC,IAAI,YAAY,QAAQ,QAAQ,UAAU,IACtC,MAAM,YAAY,eAAe,WAAW;EAEhD,MAAM,MAAM,MAAM,KAAK,QAAQ,YAAY,MAAM,OAAO,QAAQ,KAAK;EACrE,aAAa,eAAe,GAAG;EAC/B,OAAO,IAAI,WAAW,CAAC;CAC3B;;CAGA,MAAM,qBACF,YACA,WACA,SACa;EACb,IAAI,UAAU,WAAW,GACrB,MAAM,YAAY,kCAAkC,eAAe;EAGvE,aAAa,cAAc,MADT,KAAK,QAAQ,WAAW,WAAW,YAAY,CAAC,OAAO,GAAG,KAAA,CAAS,CACvD;CAClC;;CAGA,MAAM,gBAAgB,MAKJ;EAEd,aAAa,aAAa,MADR,KAAK,QAAQ,UAAU,IAAI,CAChB;CACjC;AACJ;;AAGA,SAAS,eAAe,MAAuC;CAC3D,MAAM,CAAC,SAAS;CAChB,IAAI,UAAU,KAAA,GACV,OAAO;CAEX,OAAO,MAAM,SAAS,MAAM,OAAO,GAAG,eAAe,KAAA,CAAS,KAAK;AACvE;;AAGA,SAAS,mBACL,MAC8D;CAC9D,MAAM,CAAC,SAAS;CAChB,IAAI,UAAU,KAAA,GACV,OAAO;CAEX,OACI,MAAM,SAAS,MAAM,OAAO,GAAG,2BAA2B,KAAA,CAAS,CAAC,EAC9D,0BAA0B;AAExC;;;;AChhBA,SAAS,UAAa,OAAsB,UAAgB;CACxD,OAAO,SAAS;AACpB;;AAGA,SAAS,YAAY,MAA2B,OAA0C;CACtF,MAAM,aAAa,KAAK,QAAQ;CAChC,MAAM,cAAc,MAAM,QAAQ;CAClC,OAAO;EACH,UAAU,UAAU,YAAY,cAAc,MAAM,GAAG,QAAQ;EAC/D,UAAU,UAAU,YAAY,cAAc,MAAM,GAAG,QAAQ;EAC/D,SAAS,UAAU,YAAY,cAAc,MAAM,GAAG,OAAO;EAC7D,WAAW,UAAU,KAAK,QAAQ,MAAM,SAAS,MAAM,GAAG,SAAS;EACnE,QAAQ,UAAU,YAAY,cAAc,MAAM,GAAG,QAAQ,MAAM;CACvE;AACJ;;AAGA,SAAS,UACL,SACA,UACA,KACa;CACb,MAAM,cAAc,YAAY,KAAA,IAAY,IAAI,OAAO,IAAI,KAAA;CAC3D,IAAI,gBAAgB,KAAA,GAChB,OAAO;CAEX,OAAO,aAAa,KAAA,IAAY,IAAI,QAAQ,IAAI,KAAA;AACpD;;AAGA,SAAgB,oBACZ,KACA,KACA,OACA,MACY;CACZ,MAAM,IAAI,YAAY,MAAM,KAAK;CACjC,OAAO;EACH,SAAS,OAAO,GAAG;EACnB;EACA,UAAU,UAAU,EAAE,UAAU,MAAM,EAAE;EACxC,KAAK,UAAU,EAAE,UAAU,KAAK,CAAC;EACjC,KAAK,UAAU,EAAE,UAAU,KAAK,EAAE;EAClC,UAAU,UAAU,EAAE,WAAW,SAAS,CAAC;EAC3C,KAAK,OAAO,EAAE,UAAU,GAAG;EAC3B,WAAW,UAAU,EAAE,UAAU,UAAU,EAAE;EAC7C,UAAU,UAAU,EAAE,WAAW,SAAS,CAAC;EAC3C,QAAQ,UAAU,EAAE,SAAS,UAAU,KAAK;EAC5C,cAAc,UAAU,EAAE,SAAS,aAAa,KAAK;EACrD,WAAW,UAAU,EAAE,SAAS,UAAU,CAAC;EAC3C,QAAQ,UAAU,EAAE,UAAU,QAAQ,EAAE;EACxC,QAAQ,UAAU,EAAE,QAAQ,CAAC;EAC7B,YAAY;CAChB;AACJ;;AAGA,SAAS,OAAO,KAAwD;CACpE,IAAI,QAAQ,GACR,OAAO;CAEX,IAAI,QAAQ,GACR,OAAO;CAEX,OAAO;AACX;;;;;;;;;;;;AC3FA,IAAa,aAAb,MAAwB;CACpB;CAEA,YAAY,SAAkC;EAC1C,MAAM,UAAU,QAAQ,kBAAkB;EAC1C,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,2CAA2C,eAAe;EAEhF,KAAK,UAAU;CACnB;;CAGA,MAAM,YAAY,UAAiC;EAI/C,iBAAiB,eAAe,MAHb,KAAK,QAAQ,YAAY,QAAQ,CAGjB;CACvC;;CAGA,MAAM,YAAY,UAAiC;EAI/C,iBAAiB,eAAe,MAHb,KAAK,QAAQ,YAAY,QAAQ,CAGjB;CACvC;;CAGA,MAAM,UAAU,UAAiC;EAC7C,MAAM,MAAM,MAAM,KAAK,QAAQ,UAAU,QAAQ;EACjD,IAAI,IAAI,WAAW,GACf,MAAM,YAAY,iBAAiB,IAAI,UAAU,SAAS;CAElE;;CAGA,MAAM,gBAAgB,KAAoC;EACtD,MAAM,QAAQ,MAAM,KAAK,QAAQ,uBAAuB,GAAG;EAC3D,MAAM,YAAY,MAAM,QAAQ;EAChC,IAAI,MAAM;EACV,IAAI,cAAc,KAAA,KAAa,cAAc,IACzC,MAAM;EAEV,MAAM,OAAO,MAAM,KAAK,QAAQ,kBAAkB,GAAG;EACrD,OAAO,oBAAoB,KAAK,KAAK,OAAO,IAAI;CACpD;AACJ;;;;;;;;;ACpDA,IAAa,iBAAb,MAA4B;CACxB;CAEA,YAAY,SAAkC;EAC1C,MAAM,UACF,QAAQ,sBAAsB;EAClC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,+CAA+C,eAAe;EAEpF,KAAK,UAAU;CACnB;;CAGA,MAAM,SAAS,KAAa,QAAQ,GAAkB;EAClD,MAAM,MAAM,MAAM,KAAK,QAAQ,oBAAoB;GAC/C,WAAW;GACX,UAAU;GACV,aAAa;GACb,iBAAiB;EACrB,CAAC;EACD,IAAI,IAAI,WAAW,GACf,MAAM,YAAY,SAAS,IAAI,UAAU,SAAS;CAE1D;AACJ;;;;;;;;;;AChBA,MAAM,sBAAsB;;AAE5B,MAAM,iBAAiB;;AAEvB,MAAM,kBAAkB;;AAExB,MAAM,mBAAmB;;AAEzB,MAAM,wBAAwB;;AAE9B,MAAM,qBAAqB;;AAE3B,MAAM,yBAAyB;;AAiB/B,IAAa,eAAb,MAA0B;CACtB;CAEA,YAAY,SAAkC;EAC1C,MAAM,UACF,QAAQ,oBAAoB;EAChC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,6CAA6C,eAAe;EAElF,KAAK,UAAU;CACnB;;CAGA,MAAM,eAAe,OAAoC;EACrD,MAAM,MAAM,MAAM,KAAK,QAAQ,oBAAoB,KAAK;EACxD,OAAO,uBAAuB,IAAI,QAAQ,IAAI,MAAM;EACpD,OAAO,IAAI,SAAS,CAAC;CACzB;;CAGA,MAAM,iBACF,WACA,iBAAiB,IACjB,YAAY,oBACgB;EAC5B,MAAM,SAA2B;GAC7B,UAAU;GACV;GACA,YAAY;GACZ,WAAW;GACX,qBAAqB;EACzB;EACA,MAAM,MAAM,MAAM,KAAK,QAAQ,iBAAiB,WAAW,MAAM;EACjE,IAAI,IAAI,WAAW,GACf,OAAO,oBAAoB,IAAI,QAAQ,IAAI,MAAM;EAErD,MAAM,OAAO,gBAAgB,IAAI,MAAM;EACvC,IAAI,mBAAmB,IACnB,OAAO;EAEX,OAAO,KAAK,QACP,SACI,KAAK,aAAa,KAAA,KAAa,KAAK,SAAS,WAAW,kBACxD,KAAK,eAAe,KAAA,KAAa,KAAK,WAAW,cAAc,cACxE;CACJ;;CAGA,MAAM,uBAAuB,WAAiD;EAC1E,MAAM,SAA2B;GAC7B,UAAU;GACV,WAAW;GACX,YAAY;GACZ,WAAW;GACX,qBAAqB;EACzB;EACA,MAAM,MAAM,MAAM,KAAK,QAAQ,iBAAiB,WAAW,MAAM;EACjE,IAAI,IAAI,WAAW,GACf,OAAO,oBAAoB,IAAI,QAAQ,IAAI,MAAM;EAErD,MAAM,QAAQ,IAAI;EAClB,IAAI,UAAU,KAAA,GACV,MAAM,YAAY,uCAAuC,SAAS;EAEtE,OAAO;GACH,WAAW,gBAAgB,IAAI,MAAM;GACrC,YAAY;GACZ,WAAW,MAAM;GACjB,YAAY,MAAM;EACtB;CACJ;;CAGA,MAAM,kBAAkB,WAAmB,YAAqC;EAC5E,MAAM,MAAM,MAAM,KAAK,QAAQ,kBAAkB,WAAW,UAAU;EACtE,IAAI,IAAI,WAAW,GACf,OAAO,qBAAqB,IAAI,QAAQ,IAAI,MAAM;EAGtD,MAAM,EAAE,WAAW;EACnB,IAAI,OAAO,WAAW,UAClB,OAAO;EAEX,OAAO;CACX;;CAGA,MAAM,gBAAgB,WAAmB,SAAkC;EACvE,MAAM,MAAM,MAAM,KAAK,QAAQ,gBAAgB,WAAW,CAAC,mBAAmB,GAAG,OAAO;EACxF,IAAI,IAAI,WAAW,GACf,OAAO,mBAAmB,IAAI,QAAQ,IAAI,MAAM;CAExD;;CAGA,MAAM,kBAAkB,WAAmB,UAAiC;EACxE,MAAM,MAAM,MAAM,KAAK,QAAQ,kBAAkB,WAAW,QAAQ;EACpE,IAAI,IAAI,WAAW,GACf,OAAO,qBAAqB,IAAI,QAAQ,IAAI,MAAM;CAE1D;;CAGA,MAAM,gBAAgB,WAAmB,QAAgB,SAAgC;EACrF,MAAM,KAAK,QAAQ,gBAAgB,WAAW,qBAAqB,QAAQ,SAAS,EAAE;CAC1F;;CAGA,MAAM,cACF,WACA,SACA,gBACa;EACb,MAAM,MAAM,MAAM,KAAK,QAAQ,cAC3B,WACA,CAAC,mBAAmB,GACpB,SACA,IACA,cACJ;EACA,IAAI,IAAI,WAAW,GACf,OAAO,iBAAiB,IAAI,QAAQ,IAAI,MAAM;CAEtD;;CAGA,MAAM,eAAe,WAAmB,QAA+B;EACnE,MAAM,MAAM,MAAM,KAAK,QAAQ,eAAe,WAAW,MAAM;EAC/D,IAAI,IAAI,WAAW,GACf,OAAO,kBAAkB,IAAI,QAAQ,IAAI,MAAM;CAEvD;AACJ;;AAGA,SAAgB,gBAAgB,QAAsC;CAClE,IAAI,MAAM,QAAQ,MAAM,GACpB,OAAO;CAEX,IAAI,WAAW,QAAQ,OAAO,WAAW,UACrC,OAAO,CAAC;CAEZ,MAAM,MAAM;CACZ,KAAK,MAAM,OAAO;EAAC;EAAS;EAAY;CAAM,GAAY;EACtD,MAAM,QAAQ,IAAI;EAClB,IAAI,MAAM,QAAQ,KAAK,GACnB,OAAO;CAEf;CACA,OAAO,CAAC;AACZ;;AAGA,SAAS,gBAAgB,QAAyB;CAC9C,IAAI,MAAM,QAAQ,MAAM,GACpB,OAAQ,OAA+B,QAAQ,SAAS,KAAK,aAAa,KAAA,CAAS,CAAC,CAAC;CAEzF,OAAO;AACX;;;;;;;;;;;AC5LA,MAAM,kBAAkB;;AAGxB,IAAa,YAAb,MAAuB;CACnB;CAEA,YAAY,SAAkC;EAC1C,MAAM,UAAU,QAAQ,iBAAiB;EACzC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,0CAA0C,eAAe;EAE/E,KAAK,UAAU;CACnB;;CAGA,MAAM,eAAoD;EACtD,MAAM,MAAM,MAAM,KAAK,QAAQ,oBAAoB,EAAE;EACrD,IAAI,IAAI,WAAW,KAAK,IAAI,cAAc,IACtC,MAAM,YAAY,2BAA2B,IAAI,UAAU,SAAS;EAExE,OAAO;CACX;;CAGA,MAAM,WAAW,QAAgB,KAA8C;EAC3E,MAAM,EAAE,cAAc,MAAM,KAAK,aAAa;EAC9C,MAAM,KAAK,WAAW,OAAO,GAAG,IAAI;EAOpC,OAAO,iBAAiB,0DAJN,mBAAmB,GAAG,EAAA,aACtB,mBAAmB,SAAS,EAAA,MACnC,mBAAmB,EAAE,EAAA,aAED;CACnC;;CAGA,OAAO,OAAO,MAAsB;EAChC,IAAI,OAAO;EACX,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;GACrC,MAAM,OAAO,KAAK,WAAW,CAAC;GAC9B,OAAO,QAAQ,QAAQ,KAAK;EAChC;EACA,QAAQ,OAAO,WAAA,CAAe,SAAS;CAC3C;AACJ;;AAGA,SAAS,iBAAiB,KAA8C;CACpE,OAAO,IAAI,SAAS,SAAS,WAAW;EACpC,MAAM,MAAMC,IACR,KACA,EACI,SAAS,EACL,cAAc,+DAClB,EACJ,IACC,QAAQ;GACL,MAAM,UAAU,sBAAsB,IAAI,QAAQ,aAAa;GAC/D,IAAI,OAAO;GACX,IAAI,GAAG,aAAa,QAAQ,OAAO,CAAC;EACxC,CACJ;EACA,IAAI,WAAW,uBAAuB;GAClC,IAAI,QAAQ;GACZ,uBAAO,IAAI,MAAM,eAAe,CAAC;EACrC,CAAC;EACD,IAAI,GAAG,UAAU,QAAQ,OAAO,GAAG,CAAC;CACxC,CAAC;AACL;;AAGA,SAAS,sBAAsB,KAAmD;CAC9E,MAAM,UAAkC,CAAC;CACzC,IAAI,QAAQ,KAAA,GACR,OAAO;CAEX,KAAK,MAAM,QAAQ,KAAK;EACpB,MAAM,CAAC,QAAQ,KAAK,MAAM,GAAG;EAC7B,IAAI,SAAS,KAAA,GAAW;GACpB,MAAM,KAAK,KAAK,QAAQ,GAAG;GAC3B,IAAI,KAAK,GAAG;IACR,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK;IACpC,MAAM,QAAQ,KAAK,MAAM,KAAK,CAAC;IAC/B,IAAI,SAAS,IACT,QAAQ,QAAQ;GAExB;EACJ;CACJ;CACA,OAAO;AACX;;;;;;;;;;;;AC/FA,MAAM,aAAa;;AAGnB,MAAM,qBAAqB;;AAG3B,MAAM,oBAAoB;;AAsC1B,MAAa,eAAe;CACxB,KAAK;CACL,WAAW;CACX,WAAW;CACX,QAAQ;CACR,SAAS;AACb;;AAIA,IAAa,SAAb,MAAoB;CAChB;CAEA,YAAY,MAA2E;EACnF,KAAK,aAAa,KAAK;CAC3B;;CAGA,MAAc,iBACV,WACA,QACA,KACA,MAC2D;EAS3D,MAAM,MAAM,MAAM,aAPd,yDACA,IAAI,gBAAgB;GAChB;GACA,YAAY,OAAO,OAAO,kBAAkB;GAC5C,YAAY,OAAO,kBAAkB;GACrC,YAAY;EAChB,CAAC,CAAC,CAAC,SAAS,GACoB,MAAM;EAC1C,IAAI,QAAQ,QAAQ,IAAI,YAAY,GAChC,OAAO;EAEX,MAAM,OAAO,IAAI,MAAM;EACvB,OAAO;GACH,OAAO,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ,SAAS,SAAS,IAAI,IAAI,CAAC;GACrE,OAAO,IAAI,MAAM,WAAW;EAChC;CACJ;;CAGA,MAAM,kBAAkB,WAA8C;EAClE,MAAM,SAAS,MAAM,KAAK,WAAW,UAAU;EAC/C,MAAM,MAAM,UAAU,OAAO,OAAO,WAAW,EAAE;EACjD,MAAM,MAAwB,CAAC;EAC/B,KAAK,IAAI,OAAO,GAAG,OAAO,mBAAmB,QAAQ,GAAG;GACpD,MAAM,SAAS,MAAM,KAAK,iBAAiB,WAAW,QAAQ,KAAK,IAAI;GACvE,IAAI,WAAW,MACX;GAEJ,IAAI,KAAK,GAAG,OAAO,KAAK;GACxB,IAAI,OAAO,OACP;EAER;EACA,OAAO;CACX;;CAGA,MAAM,kBAAkB,WAAmB,MAAgD;EACvF,MAAM,SAAS,MAAM,KAAK,WAAW,UAAU;EAC/C,MAAM,SAA4B;GAC9B,UAAU,OAAO,SAAS;GAC1B,mBAAmB;GACnB,gBAAgB,CAAC;GACjB,gBAAgB,CAAC;GACjB,aAAa,CAAC;GACd,cAAc,CAAC;GACf,oBAAoB,CAAC;EACzB;EACA,MAAM,KAAK,eAAe,QAAQ,WAAW,QAAQ,IAAI;EACzD,OAAO;CACX;;CAGA,MAAc,eACV,QACA,WACA,QACA,MACa;EACb,KAAK,MAAM,UAAU,aAAa,IAAI,GAAG;GACrC,MAAM,OAAO,MAAM,KAAK,eAAe,WAAW,QAAQ,OAAO,IAAI;GACrE,OAAO,OAAO,WAAW;GACzB,IAAI,OAAO,SAAS,aAAa,WAAW;IACxC,MAAM,CAAC,SAAS;IAChB,IAAI,UAAU,KAAA,GACV,OAAO,oBAAoB;GAEnC;EACJ;CACJ;;CAGA,MAAc,eACV,WACA,QACA,MACwB;EAIxB,MAAM,OAAO,MAAM,aAFf,8CACA,IAAI,gBAAgB;GAAE,IAAI;GAAW,MAAM,OAAO,IAAI;EAAE,CAAC,CAAC,CAAC,SAAS,GACnC,MAAM;EAC3C,IAAI,SAAS,MACT,OAAO,CAAC;EAEZ,OAAO,eAAe,MAAM,IAAI;CACpC;AACJ;;AAGA,MAAM,mBAAmB;;AAGzB,SAAgB,eAAe,MAAc,MAA+B;CACxE,MAAM,QAAQ,iBAAiB,KAAK,IAAI;CACxC,IAAI,UAAU,QAAQ,MAAM,OAAO,KAAA,GAC/B,OAAO,CAAC;CAEZ,IAAI;CACJ,IAAI;EACA,QAAQ,KAAK,MAAM,MAAM,EAAE;CAC/B,QAAQ;EACJ,OAAO,CAAC;CACZ;CACA,MAAM,MAAM,SAAS,aAAa,YAAY,MAAM,gBAAgB,MAAM;CAC1E,IAAI,CAAC,MAAM,QAAQ,GAAG,GAClB,OAAO,CAAC;CAEZ,MAAM,MAAuB,CAAC;CAC9B,KAAK,MAAM,QAAQ,KACf,IAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;EAC3C,MAAM,MAAM;EAMZ,IAAI,KAAK;GACL,KAAK,OAAO,IAAI,OAAO,EAAE;GACzB,MAAM,OAAO,IAAI,QAAQ,EAAE;GAC3B,QAAQ,OAAO,IAAI,UAAU,EAAE;GAC/B,MAAM,OAAO,IAAI,QAAQ,EAAE;EAC/B,CAAC;CACL;CAEJ,OAAO;AACX;;AAGA,eAAe,aACX,KACA,QAC6F;CAC7F,IAAI;EACA,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,QAAQ,eAAe,MAAM,EAAE,EAAE,CAAC;EAC5E,IAAI,CAAC,IAAI,IACL,OAAO;EAEX,MAAM,OAAQ,MAAM,IAAI,KAAK;EAI7B,OAAO;GACH,SAAS,KAAK,WAAW;GACzB,MAAM,KAAK;EACf;CACJ,SAAS,KAAK;EACV,MAAM,YAAY,oBAAoB,OAAO,GAAG,KAAK,SAAS;CAClE;AACJ;;AAGA,eAAe,aAAa,KAAa,QAAwD;CAC7F,IAAI;EACA,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,QAAQ,eAAe,MAAM,EAAE,EAAE,CAAC;EAC5E,IAAI,CAAC,IAAI,IACL,OAAO;EAEX,OAAO,MAAM,IAAI,KAAK;CAC1B,SAAS,KAAK;EACV,MAAM,YAAY,oBAAoB,OAAO,GAAG,KAAK,SAAS;CAClE;AACJ;;AAGA,SAAS,eAAe,QAAwC;CAC5D,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAC5C,MAAM,KAAK,GAAG,IAAI,GAAG,OAAO;CAEhC,OAAO,MAAM,KAAK,IAAI;AAC1B;;AAGA,SAAS,aAAa,MAGnB;CACC,MAAM,MAAM;EACR;GAAE,MAAM,aAAa;GAAW,SAAS;EAAiB;EAC1D;GAAE,MAAM,aAAa;GAAW,SAAS;EAAiB;EAC1D;GAAE,MAAM,aAAa;GAAQ,SAAS;EAAc;EACpD;GAAE,MAAM,aAAa;GAAS,SAAS;EAAe;CAC1D;CACA,IAAI,SAAS,aAAa,KACtB,OAAO,CAAC,GAAG,GAAG;CAElB,OAAO,IAAI,QAAQ,WAAW,OAAO,SAAS,IAAI;AACtD;;;;;;;AC5OA,IAAa,eAAb,MAA0B;CACtB;CACA;CACA,aAAoC;CAEpC,YAAY,SAAkC,SAA4B;EACtE,MAAM,UAAU,QAAQ,gBAAgB;EACxC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,yCAAyC,eAAe;EAE9E,KAAK,UAAU;EACf,KAAK,UAAU;CACnB;;CAGA,WAAiB;EACb,IAAI,KAAK,eAAe,MACpB;EAGJ,MAAM,WAA0B;GAC5B,mBAAmB,QAAQ,KAAK,QAAQ,KAAK,0BAA0B,GAAG;GAC1E,oBAAoB,QAAQ,KAAK,QAAQ,KAAK,2BAA2B,GAAG;GAC5E,uBAAuB,QAAQ,KAAK,QAAQ,KAAK,8BAA8B,GAAG;GAClF,iBAAiB,QAAQ,KAAK,QAAQ,KAAK,wBAAwB,GAAG;EAC1E;EACA,KAAK,aAAa,KAAK,QAAQ,uBAAuB,QAAQ;CAClE;;CAGA,aAAmB;EACf,IAAI,KAAK,eAAe,MACpB;EAEJ,KAAK,QAAQ,0BAA0B,KAAK,UAAU;EACtD,KAAK,aAAa;CACtB;AACJ;;;;;;;ACtCA,IAAa,cAAb,MAAyB;CACrB;CACA;CACA,aAAoC;CAEpC,YAAY,SAAkC,SAA4B;EACtE,MAAM,UAAU,QAAQ,gBAAgB;EACxC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,yCAAyC,eAAe;EAE9E,KAAK,UAAU;EACf,KAAK,UAAU;CACnB;;CAGA,WAAiB;EACb,IAAI,KAAK,eAAe,MACpB;EAGJ,MAAM,WAA0B;GAC5B,oBAAoB,cAChB,KAAK,QAAQ,KAAK,2BAA2B,SAAS;GAC1D,oBAAoB,YAAY,cAC5B,KAAK,QAAQ,KAAK,2BAA2B,YAAY,SAAS;GACtE,0BAA0B,eACtB,KAAK,QAAQ,KAAK,iCAAiC,UAAU;GACjE,qBAAqB,QAAQ,KAAK,QAAQ,KAAK,4BAA4B,GAAG;GAC9E,qBAAqB,WAAW,YAAY,YACxC,KAAK,QAAQ,KAAK,4BAA4B,WAAW,YAAY,OAAO;GAChF,yBAAyB,OAAO,aAC5B,KAAK,QAAQ,KAAK,gCAAgC,OAAO,QAAQ;GACrE,8BAA8B,OAAO,KAAK,aACtC,KAAK,QAAQ,KAAK,qCAAqC,OAAO,KAAK,QAAQ;GAC/E,4BAA4B,WAAW,YACnC,KAAK,QAAQ,KAAK,mCAAmC,WAAW,OAAO;GAE3E,2BAA2B,QACvB,KAAK,QAAQ,KAAK,kCAAkC,GAAG;EAC/D;EACA,KAAK,aAAa,KAAK,QAAQ,uBAAuB,QAAQ;CAClE;;CAGA,aAAmB;EACf,IAAI,KAAK,eAAe,MACpB;EAEJ,KAAK,QAAQ,0BAA0B,KAAK,UAAU;EACtD,KAAK,aAAa;CACtB;AACJ;;;;;;;ACpDA,IAAa,YAAb,MAAuB;CACnB;CACA;CACA,aAAoC;CAEpC,YAAY,SAAkC,SAA0B;EACpE,MAAM,UAAU,QAAQ,cAAc;EACtC,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,uCAAuC,eAAe;EAE5E,KAAK,UAAU;EACf,KAAK,UAAU;CACnB;;CAGA,WAAiB;EACb,IAAI,KAAK,eAAe,MACpB;EAIJ,MAAM,WAAwB;GAC1B,YAAY,SAAS,KAAK,QAAQ,KAAK,iBAAiB,IAAI;GAC5D,sBAAsB,YAAY,KAAK,QAAQ,KAAK,2BAA2B,OAAO;GACtF,mBAAmB,aAAa,KAAK,QAAQ,KAAK,wBAAwB,QAAQ;GAElF,sBAAsB,SAAS,KAAK,QAAQ,KAAK,2BAA2B,IAAI;GAEhF,uBAAuB,QAAQ,KAAK,QAAQ,KAAK,4BAA4B,GAAG;GAChF,sBAAsB,QAAQ,KAAK,QAAQ,KAAK,2BAA2B,GAAG;GAC9E,eAAe,QAAQ,KAAK,QAAQ,KAAK,oBAAoB,GAAG;EACpE;EACA,KAAK,aAAa,KAAK,QAAQ,qBAAqB,QAAQ;CAChE;;CAGA,aAAmB;EACf,IAAI,KAAK,eAAe,MACpB;EAEJ,KAAK,QAAQ,wBAAwB,KAAK,UAAU;EACpD,KAAK,aAAa;CACtB;AACJ;;;;;;;;;;;;;;;;;;;ACtDA,MAAM,0BAA0B;;;;;;;AA+ChC,IAAa,iBAAb,MAA0E;CACtE;CAEA,UAA2B,IAAI,aAAa;CAC5C,gCAAiC,IAAI,IAA4B;CACjE,8BAA+B,IAAI,IAAiD;CAEpF,YAAY,aAAmB;EAC3B,KAAK,cAAc;EAEnB,KAAK,QAAQ,gBAAgB,CAAC;CAClC;;;;;CAMA,GAAiC,OAAU,SAA+C;EACtF,KAAK,QAAQ,YAAY,OAAiB,OAAuC;EACjF,aAAa;GACT,KAAK,QAAQ,eAAe,OAAiB,OAAuC;EACxF;CACJ;;;;;CAMA,QACI,OACA,OAGI,CAAC,GACyB;EAC9B,MAAM,YAAY,KAAK,WAAW;EAClC,OAAO,IAAI,SAAS,SAAS,WAAW;GACpC,MAAM,WAAW,GAAG,SAAgC;IAChD,IAAI,KAAK,UAAU,CAAC,KAAK,OAAO,GAAG,IAAI,GACnC;IAEJ,QAAQ;IACR,QAAQ,IAAI;GAChB;GACA,MAAM,QAAQ,iBAAiB;IAC3B,QAAQ;IACR,OAAO,IAAI,YAAY,WAAW,SAAmB,SAAS,CAAC;GACnE,GAAG,SAAS;GACZ,MAAM,gBAAsB;IACxB,aAAa,KAAK;IAClB,KAAK,QAAQ,eACT,OACA,OACJ;GACJ;GACA,KAAK,QAAQ,YAAY,OAAiB,OAAuC;EACrF,CAAC;CACL;;;;;;;;;;;CAYA,MAAM,SAAkE;EACpE,KAAK,YAAY,IAAI,OAAO;EAC5B,aAAa;GACT,KAAK,YAAY,OAAO,OAAO;EACnC;CACJ;CAEA,KAAmC,OAAU,GAAG,MAAmC;EAE/E,KAAK,MAAM,cAAc,KAAK,aAC1B,IAAI;GACA,WAAW,OAAiB,GAAG,IAAI;EACvC,SAAS,KAAK;GACV,KAAK,YAAY,GAAG;EACxB;EAEJ,MAAM,YAAY,KAAK,QAAQ,UAAU,KAAe;EACxD,KAAK,MAAM,YAAY,WACnB,IAAI;GACA,SAAwC,GAAG,IAAI;EACnD,SAAS,KAAK;GAEV,KAAK,YAAY,GAAG;EACxB;CAER;;CAGA,YAAoB,KAAoB;EACpC,KAAK,MAAM,WAAW,KAAK,eACvB,QAAQ,GAAG;CAEnB;;CAGA,QAAQ,SAA6C;EACjD,KAAK,cAAc,IAAI,OAAO;EAC9B,aAAa;GACT,KAAK,cAAc,OAAO,OAAO;EACrC;CACJ;AACJ;;;;;;;AC1IA,SAAgB,sBAAsB,KAAsC;CACxE,IAAI,CAAC,MAAM,QAAQ,GAAG,GAClB,OAAO;CAEX,KAAK,MAAM,QAAQ,KACf,IAAI,SAAS,QAAQ,OAAO,SAAS,UACjC,OAAO;CAGf,OAAO;AACX;;AAGA,SAAS,eAAe,YAA+C;CACnE,MAAM,MAAwB,CAAC;CAC/B,KAAK,MAAM,OAAO,YACd,KAAK,MAAM,SAAS,IAAI,aAAa,CAAC,GAClC,IAAI,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAChD,IAAI,KAAK,KAAK;CAI1B,OAAO;AACX;;AAGA,IAAa,aAAb,MAAwB;CACpB;CACA;;CAGA,SAA0C,IAAI,eAAe,YAAY;;CAGzE,0BAA2B,IAAI,IAA4B;;CAG3D,eAAuB;CAEvB,eAA4C;CAE5C,YAAY,MAAyB;EACjC,KAAK,UAAU,KAAK;EACpB,KAAK,SAAS,KAAK;CACvB;;CAGA,WAAiB;EACb,IAAI,KAAK,iBAAiB,MACtB;EAEJ,MAAM,QAAQ,KAAK,QAAQ,GAAG,4BAA4B,QAAiB;GACvE,MAAM,aAAa,sBAAsB,GAAG;GAC5C,IAAI,eAAe,MAAM;IAErB,KAAK,QAAQ,KACT,EAAE,KAAK,KAAK,UAAU,GAAG,CAAC,EAAE,MAAM,GAAG,GAAI,EAAE,GAC3C,sCACJ;IACA;GACJ;GACA,KAAK,cAAc,eAAe,UAAU,CAAC;EACjD,CAAC;EACD,KAAK,eAAe;CACxB;;CAGA,aAAmB;EACf,KAAK,eAAe;EACpB,KAAK,eAAe;EACpB,KAAK,QAAQ,MAAM;EACnB,KAAK,eAAe;CACxB;;CAGA,cAAsB,SAAiC;EACnD,IAAI,CAAC,KAAK,cAAc;GACpB,KAAK,MAAM,SAAS,SAChB,KAAK,QAAQ,IAAI,MAAM,KAAK,KAAK;GAErC,KAAK,eAAe;GACpB;EACJ;EACA,MAAM,uBAAO,IAAI,IAA4B;EAC7C,KAAK,MAAM,SAAS,SAChB,KAAK,IAAI,MAAM,KAAK,KAAK;EAE7B,KAAK,MAAM,CAAC,KAAK,UAAU,MACvB,IAAI,CAAC,KAAK,QAAQ,IAAI,GAAG,GACrB,KAAK,OAAO,KAAK,2BAA2B,KAAK;EAGzD,KAAK,MAAM,CAAC,KAAK,UAAU,KAAK,SAC5B,IAAI,CAAC,KAAK,IAAI,GAAG,GACb,KAAK,OAAO,KAAK,6BAA6B,KAAK;EAG3D,KAAK,QAAQ,MAAM;EACnB,KAAK,MAAM,CAAC,KAAK,UAAU,MACvB,KAAK,QAAQ,IAAI,KAAK,KAAK;CAEnC;;CAGA,cAAgC;EAC5B,OAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;CACpC;;CAGA,SAAS,KAAyC;EAC9C,OAAO,KAAK,QAAQ,IAAI,GAAG;CAC/B;;CAGA,IAAI,gBAAyB;EACzB,OAAO,KAAK;CAChB;AACJ;;;;;;;ACtJA,SAAgB,QACZ,WACA,WACA,aAAa,KACG;CAChB,OAAO,IAAI,SAAS,YAAY;EAC5B,MAAM,UAAU,KAAK,IAAI;EACzB,MAAM,aAAmB;GACrB,IAAI,UAAU,GAAG;IACb,QAAQ,IAAI;IACZ;GACJ;GACA,IAAI,KAAK,IAAI,IAAI,UAAU,WAAW;IAClC,QAAQ,KAAK;IACb;GACJ;GACA,WAAW,MAAM,UAAU;EAC/B;EACA,KAAK;CACT,CAAC;AACL;;AAGA,SAAgB,MAAM,IAA2B;CAC7C,OAAO,IAAI,SAAS,YAAY;EAC5B,WAAW,SAAS,EAAE;CAC1B,CAAC;AACL;;;;;;;ACEA,IAAa,aAAb,MAAwB;CACpB;CACA;;CAGA,4BAA6B,IAAI,IAAmB;;CAEpD,+BAAgC,IAAI,IAA6B;;CAEjE,0BAA2B,IAAI,IAAsC;;CAErE,2BAA4B,IAAI,IAA8B;CAE9D,eAA4C;CAE5C,YAAY,MAAyB;EACjC,KAAK,UAAU,KAAK;EACpB,KAAK,WAAW,KAAK;CACzB;;CAGA,WAAiB;EACb,IAAI,KAAK,iBAAiB,MACtB;EAEJ,MAAM,SAA4B,CAAC;EACnC,OAAO,KACH,KAAK,QAAQ,GAAG,4BAA4B,cAAuB;GAC/D,IAAI,WACA,KAAK,UAAU,MAAM;EAE7B,CAAC,CACL;EACA,OAAO,KACH,KAAK,QAAQ,GACT,4BACC,YAAiC,cAAuB;GACrD,IAAI,eAAe,oBAAoB,KACnC,KAAK,UAAU,MAAM;GAEzB,KAAK,MAAM,SAAS,WAChB,KAAK,UAAU,IAAI,MAAM,WAAW,KAAK;EAEjD,CACJ,CACJ;EACA,OAAO,KACH,KAAK,QAAQ,GAAG,kCAAkC,eAAgC;GAC9E,KAAK,aAAa,IAAI,WAAW,WAAW,UAAU;EAC1D,CAAC,CACL;EACA,OAAO,KACH,KAAK,QAAQ,GAAG,6BAA6B,QAA+B;GACxE,KAAK,cAAc,IAAI,SAAS,IAAI,KAAK;EAC7C,CAAC,CACL;EACA,OAAO,KACH,KAAK,QAAQ,GACT,6BAEI,YACA,aACA,YACC;GACD,KAAK,cAAc,YAAY,OAAO;EAC1C,CACJ,CACJ;EACA,KAAK,qBAAqB;GACtB,KAAK,MAAM,SAAS,QAChB,MAAM;EAEd;CACJ;;CAGA,aAAmB;EACf,KAAK,eAAe;EACpB,KAAK,eAAe;EACpB,KAAK,UAAU,MAAM;EACrB,KAAK,aAAa,MAAM;EACxB,KAAK,QAAQ,MAAM;EACnB,KAAK,SAAS,MAAM;CACxB;;CAGA,cAAsB,WAAmB,OAAuC;EAC5E,IAAI,MAAM,KAAK,QAAQ,IAAI,SAAS;EACpC,IAAI,QAAQ,KAAA,GAAW;GACnB,sBAAM,IAAI,IAAI;GACd,KAAK,QAAQ,IAAI,WAAW,GAAG;EACnC;EACA,KAAK,MAAM,CAAC,KAAK,WAAW,OACxB,IAAI,IAAI,KAAK,MAAM;CAE3B;;CAGA,SAAS,WAA4B;EACjC,OAAO,KAAK,UAAU,IAAI,SAAS,KAAK,KAAK,aAAa,IAAI,SAAS;CAC3E;;;;;;CAOA,aAAsB;EAClB,OAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC;CACtC;;;;;;;;;CAUA,MAAM,sBAAwC;EAC1C,IAAI,KAAK,UAAU,OAAO,GACtB,OAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC;EAEtC,MAAM,KAAK,SAAS,aAAa,IAAI;EACrC,MAAM,cAAc,KAAK,UAAU,OAAO,GAAG,KAAO,GAAG;EACvD,OAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC;CACtC;;;;;CAMA,MAAM,SAAS,WAA+C;EAC1D,MAAM,SAAS,KAAK,UAAU,IAAI,SAAS;EAC3C,IAAI,WAAW,KAAA,GACX,OAAO;EAEX,MAAM,KAAK,cAAc;EACzB,OAAO,KAAK,UAAU,IAAI,SAAS;CACvC;;;;;CAMA,MAAM,eAAe,WAA6C;EAC9D,MAAM,SAAS,KAAK,aAAa,IAAI,SAAS;EAC9C,IAAI,WAAW,KAAA,GACX,OAAO;EAEX,MAAM,SAAS,MAAM,KAAK,OAAwB,UAAU,mBACxD,KAAK,SAAS,aAAa,SAAS,CACxC;EACA,KAAK,aAAa,IAAI,WAAW,MAAM;EACvC,OAAO;CACX;;;;;CAMA,MAAM,WAAW,WAA2C;EACxD,MAAM,MAAM,KAAK,QAAQ,IAAI,SAAS;EACtC,IAAI,QAAQ,KAAA,KAAa,IAAI,OAAO,GAChC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC;EAE3B,MAAM,UAAU,MAAM,KAAK,OAAsB,WAAW,mBACxD,KAAK,SAAS,mBAAmB,WAAW,IAAI,CACpD;EACA,MAAM,yBAAS,IAAI,IAAyB;EAC5C,KAAK,MAAM,UAAU,SACjB,OAAO,IAAI,OAAO,KAAK,MAAM;EAEjC,KAAK,QAAQ,IAAI,WAAW,MAAM;EAClC,OAAO;CACX;;;;;CAMA,MAAM,UAAU,WAAmB,KAA+C;EAC9E,MAAM,SAAS,MAAM,KAAK,gBAAgB,WAAW,GAAG;EACxD,IAAI,WAAW,KAAA,GACX,OAAO;EAKX,MAAM,CAAC,UAAU,MAHK,KAAK,OAAsB,UAAU,UAAU,GAAG,aACpE,KAAK,SAAS,mBAAmB,WAAW,CAAC,GAAG,CAAC,CACrD;EAEA,IAAI,WAAW,KAAA,GACX;EAEJ,MAAM,SAAS,KAAK,QAAQ,IAAI,SAAS;EACzC,IAAI,WAAW,KAAA,GACX,OAAO,IAAI,OAAO,KAAK,MAAM;EAEjC,OAAO;CACX;;CAGA,MAAc,gBACV,WACA,KACgC;EAChC,IAAI,MAAM,KAAK,QAAQ,IAAI,SAAS;EACpC,IAAI,QAAQ,KAAA,KAAa,IAAI,SAAS,GAAG;GACrC,MAAM,KAAK,WAAW,SAAS;GAC/B,MAAM,KAAK,QAAQ,IAAI,SAAS;EACpC;EACA,OAAO,KAAK,IAAI,GAAG;CACvB;;CAGA,MAAc,gBAA+B;EACzC,MAAM,OAAO,MAAM,KAAK,OAAgB,oBACpC,KAAK,SAAS,aAAa,KAAK,CACpC;EACA,KAAK,MAAM,SAAS,MAChB,KAAK,UAAU,IAAI,MAAM,WAAW,KAAK;CAEjD;;CAGA,MAAc,OAAU,KAAa,QAAsC;EACvE,MAAM,WAAW,KAAK,SAAS,IAAI,GAAG;EACtC,IAAI,aAAa,KAAA,GACb,OAAO,MAAO;EAElB,MAAM,OAAO,OAAO,CAAC,CAChB,OAAO,QAAiB;GACrB,MAAM;EACV,CAAC,CAAC,CACD,cAAc;GACX,KAAK,SAAS,OAAO,GAAG;EAC5B,CAAC;EACL,KAAK,SAAS,IAAI,KAAK,IAAI;EAC3B,OAAO,MAAM;CACjB;AACJ;;;;AC9OA,SAAgB,kBAAkB,MAAuC;CACrE,OAAO;EACH,QAAQ,KAAK;EACb,OAAO,KAAK;EACZ,SAAS;EACT,OAAO;CACX;AACJ;;;;AChBA,MAAM,aAAmB,KAAA;;AAGzB,IAAa,gBAAb,MAAyD;CACrD,QAAQ;CACR,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,oBAAoB;CACpB,sBAAsB;CACtB,kBAAkB;AACtB;;AAGA,IAAa,iBAAb,MAA2D;CACvD,oBAAoB;CACpB,gBAAgB;CAChB,eAAe;AACnB;;AAGA,IAAa,oBAAb,MAAiE;CAC7D,kBAAkB;CAClB,eAAe;CACf,uBAAuB;AAC3B;;;;;;AAaA,SAAgB,sBACZ,OAA+B,CAAC,GACN;CAC1B,MAAM,EAAE,YAAY;CACpB,MAAM,QAAQ,MAAc,GAAG,SAA0B;EACrD,UAAU,MAAM,GAAG,IAAI;CAC3B;CACA,OAAO;EACH,oBAAoB,cAAc,KAAK,qBAAqB,SAAS;EACrE,sBAAsB,cAAc,KAAK,uBAAuB,SAAS;EACzE,wBAAwB,cAAc,KAAK,yBAAyB,SAAS;EAC7E,sBAAsB,SAAS,KAAK,uBAAuB,IAAI;EAC/D,qBAAqB,WAAW,KAAK,sBAAsB,MAAM;EACjE,kBAAkB,WAAW,KAAK,mBAAmB,MAAM;CAC/D;AACJ;;AAGA,SAAgB,sBAA4C;CACxD,OAAO,CAAC;AACZ;;;;;;;;;;;;;;AC9CA,MAAM,uBAAuB;;AAG7B,MAAM,qBAAqB;;AAG3B,MAAM,wBAAwB;;AAG9B,MAAM,oBAAoB;;AAG1B,MAAM,2BAA2B;;AAGjC,MAAM,wBAAwB;;AAG9B,MAAM,uBAAuB;;;;;;;AAQ7B,SAAgB,yBACZ,KACA,OAA+B,CAAC,GAChB;CAChB,MAAM,MAAM,IAAI;CAChB,IAAI,QAAQ,QAAQ,OAAO,IAAI,iBAAiB,YAC5C,OAAO,QAAQ,QAAQ,KAAK;CAEhC,OAAO,cACG;EACF,IAAI;GACA,OAAO,IAAI,aAAa,MAAM;EAClC,QAAQ;GACJ,OAAO;EACX;CACJ,GACA,KAAK,aAAa,0BAClB,qBACJ;AACJ;;AAGA,eAAsB,kBAAkB,KAAkD;CACtF,MAAM,MAAM,IAAI;CAChB,IAAI,QAAQ,MACR,MAAM,YAAY,mCAAmC,eAAe;CAGxE,QAAO,MADY,IAAI,aAAa,EAAA,CACxB;AAChB;;;;;;;;;AAUA,eAAe,qBACX,KACA,MACa;CACb,MAAM,MAAM,IAAI;CAIhB,IAAI,QAAQ,QAAQ,OAAO,IAAI,YAAY,YACvC;CAEJ,MAAM,mBAAmB,KAAK,KAAK,SAAS;CAG5C,MAAM,MAAM,oBAAoB;AACpC;;AAGA,SAAS,mBACL,KAIA,WACa;CACb,OAAO,IAAI,SAAe,YAAY;EAClC,IAAI,UAAU;EACd,MAAM,eAAqB;GACvB,IAAI,CAAC,SAAS;IACV,UAAU;IACV,QAAQ;GACZ;EACJ;EACA,2BAA2B,KAAK,MAAM;EACtC,WAAW,QAAQ,aAAa,wBAAwB;EACxD,IAAI;GACA,IAAI,UAAU;EAClB,QAAQ;GACJ,OAAO;EACX;CACJ,CAAC;AACL;;AAGA,SAAS,2BACL,KAIA,QACI;CACJ,IAAI;EAGA,MAAM,WAAW,oBAAoB;EACrC,SAAS,yBAAyB;GAC9B,OAAO;EACX;EACA,IAAI,OAAO,IAAI,2BAA2B,YACtC,IAAI,uBAAuB,QAAQ;CAE3C,QAAQ,CAER;AACJ;;;;;;AAOA,SAAgB,gBACZ,OACA,KACgB;CAChB,IAAI,QAAQ,KAAA,GAAW;EACnB,MAAM,SAAS,MAAM,MAAM,MAAM,EAAE,QAAQ,GAAG;EAC9C,IAAI,WAAW,KAAA,GAEX,MAAM,YAAY,MAAM,IAAI,aADV,MAAM,KAAK,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,GACN,EAAU,IAAI,WAAW;EAEtE,OAAO;CACX;CAEA,MAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,YAAY;CAC9C,IAAI,UAAU,KAAA,GACV,OAAO;CAEX,MAAM,CAAC,SAAS;CAChB,IAAI,UAAU,KAAA,GACV,MAAM,YAAY,WAAW,WAAW;CAE5C,OAAO;AACX;;;;;AAMA,eAAe,sBACX,KACA,cACA,QACA,MACoB;CACpB,IAAI,aAAa;CACjB,KAAK,IAAI,UAAU,GAAG,WAAW,mBAAmB,WAAW,GAAG;EAC9D,MAAM,gBAAgB,MAAM,kBAAkB,cAAc,MAAM;EAClE,IAAI,cAAc,IACd,OAAO,cAAc;EAEzB,aAAa,cAAc;EAE3B,IAAI,EADgB,eAAe,UAAU,KAAK,UAAU,oBAExD;EAKJ,IAAI,CAAC,MAHe,yBAAyB,KAAK,EAC9C,WAAW,KAAK,aAAa,yBACjC,CAAC,GAEG;CAER;CACA,MAAM,YAAY,WAAW,cAAc,WAAW;AAC1D;;AAGA,eAAe,kBACX,cACA,QAC0E;CAE1E,MAAM,EAAE,YAAW,MADE,aAAa,kBAAkB,OAAO,GAAG,EAAA,CACpC;CAC1B,IAAI,QACA,OAAO;EAAE,IAAI;EAAO;CAAO;CAE/B,OAAO;EACH,IAAI;EACJ,QAAQ;GACJ,KAAK,OAAO;GACZ,KAAK,OAAO,OAAO;GACnB,MAAM,OAAO,YAAY;EAC7B;CACJ;AACJ;;AAGA,SAAgB,eAAe,QAAyB;CACpD,OAAO,OAAO,SAAS,kBAAkB,KAAK,OAAO,SAAS,qBAAqB;AACvF;;;;;AAMA,eAAsB,WAClB,KACA,MACoB;CACpB,MAAM,MAAM,IAAI;CAChB,IAAI,QAAQ,MACR,MAAM,YAAY,mCAAmC,eAAe;CAExE,MAAM,eAAe;CAErB,MAAM,qBAAqB,KAAK,EAC5B,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC,EACxE,CAAC;CAED,MAAM,SAAQ,MADK,aAAa,aAAa,EAAA,CAC1B;CACnB,IAAI,MAAM,WAAW,GACjB,MAAM,YAAY,WAAW,WAAW;CAG5C,OAAO,sBAAsB,KAAK,cADnB,gBAAgB,OAAO,KAAK,GACU,GAAG,IAAI;AAChE;;;;;;;;;;;;;;;;;;;;;;;;;ACvPA,MAAM,0BAA0B;;AAGhC,MAAM,2BAA2B;;AAGjC,MAAM,wBAAwB;;;;;;;;;AAU9B,eAAsB,iBAClB,KACA,MACa;CACb,MAAM,EAAE,YAAY;CACpB,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,eAAe,eAAe;CAcpD,IAAI,CAAC,MAZY,cACP;EACF,IAAI;GACA,MAAM,MAAM,QAAQ,cAAc;GAClC,OAAO,QAAQ,QAAQ,QAAQ,KAAA;EACnC,QAAQ;GACJ,OAAO;EACX;CACJ,GACA,KAAK,aAAa,0BAClB,qBACJ,GAEI,MAAM,YAAY,mCAAmC,SAAS;AAEtE;;;AAKA,eAAsB,oBAClB,KACA,QACA,UACA,MACa;CACb,MAAM,EAAE,YAAY;CACpB,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,MAAM,YAAY,eAAe,eAAe;CAKpD,MAAM,UAAU,IAAI,eAAe;CACnC,MAAM,aAAa,IAAI,kBAAkB;CAEzC,MAAM,eAAe,gBAAgB,QAAQ;CAC7C,QAAQ,KAAK,QAAQ,SAAS,YAAY,QAAQ;CAClD,uBAAuB,KAAK,OAAO;CAMnC,IAAI,CAAC,MAJY,QAAQ,KAAK,CAC1B,aAAa,WAAW,IAAI,GAC5B,cAAc,OAAO,KAAK,aAAa,uBAAuB,CAAC,CAAC,WAAW,KAAK,CACpF,CAAC,GAEG,MAAM,YAAY,mBAAmB,SAAS;AAEtD;;AAGA,SAAS,gBAAgB,UAAqD;CAC1E,OAAO,IAAI,SAAe,SAAS,WAAW;EAC1C,MAAM,kBAAkB,SAAS;EACjC,SAAS,uBAAuB,SAAS;GACrC,IAAI,KAAK,SACL,QAAQ;GAEZ,IAAI,OAAO,oBAAoB,YAC3B,gBAAgB,IAAI;EAE5B;EACA,MAAM,iBAAiB,SAAS;EAChC,SAAS,yBAAyB,MAAM;GACpC,IAAI,MAAM,KAAK,MAAM,KACjB,QAAQ;QAER,OAAO,YAAY,oBAAoB,OAAO,CAAC,KAAK,SAAS,CAAC;GAElE,IAAI,OAAO,mBAAmB,YAC1B,eAAe,CAAC;EAExB;CACJ,CAAC;AACL;;;;;;;;;AAUA,SAAS,uBACL,KACA,SACI;CACJ,IAAI,oBAAoB,GAAG,GACvB;CAEJ,IAAI;EACA,QAAQ,QAAQ,CAAC;CACrB,QAAQ;EACJ,IAAI;GACA,QAAQ,QAAQ;EACpB,QAAQ,CAER;CACJ;AACJ;;AAGA,SAAS,oBAAoB,KAA8B;CACvD,MAAM,EAAE,mBAAmB;CAC3B,IAAI,mBAAmB,QAAQ,OAAO,eAAe,UAAU,YAC3D,OAAO;CAEX,IAAI;EACA,eAAe,MAAM;CACzB,QAAQ,CAER;CACA,OAAO;AACX;;;;;;;;;;;;;;;ACvHA,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;;AAM5B,MAAa,2BAA2B;;AAGxC,SAAS,gBAAgB,SAAiB,SAA0B;CAChE,OAAO,YAAY,uBAAuB,YAAY;AAC1D;;AAGA,MAAM,qBAAqB;;AAG3B,IAAa,iBAAb,MAA4B;CACxB;CACA,QAA4B;CAC5B,aAAoC;CACpC,OAAgC;CAChC,iCAAkC,IAAI,IAA8B;CACpE,gCAAiC,IAAI,IAAiC;;CAEtE,eAA6D;;CAE7D,qBAA4C;;CAE5C;CAEA,YAAY,cAAuB,UAAkC,CAAC,GAAG;EACrE,MAAM,MAAM;EACZ,IAAI,QAAQ,QAAQ,OAAO,IAAI,2BAA2B,YACtD,MAAM,YAAY,6CAA6C,eAAe;EAElF,KAAK,eAAe;EACpB,KAAK,YAAY,QAAQ,aAAA;CAC7B;;CAGA,IAAI,eAA2B;EAC3B,OAAO,KAAK;CAChB;;CAGA,IAAI,WAA4B;EAC5B,OAAO,KAAK;CAChB;;CAGA,IAAI,gBAA+B;EAC/B,OAAO,KAAK;CAChB;;CAGA,SAAS,IAA0C;EAC/C,KAAK,eAAe,IAAI,EAAE;EAC1B,aAAa,KAAK,eAAe,OAAO,EAAE;CAC9C;;CAGA,cAAc,IAA6C;EACvD,KAAK,cAAc,IAAI,EAAE;EACzB,aAAa,KAAK,cAAc,OAAO,EAAE;CAC7C;;CAGA,MAAM,OAA8B,CAAC,GAAS;EAC1C,IAAI,KAAK,UAAU,QACf;EAEJ,KAAK,aAAa,KAAK,aAAa,uBAAuB,KAAK,cAAc,CAAC;EAC/E,KAAK,aAAa,QAAQ;EAC1B,IAAI,KAAK,aAAa,KAAA,KAAa,KAAK,aAAa,IACjD,KAAK,aACA,kBAAkB,KAAK,QAAQ,CAAC,CAChC,MAAM,WAAW;GAKd,IAAI,OAAO,eAAe,WAAW,IACjC,KAAK,QAAQ;EAErB,CAAC,CAAC,CACD,YAAY;GAET,KAAK,QAAQ;EACjB,CAAC;OAEL,KAAK,QAAQ;CAErB;;CAGA,UAAgB;EACZ,KAAK,qBAAqB;EAC1B,KAAK,SAAS,cAAc;EAC5B,KAAK,aAAa,iBAAiB;EACnC,KAAK,eAAe;CACxB;;CAGA,gBAA8C;EAC1C,MAAM,WAAW,oBAAoB;EACrC,SAAS,sBAAsB,EAAE,qBAAqB,gBAAgB;GAClE,MAAM,aAAa,oBAAoB,QAAQ,oBAAoB,EAAE;GACrE,KAAK,WAAW;IAAE,WAAW;IAAY;GAAU,CAAC;EACxD;EACA,SAAS,oCAAoC;GACzC,KAAK,SAAS,cAAc;EAChC;EACA,SAAS,kCAAkC;GACvC,KAAK,SAAS,SAAS;EAC3B;EACA,SAAS,wBAAwB,WAAW;GACxC,KAAK,kBAAkB;GACvB,KAAK,OAAO;IAAE,KAAK,OAAO;IAAK,KAAK,OAAO;IAAK,MAAM,OAAO,QAAQ;GAAG;GACxE,KAAK,SAAS,WAAW;EAC7B;EACA,SAAS,yBAAyB,SAAS,YAAY;GACnD,IAAI,gBAAgB,SAAS,OAAO,GAAG;IAEnC,KAAK,QAAQ;IACb;GACJ;GACA,KAAK,kBAAkB;GACvB,KAAK,qBAAqB;GAC1B,KAAK,SAAS,QAAQ;EAC1B;EACA,SAAS,sBAAsB;GAC3B,KAAK,kBAAkB;GACvB,KAAK,qBAAqB;GAC1B,KAAK,SAAS,QAAQ;EAC1B;EACA,OAAO;CACX;;CAGA,OAAa;EACT,KAAK,kBAAkB;EACvB,IAAI,KAAK,eAAe,MAAM;GAC1B,KAAK,aAAa,0BAA0B,KAAK,UAAU;GAC3D,KAAK,aAAa;EACtB;CACJ;;CAGA,iBAA+B;EAC3B,KAAK,kBAAkB;EACvB,KAAK,eAAe,iBAAiB;GACjC,KAAK,eAAe;GACpB,KAAK,qBAAqB;GAC1B,KAAK,SAAS,QAAQ;EAC1B,GAAG,KAAK,SAAS;CACrB;;CAGA,oBAAkC;EAC9B,IAAI,KAAK,iBAAiB,MAAM;GAC5B,aAAa,KAAK,YAAY;GAC9B,KAAK,eAAe;EACxB;CACJ;CAEA,SAAiB,MAAwB;EACrC,KAAK,QAAQ;EACb,KAAK,MAAM,MAAM,KAAK,eAClB,GAAG,IAAI;CAEf;CAEA,WAAmB,IAAsB;EACrC,KAAK,MAAM,MAAM,KAAK,gBAClB,GAAG,EAAE;CAEb;AACJ;;;;;;;;;;;;;;;;;;;;ACjNA,SAAgB,oBAAoB,MAAsB;CACtD,MAAM,OAAO,KAAK,QAAQ,OAAO,GAAG;CACpC,IAAI,KAAK,SAAS,eAAe,GAC7B,OAAO;CAEX,IAAI,KAAK,SAAS,QAAQ,GACtB,OAAO,KAAK,MAAM,QAAQ;CAE9B,OAAO,KAAK,MAAM,SAAS,QAAQ;AACvC;;AAGA,SAAgB,gBAAgB,KAA4B;CACxD,IAAI,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,GAAG,GACzC,IAAI;EACA,OAAO,gBAAgB,KAAK,MAAM,GAAG,CAAC;CAC1C,QAAQ;EACJ,OAAO;CACX;CAEJ,OAAO;AACX;;AAGA,SAAS,gBAAgB,OAA+B;CACpD,IAAI,OAAO,UAAU,UACjB,OAAO,aAAa,KAAK,IAAI,QAAQ;CAEzC,IAAI,MAAM,QAAQ,KAAK,GACnB,OAAO,YAAY,KAAK;CAE5B,IAAI,OAAO,UAAU,YAAY,UAAU,MACvC,OAAO,aAAa,KAAK;CAE7B,OAAO;AACX;;AAGA,SAAS,aAAa,OAAwB;CAC1C,OAAO,MAAM,SAAS,eAAe,KAAK,MAAM,SAAS,OAAO;AACpE;;AAGA,SAAS,YAAY,OAAiC;CAClD,KAAK,MAAM,QAAQ,OAAO;EACtB,MAAM,QAAQ,gBAAgB,IAAI;EAClC,IAAI,UAAU,MACV,OAAO;CAEf;CACA,OAAO;AACX;;AAGA,SAAS,aAAa,OAA8B;CAChD,KAAK,MAAM,KAAK,OAAO,OAAO,KAAK,GAAG;EAClC,MAAM,QAAQ,gBAAgB,CAAC;EAC/B,IAAI,UAAU,MACV,OAAO;CAEf;CACA,OAAO;AACX;;;;;;;;;;;;;;;;;;ACxDA,MAAM,mBAAmB;;;;;;AAOzB,MAAM,eAAe;CACjB,SAAS;CACT,SAAS;CACT,WAAW;CACX,SAAS;AACb;;;;;AAMA,MAAM,qBAAqB;;AAG3B,MAAM,kBAAkB;;AAGxB,MAAM,uBAA+C;CACjD,OAAO;CACP,QAAQ;CACR,OAAO;CACP,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;CACP,KAAK;AACT;;;;;;AAOA,SAAS,aAAsE;CAC3E,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,IAAI;EACA,UAAU,qBAAqB,SAAS,MAAM,SAAS;EAEvD,UAAU,GAAG,QAAQ,GAAG,QAAQ;EAEhC,YAAYC,QAAgB,KAAKC,KAAO;CAC5C,QAAQ,CAER;CACA,OAAO;EAAE;EAAS;EAAW;CAAQ;AACzC;;AAGA,MAAM,iBAAiB;;;;;;AAOvB,SAAgB,oBAAoB,WAAkC;CAClE,IAAI,CAAC,WAAW,SAAS,GACrB,OAAO;CAEX,IAAI;CACJ,IAAI;EACA,MAAM,aAAa,SAAS;CAChC,QAAQ;EACJ,OAAO;CACX;CACA,OAAO,iBAAiB,GAAG;AAC/B;;AAGA,SAAS,iBAAiB,KAA4B;CAClD,MAAM,SAAS,OAAO,KAAK,YAAY,OAAO;CAC9C,IAAI,MAAM;CACV,OAAO,MAAM,IAAI,QAAQ;EACrB,MAAM,MAAM,IAAI,QAAQ,QAAQ,GAAG;EACnC,IAAI,QAAQ,IACR,OAAO;EAEX,MAAM,MAAM,gBAAgB,KAAK,MAAM,OAAO,MAAM;EACpD,IAAI,eAAe,KAAK,GAAG,GACvB,OAAO;EAEX,MAAM,MAAM,OAAO,SAAS,IAAI,SAAS;CAC7C;CACA,OAAO;AACX;;AAGA,SAAS,gBAAgB,KAAa,OAAuB;CACzD,IAAI,MAAM;CACV,OAAO,MAAM,IAAI,UAAU,IAAI,SAAS,GACpC,OAAO;CAEX,OAAO,IAAI,SAAS,OAAO,GAAG,CAAC,CAAC,SAAS,OAAO;AACpD;;;;;;;AAQA,SAAgB,gBACZ,aACA,WAC8B;CAC9B,MAAM,QAAQ,cAAc,KAAA,IAAY,oBAAoB,SAAS,IAAI;CACzE,IAAI,UAAU,MACV,MAAM,YACF,+EACA,eACJ;CAEJ,OAAO;EACH;EACA,KAAK,aAAa,YAAY,GAAG,YAAY,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG;CACrE;AACJ;;AAGA,SAAgB,kBACZ,aACA,gBACA,WACsB;CACtB,MAAM,EAAE,cAAc,WAAW;CACjC,MAAM,EAAE,QAAQ,gBAAgB,aAAa,SAAS;CACtD,OAAO;EACH,kBAAkB;EAClB,eAAeC,aAAkB;EACjC,UAAU;EACV,aAAa;EACb,YAAY;EACZ,UAAU;EACV;EACA,oBAAoB,EAChB,mBAAmB,eACvB;EACA,cAAc;CAClB;AACJ;;AAGA,SAAgB,iBACZ,OACA,aACA,YACuB;CACvB,MAAM,EAAE,YAAY,WAAW;CAC/B,OAAO;EACH,WAAW;EACX;EACA;EACA;EACA,WAAW;EACX,UAAU,SAAS;EACnB,iBAAiB;CACrB;AACJ;;AAeA,SAAgB,mBAAmB,SAAyD;CACxF,MAAM,EAAE,OAAO,aAAa,SAAS,SAAS,aAAa,cAAc,gBACrE;CACJ,MAAM,EAAE,SAAS,WAAW,YAAY,WAAW;CACnD,MAAM,aAAyB;EAE3B,MAAM,eAAe;EACrB,UAAU;EACV,SAAS;EACT,SAAS,SAAS;EAClB;EACA,YAAY;EACZ,OAAO;EACP,cAAc;EACd,SAAS;EACT,YAAY,WAAW;CAC3B;CACA,OAAO;EACH;EACA;EACA,mBAAmB,EACf,cAAc,YAClB;EACA,WAAW;EACX,IAAI;EACJ,IAAI;EACJ,OAAO;EACP,WAAW;EACX,UAAUA,aAAkB;EAC5B;EACA;EACA,iBAAiB;GACb,QAAQ;GACR,UAAU;GACV,OAAO;GACP,kBAAkB;GAClB,UAAUA,aAAkB;GAC5B,UAAU;GACV,YAAY;GACZ,QAAQ;GACR,YAAY;GACZ,WAAW;GACX,UAAU;GACV,WAAW;GACX,mBAAmB,CAAC,EAAE;EAC1B;EACA,yBAAyB;EACzB;EACA,cAAc;CAClB;AACJ;;;;;;;AAQA,SAAgB,gBAAgB,cAA+B;CAC3D,IAAI;EACA,MAAM,KAAM,eAA8D;EAC1E,IAAI,OAAO,OAAO,YACd,OAAO;EAEX,MAAM,QAAiB,GAAG,KAAK,YAAY;EAC3C,OAAO,OAAO,UAAU,WAAW,QAAQ;CAC/C,QAAQ;EACJ,OAAO;CACX;AACJ;;;;;;;;;;;;;;AC7OA,SAAS,oBAAoB,KAAc,YAA6C;CACpF,OAAO;EACH,kBAAkB,IAAI,WAAW;EACjC,eAAe,aAAa;EAC5B,UAAU;EACV,aAAa,IAAI,aAAa;EAC9B,YAAY,QAAQ;EACpB,UAAU;EACV,KAAK;EACL,oBAAoB,EAChB,mBAAmB,cAAc,IAAI,WAAW,GACpD;EACA,cAAc;GAAE,SAAS;GAAK,SAAS;GAAI,WAAW;GAAG,SAAS;EAAE;CACxE;AACJ;;;;;;AAOA,SAAgB,sBAA0C;CACtD,OAAQ,QAAyC;AACrD;;;;;;;;;;;AAYA,SAAgB,sBAAsB,SAAwC;CAC1E,OAAO,oBAAoB,OAAO,KAAK,KAAK,QAAQ,GAAG,aAAa,eAAe;AACvF;;AAGA,SAAS,oBAAoB,SAAwC;CACjE,IAAI;EAIA,MAAM,MAHO,QAAQ,qBAGH,MAAM,CAAC,CAAC,0BAA0B;EACpD,IAAI,OAAO,QAAQ,YAAY,IAAI,KAAK,MAAM,IAC1C,OAAO,gBAAgB,IAAI,KAAK,CAAC;CAEzC,QAAQ,CAER;CACA,OAAO;AACX;;;;;AAgCA,SAAgB,cACZ,SACA,aACc;CACd,MAAM,cAAc,QAAQ;CAG5B,IAAI,gBAAgB,QAAQ,OAAO,YAAY,QAAQ,YACnD,MAAM,YACF,sDACA,eACJ;CAEJ,MAAM,SAAS,YAAY,IAAI;CAC/B,IAAI,CAAC,UAAU,OAAO,OAAO,0BAA0B,YACnD,MAAM,YAAY,uCAAuC,eAAe;CAE5E,OAAO;EACH;EACA;EACA;EACA,SAAS;EACT,gBAAgB;EAChB,cAAc;CAClB;AACJ;;AAGA,SAAgB,WAAW,KAAqB,QAAsC;CAClF,IAAI,OAAO,sBAAsB,QAAQ,IAAI,cAAc,CAAC;AAChE;;;;;;;;;;;;AAaA,SAAgB,cAAc,KAA8C;CACxE,MAAM,IAAI,IAAI,QAAQ;CACtB,MAAM,UACF,qBAAqB,GAAG,KAAK,oBAAoB,CAAC,KAAK,iBAAiB,CAAC,KAAK,IAAI,EAAE;CACxF,IAAI,UAAU;CACd,OAAO;AACX;;AAGA,SAAS,qBAAqB,KAAqD;CAC/E,MAAM,MAAM,IAAI,QAAQ;CAKxB,IAAI,QAAQ,KAAA,KAAa,OAAO,IAAI,WAAW,YAC3C,OAAO;CAEX,IAAI;EACA,IAAI,iBAAiB,IAAI,OAAO;CACpC,QAAQ,CAER;CACA,OAAO;AACX;;AAGA,SAAS,oBAAoB,GAAgE;CACzF,IAAI,OAAO,EAAE,wBAAwB,YACjC,OAAO;CAEX,IAAI;EACA,OAAO,EAAE,oBAAoB,MAAM;CACvC,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAS,iBAAiB,GAAgE;CACtF,IAAI,OAAO,EAAE,WAAW,YACpB,OAAO;CAEX,IAAI;EACA,OAAO,EAAE,OAAO;CACpB,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAgB,YACZ,KACA,QACA,UACI;CAEJ,CADgB,IAAI,WAAW,cAAc,GAAG,EAAA,CACxC,KAAK,QAAQ,IAAI,eAAe,GAAG,IAAI,kBAAkB,GAAG,QAAQ;AAChF;;;;;;;;;AAUA,SAAgB,aAAa,KAA2B;CACpD,MAAM,EAAE,SAAS,mBAAmB;CACpC,IAAI,YAAY,MACZ,MAAM,YAAY,oBAAoB,eAAe;CAEzD,IAAI,mBAAmB,QAAQ,OAAO,eAAe,UAAU,YAAY;EACvE,eAAe,MAAM;EACrB;CACJ;CACA,QAAQ,QAAQ,CAAC;AACrB;;;;;;;;;;;;AAaA,SAAgB,eAAe,SAAgD;CAC3E,MAAM,EAAE,KAAK,cAAc,eAAe,gBAAgB,WAAW,mBAAmB;CAKxF,MAAM,MAAM,cAAc,gBAAgB;EAHtC,aAAa,KAAK,aAAa;EAC/B,cAAc,KAAK,aAAa;CAEgB,CAAC;CACrD,MAAM,qBAAqB,eAAe,KAAK,SAAS;CAMxD,MAAM,aAAa,oBAAoB,MAAM,KAAA;CAC7C,eAAe,KAAK,oBAAoB,UAAU;CAClD,WAAW,KAAK,oBAAoB,KAAK,KAAK,YAAY,CAAC;CAC3D,oBAAoB,KAAK,cAAc;CAGvC,cAAc,KAAK,oBAAoB,YAAY,aAAa;CAChE,OAAO;AACX;;AAGA,SAAS,eAAe,KAAqB,aAAsB,YAA2B;CAC1F,IAAI,CAAC,eAAe,YAChB,cAAc,GAAG;AAEzB;;AAGA,SAAS,cACL,KACA,aACA,YACA,eACI;CACJ,IAAI,CAAC,eAAe,CAAC,YACjB,cAAc,GAAG;CAErB,IAAI,kBAAkB,KAAA,GAAW;EAC7B,YAAY,KAAK,eAAe,sBAAsB,CAAC;EACvD,aAAa,GAAG;CACpB;AACJ;;AAGA,SAAS,oBACL,KACA,KACA,cACsB;CACtB,IAAI,iBAAiB,KAAA,GACjB,OAAO;CAEX,OAAO,oBAAoB,OAAO,CAAC,GAAG,wBAAwB,GAAG,CAAC;AACtE;;AAGA,SAAS,eACL,KACA,WACO;CACP,IAAI,cAAc,KAAA,KAAa,cAAc,MACzC,OAAO;CAEX,IAAI,UAAU;CACd,OAAO;AACX;;;;;;;;AASA,SAAS,wBAAwB,KAAyC;CACtE,IAAI,oBAAoB,MAAM,WAC1B;CAEJ,MAAM,OAAO,sBAAsB,IAAI,OAAO;CAC9C,IAAI,SAAS,MACT;CAEJ,OAAO,oBAAoB,IAAI;AACnC;;;;;;AAOA,SAAS,oBAAoB,KAAqB,gBAA+B;CAC7E,IAAI,mBAAmB,KAAA,KAAa,mBAAmB,MAAM;EACzD,IAAI,eAAe;EACnB;CACJ;CACA,IAAI,eAAe,wBAAwB,IAAI,OAAO;AAC1D;;AAGA,SAAS,wBAAwB,SAAkC;CAC/D,IAAI;EACA,MAAM,eAAe,QAAQ;EAG7B,IAAI,iBAAiB,QAAQ,OAAO,aAAa,QAAQ,YACrD,OAAO,aAAa,IAAI;EAE5B,OAAO,IAAI,QAAQ,wBAAwB;CAC/C,QAAQ;EACJ,OAAO;CACX;AACJ;;;;;;;;;;;;;;;;;;;;;;AC1RA,IAAa,gBAAb,MAAa,cAAc;;CAEvB;;CAGA,YAA2C;;CAG3C,YAAoB,KAAkB;EAClC,KAAK,MAAM;CACf;;;;;CAMA,OAAO,OAAO,OAA6B,CAAC,GAAkB;EAC1D,MAAM,QAAQ,IAAI,YAAY,KAAK,KAAK;EACxC,MAAM,OAAO;EACb,MAAM,SAAS,aAAa;GACxB,OAAO,KAAK,YAAY;GACxB,SAAS,KAAK,cAAc;GAC5B,MAAM,KAAK,WAAW,MAAM,KAAK,QAAQ,eAAe;GAGxD,MAAM,EAAE,MAAM,SAAS;EAC3B,CAAC;EACD,OAAO,IAAI,cAAc,kBAAkB;GAAE;GAAQ;EAAM,CAAC,CAAC;CACjE;;;;;;CAOA,cACI,gBACA,KACA,UACc;EACd,IAAI;EACJ,IAAI,QAAQ,KAAA,GACR,UAAU,eAAe;GAAE;GAAgB,GAAG;EAAS,CAAC;OAExD,UAAU,eAAe;GAAE;GAAgB;GAAK,GAAG;EAAS,CAAC;EAEjE,KAAK,IAAI,UAAU;EACnB,KAAK,IAAI,OAAO,KAAK,EAAE,SAAS,KAAK,UAAU,GAAG,gCAAgC;EAClF,OAAO;CACX;;;;;;;;;;CAWA,MAAM,MAAM,MAA8C;EACtD,MAAM,EAAE,YAAY,KAAK;EACzB,IAAI,YAAY,MACZ,MAAM,YAAY,oBAAoB,eAAe;EAIzD,KAAK,gBAAgB,SAAS,KAAK,KAAK;EAGxC,IAAI,KAAK,WAAW,MAAM;GACtB,MAAM,cAAc,MAAM,KAAK,UAAU,IAAI;GAC7C,KAAK,IAAI,QAAQ;GACjB,OAAO;EACX;EAGA,IAAI;EACJ,IAAI;GACA,MAAM,YAA8B,CAAC;GACrC,IAAI,KAAK,aAAa,KAAA,GAClB,UAAU,MAAM,KAAK;GAEzB,cAAc,MAAM,WAAW,SAAS,SAAS;GACjD,KAAK,IAAI,OAAO,KAAK;IAAE,KAAK,YAAY;IAAK,KAAK,YAAY;GAAI,GAAG,QAAQ;EACjF,SAAS,KAAK;GACV,IAAI,KAAK,eAAe,MACpB,MAAM;GAEV,cAAc,MAAM,KAAK,UAAU,IAAI;EAC3C;EAKA,KAAK,IAAI,QAAQ;EACjB,OAAO;CACX;;;;;;;;;CAUA,gBAAwB,SAAyB,OAAqB;EAClE,MAAM,eAAe,QAAQ;EAG7B,IAAI,iBAAiB,QAAQ,OAAO,aAAa,eAAe,YAAY;GACxE,KAAK,IAAI,OAAO,KAAK,gCAAgC;GACrD;EACJ;EACA,MAAM,aAAa,kBAAkB,KAAK,KAAK,OAAO;EACtD,MAAM,WAAW,iBAAiB,OAAO,QAAQ,YAAY,aAAa,UAAU;EACpF,aAAa,WAAW,QAAQ;EAChC,KAAK,IAAI,OAAO,KAAK,EAAE,WAAW,GAAG,4BAA4B;CACrE;;;;;CAMA,WAAW,SAAwC;EAC/C,IAAI,KAAK,IAAI,YAAY,MACrB,MAAM,YAAY,4BAA4B,eAAe;EAEjE,KAAK,IAAI,QAAQ,UAAU;CAC/B;;;;;CAMA,YAAqB;EACjB,IAAI,KAAK,cAAc,MACnB,OAAO;EAEX,KAAK,UAAU,QAAQ;EACvB,OAAO;CACX;;CAGA,MAAc,UAAU,MAA8C;EAClE,MAAM,EAAE,YAAY,KAAK;EACzB,IAAI,YAAY,QAAQ,QAAQ,iBAAiB,MAC7C,MAAM,YAAY,qCAAqC,eAAe;EAE1E,MAAM,UAAU,IAAI,eAAe,QAAQ,YAAY;EAEvD,KAAK,YAAY;EACjB,MAAM,SAAS,KAAK,cAAc,KAAK,IAAI,MAAM,KAAK,SAAS,YAAY;EAC3E,KAAK,oBAAoB,SAAS,MAAM,MAAM;EAE9C,IAAI;GAKA,QAAQ,MAAM;GAEd,MAAM,eAAe,OAAO;GAC5B,MAAM,OAAO,QAAQ;GACrB,IAAI,SAAS,MACT,MAAM,YAAY,wBAAwB,eAAe;GAE7D,OAAO;IAAE,KAAK,KAAK;IAAK,KAAK,KAAK;IAAK,MAAM,KAAK;GAAK;EAC3D,UAAU;GACN,QAAQ,KAAK;GACb,KAAK,YAAY;EACrB;CACJ;;CAGA,oBACI,SACA,MACA,QACI;EACJ,QAAQ,UAAU,OAAO;GACrB,KAAK,IAAI,OAAO,KAAK,gBAAgB,OAAO,UAAU,GAAG,UAAU,EAAE;GACrE,IAAI,GAAG,cAAc,IACjB,eAAe,QAAQ,GAAG,SAAS;GAEvC,KAAK,kBAAkB;IAAE,OAAO;IAAgB;GAAG,CAAC;EACxD,CAAC;EACD,QAAQ,eAAe,UAAU;GAC7B,KAAK,IAAI,OAAO,KAAK,EAAE,MAAM,GAAG,SAAS;GACzC,MAAM,UAAU,UAAU,WAAY,QAAQ,iBAAiB,KAAA,IAAa,KAAA;GAC5E,KAAK,kBAAkB;IAAE;IAAO,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;GAAG,CAAC;EACnF,CAAC;CACL;;CAGA,OAAa;EACT,KAAK,IAAI,OAAO,KAAK,kBAAkB;CAC3C;AACJ;;AAGA,SAAS,eAAe,UAAkB,WAAyB;CAC/D,IAAI;EACA,UAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EAChD,cAAc,UAAU,OAAO,KAAK,WAAW,QAAQ,CAAC;CAC5D,QAAQ,CAER;AACJ;;AAGA,SAAS,eAAe,SAAwC;CAC5D,OAAO,IAAI,SAAe,SAAS,WAAW;EAC1C,MAAM,WAAW,QAAQ,eAAe,UAAU;GAC9C,IAAI,UAAU,aAAa;IACvB,SAAS;IACT,QAAQ;GACZ,OAAO,IAAI,UAAU,UAAU;IAC3B,SAAS;IACT,OAAO,YAAY,WAAW,WAAW,CAAC;GAC9C;EACJ,CAAC;CACL,CAAC;AACL;;;;;;AAOA,SAAS,kBAAkB,KAAkB,SAAiC;CAC1E,IAAI,oBAAoB,MAAM,WAAW;EACrC,MAAM,OAAO,sBAAsB,QAAQ,OAAO;EAClD,IAAI,SAAS,MACT,OAAO,oBAAoB,IAAI;CAEvC;CACA,OAAO,IAAI,MAAM;AACrB;;;;AC7TA,SAAS,kBAAkB,KAA2C;CAClE,OAAO,cAAc,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC,KAAK,cAAc,WAAW,IAAI;AAC1F;;AAGA,SAAS,cAAc,KAA4B,MAA6C;CAC5F,KAAK,MAAM,CAAC,GAAG,MAAM,KAAK;EACtB,IAAI,OAAO,MAAM,YAAY,KAAK,CAAC,GAC/B,OAAO;EAEX,IAAI,OAAO,MAAM,YAAY,KAAK,CAAC,GAC/B,OAAO;CAEf;CACA,OAAO;AACX;;AAGA,SAAS,mBACL,SACA,qBAC8B;CAC9B,QAAQ,QAAQ;CAChB,MAAM,MAAM,OAAO,QAAQ,qBAAqB,aAAa,QAAQ,iBAAiB,IAAI;CAC1F,IAAI,EAAE,eAAe,MACjB,OAAO;CAEX,MAAM,SAAS,kBAAkB,GAAG;CACpC,IAAI,WAAW,MACX,OAAO;CAEX,MAAM,QAAQ,oBAAoB,MAAM;CACxC,OAAO,UAAU,QAAQ,UAAU,KAAA,KAAa,OAAO,MAAM,kBAAkB,aACzE,QACA;AACV;;;;;AAMA,SAAgB,mBAAmB,KAAqD;CACpF,MAAM,MAAM,cAAc,GAAG;CAC7B,IAAI,QAAQ,MAAM;EACd,IAAI,UAAU;EACd,OAAO;CACX;CACA,OAAO;AACX;;AAGA,SAAS,cAAc,KAAqD;CACxE,IAAI;EACA,MAAM,IAAI,IAAI,QAAQ;EAGtB,IAAI,OAAO,EAAE,QAAQ,YACjB,OAAO;EAEX,MAAM,MAAM,EAAE,IAAI;EAClB,OAAO,OAAO,OAAO,IAAI,kBAAkB,aAAa,MAAM;CAClE,QAAQ;EACJ,OAAO;CACX;AACJ;;;;;;;AAQA,SAAgB,eAAe,KAAqD;CAChF,IAAI;EACA,OAAO,uBAAuB,GAAG,KAAK;CAC1C,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAS,uBAAuB,KAAqD;CACjF,MAAM,aAAa,IAAI,QAAQ;CAG/B,MAAM,cAAc,IAAI,QAAQ;CAGhC,IACI,OAAO,WAAW,WAAW,cAC7B,OAAO,YAAY,wBAAwB,YAE3C,OAAO;CAEX,MAAM,UAAU,WAAW,OAAO;CAClC,IAAI,CAAC,SACD,OAAO;CAEX,MAAM,cAAc,OACf,YAAY,oBAAkE,EAAE;CACrF,MAAM,UAAU,mBAAmB,SAAS,UAAU;CACtD,IAAI,YAAY,MACZ,IAAI,UAAU;CAElB,OAAO;AACX;;;;;;;;;;;AC5GA,MAAM,sBAAsB;;AAE5B,MAAM,kBAAkB;;AAExB,MAAM,kBAAkB;;AAGxB,SAAgB,UAAU,OAAgB,QAAQ,GAAY;CAC1D,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;CAClD,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS;CACrD,IAAI,OAAO,UAAU,YAAY,OAAO,aAAa,MAAM,QAAQ,OAAO;CAC1E,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,QAAQ,qBAAqB,OAAO;CACxC,IAAI;EACA,OAAO,mBAAmB,OAAO,KAAK;CAC1C,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAS,mBAAmB,OAAe,OAAwB;CAC/D,IAAI,MAAM,QAAQ,KAAK,GACnB,OAAO,eAAe,OAAO,KAAK;CAEtC,IAAI,iBAAiB,KACjB,OAAO,aAAa,OAAO,KAAK;CAEpC,IAAI,iBAAiB,KACjB,OAAO,aAAa,OAAO,KAAK;CAEpC,IAAI,OAAQ,MAA2B,SAAS,YAC5C,OAAO;CAEX,OAAO,gBAAgB,OAAO,KAAK;AACvC;;AAGA,SAAS,eAAe,OAAkB,OAA0B;CAChE,OAAO,MAAM,MAAM,GAAG,eAAe,CAAC,CAAC,KAAK,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC;AAC7E;;AAGA,SAAS,aAAa,OAA8B,OAAwB;CACxE,MAAM,MAA+B,CAAC;CACtC,IAAI,IAAI;CACR,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO;EACxB,IAAI,KAAK,iBAAiB;EAC1B,IAAI,OAAO,CAAC,KAAK,UAAU,GAAG,QAAQ,CAAC;EACvC,KAAK;CACT;CACA,OAAO;EAAE,MAAM;EAAO,MAAM,MAAM;EAAM,SAAS;CAAI;AACzD;;AAGA,SAAS,aAAa,OAAqB,OAAwB;CAC/D,OAAO;EACH,MAAM;EACN,MAAM,MAAM;EACZ,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC,KAAK,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC;CACnF;AACJ;;AAGA,SAAS,gBAAgB,OAAe,OAAwB;CAC5D,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG,eAAe,GACzD,IAAI,OAAO,UAAW,MAAkC,MAAM,QAAQ,CAAC;CAE3E,OAAO;AACX;;AAGA,SAAgB,SAAS,SAAkB,QAAyB;CAChE,IAAI;EACA,MAAM,KAAM,QAAoC;EAChD,IAAI,OAAO,OAAO,YAAY,OAAO;EACrC,MAAM,QAAQ,GAAG,KAAK,OAAO;EAC7B,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;EAClD,OAAO,UAAU,KAAK;CAC1B,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,MAAM,iBAAiB;;AAGvB,SAAgB,gBACZ,SACA,gBACuB;CACvB,MAAM,MAA+B,CAAC;CACtC,MAAM,aAAa,eAAe,QAC7B,MAAM,eAAe,KAAK,CAAC,KAAK,CAAC,EAAE,SAAS,UAAU,CAC3D;CACA,KAAK,MAAM,KAAK,WAAW,MAAM,GAAG,CAAC,GACjC,IAAI,KAAK,SAAS,SAAS,CAAC;CAEhC,OAAO;AACX;;;;;;;;;;ACtGA,SAAgB,YAAY,KAAwB;CAChD,IAAI,QAAQ,QAAQ,QAAQ,KAAA,GACxB,OAAO,CAAC;CAEZ,MAAM,wBAAQ,IAAI,IAAY;CAC9B,sBAAsB,KAAK,KAAK;CAChC,gBAAgB,KAAK,KAAK;CAC1B,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK;AAC3B;;AAGA,SAAS,sBAAsB,KAAc,OAA0B;CACnE,IAAI,QAAQ,OAAO,eAAe,GAAG;CACrC,OAAO,aAAa,KAAK,GAAG;EACxB,KAAK,MAAM,QAAQ,OAAO,oBAAoB,KAAK,GAC/C,IAAI,SAAS,eACT,MAAM,IAAI,IAAI;EAGtB,QAAQ,OAAO,eAAe,KAAK;CACvC;AACJ;;AAGA,SAAS,aAAa,OAAuC;CACzD,OAAO,UAAU,QAAQ,UAAU,OAAO,aAAa,UAAU,SAAS;AAC9E;;AAGA,SAAS,gBAAgB,KAAc,OAA0B;CAC7D,KAAK,MAAM,QAAQ,OAAO,oBAAoB,GAAG,GAC7C,IAAI,SAAS,eACT,MAAM,IAAI,IAAI;AAG1B;;AAGA,SAAgB,QACZ,KACA,MACgD;CAChD,IAAI;EACA,MAAM,KAAM,IAAgC;EAC5C,IAAI,OAAO,OAAO,YACd,OAAO;GAAE,IAAI;GAAO,OAAO,0BAA0B,OAAO,GAAG;EAAG;EAGtE,OAAO;GAAE,IAAI;GAAM,OADL,GAAG,KAAK,GACC;EAAE;CAC7B,SAAS,GAAG;EACR,IAAI,UAAU,OAAO,CAAC;EACtB,IAAI,aAAa,OACb,UAAU,EAAE;EAEhB,OAAO;GAAE,IAAI;GAAO,OAAO;EAAQ;CACvC;AACJ;;;;ACnDA,SAAgB,iBAAiB,KAA8C;CAC3E,MAAM,OAAO;EACT;EACA;EACA;EACA;EACA;CACJ;CACA,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,OAAO,MAAM;EACpB,MAAM,OAAQ,IAAI,QAA+C;EACjE,IAAI,SAAS,QAAQ,SAAS,KAAA,GAC1B,IAAI,OAAO;GAAE,SAAS,YAAY,IAAI;GAAG,SAAS,OAAO,oBAAoB,IAAI;EAAE;CAE3F;CACA,OAAO;AACX;;AAGA,SAAgB,iBAAiB,KAA8C;CAC3E,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,KAAK,oBACZ,IAAI,KAAK,gBAAgB,KAAK,CAAC;CAEnC,OAAO;AACX;;AAGA,SAAS,gBAAgB,KAAqB,GAAoB;CAC9D,MAAM,OAAO,QAAQ,IAAI,QAAQ,CAAC;CAClC,OAAO,KAAK,KACN;EAAE,OAAO,UAAU,KAAK,KAAK;EAAG,SAAS,KAAK,QAAQ,YAAY,KAAK,KAAK,IAAI,CAAC;CAAE,IACnF,EAAE,OAAO,KAAK,SAAS,iBAAiB;AAClD;;AAGA,MAAM,qBAAqB;CACvB;CACA;CACA;CACA;AACJ;;AAGA,SAAgB,kBAAkB,KAAqD;CACnF,MAAM,OAAO,IAAI,QAAQ;CAGzB,IAAI,SAAS,QAAQ,OAAO,KAAK,QAAQ,YACrC,OAAO;CAEX,MAAM,OAAO,QAAQ,MAAM,KAAK;CAChC,IAAI,EAAE,KAAK,MAAM,KAAK,QAClB,OAAO,EAAE,KAAK,KAAK,SAAS,iBAAiB;CAEjD,OAAO;EACH,SAAS,YAAY,KAAK,KAAK;EAC/B,SAAS,OAAO,oBAAoB,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;EAC3D,GAAG,kBAAkB,KAAK,KAAK;CACnC;AACJ;;AAGA,SAAS,kBAAkB,MAAwC;CAC/D,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,KAAK,qBACZ,IAAI,OAAQ,KAAiC,OAAO,YAChD,IAAI,KAAK,SAAS,MAAM,CAAC;CAGjC,OAAO;AACX;;AAGA,MAAM,sBAAsB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;;;;;;;;;;;;;;;;;;ACtEA,SAAS,oBAAoB,KAA8C;CACvE,MAAM,IAAI,IAAI,QAAQ;CACtB,MAAM,SAAS,sBAAsB,GAAG;CACxC,IAAI,WAAW,MACX,OAAO,CAAC;CAEZ,MAAM,MAA+B,CAAC;CACtC,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;EAC5B,eAAe,QAAQ,GAAG,MAAM,KAAK,GAAG;EACxC,eAAe,QAAQ,GAAG,QAAQ,KAAK,GAAG;CAC9C;CACA,OAAO;AACX;;AAGA,SAAS,eACL,QACA,SACA,IACA,KACI;CACJ,IAAI;EACA,MAAM,QAAQ,OAAO,KAAK,SAAS,EAAE;EACrC,IAAI,CAAC,OACD;EAEJ,IAAI,MAAM,mBAAmB,KAAK;CACtC,QAAQ,CAER;AACJ;;AAGA,SAAS,mBAAmB,OAAyC;CACjE,MAAM,MAAM;CACZ,MAAM,MAAM,cAAc,KAAK,KAAK;CACpC,OAAO;EACH,qBAAqB,OAAO,IAAI,qBAAqB;EACrD,cAAc,IAAI,YAAY;EAC9B,mBAAmB,IAAI,UAAU,YAAY,IAAI,OAAO,CAAC,CAAC,SAAS;EACnE,UAAU,IAAI;EACd,SAAS,YAAY,KAAK;CAC9B;AACJ;;AAGA,SAAS,cACL,KACA,SAC0C;CAC1C,IAAI;EAEA,OAAO;GAAE,SADI,IAAI,gBAAgB,CAAmB,KAAK,OACrC,KAAK;GAAM,OAAO;EAAK;CAC/C,SAAS,GAAG;EACR,OAAO;GAAE,SAAS;GAAM,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;EAAE;CAC9E;AACJ;;AAGA,SAAS,sBAAsB,KAA+D;CAC1F,MAAM,KAAM,IAAI,QAAQ,wBACpB;CAEJ,OAAO,OAAO,OAAO,aAAc,KAAyC;AAChF;;AAGA,SAAS,kBAAkB,SAIzB;CACE,MAAM,UAAU,QAAQ,SAAS,OAAO;CACxC,MAAM,MAAM,QAAQ,SAAS,kBAAkB;CAC/C,OAAO;EACH,OAAO,QAAQ,KAAK,EAAE,IAAI,KAAK,IAAI,EAAE,OAAO,QAAQ,MAAM;EAC1D,yBAAyB,IAAI,KAAK,EAAE,OAAO,UAAU,IAAI,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,MAAM;EACvF,YAAY,kBAAkB,GAAG;CACrC;AACJ;;AAGA,SAAS,kBAAkB,KAAiD;CACxE,IAAI,EAAE,IAAI,MAAM,IAAI,iBAAiB,MACjC,OAAO,CAAC;CAEZ,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,CAAC,GAAG,MAAM,IAAI,OACrB,IAAI,KAAK,OAAO,MAAM,WAAW,IAAI,OAAO,CAAC,CAAC;CAElD,OAAO;AACX;;AAGA,SAAS,iBAAiB,KAAqB,IAAqB;CAChE,MAAM,KAAK,sBAAsB,GAAG;CACpC,IAAI,OAAO,MACP,OAAO;CAEX,IAAI;EACA,MAAM,QAAQ,GAAG,KAAK,IAAI,QAAQ,yBAAyB,EAAE;EAC7D,OAAO,QACD;GAAE,SAAS,YAAY,KAAK;GAAG,SAAS,OAAO,oBAAoB,KAAK;EAAE,IAC1E;CACV,SAAS,GAAG;EACR,OAAO,EAAE,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,EAAE;CAC/D;AACJ;;AAGA,SAAS,oBAAoB,KAA8C;CACvE,MAAM,aAAa;EAAC;EAAQ;EAAW;EAAY;EAAW;CAAE;CAChE,MAAM,aAAsC,CAAC;CAC7C,KAAK,MAAM,QAAQ,YACf,WAAW,SAAS,KAAK,YAAY,QAAQ,mBAAmB,KAAK,IAAI;CAE7E,OAAO;AACX;;AAGA,SAAS,mBAAmB,KAAqB,MAAuB;CACpE,IAAI;EACA,MAAM,KAAK,sBAAsB,GAAG;EACpC,IAAI,OAAO,MACP,OAAO;EAEX,MAAM,QAAQ,GAAG,KAAK,IAAI,QAAQ,yBAAyB,IAAI;EAC/D,OAAO,QACD;GAAE,SAAS,YAAY,KAAK;GAAG,SAAS,OAAO,oBAAoB,KAAK;EAAE,IAC1E;CACV,SAAS,GAAG;EACR,OAAO,EAAE,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,EAAE;CAC/D;AACJ;;AAGA,SAAS,aAAa,KAAqD;CACvE,MAAM,UAAU,IAAI,QAAQ;CAG5B,IAAI,YAAY,QAAQ,OAAO,QAAQ,WAAW,YAC9C,OAAO;CAEX,MAAM,MAA+B,EAAE,eAAe,YAAY,OAAO,EAAE;CAC3E,MAAM,UAAU,QAAQ,SAAS,QAAQ;CACzC,IAAI,YAAY,QAAQ,KAClB;EACI,SAAS,QAAQ,QAAQ,YAAY,QAAQ,KAAK,IAAI,CAAC;EACvD,OAAO,UAAU,QAAQ,KAAK;CAClC,IACA,EAAE,OAAO,QAAQ,MAAM;CAC7B,IAAI,QAAQ,MAAM,QAAQ,OACtB,OAAO,OAAO,KAAK,kBAAkB,KAAK,QAAQ,KAAK,CAAC;CAE5D,IAAI,oBAAoB,oBAAoB,GAAG;CAE/C,IAAI,IAAI,kBAAkB,KAAA,GACtB,IAAI,yBAAyB,oBAAoB,GAAG;CAExD,OAAO;AACX;;AAGA,SAAS,kBAAkB,KAAqB,SAA2C;CACvF,MAAM,QAAQ,kBAAkB,OAAO;CACvC,MAAM,MAA+B;EACjC,OAAO,MAAM;EACb,yBAAyB,MAAM;EAC/B,YAAY,MAAM;CACtB;CACA,MAAM,SAAS,gBAAgB,MAAM,UAAU;CAC/C,IAAI,WAAW,KAAA,GAAW;EACtB,IAAI,mBAAmB;EACvB,IAAI,iBAAiB,iBAAiB,KAAK,MAAM;CACrD;CACA,OAAO;AACX;;AAGA,SAAS,gBAAgB,YAA0C;CAC/D,OAAO,WAAW,MAAM,OAAO,GAAG,WAAW,KAAK,CAAC,KAAK,WAAW;AACvE;;AAGA,SAAS,aAAa,KAA8C;CAChE,MAAM,MAA+B,CAAC;CACtC,MAAM,UAAU,iBAAiB,KAAK,GAAG;CACzC,IAAI,aAAa,UACX;EAAE,SAAS,YAAY,OAAO;EAAG,SAAS,OAAO,oBAAoB,OAAO;CAAE,IAC9E;CACN,IAAI,YAAY,QAAQ,YAAY,KAAA,GAChC,IAAI,cAAc,sBAAsB,OAAO;CAEnD,OAAO;AACX;;AAGA,SAAS,iBAAiB,KAAqB,KAAuC;CAClF,IAAI;EACA,MAAM,OAAO,eAAe,GAAG;EAC/B,IAAI,SAAS,MAAM;GACf,IAAI,mBAAmB;GACvB,OAAO;EACX;CACJ,QAAQ,CAER;CACA,IAAI,IAAI,YAAY,MAAM;EACtB,IAAI,mBAAmB;EACvB,OAAO,IAAI;CACf;CACA,OAAO;AACX;;AAGA,SAAS,sBAAsB,SAA2C;CACtE,MAAM,UAAU,YAAY,OAAO,CAAC,CAAC,QAChC,MAAM,EAAE,WAAW,KAAK,KAAK,EAAE,SAAS,SAAS,CACtD;CACA,MAAM,WAAoC,CAAC;CAC3C,KAAK,MAAM,UAAU,SACjB,SAAS,UAAU,mBAAmB,SAAS,MAAM;CAEzD,OAAO;AACX;;AAGA,SAAS,mBAAmB,SAAkB,QAAyB;CACnE,MAAM,OAAO,QAAQ,SAAS,MAAM;CACpC,IAAI,EAAE,KAAK,MAAM,KAAK,QAClB,OAAO;EAAE,OAAO,KAAK,SAAS;EAAkB,IAAI,KAAK;CAAG;CAEhE,MAAM,UAAU,YAAY,KAAK,KAAK;CACtC,OAAO;EACH;EACA,SAAS,OAAO,oBAAoB,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;EAC3D,QAAQ,gBAAgB,KAAK,OAAO,OAAO;CAC/C;AACJ;;;;;AAMA,SAAgB,aACZ,KACA,OAAO,wBACgB;CACvB,MAAM,SAAkC;EACpC,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EAClC,SAAS,YAAY,IAAI,OAAO;EAChC,eAAe,YAAY,IAAI,MAAM;EACrC,aAAa,iBAAiB,GAAG;EACjC,aAAa,iBAAiB,GAAG;EACjC,cAAc,kBAAkB,GAAG;EACnC,SAAS,aAAa,GAAG;EACzB,GAAG,aAAa,GAAG;CACvB;CAGA,IAAI;EACA,MAAM,MAAM,QAAQ,IAAI,qBAAqB,QAAQ,IAAI;EACzD,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;EAClC,MAAM,OAAO,KAAK,KAAK,IAAI;EAC3B,cAAc,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,MAAM;EAC3D,OAAO,eAAe;CAC1B,SAAS,GAAG;EACR,OAAO,oBAAoB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;CACxE;CACA,OAAO;AACX;;;;;;;;;;;;;;;ACtRA,SAAS,mBACL,YACA,SACwD;CACxD,MAAM,UAAU,KAAK,YAAY,YAAY,SAAS,aAAa,OAAO,cAAc;CACxF,IAAI,CAAC,WAAW,OAAO,GACnB,MAAM,YAAY,2BAA2B,WAAW,WAAW;CAEvE,IAAI;EACA,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,MAAM,CAAC;EAKpD,OAAO;GACH,MAAM,IAAI,QAAQ;GAClB,SAAS,IAAI,WAAW;GACxB,GAAI,IAAI,iBAAiB,KAAA,IAAY,EAAE,cAAc,IAAI,aAAa,IAAI,CAAC;EAC/E;CACJ,SAAS,OAAO;EACZ,MAAM,YAAY,4BAA4B,WAAW,iBAAiB,EAAE,MAAM,CAAC;CACvF;AACJ;;AAGA,SAAgB,mBAAmB,YAAoB,SAAyB;CAC5E,OAAO,KAAK,YAAY,YAAY,SAAS,aAAa,OAAO,cAAc;AACnF;;;;;AAMA,SAAgB,iBAAiB,YAAoB,SAAgC;CACjF,MAAM,cAAc,mBAAmB,YAAY,OAAO;CAC1D,IAAI,CAAC,WAAW,WAAW,GACvB,MAAM,YAAY,qBAAqB,eAAe,WAAW;CAErE,MAAM,MAAM,mBAAmB,YAAY,OAAO;CAClD,OAAO;EACH,aAAa,IAAI,WAAW;EAC5B,cAAc,IAAI,gBAAgB;EAClC;EACA,OAAO;EACP,KAAK;CACT;AACJ;;AAGA,SAAgB,eAAe,YAA8B;CACzD,MAAM,cAAc,KAAK,YAAY,UAAU;CAC/C,IAAI,CAAC,WAAW,WAAW,GACvB,OAAO,CAAC;CASZ,OAPa,YAAY,WAAW,CAAC,CAAC,QAAQ,SAAS;EACnD,MAAM,OAAO,KAAK,aAAa,IAAI;EACnC,OACI,SAAS,IAAI,CAAC,CAAC,YAAY,KAC3B,WAAW,KAAK,MAAM,aAAa,OAAO,cAAc,CAAC;CAEjE,CACU,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ;AAC/B"}
1
+ {"version":3,"file":"index.mjs","names":["stringifyTomlText","parseTomlText","sleep","httpsGet","osVersionString","osType","PlatformTypeValue"],"sources":["../src/infra/errors.ts","../src/infra/config.ts","../src/infra/logger.ts","../src/infra/paths.ts","../src/apis/result.ts","../src/apis/friend.ts","../src/types/entities.ts","../src/types/listeners/group.ts","../src/types/services/msg-service.ts","../src/types/message-element.ts","../src/types/services/group-service.ts","../src/types/wrapper.ts","../src/apis/group.ts","../src/apis/group-notify.ts","../src/apis/msg.ts","../src/apis/stranger-info.ts","../src/apis/profile.ts","../src/apis/profile-like.ts","../src/apis/richmedia.ts","../src/apis/ticket.ts","../src/apis/webapi.ts","../src/bridge/friend-bridge.ts","../src/bridge/group-bridge.ts","../src/bridge/msg-bridge.ts","../src/event-channel.ts","../src/cache/buddy-cache.ts","../src/login/wait.ts","../src/cache/group-cache.ts","../src/context.ts","../src/wrapper/wrapper-adapters.ts","../src/login/login-connect.ts","../src/login/lifecycle.ts","../src/login/login.ts","../src/wrapper/qq-data-path.ts","../src/wrapper/wrapper-config.ts","../src/wrapper/wrapper-loader.ts","../src/core.ts","../src/wrapper/session-resolver.ts","../src/wrapper/probe/probe-serialize.ts","../src/wrapper/probe/probe-utils.ts","../src/wrapper/probe/probe-services.ts","../src/wrapper/probe/probe.ts","../src/wrapper/wrapper-version.ts"],"sourcesContent":["/**\n * kernel 类型化错误(ADR-009 / ADR-017)\n *\n * apis 层内部解包原生 `{ result, errMsg }`:成功返回纯业务值,失败抛 KernelError。\n * 协议层只需维护 `KernelErrorCode → 协议错误码` 映射表,不解析错误逻辑。\n */\n\n/**\n * 错误码分类。P1 探测真实错误返回后再增删。\n * 当前 INVALID_PARAM 也承担基础设施层(配置校验失败等)的错误语义。\n */\nexport type KernelErrorCode =\n | \"SEND_FAILED\" // 发送失败(原生拒绝)\n | \"PERMISSION_DENIED\" // 无权限(禁言、非管理员等)\n | \"NOT_FOUND\" // 目标不存在(群/成员/消息)\n | \"TIMEOUT\" // 操作超时(含文件预测超时)\n | \"NOT_LOGIN\" // 未登录或已掉线\n | \"INVALID_PARAM\" // 参数非法(原生拒绝 / 配置校验失败)\n | \"INVALID_STATE\" // 状态非法(如 session 未创建即 startNT)\n | \"UNKNOWN\"; // 兜底\n\n/**\n * 全部错误码集合。协议层映射表可据此推导:\n * `Record<(typeof KERNEL_ERROR_CODES)[number], number>`。\n */\nexport const KERNEL_ERROR_CODES = [\n \"SEND_FAILED\",\n \"PERMISSION_DENIED\",\n \"NOT_FOUND\",\n \"TIMEOUT\",\n \"NOT_LOGIN\",\n \"INVALID_PARAM\",\n \"INVALID_STATE\",\n \"UNKNOWN\",\n] as const;\n\n/** kernel 类型化错误:apis / 基础设施层抛出的统一错误形态。 */\nexport class KernelError extends Error {\n readonly code: KernelErrorCode;\n\n constructor(message: string, code: KernelErrorCode = \"UNKNOWN\", options?: ErrorOptions) {\n super(message, options);\n this.name = \"KernelError\";\n this.code = code;\n }\n}\n\n/** 便捷构造:`kernelError('消息不存在', 'NOT_FOUND', { cause })`。 */\nexport function kernelError(\n message: string,\n code: KernelErrorCode = \"UNKNOWN\",\n options?: ErrorOptions,\n): KernelError {\n return new KernelError(message, code, options);\n}\n\n/** 类型守卫:协议层 catch 统一映射时区分 KernelError 与意外错误。 */\nexport function isKernelError(err: unknown): err is KernelError {\n return err instanceof KernelError;\n}\n","/**\n * 配置基类(ADR-012)\n *\n * 读文件 → 校验 → 内存对象 → 变更写回。支持 **JSON / TOML** 两种文本格式\n * (按扩展名推断:.toml → TOML(smol-toml),其余 → JSON)。\n *\n * kernel 不依赖 zod:只依赖校验器的最小 `parse` 形状(`ConfigSchema`),\n * 协议包的 zod schema 天然满足;kernel 主配置用手写校验器包装。\n * 协议配置 schema 在各自协议包,见各包 docs/design.md。\n */\nimport { mkdirSync } from \"node:fs\";\nimport { readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, extname } from \"node:path\";\nimport { parse as parseTomlText, stringify as stringifyTomlText } from \"smol-toml\";\nimport { KernelError } from \"./errors.js\";\n\n/** 配置文本格式。 */\nexport type ConfigFormat = \"json\" | \"toml\";\n\n/** 按扩展名推断格式(.toml → toml,其余 → json)。 */\nfunction inferFormat(path: string): ConfigFormat {\n if (extname(path).toLowerCase() === \".toml\") {\n return \"toml\";\n }\n return \"json\";\n}\n\nfunction isMissingFileError(err: unknown): boolean {\n if (err instanceof Error) {\n return (err as NodeJS.ErrnoException).code === \"ENOENT\";\n }\n return false;\n}\n\n/**\n * 校验器最小接口。zod 的 `ZodType<T>`(`parse(data: unknown): T`)天然满足;\n * 手写校验器包装成该形状即可。\n */\nexport interface ConfigSchema<T> {\n parse: (input: unknown) => T;\n}\n\nexport interface ConfigOptions<T> {\n /** 配置文件绝对路径。 */\n path: string;\n /** 校验器(协议 schema 用 zod;kernel 主配置用手写校验器包装)。 */\n schema: ConfigSchema<T>;\n /** 默认值:文件缺失时生成并落盘;校验以它为兜底。 */\n defaults: T;\n /** 内存初值(已校验对象):load() 时直接使用,跳过文件读写(全局 TOML 分段的装配方式)。 */\n seed?: T | undefined;\n /** 文本格式(缺省按扩展名推断)。 */\n format?: ConfigFormat | undefined;\n}\n\n/**\n * 配置基类。每次 `load()` / `save()` 都经过 schema 校验,\n * 校验失败抛 `KernelError('INVALID_PARAM')`,不静默吞掉。\n * - `load()`:seed 存在 → 直接用 seed;否则读文件(缺失落默认值)。\n * - `save()`:按格式序列化(TOML/JSON)原子写。\n */\nexport class ConfigBase<T> {\n readonly path: string;\n readonly defaults: T;\n\n private readonly schema: ConfigSchema<T>;\n private readonly format: ConfigFormat;\n private readonly seed: T | undefined;\n private value: T;\n\n constructor(opts: ConfigOptions<T>) {\n this.path = opts.path;\n this.defaults = opts.defaults;\n this.schema = opts.schema;\n this.format = opts.format ?? inferFormat(opts.path);\n this.seed = opts.seed;\n this.value = opts.seed ?? opts.defaults;\n }\n\n /** 当前内存值(只读消费;外部不得直接修改返回对象)。 */\n get(): T {\n return this.value;\n }\n\n /**\n * 加载配置:\n * - seed 存在 → 直接用 seed(已校验,不读文件;全局 TOML 分段装配方式);\n * - 文件缺失 → 写入默认值并落盘(首次运行自动生成),返回默认值;\n * - 文件存在 → 读取 + 校验,更新内存,返回。\n */\n async load(): Promise<T> {\n if (this.seed !== undefined) {\n this.value = this.seed;\n return this.value;\n }\n let raw: string;\n try {\n raw = await readFile(this.path, \"utf8\");\n } catch (err) {\n if (isMissingFileError(err)) {\n await this.save(this.defaults);\n return this.value;\n }\n throw new KernelError(`读取配置文件失败: ${this.path}`, \"UNKNOWN\", { cause: err });\n }\n this.value = this.parse(raw);\n return this.value;\n }\n\n /** 重新从磁盘加载(外部变更后的热更新入口,P2 配置热更新使用)。 */\n reload(): Promise<T> {\n return this.load();\n }\n\n /** 校验并落盘(原子写:临时文件 + rename),随后更新内存。 */\n async save(next: T): Promise<void> {\n const parsed = this.parse(this.serialize(next));\n mkdirSync(dirname(this.path), { recursive: true });\n const tmpPath = `${this.path}.tmp`;\n await writeFile(tmpPath, `${this.serialize(parsed)}\\n`, \"utf8\");\n await rename(tmpPath, this.path);\n this.value = parsed;\n }\n\n /** 对象 → 文本(TOML/JSON)。 */\n private serialize(value: T): string {\n if (this.format === \"toml\") {\n return stringifyTomlText(value as Record<string, unknown>);\n }\n return JSON.stringify(value, null, 4);\n }\n\n /** 文本 → 对象(TOML/JSON)。 */\n private deserialize(raw: string): unknown {\n if (this.format === \"toml\") {\n return parseTomlText(raw);\n }\n return JSON.parse(raw) as unknown;\n }\n\n /** 反序列化 + schema 校验,错误统一包装为 KernelError。 */\n private parse(raw: string): T {\n let input: unknown;\n try {\n input = this.deserialize(raw);\n } catch (err) {\n throw new KernelError(\n `配置文件 ${this.format.toUpperCase()} 解析失败: ${this.path}`,\n \"INVALID_PARAM\",\n { cause: err },\n );\n }\n try {\n return this.schema.parse(input);\n } catch (err) {\n throw new KernelError(`配置文件校验失败: ${this.path}`, \"INVALID_PARAM\", {\n cause: err,\n });\n }\n }\n}\n\n/** 解析 TOML 文本 → 对象(boot.cjs / 探测脚本等无 ConfigBase 场景复用)。 */\nexport function parseToml(text: string): Record<string, unknown> {\n return parseTomlText(text);\n}\n\n/** 对象 → TOML 文本(config init 生成全局配置文件等场景)。 */\nexport function stringifyToml(value: Record<string, unknown>): string {\n return stringifyTomlText(value);\n}\n","/**\n * pino 日志封装(ADR-007)\n *\n * - console(pino-pretty 可读输出)+ 可选文件双目的地\n * - 统一 level 控制\n * - 内置 redact(token / 票据等敏感字段不打日志),可追加\n * - 无全局单例(ADR-015 推论):每进程实例化一份,由装配层持有\n */\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport process from \"node:process\";\nimport pino from \"pino\";\nimport pinoPretty from \"pino-pretty\";\nimport { KernelError } from \"./errors.js\";\n\n/** 默认 redact 路径:顶层 token/票据 + 任意深度的密码/密钥。 */\nconst DEFAULT_REDACT_PATHS = [\"token\", \"ticket\", \"cookie\", \"password\", \"secret\"] as const;\n\nfunction toSingle(streams: Array<{ stream: pino.DestinationStream }>): pino.DestinationStream {\n const [only] = streams;\n if (only === undefined) {\n // 不可达:调用方已保证 streams 非空\n throw new KernelError(\"logger 内部错误:目的地列表为空\", \"UNKNOWN\");\n }\n return only.stream;\n}\n\n/** 日志级别(pino 标准级别 + silent)。 */\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\" | \"silent\";\n\nexport interface LoggerOptions {\n /** 日志级别,默认 `info`。 */\n level?: LogLevel;\n /** 是否输出到 console(pretty 模式),默认 true。 */\n console?: boolean;\n /** 文件日志路径;不传则不写文件(父目录自动创建)。 */\n file?: string;\n /** 附加基础字段(如 `{ name: 'kernel' }`),自动保留 pid;name 用 pino 保留字段\n * (logger name),pino-pretty 渲染为 `(name/pid)` 元数据头。 */\n base?: Record<string, unknown>;\n /** 追加的敏感字段路径(默认内置 token/票据等,见 DEFAULT_REDACT_PATHS)。 */\n redact?: string[];\n}\n\n/**\n * 创建 pino logger:console(pretty)+ 可选文件,统一 level / redact。\n *\n * 文件目的地用同步写入(`sync: true`):日志量级下性能可接受,且进程退出不丢日志,\n * 无需额外的 flush 步骤(对比异步 SonicBoom 的 flushSync 时序陷阱)。\n */\nexport function createLogger(opts: LoggerOptions = {}): pino.Logger {\n const level = opts.level ?? \"info\";\n const consoleEnabled = opts.console ?? true;\n const { file, base } = opts;\n\n const streams: Array<{ stream: pino.DestinationStream }> = [];\n if (consoleEnabled) {\n streams.push({\n stream: pinoPretty({\n colorize: true,\n // 短格式时间(2026-08-07 用户定稿):完整日期+时区前缀近 60 字符,\n // 每条日志重复占满横向视线;缩短为时分秒+毫秒,文件日志(JSON)\n // 仍保留完整时间戳可精确追溯。\n // ⚠️ 必须带 SYS: 前缀:无前缀按 UTC 渲染(北京 = UTC+8,时间会差 8 小时),\n // SYS: 才用本地系统时间。\n translateTime: \"SYS:HH:MM:ss.l\",\n // 原生多行展开(默认值,显式声明):属性按 key/value 逐行渲染,\n // 由 pino-pretty 自带颜色高亮(key 紫色 / 字符串青色),不手写格式化\n // 函数对抗原生渲染——高频业务日志(如收到消息)在调用点直接传\n // 纯字符串,不传对象,天然单行。\n singleLine: false,\n // ⚠️ 必须显式传 destination=process.stdout:pino-pretty 默认用\n // sonic-boom 直写 fd 1(UTF-8 字节流),在 pnpm start(cmd.exe +\n // 管道 936 转码)链路下中文被按 GBK 解码成乱码、ANSI 残留;\n // 走 process.stdout 的 TTY 路径(WriteConsoleW UTF-16)编码正确。\n destination: process.stdout,\n }),\n });\n }\n if (file) {\n mkdirSync(dirname(file), { recursive: true });\n streams.push({ stream: pino.destination({ dest: file, sync: true }) });\n }\n if (streams.length === 0) {\n throw new KernelError(\"logger 至少需要一个输出目标:console 或 file\", \"INVALID_PARAM\");\n }\n\n const pinoOptions: pino.LoggerOptions = {\n level,\n redact: [...DEFAULT_REDACT_PATHS, ...(opts.redact ?? [])],\n };\n if (base) {\n pinoOptions.base = { pid: process.pid, ...base };\n }\n\n let stream: pino.DestinationStream | ReturnType<typeof pino.multistream>;\n if (streams.length === 1) {\n stream = toSingle(streams);\n } else {\n stream = pino.multistream(streams);\n }\n\n return pino(pinoOptions, stream);\n}\n","/**\n * 数据目录布局(ADR-016,2026-08-08 修订:数据根默认放项目根)\n *\n * ```\n * <数据根>/<qq号>/ # 每账号独立目录(ADR-015 多账号前提)\n * ├── config/ # 各账号独立配置(历史产物,协议段已走 seed)\n * ├── logs/ # pino 文件日志\n * └── cache/ # 临时文件、媒体缓存\n *\n * <项目根>/napuketto.toml # 全局配置文件(放项目根目录,不入库)\n * <项目根>/.napuketto/ # 数据根(默认值,2026-08-08 用户拍板:直接放项目主目录)\n * ```\n *\n * 数据根优先级:cli `--data-dir`(显式参数)> `NAPKETTO_DATA` 环境变量 >\n * `<项目根>/.napuketto`(默认,部署原因不放用户目录)。\n * 配置文件路径见 `resolveConfigPath`(项目根探测,与数据根解耦)。\n */\nimport { existsSync, mkdirSync, readdirSync, rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\n\n/** 默认数据根目录名(项目根下,2026-08-08 用户拍板:不放用户目录)。 */\nexport const DEFAULT_DATA_ROOT_NAME = \".napuketto\";\n\n/** 全局配置文件名。 */\nexport const MAIN_CONFIG_FILE = \"napuketto.toml\";\n\nexport interface PathOptions {\n /** 显式数据根(cli `--data-dir` 传入,优先级最高)。 */\n dataRoot?: string;\n /** QQ 号,用于多账号隔离;缺省时直接使用数据根本身。 */\n account?: string;\n}\n\n/** `~` 前缀展开为用户主目录(跨平台配置文件写法 `dataDir = \"~/.napuketto\"`)。 */\nfunction expandTilde(p: string): string {\n if (p === \"~\") {\n return homedir();\n }\n if (p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n return join(homedir(), p.slice(2));\n }\n return p;\n}\n\n/**\n * 项目根查找:入口模块向上找 `pnpm-workspace.yaml`(monorepo 开发场景,即仓库根)→\n * cwd 向上找 `pnpm-workspace.yaml` / `package.json`(发布后用户项目)→ undefined(兜底)。\n */\nfunction resolveProjectRoot(): string | undefined {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const moduleRoot = findProjectRoot(moduleDir, \"pnpm-workspace.yaml\");\n if (moduleRoot !== undefined) {\n return moduleRoot;\n }\n return (\n findProjectRoot(process.cwd(), \"pnpm-workspace.yaml\") ??\n findProjectRoot(process.cwd(), \"package.json\")\n );\n}\n\n/**\n * 解析数据根:显式参数 > 环境变量 > `<项目根>/.napuketto`(默认)> 用户目录兜底(裸脚本)。\n * 支持 `~` 前缀(展开为用户主目录)与相对路径(相对 cwd 解析)。\n * 返回绝对路径。独立导出便于 cli / 探测脚本复用。\n */\nexport function resolveDataRoot(dataRoot?: string): string {\n const explicit = dataRoot ?? process.env[\"NAPKETTO_DATA\"];\n if (explicit !== undefined && explicit !== \"\") {\n return resolve(expandTilde(explicit));\n }\n const projectRoot = resolveProjectRoot();\n if (projectRoot !== undefined) {\n return join(projectRoot, DEFAULT_DATA_ROOT_NAME);\n }\n return join(homedir(), DEFAULT_DATA_ROOT_NAME);\n}\n\n/** 向上查找含标志文件的目录(项目根探测:monorepo 根 / 用户项目根)。 */\nfunction findProjectRoot(startDir: string, marker: string): string | undefined {\n let dir = startDir;\n for (;;) {\n if (existsSync(join(dir, marker))) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n return undefined;\n }\n dir = parent;\n }\n}\n\nexport interface ConfigPathOptions {\n /** 兜底数据根(探测全部失败时用 `<dataRoot>/napuketto.toml`,保持旧行为)。 */\n dataRoot?: string;\n}\n\n/**\n * 全局配置文件路径解析(2026-08-07 用户拍板:**配置文件放项目根目录,\n * 数据仍按数据根组织**——数据根只承载账号目录/日志/缓存/QQ 数据)。\n *\n * 优先级:\n * 1. `NAPKETTO_CONFIG` 环境变量(显式指定完整路径,任意场景可用)\n * 2. 项目根探测(入口模块向上找 pnpm-workspace.yaml → cwd 向上找,见 resolveProjectRoot)\n * 3. 数据根兜底 `<dataRoot>/napuketto.toml`(旧行为兼容)\n *\n * 返回绝对路径。独立导出便于 cli / loader 装配链复用。\n */\nexport function resolveConfigPath(opts: ConfigPathOptions = {}): string {\n const explicit = process.env[\"NAPKETTO_CONFIG\"];\n if (explicit !== undefined && explicit !== \"\") {\n return resolve(explicit);\n }\n const projectRoot = resolveProjectRoot();\n if (projectRoot !== undefined) {\n return join(projectRoot, MAIN_CONFIG_FILE);\n }\n return join(opts.dataRoot ?? resolveDataRoot(), MAIN_CONFIG_FILE);\n}\n\n/**\n * 路径装配器:持有账号数据目录下的全部子目录。\n * 无全局单例(ADR-015 推论)——每账号每进程实例化一份。\n */\nexport class PathWrapper {\n /** 数据根(绝对路径)。 */\n readonly root: string;\n /** 账号数据目录(绝对路径),缺省 account 时等于 root。 */\n readonly accountDir: string;\n /** config 目录(napuketto.json + 各协议配置)。 */\n readonly configDir: string;\n /** logs 目录(pino 文件日志)。 */\n readonly logsDir: string;\n /** cache 目录(临时文件、媒体缓存)。 */\n readonly cacheDir: string;\n\n constructor(opts: PathOptions = {}) {\n const root = resolveDataRoot(opts.dataRoot);\n this.root = root;\n if (opts.account) {\n this.accountDir = join(root, opts.account);\n } else {\n this.accountDir = root;\n }\n this.configDir = join(this.accountDir, \"config\");\n this.logsDir = join(this.accountDir, \"logs\");\n this.cacheDir = join(this.accountDir, \"cache\");\n }\n\n /** 递归创建全部目录,启动装配时调用一次。 */\n ensure(): void {\n for (const dir of [this.configDir, this.logsDir, this.cacheDir]) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n /** 账号目录内的文件路径(如 `file('config', 'napuketto.json')`)。 */\n file(...segments: string[]): string {\n return join(this.accountDir, ...segments);\n }\n\n /**\n * 清空缓存目录(clean_cache 动作经注入回调消费)。\n * 只删除 cacheDir 下的条目,保留目录本身。\n */\n clearCache(): void {\n let entries: string[];\n try {\n entries = readdirSync(this.cacheDir);\n } catch {\n return; // 目录不存在视为空\n }\n for (const entry of entries) {\n rmSync(join(this.cacheDir, entry), { recursive: true, force: true });\n }\n }\n}\n","/**\n * 原生调用结果解包(ADR-009 统一错误语义)\n *\n * wrapper.node 的 service 方法大多返回 `{ result, errMsg }`:\n * - unwrap:result 为 number 且非 0 → 抛 KernelError\n * - checkLooseResult:宽松形状(result 可能缺失/非数字)→ 仅当 result 为\n * 数字且非 0 时抛错(探测期兼容:某些 API 成功时 result 缺省或为对象)\n * - unwrapResult:result !== 0 → 按 errMsg 内容映射语义错误码(msg 系专用)\n */\n\nimport { type KernelErrorCode, kernelError } from \"../infra/index.js\";\nimport type { GeneralCallResult } from \"../types/index.js\";\n\n/** errMsg 关键词 → 错误码规则(顺序即优先级,命中任一组关键词即映射)。 */\nconst RESULT_CODE_RULES: ReadonlyArray<{ code: KernelErrorCode; keywords: readonly string[] }> = [\n { code: \"NOT_LOGIN\", keywords: [\"未登录\", \"login\"] },\n { code: \"PERMISSION_DENIED\", keywords: [\"无权限\", \"permission\"] },\n { code: \"NOT_FOUND\", keywords: [\"不存在\", \"not found\"] },\n];\n\n/** 按 errMsg 关键词映射错误码(未命中 → UNKNOWN)。 */\nfunction mapResultCode(msg: string): KernelErrorCode {\n for (const rule of RESULT_CODE_RULES) {\n if (rule.keywords.some((k) => msg.includes(k))) {\n return rule.code;\n }\n }\n return \"UNKNOWN\";\n}\n\n/** 原生 result 解包(result 字段非 0 抛 KernelError)。 */\nexport function unwrap(label: string, result: number, errMsg?: string): void {\n if (result === 0) {\n return;\n }\n throw kernelError(`${label} 失败: ${errMsg ?? \"无错误详情\"}`, \"UNKNOWN\");\n}\n\n/** 宽松 result 校验(result 为数字非 0 视为失败;缺失/非数字视为成功)。 */\nexport function checkLooseResult(\n label: string,\n res: { result?: unknown; errMsg?: unknown } | null | undefined,\n): void {\n if (res === undefined || res === null || typeof res.result !== \"number\" || res.result === 0) {\n return;\n }\n throw kernelError(`${label} 失败: ${String(res.errMsg ?? \"\")}`, \"UNKNOWN\");\n}\n\n/** 原生结果解包:result !== 0 抛 KernelError(errMsg 语义映射错误码)。 */\nexport function unwrapResult<T extends GeneralCallResult>(label: string, raw: T): void {\n if (raw.result === 0) {\n return;\n }\n const msg = raw.errMsg || \"无错误详情\";\n // 语义码优先(未登录/无权限/不存在),未命中且为 sendMsg 时兜底 SEND_FAILED\n let code = mapResultCode(msg);\n if (code === \"UNKNOWN\" && label === \"sendMsg\") {\n code = \"SEND_FAILED\";\n }\n throw kernelError(`${label} 失败: ${msg}`, code);\n}\n","/**\n * FriendApi:好友语义化 API(ADR-009 统一错误语义)\n *\n * 内部解包原生返回:成功返回纯业务值,失败抛 KernelError。\n * 方法面(P2-4 + P2-11):好友列表(uid + 昵称/备注)+ 好友请求\n * (getBuddyReq / approvalFriendRequest / setBuddyRemark / delBuddy /\n * getDoubtBuddyReq / approvalDoubtBuddyReq)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n BuddyCategory,\n BuddyReq,\n DoubtBuddyReq,\n NodeIKernelBuddyService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\nimport { checkLooseResult, unwrap } from \"./result.js\";\n\n/** 好友条目(uid + 昵称/备注,昵称由 buddy service 缓存补全)。 */\nexport interface Friend {\n uid: string;\n uin: string;\n nickname: string;\n remark: string;\n}\n\n/** 带分类的好友列表项(get_friends_with_category 返回)。 */\nexport interface FriendCategory {\n categoryId: number;\n categorySortId: number;\n categoryName: string;\n categoryMbCount: number;\n onlineCount: number;\n buddyList: Friend[];\n}\n\n/** 可疑好友申请(OB11 结构,get_doubt_friends_add_request 返回)。 */\nexport interface DoubtFriendRequestInfo {\n flag: string;\n uin: number;\n nick?: string;\n source?: string;\n reason?: string;\n msg?: string;\n group_code?: string;\n time?: string;\n type: \"doubt\";\n}\n\n/** FriendApi 构造选项。 */\nexport interface FriendApiOptions {\n /** uid→uin 转换器(通常注入 GroupApi.uidToUin);缺省 uin 退化为 uid。 */\n uidToUin?: (uids: string[]) => Promise<Map<string, string>>;\n}\n\n/** 好友 API:从 session 拿 buddy service,包装成语义化方法。 */\nexport class FriendApi {\n private readonly service: NodeIKernelBuddyService;\n private readonly uidToUin: ((uids: string[]) => Promise<Map<string, string>>) | undefined;\n\n constructor(session: NodeIQQNTWrapperSession, opts: FriendApiOptions = {}) {\n const service = session.getBuddyService() as unknown as NodeIKernelBuddyService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getBuddyService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.uidToUin = opts.uidToUin;\n }\n\n /** 好友列表(分类拍平为 Friend[];昵称/备注从 service 缓存读取)。 */\n async getFriendList(): Promise<Friend[]> {\n let categories: BuddyCategory[];\n try {\n categories = await this.service.getBuddyListFromCache(0);\n } catch {\n const raw = await this.service.getBuddyListV2(\"cli\", true, 0);\n if (raw.result !== 0) {\n throw kernelError(`getBuddyListV2 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n categories = raw.data;\n }\n // buddyList(明细,T10 实证)优先,buddyUids(旧描述字段)兜底\n const uids = categories.flatMap(\n (cat) => cat.buddyList?.map((b) => b.uid) ?? cat.buddyUids ?? [],\n );\n let uinMap = new Map<string, string>();\n if (this.uidToUin !== undefined) {\n uinMap = await this.uidToUin(uids);\n }\n const out: Friend[] = [];\n for (const uid of uids) {\n out.push({\n uid,\n uin: uinMap.get(uid) ?? uid,\n nickname: this.service.getBuddyNick(uid),\n remark: this.service.getBuddyRemark(uid),\n });\n }\n return out;\n }\n\n /** 带分类的好友列表(get_friends_with_category,分类保留)。 */\n async getFriendCategories(): Promise<FriendCategory[]> {\n const categories = await this.getBuddyCategories();\n const friendMap = new Map<string, Friend>();\n for (const friend of await this.getFriendList()) {\n friendMap.set(friend.uid, friend);\n }\n return categories.map((cat) => {\n const buddies: Friend[] = [];\n for (const uid of cat.buddyList?.map((b) => b.uid) ?? cat.buddyUids ?? []) {\n const friend = friendMap.get(uid);\n if (friend !== undefined) {\n buddies.push(friend);\n }\n }\n return {\n categoryId: cat.categoryId,\n categorySortId: cat.categorySortId,\n categoryName: cat.categroyName,\n categoryMbCount: cat.categroyMbCount,\n onlineCount: cat.onlineCount,\n buddyList: buddies,\n };\n });\n }\n\n /** 原始分类(getBuddyListV2 / 缓存)。 */\n private async getBuddyCategories(): Promise<BuddyCategory[]> {\n try {\n return await this.service.getBuddyListFromCache(0);\n } catch {\n const raw = await this.service.getBuddyListV2(\"cli\", true, 0);\n if (raw.result !== 0) {\n throw kernelError(`getBuddyListV2 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw.data;\n }\n }\n\n /** 判断是否好友。 */\n isBuddy(uid: string): boolean {\n return this.service.isBuddy(uid);\n }\n\n /** 好友申请列表(set_friend_add_request 应答前查找匹配项)。 */\n async getBuddyReqList(): Promise<BuddyReq[]> {\n const raw = await this.service.getBuddyReq();\n unwrap(\"getBuddyReq\", raw.result, raw.errMsg);\n return raw.buddyReqs ?? [];\n }\n\n /** 应答好友申请(accept=true 同意;remark 由调用方另行 setFriendRemark)。 */\n async handleFriendRequest(notify: BuddyReq, accept: boolean): Promise<void> {\n await this.service.approvalFriendRequest({\n friendUid: notify.friendUid,\n reqTime: notify.reqTime,\n accept,\n });\n }\n\n /** 设置好友备注(void 语义乐观处理)。 */\n setFriendRemark(uid: string, remark: string): void {\n this.service.setBuddyRemark({ uid, remark });\n }\n\n /** 删除好友(tempBlock=拉黑,tempBothDel=双向删除)。 */\n async deleteFriend(uid: string, tempBlock = false, tempBothDel = false): Promise<void> {\n const res = (await this.service.delBuddy({\n friendUid: uid,\n tempBlock,\n tempBothDel,\n })) as { result?: unknown; errMsg?: unknown } | undefined;\n checkLooseResult(\"delBuddy\", res);\n }\n\n /** 可疑好友申请列表(get_doubt_friends_add_request;uin 经 uidToUin 转换)。 */\n async getDoubtFriendRequest(count: number): Promise<DoubtFriendRequestInfo[]> {\n const reqId = String(Date.now());\n const raw = await this.service.getDoubtBuddyReq(reqId, count, \"\");\n unwrap(\"getDoubtBuddyReq\", raw.result, raw.errMsg);\n const list: DoubtBuddyReq[] = raw.doubtList ?? [];\n let uinMap = new Map<string, string>();\n if (this.uidToUin !== undefined && list.length > 0) {\n uinMap = await this.uidToUin(list.map((item) => item.uid));\n }\n const out: DoubtFriendRequestInfo[] = [];\n for (const item of list) {\n out.push(this.toDoubtFriendRequestInfo(item, uinMap));\n }\n return out;\n }\n\n /** 单项转换:DoubtBuddyReq → OB11 结构(可选字段条件赋值,exactOptionalPropertyTypes)。 */\n private toDoubtFriendRequestInfo(\n item: DoubtBuddyReq,\n uinMap: Map<string, string>,\n ): DoubtFriendRequestInfo {\n return toDoubtFriendRequestInfo(item, uinMap);\n }\n\n /** 处理可疑好友申请(set_doubt_friends_add_request;uid=flag)。 */\n handleDoubtFriendRequest(uid: string): void {\n this.service.approvalDoubtBuddyReq(uid, \"\", \"\");\n }\n}\n\n/** 可选字段条件赋值(undefined 跳过,兼容 exactOptionalPropertyTypes)。 */\nfunction assignOptional<K extends keyof DoubtFriendRequestInfo>(\n info: DoubtFriendRequestInfo,\n key: K,\n value: DoubtFriendRequestInfo[K] | undefined,\n): void {\n if (value !== undefined) {\n info[key] = value;\n }\n}\n\n/** 单项转换:DoubtBuddyReq → OB11 结构(导出供单测,friend.test.ts)。 */\nexport function toDoubtFriendRequestInfo(\n item: DoubtBuddyReq,\n uinMap: Map<string, string>,\n): DoubtFriendRequestInfo {\n const info: DoubtFriendRequestInfo = {\n flag: item.uid,\n uin: Number(uinMap.get(item.uid) ?? item.uid),\n type: \"doubt\",\n };\n assignOptional(info, \"nick\", item.nick);\n assignOptional(info, \"source\", item.source);\n assignOptional(info, \"reason\", item.reason);\n assignOptional(info, \"msg\", item.msg);\n assignOptional(info, \"group_code\", item.groupCode);\n assignOptional(info, \"time\", item.reqTime);\n return info;\n}\n","/**\n * 实体类型层(运行时探测产物 + 公开资料作说明书,ADR-006)\n *\n * ⚠️ 部分字段按公开资料(wrapper 外部契约)自研描述,待下一次进程内\n * 探测(probe.ts)校准——接口签名是外部系统事实,自研描述其形状。\n */\n\n/** 会话类型(QQ wrapper 外部契约)。 */\nexport const ChatType = {\n C2C: 1, // 私聊\n GROUP: 2, // 群聊\n DISC: 3, // 讨论组\n GUILD: 4, // 频道\n C2C_TEMP: 100, // 临时会话(群内私聊)\n} as const;\nexport type ChatType = (typeof ChatType)[keyof typeof ChatType];\n\n/** 会话对象(peer 目标:群号 / 用户 uid)。 */\nexport interface Peer {\n chatType: ChatType;\n peerUid: string;\n guildId?: string;\n}\n\n/** 文本元素(at 用 atType + atUid 表达)。 */\nexport interface TextElement {\n content: string;\n atType?: number; // 1=全体 2=单人 4=我\n atUid?: string;\n}\n\n/** 图片元素(发送时 picPath 必须;接收含 url)。 */\nexport interface PicElement {\n picPath?: string;\n picUrl?: string;\n picType?: number;\n picWidth?: number;\n picHeight?: number;\n md5?: string;\n fileName?: string;\n sourcePath?: string;\n thumbPath?: string;\n // 发送用完整字段(NapCat 式,2026-08-11 实证:缺任一发送失败)\n md5HexStr?: string;\n fileSize?: string;\n original?: boolean;\n picSubType?: number;\n fileUuid?: string;\n fileSubId?: string;\n thumbFileSize?: number;\n summary?: string;\n}\n\n/** 语音元素(silk)。 */\nexport interface PttElement {\n filePath?: string;\n md5?: string;\n fileName?: string;\n duration?: number;\n /** 语音转文字结果(translatePtt2Text 异步写回)。 */\n text?: string;\n // 发送用完整字段(NapCat 式,2026-08-12 实证:只给 filePath 会触发 wrapper\n // 内部 \"Cannot convert undefined or null to object\"(缺字段转换失败))\n md5HexStr?: string;\n fileSize?: string;\n formatType?: number;\n voiceType?: number;\n voiceChangeType?: number;\n canConvert2Text?: boolean;\n waveAmplitudes?: number[];\n fileSubId?: string;\n playState?: number;\n autoConvertText?: number;\n storeID?: number;\n otherBusinessInfo?: { aiVoiceType: number };\n // 收向完整字段(2026-09-08 B1 实测快照,getLatestDbMsgs 历史消息提取)\n fileId?: number;\n fileUuid?: string;\n translateStatus?: number;\n /** 传输状态:实测 2=收到已下载(数据库态,此时挪走本地文件调 downloadRichMedia 不重下)、4=自己发送已上传。 */\n transferStatus?: number;\n progress?: number;\n invalidState?: number;\n fileBizId?: unknown;\n import_rich_media_context?: unknown;\n isInApplicationDataPath?: boolean | null;\n}\n\n/** 表情元素。 */\nexport interface FaceElement {\n faceIndex: number;\n faceType?: number;\n}\n\n/** 回复元素(引用消息)。 */\nexport interface ReplyElement {\n replayMsgId: string;\n replayMsgSeq?: string;\n replayMsgTime?: string;\n senderUid?: string;\n senderUin?: string;\n}\n\n/** 视频元素。 */\nexport interface VideoElement {\n filePath?: string;\n videoUrl?: string;\n fileName?: string;\n fileSize?: string;\n}\n\n/** 文件元素。 */\nexport interface FileElement {\n filePath?: string;\n fileName?: string;\n fileSize?: string;\n fileUuid?: string;\n}\n\n/** 灰色提示子类型(系统事件类型,QQ wrapper 契约)。 */\nexport const GrayTipSubType = {\n REVOKE: 1, // 撤回\n PROCLAMATION: 2, // 群公告\n GROUP: 4, // 群成员变动(TipGroupElement)\n BUDDY: 5, // 好友变动\n FEED: 6,\n ESSENCE: 7, // 精华消息\n GROUP_NOTIFY: 8, // 群通知\n BUDDY_NOTIFY: 9,\n FILE: 10,\n JSON: 17,\n} as const;\nexport type GrayTipSubType = (typeof GrayTipSubType)[keyof typeof GrayTipSubType];\n\n/** 群成员变动类型(TipGroupElement.type)。 */\nexport const TipGroupElementType = {\n MEMBER_ADD: 1, // 入群\n DISBANDED: 2, // 群解散\n QUIT: 3, // 退群\n CREATED: 4, // 建群\n GROUP_NAME_MODIFIED: 5, // 群名修改\n BLOCK: 6,\n UNBLOCK: 7,\n SHUT_UP: 8, // 禁言\n} as const;\nexport type TipGroupElementType = (typeof TipGroupElementType)[keyof typeof TipGroupElementType];\n\n/** 撤回元素(revokeElement)。 */\nexport interface GrayTipRevokeElement {\n operatorUid: string;\n operatorNick?: string;\n operatorRole?: string;\n isSelfOperate?: boolean;\n wording?: string;\n}\n\n/** AIOP 操作元素(戳一戳等,aioOpGrayTipElement)。 */\nexport interface TipAioOpGrayTipElement {\n operateType?: number;\n peerUid?: string;\n fromGrpCodeOfTmpChat?: string;\n}\n\n/** 群成员变动元素(groupElement 内的 TipGroupElement)。 */\nexport interface TipGroupElement {\n type?: TipGroupElementType;\n groupName?: string;\n memberUid?: string;\n memberNick?: string;\n memberRemark?: string;\n adminUid?: string;\n adminNick?: string;\n adminRemark?: string;\n shutUp?: {\n curTime?: string;\n duration?: string; // 禁言秒数\n admin?: { uid?: string; card?: string; name?: string; role?: number };\n member?: { uid?: string; card?: string; name?: string; role?: number };\n };\n}\n\n/** 灰色提示元素(elementType=8,系统事件载体)。 */\nexport interface GrayTipElement {\n subElementType?: GrayTipSubType;\n revokeElement?: GrayTipRevokeElement;\n groupElement?: TipGroupElement;\n aioOpGrayTipElement?: TipAioOpGrayTipElement;\n xmlElement?: { busiId?: string; content?: string; templId?: string };\n jsonGrayTipElement?: { busiId?: string; jsonStr?: string };\n}\n\n/** 消息元素(RawMessage.elements 成员,elementType + 子元素互斥)。 */\nexport interface RawElement {\n elementType: number;\n elementId?: string;\n textElement?: TextElement;\n picElement?: PicElement;\n pttElement?: PttElement;\n faceElement?: FaceElement;\n replyElement?: ReplyElement;\n videoElement?: VideoElement;\n fileElement?: FileElement;\n grayTipElement?: GrayTipElement;\n /** 合并转发元素(elementType=16 MULTI_FORWARD)。 */\n multiForwardMsgElement?: {\n resId: string;\n fileName: string;\n xmlContent: string;\n };\n}\n\n/** QQ 消息(RawMessage,说明书参考字段,探测后校准)。 */\nexport interface RawMessage {\n msgId: string; // 雪花 ID\n msgSeq: string; // 消息序列号\n msgTime: string; // 时间戳\n msgRandom?: string;\n msgType: number;\n chatType: ChatType;\n peerUid: string; // 群号 / 用户 uid\n peerUin: string; // 群号 / 用户 QQ 号\n senderUid: string;\n senderUin: string;\n peerName: string;\n sendNickName: string;\n sendMemberName?: string;\n /** 发送状态(0=失败 1=发送中 2=成功 3=成功无seq)。onMsgInfoListUpdate 事件填充。 */\n sendStatus?: number;\n elements: RawElement[];\n records?: RawMessage[];\n guildId?: string;\n [key: string]: unknown;\n}\n","/**\n * Listener 接口层(运行时探测产物 + 公开资料作说明书,ADR-003 / ADR-006)\n *\n * GroupListener:群服务(GroupService)的原生回调监听接口。\n * 签名依据 wrapper 外部契约自研描述(零复制)——事件名约定 `${Service}/${method}`,由 event-channel 编译期推导。\n * 用 type 别名(非 interface):满足 ListenerShape(Record<string, unknown>)约束。\n */\nimport type {\n Group,\n GroupDetailInfo,\n GroupMember,\n GroupNotify,\n ShutUpGroupMember,\n} from \"../services/group-service.js\";\n\n/** 群列表更新类型(说明书参考,值待探测校准)。 */\nexport const GroupListUpdateType = {\n /** 全量更新(groupList 为完整列表)。 */\n ALL: 0,\n /** 增量更新(合并到现有缓存)。 */\n PARTIAL: 1,\n} as const;\nexport type GroupListUpdateType = (typeof GroupListUpdateType)[keyof typeof GroupListUpdateType];\n\n/** 成员数据来源(onMemberInfoChange 第二参,说明书参考)。 */\nexport const GroupMemberDataSource = {\n /** 本地缓存。 */\n CACHE: 0,\n /** 网络拉取。 */\n NETWORK: 1,\n} as const;\nexport type GroupMemberDataSource =\n (typeof GroupMemberDataSource)[keyof typeof GroupMemberDataSource];\n\n/** onMemberListChange 参数(sceneId 为群号 groupCode)。 */\nexport interface GroupMemberListChange {\n sceneId: string;\n ids: string[];\n /** uid → GroupMember。 */\n infos: Map<string, GroupMember>;\n hasPrev: boolean;\n hasNext: boolean;\n hasRobot: boolean;\n}\n\n/** 群服务(GroupService)的原生回调监听接口。 */\nexport type GroupListener = {\n /** 群列表初始化完成(listEmpty:是否无群)。 */\n onGroupListInited: (listEmpty: boolean) => void;\n /** 群列表更新。 */\n onGroupListUpdate: (updateType: GroupListUpdateType, groupList: Group[]) => void;\n /** 群详情变化。 */\n onGroupDetailInfoChange: (detailInfo: GroupDetailInfo) => void;\n /** 群成员列表变化(sceneId=groupCode)。 */\n onMemberListChange: (arg: GroupMemberListChange) => void;\n /** 群成员信息变化(dataSource 区分本地缓存/网络)。 */\n onMemberInfoChange: (\n groupCode: string,\n dataSource: GroupMemberDataSource,\n members: Map<string, GroupMember>,\n ) => void;\n /** 群通知更新(doubt:是否可疑/忽略列表)。 */\n onGroupNotifiesUpdated: (doubt: boolean, notifies: GroupNotify[]) => void;\n /** 群通知单屏(分页 seq)。 */\n onGroupSingleScreenNotifies: (doubt: boolean, seq: string, notifies: GroupNotify[]) => void;\n /** 群禁言成员列表变化。 */\n onShutUpMemberListChanged: (groupCode: string, members: ShutUpGroupMember[]) => void;\n /**\n * 群精华列表变化(OB11 group_essence 候选源,2026-09-10 接线)。\n * 证据:wrapper.node 9.9.33-52230 字符串——小写名位于 listener 反射字符串簇,\n * 与已知 onMemberListChange/onBuddyReqChange 交错(同一字符串表页);\n * API 族旁证 addGroupEssence/getGroupLatestEssenceList。\n * ⚠️ 参数形状待真实事件校准(unknown 透传,订阅方 raw 日志观测)。\n */\n onGroupEssenceListChange: (arg: unknown) => void;\n};\n","/**\n * NodeIKernelMsgService:消息服务接口面(自研描述,非移植)\n *\n * 依据:getMsgService() 运行时反射 + wrapper 外部契约(接口签名是 QQ 的外部事实,\n * 自研描述其形状,零复制实现)。\n * 只收录 apis/msg 当前需要的方法;其余 200+ 方法按需探测后补齐。\n */\nimport type {\n FaceElement,\n Peer,\n PicElement,\n PttElement,\n RawMessage,\n ReplyElement,\n TextElement,\n} from \"../entities.js\";\n\n/** 原生通用返回:result 非 0 即失败(errMsg 附原因)。 */\nexport interface GeneralCallResult {\n result: number;\n errMsg: string;\n}\n\n/** 消息元素类型枚举(QQ wrapper 外部契约,自研描述)。 */\nexport const ElementType = {\n UNKNOWN: 0,\n TEXT: 1,\n PIC: 2,\n FILE: 3,\n PTT: 4,\n VIDEO: 5,\n FACE: 6,\n REPLY: 7,\n GRAY_TIP: 8,\n WALLET: 9,\n ARK: 10,\n MFACE: 11,\n LIVE_GIFT: 12,\n STRUCT_LONG_MSG: 13,\n MARKDOWN: 14,\n GIPHY: 15,\n MULTI_FORWARD: 16,\n INLINE_KEYBOARD: 17,\n INTEXT_GIFT: 18,\n CALENDAR: 19,\n} as const;\nexport type ElementType = (typeof ElementType)[keyof typeof ElementType];\n\n/** 发送消息元素(session 发送用,elementType + 各子元素互斥)。 */\nexport interface SendMessageElement {\n elementType: ElementType;\n elementId?: string;\n textElement?: TextElement;\n faceElement?: FaceElement;\n replyElement?: ReplyElement;\n picElement?: PicElement;\n pttElement?: PttElement;\n}\n\n/** 消息服务:apis/msg 用到的核心方法面。 */\nexport interface NodeIKernelMsgService {\n addKernelMsgListener(listener: unknown): number;\n removeKernelMsgListener(listenerId: number): void;\n generateMsgUniqueId(chatType: number, time: string): string;\n sendMsg(\n msgId: string,\n peer: Peer,\n elements: SendMessageElement[],\n map: Map<number, unknown>,\n ): Promise<GeneralCallResult>;\n recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;\n getMsgs(\n peer: Peer,\n msgId: string,\n count: number,\n queryOrder: boolean,\n ): Promise<GeneralCallResult & { msgList?: RawMessage[] }>;\n /** 按 msgId 批量拉取消息(get_msg / 精华消息 / ptt 转文字共用)。 */\n getMsgsByMsgId(\n peer: Peer,\n ids: string[],\n ): Promise<GeneralCallResult & { msgList?: RawMessage[] }>;\n /**\n * 富媒体主动下载(get_record 语音下载,2026-09-08 B1 实测:单参数对象,\n * resolve undefined——结果只能经重拉消息元素观察;参数残缺时静默 no-op 不抛错;\n * transferStatus=2 已下载态调用为 no-op,详见 kernel design.md §5)。\n */\n downloadRichMedia(param: {\n msgId: string;\n elemId: string;\n chatType: number;\n downloadType: number;\n thumbSize: number;\n }): Promise<void>;\n setMsgRead(peer: Peer): Promise<GeneralCallResult>;\n /** 消息表情表态(set_msg_emoji_like;setOrCancel=true 点赞,false 取消)。 */\n setMsgEmojiLikes(\n peer: Peer,\n msgSeq: string,\n emojiId: string,\n emojiType: string,\n setOrCancel: boolean,\n ): Promise<GeneralCallResult>;\n /** 语音转文字(fetch_ptt_text;异步转写,结果写回 pttElement.text)。 */\n translatePtt2Text(msgId: string, peer: Peer, msgElement: unknown): Promise<GeneralCallResult>;\n /** 输入状态(set_input_status;eventType=1 输入中,0 停止)。 */\n sendShowInputStatusReq(chatType: number, eventType: number, toUid: string): Promise<unknown>;\n /**\n * 富媒体文件目标路径(NapCat 式发送图片前置:md5+文件名 → QQ 内部 Ori/Thumb 路径)。\n * 返回纯文件名(相对 QQ 数据根),需配合 NodeQQNTWrapperUtil.copyFile 放置文件。\n */\n getRichMediaFilePathForGuild(req: {\n md5HexStr: string;\n fileName: string;\n elementType: number;\n elementSubType: number;\n thumbSize: number;\n needCreate: boolean;\n downloadType: number;\n file_uuid: string;\n }): string;\n /** 合并转发组装(send_group/private_forward_msg;返回 MULTI_FORWARD 元素)。 */\n buildMultiForwardMsg(req: {\n srcMsgIds: string[];\n srcContact: Peer;\n }): Promise<GeneralCallResult & { rspInfo?: { elements?: SendMessageElement[] } }>;\n /** 获取合并转发内容(get_forward_msg;resId 取自 multiForwardMsgElement)。 */\n getMultiMsg(\n peer: Peer,\n msgId: string,\n resId: string,\n ): Promise<GeneralCallResult & { msgList?: RawMessage[] }>;\n /** 单条转发(forward_group/friend_single_msg;msgIds 源、dstPeers 目标)。 */\n forwardMsg(\n msgIds: string[],\n peer: Peer,\n dstPeers: Peer[],\n commentElements: unknown,\n ): Promise<GeneralCallResult>;\n /** 设置在线状态(set_online_status;customStatus 为自定义状态)。 */\n setStatus(args: {\n status: number;\n extStatus: number;\n batteryStatus: number;\n customStatus?: {\n faceId: string;\n wording: string;\n faceType: string;\n };\n }): Promise<GeneralCallResult>;\n}\n","/**\n * canonical 消息元素模型(协议无关,ADR-008 延伸)\n *\n * 描述 QQ 消息的事实结构,是各协议翻译层的公共中间表示:\n * kernel 只做一次 NT ↔ canonical(toCanonicalElements / toSendElements),\n * 各协议(onebot11/satori)只写薄映射 canonical ↔ 协议格式。\n */\n\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n FaceElement,\n FileElement,\n PicElement,\n PttElement,\n RawElement,\n RawMessage,\n ReplyElement,\n TextElement,\n VideoElement,\n} from \"./entities.js\";\nimport { ElementType, type SendMessageElement } from \"./services/msg-service.js\";\n\n/** 文本 at 类型(QQ wrapper 契约,自研描述)。 */\nconst AtType = { ALL: 1, ONE: 2, ME: 4 } as const;\n\n/** TEXT 元素 → canonical(at 或纯文本)。 */\nfunction textToCanonical(textEl: TextElement | undefined): CanonicalElement {\n if (textEl === undefined) {\n return { type: \"text\", text: \"\" };\n }\n const { atType, atUid, content } = textEl;\n if (atType === AtType.ALL) {\n return { type: \"at\", target: \"all\" };\n }\n if (atType === AtType.ONE || atType === AtType.ME) {\n // 2026-08-07:at 段补 display(显示名)——QQ 的 content 通常是 \"@昵称\",\n // 剥掉 @ 作 display,供 OB11 at 段 name / 日志渲染使用(此前只有 target=uid)。\n const base = { type: \"at\" as const, target: atUid ?? content };\n const display = content.startsWith(\"@\") ? content.slice(1) : content;\n if (display === \"\") {\n return base;\n }\n return { ...base, display };\n }\n return { type: \"text\", text: content };\n}\n\n/** PIC 元素 → canonical image。 */\nfunction picToCanonical(pic: PicElement | undefined): CanonicalElement {\n if (pic === undefined) {\n return { type: \"image\", path: \"\" };\n }\n const path = pic.picPath ?? pic.picUrl ?? \"\";\n const url = pic.picUrl;\n if (url === undefined) {\n return { type: \"image\", path };\n }\n return { type: \"image\", path, url };\n}\n\n/** FACE 元素 → canonical face。 */\nfunction faceToCanonical(face: FaceElement | undefined): CanonicalElement {\n if (face === undefined) {\n return { type: \"face\", id: \"0\" };\n }\n return { type: \"face\", id: String(face.faceIndex) };\n}\n\n/** PTT 元素 → canonical voice。 */\nfunction pttToCanonical(ptt: PttElement | undefined): CanonicalElement {\n if (ptt === undefined) {\n return { type: \"voice\", path: \"\" };\n }\n return { type: \"voice\", path: ptt.filePath ?? \"\" };\n}\n\n/** VIDEO 元素 → canonical video。 */\nfunction videoToCanonical(video: VideoElement | undefined): CanonicalElement {\n if (video === undefined) {\n return { type: \"video\", path: \"\" };\n }\n const path = video.filePath ?? video.videoUrl ?? \"\";\n const url = video.videoUrl;\n if (url === undefined) {\n return { type: \"video\", path };\n }\n return { type: \"video\", path, url };\n}\n\n/** FILE 元素 → canonical file。 */\nfunction fileToCanonical(file: FileElement | undefined): CanonicalElement {\n if (file === undefined) {\n return { type: \"file\", path: \"\" };\n }\n const path = file.filePath ?? \"\";\n const name = file.fileName;\n if (name === undefined) {\n return { type: \"file\", path };\n }\n return { type: \"file\", path, name };\n}\n\n/** REPLY 元素 → canonical reply。 */\nfunction replyToCanonical(reply: ReplyElement | undefined): CanonicalElement {\n if (reply === undefined) {\n return { type: \"reply\", messageId: \"\" };\n }\n return { type: \"reply\", messageId: reply.replayMsgId };\n}\n\n/** 单个 NT 元素 → canonical(接收方向,宽容:无法表达回 unknown,不抛错)。 */\nfunction toCanonicalElement(el: RawElement): CanonicalElement {\n switch (el.elementType) {\n case ElementType.TEXT:\n return textToCanonical(el.textElement);\n case ElementType.PIC:\n return picToCanonical(el.picElement);\n case ElementType.FACE:\n return faceToCanonical(el.faceElement);\n case ElementType.PTT:\n return pttToCanonical(el.pttElement);\n case ElementType.VIDEO:\n return videoToCanonical(el.videoElement);\n case ElementType.FILE:\n return fileToCanonical(el.fileElement);\n case ElementType.REPLY:\n return replyToCanonical(el.replyElement);\n default:\n return { type: \"unknown\", raw: el };\n }\n}\n\n/** NT RawMessage → canonical 元素(与 toSendElements 对称,接收方向)。 */\nexport function toCanonicalElements(msg: RawMessage): CanonicalElement[] {\n return msg.elements.map(toCanonicalElement);\n}\n\n/** canonical 元素 → NT 发送元素(text/at/face/image/voice/reply 核心五类)。 */\nexport function toSendElements(elements: CanonicalElement[]): SendMessageElement[] {\n const out: SendMessageElement[] = [];\n for (const el of elements) {\n switch (el.type) {\n case \"text\":\n out.push({ elementType: ElementType.TEXT, textElement: { content: el.text } });\n break;\n case \"at\":\n if (el.target === \"all\") {\n out.push({\n elementType: ElementType.TEXT,\n textElement: { content: el.display ?? \"@\", atType: 1 },\n });\n } else {\n out.push({\n elementType: ElementType.TEXT,\n textElement: { content: el.display ?? \"@\", atType: 2, atUid: el.target },\n });\n }\n break;\n case \"face\":\n out.push({\n elementType: ElementType.FACE,\n faceElement: { faceIndex: Number(el.id) },\n });\n break;\n case \"image\":\n out.push({ elementType: ElementType.PIC, picElement: { picPath: el.path } });\n break;\n case \"voice\":\n out.push({ elementType: ElementType.PTT, pttElement: { filePath: el.path } });\n break;\n case \"reply\":\n out.push({\n elementType: ElementType.REPLY,\n replyElement: {\n replayMsgId: el.messageId,\n replayMsgSeq: el.messageId,\n replayMsgTime: \"0\",\n },\n });\n break;\n case \"video\":\n case \"file\":\n case \"forward\":\n case \"json\":\n case \"xml\":\n case \"unknown\":\n throw kernelError(`发送元素 ${el.type} 暂不支持(P2-2 探测后补)`, \"INVALID_PARAM\");\n default:\n // exhaustive:CanonicalElement 无其他成员\n break;\n }\n }\n return out;\n}\n\n/** 协议无关的规范消息元素。 */\nexport type CanonicalElement =\n | { type: \"text\"; text: string }\n | { type: \"at\"; target: string; display?: string }\n | { type: \"image\"; path: string; url?: string; size?: { width: number; height: number } }\n | { type: \"face\"; id: string }\n | { type: \"voice\"; path: string; durationMs?: number; url?: string }\n | { type: \"video\"; path: string; url?: string }\n | { type: \"file\"; path: string; name?: string; size?: number }\n | { type: \"reply\"; messageId: string }\n | { type: \"forward\"; messageIds: string[] }\n | { type: \"json\"; raw: unknown }\n | { type: \"xml\"; raw: unknown }\n | { type: \"unknown\"; raw: unknown };\n","/**\n * NodeIKernelGroupService:群服务接口面(自研描述,非移植)\n *\n * 依据:getGroupService() 运行时反射 + wrapper 外部契约(接口签名是 QQ 的外部事实,\n * 自研描述其形状,零复制实现)。\n * 只收录 apis/group 当前需要的方法;其余按需探测后补齐。\n */\nimport type { GeneralCallResult } from \"./msg-service.js\";\n\n/** 群成员角色(QQ wrapper 契约)。 */\nexport const NTGroupMemberRole = {\n UNSPECIFIED: 0,\n STRANGER: 1,\n MEMBER: 2,\n ADMIN: 3,\n OWNER: 4,\n} as const;\nexport type NTGroupMemberRole = (typeof NTGroupMemberRole)[keyof typeof NTGroupMemberRole];\n\n/** 踢人请求项(KickMemberV2Req.kickList 成员,说明书参考,字段值待探测校准)。 */\nexport interface KickMemberInfo {\n optFlag: number;\n optOperate: number;\n optMemberUid: string;\n optBytesMsg: string;\n}\n\n/** 踢人请求(set_group_kick 的 kickMemberV2 参数,说明书参考)。 */\nexport interface KickMemberV2Req {\n groupCode: string;\n kickFlag: number;\n kickList: KickMemberInfo[];\n kickListUids: string[];\n kickMsg: string;\n}\n\n/** @all 剩余次数(getGroupRemainAtTimes 返回)。 */\nexport interface GroupRemainAtTimes {\n canAtAll: boolean;\n remainAtAllCountForUin: number;\n remainAtAllCountForGroup: number;\n atTimesMsg: string;\n canNotAtAllMsg: string;\n}\n\n/** 群通知类型(GroupNotifyMsgType,说明书参考)。 */\nexport const GroupNotifyMsgType = {\n UN_SPECIFIED: 0,\n INVITED_BY_MEMBER: 1, // 邀请入群\n REFUSE_INVITED: 2,\n REFUSED_BY_ADMINI_STRATOR: 3,\n AGREED_TOJOIN_DIRECT: 4,\n INVITED_NEED_ADMINI_STRATOR_PASS: 5,\n AGREED_TO_JOIN_BY_ADMINI_STRATOR: 6,\n REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS: 7, // 申请入群\n SET_ADMIN: 8,\n KICK_MEMBER_NOTIFY_ADMIN: 9,\n KICK_MEMBER_NOTIFY_KICKED: 10,\n MEMBER_LEAVE_NOTIFY_ADMIN: 11,\n CANCEL_ADMIN_NOTIFY_CANCELED: 12,\n CANCEL_ADMIN_NOTIFY_ADMIN: 13,\n TRANSFER_GROUP_NOTIFY_OLDOWNER: 14,\n TRANSFER_GROUP_NOTIFY_ADMIN: 15,\n} as const;\nexport type GroupNotifyMsgType = (typeof GroupNotifyMsgType)[keyof typeof GroupNotifyMsgType];\n\n/** 群通知处理状态(GroupNotifyMsgStatus,说明书参考)。 */\nexport const GroupNotifyMsgStatus = {\n KINIT: 0,\n KUNHANDLE: 1, // 未处理\n KAGREED: 2,\n KREFUSED: 3,\n KIGNORED: 4,\n} as const;\nexport type GroupNotifyMsgStatus = (typeof GroupNotifyMsgStatus)[keyof typeof GroupNotifyMsgStatus];\n\n/** 群请求操作类型(NTGroupRequestOperateTypes,说明书参考)。 */\nexport const NTGroupRequestOperateTypes = {\n KUNSPECIFIED: 0,\n KAGREE: 1,\n KREFUSE: 2,\n KIGNORE: 3,\n KDELETE: 4,\n} as const;\nexport type NTGroupRequestOperateTypes =\n (typeof NTGroupRequestOperateTypes)[keyof typeof NTGroupRequestOperateTypes];\n\n/** 群通知(getSingleScreenNotifies 返回项,说明书参考,待探测校准)。 */\nexport interface GroupNotify {\n seq: string;\n type: GroupNotifyMsgType;\n status: GroupNotifyMsgStatus;\n group?: { groupCode: string; groupName: string };\n user1?: { uid: string; nickName: string };\n user2?: { uid: string; nickName: string };\n actionTime?: string;\n postscript?: string;\n warningTips?: string;\n}\n\n/** 禁言成员(getGroupShutUpMemberList 返回项,说明书参考,待探测校准)。 */\nexport interface ShutUpGroupMember {\n uid: string;\n qid: string;\n uin: string;\n nick: string;\n remark: string;\n cardName: string;\n shutUpTime: number;\n memberSpecialTitle?: string;\n}\n\n/** 群列表项(Group 实体,说明书参考)。 */\nexport interface Group {\n groupCode: string;\n groupName: string;\n memberCount: number;\n maxMember: number;\n remarkName?: string;\n groupOwnerId?: { memberUin: string; memberUid: string };\n memberRole?: number;\n isTop?: boolean;\n [key: string]: unknown;\n}\n\n/** 群详情(getGroupDetailInfo 返回,说明书参考)。 */\nexport interface GroupDetailInfo {\n groupCode: string;\n groupUin: string;\n ownerUin: string;\n ownerUid?: string;\n groupName: string;\n memberNum: number;\n maxMemberNum: number;\n remarkName?: string;\n isTop?: boolean;\n groupCreateTime?: number;\n [key: string]: unknown;\n}\n\n/** 群成员(getAllMemberList result.infos 的 value,说明书参考)。 */\nexport interface GroupMember {\n uid: string;\n uin: string;\n nick: string;\n cardName: string;\n remark: string;\n role: NTGroupMemberRole;\n shutUpTime: number; // 禁言秒数\n joinTime: string;\n lastSpeakTime: string;\n memberSpecialTitle?: string;\n sex?: number;\n age?: number;\n isRobot?: boolean;\n [key: string]: unknown;\n}\n\n/** 群服务:apis/group 用到的核心方法面。 */\nexport interface NodeIKernelGroupService {\n addKernelGroupListener(listener: unknown): number;\n removeKernelGroupListener(listenerId: number): void;\n getGroupList(force: boolean): Promise<GeneralCallResult>;\n getGroupDetailInfo(groupCode: string, groupInfoSource: number): Promise<GeneralCallResult>;\n getAllMemberList(\n groupCode: string,\n forceFetch: boolean,\n ): Promise<{\n errCode: number;\n errMsg: string;\n result: {\n ids: Array<{ uid: string; index: number }>;\n infos: Map<string, GroupMember>;\n finish: boolean;\n };\n }>;\n getMemberInfo(\n groupCode: string,\n uids: string[],\n forceFetch: boolean,\n ): Promise<GeneralCallResult>;\n /** uin → uid(私聊发送等需要,说明书参考)。 */\n getUidByUins(\n uins: string[],\n ): Promise<{ errCode: number; errMsg: string; uids: Map<string, string> }>;\n /** uid → uin。 */\n getUinByUids(\n uids: string[],\n ): Promise<{ errCode: number; errMsg: string; uins: Map<string, string> }>;\n /** 踢人(set_group_kick)。 */\n kickMemberV2(param: KickMemberV2Req): Promise<GeneralCallResult>;\n /** 成员禁言(set_group_ban;timeStamp=0 解除禁言)。 */\n setMemberShutUp(\n groupCode: string,\n memberTimes: Array<{ uid: string; timeStamp: number }>,\n ): Promise<GeneralCallResult>;\n /** 全员禁言(set_group_whole_ban)。 */\n setGroupShutUp(groupCode: string, shutUp: boolean): Promise<GeneralCallResult>;\n /** 设置管理员(set_group_admin;void 语义,乐观处理)。 */\n modifyMemberRole(groupCode: string, uid: string, role: NTGroupMemberRole): void;\n /** 设置群名片(set_group_card;void 语义)。 */\n modifyMemberCardName(groupCode: string, uid: string, cardName: string): void;\n /** 修改群名(set_group_name)。 */\n modifyGroupName(\n groupCode: string,\n groupName: string,\n isNormalMember: boolean,\n ): Promise<GeneralCallResult>;\n /** 退群(set_group_leave)。 */\n quitGroupV2(param: {\n groupCode: string;\n needDeleteLocalMsg: boolean;\n }): Promise<GeneralCallResult>;\n /** 设置精华消息(msgSeq/msgRandom 取自消息本体)。 */\n addGroupEssence(param: {\n groupCode: string;\n msgRandom: number;\n msgSeq: number;\n }): Promise<unknown>;\n /** 取消精华消息。 */\n removeGroupEssence(param: {\n groupCode: string;\n msgRandom: number;\n msgSeq: number;\n }): Promise<unknown>;\n /** @all 剩余次数(get_group_at_all_remain)。 */\n getGroupRemainAtTimes(groupCode: string): Promise<\n Omit<GeneralCallResult, \"result\"> & {\n errCode: number;\n atInfo: GroupRemainAtTimes;\n }\n >;\n /** 单屏通知列表(get_group_system_msg / set_group_add_request 共用;列表可能走 listener,宽松解包)。 */\n getSingleScreenNotifies(\n doubt: boolean,\n startSeq: string,\n count: number,\n ): Promise<GeneralCallResult & { result?: unknown }>;\n /** 禁言成员列表(get_group_shut_list;同 listener 风格,宽松解包)。 */\n getGroupShutUpMemberList(groupCode: string): Promise<GeneralCallResult & { result?: unknown }>;\n /** 处理群请求(set_group_add_request 应答;postscript 空值可能导致失败,默认空格)。 */\n operateSysNotify(\n doubt: boolean,\n operateMsg: {\n operateType: NTGroupRequestOperateTypes;\n targetMsg: {\n seq: string;\n type: GroupNotifyMsgType;\n groupCode: string;\n postscript: string;\n };\n },\n ): Promise<void>;\n}\n","/**\n * wrapper.node 的 NAPI 接口面(自研描述,非移植)\n *\n * 来源:wrapper.node 在 QQ 定制版 Electron 里通过 napi_define_class / napi_set_named_property\n * 注册的 JS 类(2026-08-05 Ghidra 反编译 FUN_180c6274a / FUN_180c80224 确认类名)。\n * 这些接口是 wrapper.node 对外暴露的外部系统契约——我们自研描述其形状,未复制任何第三方代码。\n *\n * 注意:这里所有对象由 QQ 运行时的 NAPI 层自动构建/转换,业务层禁止触碰 C++ ABI。\n */\n\n/** 平台类型(与 wrapper.node 的 platform 字段对应)。 */\nexport const PlatformType = {\n KUNKNOWN: 0,\n KANDROID: 1,\n KIOS: 2,\n KWINDOWS: 3,\n KMAC: 4,\n KLINUX: 5,\n} as const;\nexport type PlatformType = (typeof PlatformType)[keyof typeof PlatformType];\n\n/** 厂商类型(deviceInfo.vendorType)。 */\nexport const VendorType = {\n KNOSETONIOS: 0,\n KUNSUPPORTANDROIDPUSH: 1,\n KSUPPORTTPNS: 2,\n KSUPPORTHMS: 3,\n KSUPPORTOPPOPUSH: 4,\n KSUPPORTVIVOPUSH: 5,\n KSUPPORTGOOGLEPUSH: 99,\n} as const;\nexport type VendorType = (typeof VendorType)[keyof typeof VendorType];\n\n/** NodeQQNTWrapperUtil:通用工具类(静态服务)。 */\nexport interface NodeQQNTWrapperUtil {\n get(): NodeQQNTWrapperUtil;\n getNTUserDataInfoConfig(): string;\n // 文件/哈希工具(按需补齐,运行时反射枚举为准)\n getFileSize(path: string): Promise<number>;\n fileIsExist(path: string): unknown;\n makeDirByPath(path: string): unknown;\n}\n\n/** engine 初始化配置(initWithDeskTopConfig 第一参,JS 对象直接传)。 */\nexport interface EnginInitDesktopConfig {\n base_path_prefix: string;\n platform_type: PlatformType;\n /** 桌面端应用类型(engine.initWithDeskTopConfig 契约,桌面端固定 4;移动端为其它值)。 */\n app_type: 4;\n app_version: string;\n os_version: string;\n use_xlog: boolean;\n qua: string;\n global_path_config: {\n desktopGlobalPath: string;\n };\n /**\n * 缩略图生成参数(wrapper 契约):maxSide 最长边上限 / minSide 最短边下限 /\n * longLimit 长图最长短边比例上限 / density 像素密度倍数。\n */\n thumb_config: { maxSide: 324; minSide: 48; longLimit: 6; density: 2 };\n}\n\n/** NodeIQQNTWrapperEngine 实例方法。 */\nexport interface NodeIQQNTWrapperEngine {\n initWithDeskTopConfig(config: EnginInitDesktopConfig, adapter: NodeIGlobalAdapter): void;\n initWithMobileConfig(config: unknown, adapter: NodeIGlobalAdapter): void;\n initLog(arg: unknown): void;\n setLogLevel(arg: unknown): void;\n onSendSSOReply(a: unknown, b: unknown, c: unknown, d: unknown, e: unknown): void;\n}\n\n/** NodeIQQNTWrapperEngine 构造器(含静态 get())。 */\nexport type NodeIQQNTWrapperEngineCtor = new () => NodeIQQNTWrapperEngine & {\n get(): NodeIQQNTWrapperEngine;\n};\n\n/** desktopPathConfig 结构。 */\nexport interface DesktopPathConfig {\n account_path: string;\n}\n\n/** rdeliveryConfig 结构。 */\nexport interface RDeliveryConfig {\n appKey: string;\n systemId: number;\n appId: string;\n logicEnvironment: string;\n platform: PlatformType;\n language: string;\n sdkVersion: string;\n userId: string;\n appVersion: string;\n osVersion: string;\n bundleId: string;\n serverUrl: string;\n fixedAfterHitKeys: string[];\n}\n\n/** deviceInfo 结构。 */\nexport interface DeviceInfo {\n guid: string;\n buildVer: string;\n localId: number;\n devName: string;\n devType: string;\n vendorName: string;\n osVer: string;\n vendorOsName: string;\n setMute: boolean;\n vendorType: VendorType;\n}\n\n/** session.init 的完整配置(JS 对象,NAPI 自动转换)。 */\nexport interface WrapperSessionInitConfig {\n selfUin: string;\n selfUid: string;\n desktopPathConfig: DesktopPathConfig;\n clientVer: string;\n a2: string;\n d2: string;\n d2Key: string;\n machineId: string;\n platform: PlatformType;\n platVer: string;\n appid: string;\n rdeliveryConfig: RDeliveryConfig;\n defaultFileDownloadPath: string;\n deviceInfo: DeviceInfo;\n deviceConfig: string;\n}\n\n/** NodeIQQNTWrapperSession 构造器(含静态 get / getNTWrapperSession / create)。 */\nexport type NodeIQQNTWrapperSessionCtor = {\n new (): NodeIQQNTWrapperSession;\n get(): NodeIQQNTWrapperSession;\n getNTWrapperSession(name: string): NodeIQQNTWrapperSession;\n create(): NodeIQQNTWrapperSession;\n};\n\n/** NodeIQQNTWrapperSession:会话主入口(实例方法)。 */\nexport interface NodeIQQNTWrapperSession {\n init(\n config: WrapperSessionInitConfig,\n depends: NodeIDependsAdapter,\n dispatcher: NodeIDispatcherAdapter,\n listener: NodeIKernelSessionListener,\n ): void;\n startNT(sessionId?: number): void;\n close(arg: unknown): void;\n onLine(arg: unknown): void;\n offLine(arg: unknown): void;\n disableIpDirect(arg: unknown): void;\n getAccountPath(arg: unknown): string;\n updateTicket(arg: unknown): void;\n // 60+ getService:运行时反射枚举后补齐\n getMsgService(): unknown;\n getGroupService(): unknown;\n getBuddyService(): unknown;\n getTicketService(): unknown;\n getRichMediaService(): unknown;\n /** 闪传服务(诊断用:调查 rich media transfer failed 时验证初始化触发点)。 */\n getFlashTransferService(): unknown;\n getProfileService(): unknown;\n getProfileLikeService(): unknown;\n getTipOffService(): unknown;\n}\n\n/** NodeIQQNTStartupSessionWrapper:启动会话实例方法。 */\nexport interface NodeIQQNTStartupSessionWrapper {\n stop(): void;\n start(): void;\n createWithModuleList(uk: unknown): unknown;\n getSessionIdList(): Promise<Map<unknown, unknown>>;\n}\n\n/** NodeIQQNTStartupSessionWrapper 构造器(含静态 create)。 */\nexport type NodeIQQNTStartupSessionWrapperCtor = new () => NodeIQQNTStartupSessionWrapper & {\n create(): NodeIQQNTStartupSessionWrapper;\n};\n\n/** NodeIKernelLoginService 实例方法。 */\nexport interface NodeIKernelLoginService {\n addKernelLoginListener(listener: unknown): number;\n removeKernelLoginListener(listener: number): void;\n initConfig(config: unknown): void;\n connect(): void;\n getLoginList(): Promise<{ result: number; LocalLoginInfoList: unknown[] }>;\n quickLoginWithUin(uin: string): Promise<unknown>;\n getQRCodePicture(): boolean;\n getMsfStatus(): number;\n /** 设备指纹 guid(wrapper.node 字符串分析实证,2026-08-12;无 getMachineId)。 */\n getMachineGuid(): string;\n}\n\n/** NodeIKernelLoginService 构造器(含静态 get)。 */\nexport type NodeIKernelLoginServiceCtor = new () => NodeIKernelLoginService & {\n get(): NodeIKernelLoginService;\n};\n\n/** NodeIO3MiscService:O3 杂项服务。 */\nexport interface NodeIO3MiscService {\n get(): NodeIO3MiscService;\n addO3MiscListener(listener: unknown): number;\n}\n\n// adapter 类型(session.init 第二/三参、engine.initWithDeskTopConfig 第二参)\n// 注意:wrapper exports **不含** NodeI*Adapter / NodeI*Listener 构造器(实测 89 键无)。\n// 传普通 JS 对象即可——NAPI 反射读取对象上的方法回调。方法名是 QQ wrapper 的外部契约\n//(2026-08-05 运行时行为实测确认,接口为自研描述)。\nexport interface NodeIGlobalAdapter {\n onLog?(...args: unknown[]): void;\n onGetSrvCalTime?(...args: unknown[]): void;\n onShowErrUITips?(...args: unknown[]): void;\n fixPicImgType?(...args: unknown[]): unknown;\n getAppSetting?(...args: unknown[]): unknown;\n onInstallFinished?(...args: unknown[]): void;\n onUpdateGeneralFlag?(...args: unknown[]): void;\n onGetOfflineMsg?(...args: unknown[]): void;\n}\n\n/** session.init 第二参:依赖回调(普通 JS 对象)。 */\nexport interface NodeIDependsAdapter {\n onMSFStatusChange?(statusType: number, changeReasonType: number): void;\n onMSFSsoError?(code: number, desc: string): void;\n getGroupCode?(...args: unknown[]): unknown;\n}\n\n/** session.init 第三参:请求分发回调(普通 JS 对象)。 */\nexport interface NodeIDispatcherAdapter {\n dispatchRequest?(arg: unknown): void;\n dispatchCall?(arg: unknown): void;\n dispatchCallWithJson?(arg: unknown): void;\n}\n\n/** 登录监听器接口(普通 JS 对象,传给 loginService.addKernelLoginListener)。自研描述。 */\nexport interface IKernelLoginListener {\n onLoginConnected?(...args: unknown[]): void;\n onLoginDisConnected?(...args: unknown[]): void;\n onLoginConnecting?(...args: unknown[]): void;\n onQRCodeGetPicture?(arg: { pngBase64QrcodeData: string; qrcodeUrl: string }): void;\n onQRCodeLoginPollingStarted?(...args: unknown[]): void;\n onQRCodeSessionUserScaned?(...args: unknown[]): void;\n onQRCodeLoginSucceed?(loginResult: { uid: string; uin: string; nick?: string }): void;\n onQRCodeSessionFailed?(errType: number, errCode: number, errMsg: string): void;\n onLoginFailed?(args: unknown): void;\n onLogoutSucceed?(...args: unknown[]): void;\n onLogoutFailed?(...args: unknown[]): void;\n onUserLoggedIn?(userid: string): void;\n onQRCodeSessionQuickLoginFailed?(...args: unknown[]): void;\n onPasswordLoginFailed?(...args: unknown[]): void;\n onQQLoginNumLimited?(...args: unknown[]): void;\n onLoginState?(...args: unknown[]): void;\n}\n\n/**\n * NodeIKernelSessionListener:session.init 第四参监听器(普通 JS 对象)。\n * init 完成信号:以 onOpentelemetryInit(is_init===true) 为准(onSessionInitComplete 为辅)。\n */\nexport interface NodeIKernelSessionListener {\n onNTSessionCreate?(sessionId: string): void;\n onGProSessionCreate?(sessionId: string): void;\n onSessionInitComplete?(result: unknown): void;\n onOpentelemetryInit?(info: { is_init: boolean; is_report: boolean }): void;\n onUserOnlineResult?(result: unknown): void;\n onGetSelfTinyId?(result: unknown): void;\n}\n\n/** wrapper.node 的 NAPI 顶层导出(module.exports)。 */\nexport interface WrapperNodeApi {\n NodeIO3MiscService: NodeIO3MiscService;\n NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;\n /** 构造器:`new wrapper.NodeIQQNTWrapperSession()`。 */\n NodeIQQNTWrapperSession: NodeIQQNTWrapperSessionCtor;\n NodeIQQNTStartupSessionWrapper: NodeIQQNTStartupSessionWrapperCtor;\n /** 构造器:`new wrapper.NodeIQQNTWrapperEngine()`。 */\n NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngineCtor;\n /** 构造器:`new wrapper.NodeIKernelLoginService()`。 */\n NodeIKernelLoginService: NodeIKernelLoginServiceCtor;\n // 注:wrapper exports **不含** NodeI*Adapter / NodeI*Listener 构造器(实测 89 键无)——\n // adapter/listener 一律传普通 JS 对象(见 NodeIGlobalAdapter 等接口)。\n}\n","/**\n * GroupApi:群语义化 API(ADR-009 统一错误语义)\n *\n * 内部解包原生返回:成功返回纯业务值,失败抛 KernelError。\n * 方法面(P2-4 + P2-10):群列表 / 群详情 / 成员列表 / 成员详情 / uin↔uid +\n * 群管操作(踢人 / 禁言 / 管理员 / 名片 / 群名 / 退群 / 精华消息 / @all 剩余)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n Group,\n GroupDetailInfo,\n GroupMember,\n KickMemberV2Req,\n NodeIKernelGroupService,\n NodeIKernelMsgService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\nimport { ChatType, NTGroupMemberRole } from \"../types/index.js\";\nimport { checkLooseResult, unwrap } from \"./result.js\";\n\n/** 群 API:从 session 拿 group service,包装成语义化方法。 */\nexport class GroupApi {\n private readonly service: NodeIKernelGroupService;\n /** 精华消息需要 msg service(按 msgId 拉消息取 msgSeq/msgRandom)。 */\n private readonly msgService: NodeIKernelMsgService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getGroupService() as unknown as NodeIKernelGroupService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getGroupService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n const msgService = session.getMsgService() as unknown as NodeIKernelMsgService | null;\n if (msgService === null || msgService === undefined) {\n throw kernelError(\"getMsgService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.msgService = msgService;\n }\n\n /**\n * 群列表(force=true 触发原生刷新)。\n *\n * ⚠️ 实测校准(2026-08-08 端到端联调):原生 getGroupList 返回仅\n * `{ result, errMsg }`——**列表数据经 onGroupListUpdate 事件推送**,\n * 不走返回值。消费群列表一律从 `GroupCache.listGroups()` 读(缓存由\n * 事件主动维护);本方法仅作「触发刷新」用,返回值恒为 []。\n */\n async getGroupList(force = false): Promise<Group[]> {\n const raw = await this.service.getGroupList(force);\n if (raw.result !== 0) {\n unwrap(\"getGroupList\", raw.result, raw.errMsg);\n }\n return [];\n }\n\n /** 群详情(groupCode 为群号字符串)。\n * getGroupDetailInfo 返回形状:失败 result=错误码;成功详情可能直接在 result\n * 对象里,也可能在 groupInfo/detailInfo 字段——三种形状都兼容(探测校准)。\n * 2026-08-07 修复:成功(result=0)时不再把 {result:0,errMsg} 原样当详情返回,\n * 而是提取详情字段;形状异常抛 UNKNOWN 而非静默返回垃圾数据。 */\n async getGroupInfo(groupCode: string): Promise<GroupDetailInfo> {\n const raw = await this.service.getGroupDetailInfo(groupCode, 0);\n const { result, errMsg } = raw as { result?: unknown; errMsg: string };\n if (typeof result === \"number\" && result !== 0) {\n unwrap(\"getGroupDetailInfo\", result, errMsg);\n }\n const detail = extractGroupDetail(result, raw);\n if (detail === null) {\n throw kernelError(\n `getGroupDetailInfo 返回形状异常(groupCode=${groupCode}): ${errMsg}`,\n \"UNKNOWN\",\n );\n }\n return detail;\n }\n\n /** 群成员列表(forceFetch=true 强制拉取,默认缓存优先)。 */\n async getGroupMemberList(groupCode: string, forceFetch = false): Promise<GroupMember[]> {\n const raw = await this.service.getAllMemberList(groupCode, forceFetch);\n if (raw.errCode !== 0) {\n throw kernelError(`getAllMemberList 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return [...raw.result.infos.values()];\n }\n\n /** 群成员详情(uids 为空返回空数组)。 */\n async getGroupMemberInfo(groupCode: string, uids: string[]): Promise<GroupMember[]> {\n if (uids.length === 0) {\n return [];\n }\n const raw = await this.service.getMemberInfo(groupCode, uids, true);\n unwrap(\"getMemberInfo\", raw.result, raw.errMsg);\n // 返回形状待探测校准:infos Map<uid, GroupMember>\n const { infos } = raw as unknown as { infos?: Map<string, GroupMember> };\n if (infos instanceof Map) {\n return [...infos.values()];\n }\n return [];\n }\n\n /** uin → uid(私聊发送等需要)。 */\n async uinToUid(uins: string[]): Promise<Map<string, string>> {\n if (uins.length === 0) {\n return new Map();\n }\n const raw = await this.service.getUidByUins(uins);\n if (raw.errCode !== 0) {\n throw kernelError(`getUidByUins 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw.uids;\n }\n\n /** uid → uin。 */\n async uidToUin(uids: string[]): Promise<Map<string, string>> {\n if (uids.length === 0) {\n return new Map();\n }\n const raw = await this.service.getUinByUids(uids);\n if (raw.errCode !== 0) {\n throw kernelError(`getUinByUids 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw.uins;\n }\n\n /** 群成员角色 → OB11 role 字符串。 */\n static roleToString(role: NTGroupMemberRole): \"owner\" | \"admin\" | \"member\" {\n if (role === NTGroupMemberRole.OWNER) {\n return \"owner\";\n }\n if (role === NTGroupMemberRole.ADMIN) {\n return \"admin\";\n }\n return \"member\";\n }\n\n /**\n * 踢人(set_group_kick)。memberUids 为成员 uid;refuseForever=true 拉黑拒绝再次入群。\n * kickMemberV2 的 kickFlag/kickList 字段值待探测校准(TODO P3 联调)。\n */\n async kickMember(\n groupCode: string,\n memberUids: string[],\n refuseForever: boolean,\n reason = \"\",\n ): Promise<void> {\n if (memberUids.length === 0) {\n throw kernelError(\"kickMember 需要至少一个成员 uid\", \"INVALID_PARAM\");\n }\n // refuseForever=true 拒绝再次入群(optFlag 字段值待探测校准)\n let optFlag = 1;\n if (refuseForever) {\n optFlag = 0;\n }\n const req: KickMemberV2Req = {\n groupCode,\n kickFlag: 0,\n kickList: memberUids.map((uid) => ({\n optFlag,\n optOperate: 0,\n optMemberUid: uid,\n optBytesMsg: \"\",\n })),\n kickListUids: memberUids,\n kickMsg: reason,\n };\n const raw = await this.service.kickMemberV2(req);\n unwrap(\"kickMemberV2\", raw.result, raw.errMsg);\n }\n\n /** 成员禁言(duration 秒,0 解除禁言)。 */\n async setMemberShutUp(\n groupCode: string,\n members: Array<{ uid: string; duration: number }>,\n ): Promise<void> {\n if (members.length === 0) {\n throw kernelError(\"setMemberShutUp 需要至少一个成员\", \"INVALID_PARAM\");\n }\n const raw = await this.service.setMemberShutUp(\n groupCode,\n members.map((m) => ({ uid: m.uid, timeStamp: m.duration })),\n );\n unwrap(\"setMemberShutUp\", raw.result, raw.errMsg);\n }\n\n /** 全员禁言(enable=true 开启全群禁言)。 */\n async setGroupShutUp(groupCode: string, enable: boolean): Promise<void> {\n const raw = await this.service.setGroupShutUp(groupCode, enable);\n unwrap(\"setGroupShutUp\", raw.result, raw.errMsg);\n }\n\n /** 设置管理员(role=ADMIN 设管理,MEMBER 取消;void 语义乐观处理)。 */\n setMemberRole(groupCode: string, uid: string, role: NTGroupMemberRole): void {\n this.service.modifyMemberRole(groupCode, uid, role);\n }\n\n /** 设置群名片(void 语义乐观处理)。 */\n setMemberCardName(groupCode: string, uid: string, cardName: string): void {\n this.service.modifyMemberCardName(groupCode, uid, cardName);\n }\n\n /** 修改群名。 */\n async modifyGroupName(groupCode: string, groupName: string): Promise<void> {\n const raw = await this.service.modifyGroupName(groupCode, groupName, false);\n unwrap(\"modifyGroupName\", raw.result, raw.errMsg);\n }\n\n /** 退群(needDeleteLocalMsg=is_dismiss:是否解散群,仅群主)。 */\n async quitGroup(groupCode: string, needDeleteLocalMsg = false): Promise<void> {\n const raw = await this.service.quitGroupV2({ groupCode, needDeleteLocalMsg });\n unwrap(\"quitGroupV2\", raw.result, raw.errMsg);\n }\n\n /** 设置精华消息(msgId 经 msgService 取 msgSeq/msgRandom)。 */\n async addGroupEssence(groupCode: string, msgId: string): Promise<void> {\n await this.setEssence(groupCode, msgId, true);\n }\n\n /** 取消精华消息。 */\n async removeGroupEssence(groupCode: string, msgId: string): Promise<void> {\n await this.setEssence(groupCode, msgId, false);\n }\n\n /** 精华消息公共流程:按 msgId 拉消息 → add/removeGroupEssence。 */\n private async setEssence(groupCode: string, msgId: string, add: boolean): Promise<void> {\n const peer = { chatType: ChatType.GROUP, peerUid: groupCode };\n const raw = await this.msgService.getMsgsByMsgId(peer, [msgId]);\n unwrap(\"getMsgsByMsgId\", raw.result, raw.errMsg);\n const msg = raw.msgList?.[0];\n if (msg === undefined) {\n throw kernelError(`消息 ${msgId} 不存在`, \"NOT_FOUND\");\n }\n const param = {\n groupCode,\n msgRandom: Number(msg.msgRandom ?? 0),\n msgSeq: Number(msg.msgSeq),\n };\n let label = \"removeGroupEssence\";\n let res: { result?: unknown; errMsg?: unknown } | undefined;\n if (add) {\n label = \"addGroupEssence\";\n res = (await this.service.addGroupEssence(param)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n } else {\n res = (await this.service.removeGroupEssence(param)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n }\n checkLooseResult(label, res);\n }\n\n /** @all 剩余次数(get_group_at_all_remain)。 */\n async getGroupRemainAtTimes(groupCode: string): Promise<{\n canAtAll: boolean;\n remainAtAllCountForUin: number;\n remainAtAllCountForGroup: number;\n }> {\n const raw = await this.service.getGroupRemainAtTimes(groupCode);\n if (raw.errCode !== 0) {\n throw kernelError(`getGroupRemainAtTimes 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return {\n canAtAll: raw.atInfo.canAtAll,\n remainAtAllCountForUin: raw.atInfo.remainAtAllCountForUin,\n remainAtAllCountForGroup: raw.atInfo.remainAtAllCountForGroup,\n };\n }\n}\n\n/**\n * 从 getGroupDetailInfo 返回提取群详情(兼容三种形状):\n * - result 为对象 → 详情直接在其中(最常见)\n * - result 为 0/空 → 从 raw.groupInfo / detailInfo / info 提取\n * 提取不到返回 null(调用方抛 UNKNOWN)。导出供单测。\n */\nexport function extractGroupDetail(result: unknown, raw: unknown): GroupDetailInfo | null {\n if (result !== null && typeof result === \"object\") {\n return result as GroupDetailInfo;\n }\n const detail = raw as {\n groupInfo?: unknown;\n detailInfo?: unknown;\n info?: unknown;\n };\n const candidate = detail.groupInfo ?? detail.detailInfo ?? detail.info;\n if (candidate !== undefined && candidate !== null && typeof candidate === \"object\") {\n return candidate as GroupDetailInfo;\n }\n return null;\n}\n","/**\n * GroupNotifyApi:群通知/禁言列表语义化 API(ADR-009 统一错误语义,P2-13)\n *\n * get_group_system_msg / set_group_add_request / get_group_shut_list 共用。\n * getSingleScreenNotifies / getGroupShutUpMemberList 的列表可能走 listener 回调,\n * 直接调用返回形状待探测校准:兼容 result 数组 / 字段 / 直接数组。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n GroupMember,\n GroupNotify,\n NodeIKernelGroupService,\n NodeIQQNTWrapperSession,\n NTGroupRequestOperateTypes,\n} from \"../types/index.js\";\nimport { unwrap } from \"./result.js\";\n\n/** 群通知 API:从 session 拿 group service,包装成语义化方法。 */\nexport class GroupNotifyApi {\n private readonly service: NodeIKernelGroupService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getGroupService() as unknown as NodeIKernelGroupService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getGroupService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 单屏通知列表(get_group_system_msg / set_group_add_request 共用)。 */\n async getSingleScreenNotifies(doubt: boolean, count: number): Promise<GroupNotify[]> {\n const raw = await this.service.getSingleScreenNotifies(doubt, \"\", count);\n return extractNotifyList(raw.result);\n }\n\n /** 禁言成员列表(get_group_shut_list)。 */\n async getGroupShutUpMemberList(groupCode: string): Promise<GroupMember[]> {\n const raw = await this.service.getGroupShutUpMemberList(groupCode);\n if (raw.result !== 0) {\n unwrap(\"getGroupShutUpMemberList\", raw.result, raw.errMsg);\n }\n return extractMemberList(raw.result);\n }\n\n /** 处理群请求(set_group_add_request 应答;approve=true 同意 / false 拒绝)。 */\n async handleGroupRequest(\n doubt: boolean,\n notify: GroupNotify,\n operateType: NTGroupRequestOperateTypes,\n reason: string,\n ): Promise<void> {\n const { group, seq, type } = notify;\n let groupCode = \"\";\n if (group !== undefined) {\n ({ groupCode } = group);\n }\n let postscript = reason;\n if (postscript === \"\") {\n postscript = \" \";\n }\n await this.service.operateSysNotify(doubt, {\n operateType,\n targetMsg: {\n seq,\n type,\n groupCode,\n postscript,\n },\n });\n }\n}\n\n/** 从 getSingleScreenNotifies 返回提取通知数组(兼容 result 数组 / notifies 字段)。 */\nexport function extractNotifyList(result: unknown): GroupNotify[] {\n if (Array.isArray(result)) {\n return result as GroupNotify[];\n }\n if (result !== null && typeof result === \"object\") {\n const obj = result as { notifies?: unknown; groupNotifies?: unknown };\n if (Array.isArray(obj.notifies)) {\n return obj.notifies as GroupNotify[];\n }\n if (Array.isArray(obj.groupNotifies)) {\n return obj.groupNotifies as GroupNotify[];\n }\n }\n return [];\n}\n\n/** 从 getGroupShutUpMemberList 返回提取成员数组(兼容 result 数组 / memberList 字段)。 */\nexport function extractMemberList(result: unknown): GroupMember[] {\n if (Array.isArray(result)) {\n return result as unknown as GroupMember[];\n }\n if (result !== null && typeof result === \"object\") {\n const obj = result as { memberList?: unknown; list?: unknown };\n if (Array.isArray(obj.memberList)) {\n return obj.memberList as unknown as GroupMember[];\n }\n if (Array.isArray(obj.list)) {\n return obj.list as unknown as GroupMember[];\n }\n }\n return [];\n}\n","/**\n * MsgApi:消息语义化 API(ADR-009 统一错误语义)\n *\n * 内部解包原生 `{ result, errMsg }`:成功返回纯业务值,失败抛 KernelError。\n * 协议层只维护 `KernelErrorCode → 协议错误码` 映射表,不解析错误逻辑。\n *\n * 方法面(P2-1):发送 / 撤回 / 拉历史 / 标记已读。group/friend 等后续 apis 同构。\n */\nimport { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { basename } from \"node:path\";\nimport type { MsgEventChannel } from \"../bridge/msg-bridge.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n CanonicalElement,\n NodeIKernelMsgService,\n NodeIQQNTWrapperSession,\n Peer,\n PttElement,\n RawElement,\n RawMessage,\n SendMessageElement,\n} from \"../types/index.js\";\nimport { ElementType, toSendElements } from \"../types/index.js\";\nimport { unwrapResult } from \"./result.js\";\n\n/** 发送状态(onMsgInfoListUpdate 事件 msg.sendStatus)。 */\nconst SEND_STATUS = { FAILED: 0, SENDING: 1, SUCCESS: 2, SUCCESS_NO_SEQ: 3 } as const;\n\n/** 发送确认超时(毫秒)。 */\nconst SEND_CONFIRM_TIMEOUT_MS = 15_000;\n\n/**\n * PIC 元素发送契约值(NapCat 同款发送契约值,未逐字段校准)。\n * 下列固定值均为实测调通的 wrapper 发送契约(picType / picSubType /\n * picWidth / picHeight / original / thumbFileSize),只抽常量不改值——改值会破坏图片发送。\n */\nconst PIC_TYPE = 1000;\nconst PIC_SUB_TYPE = 0;\nconst PIC_WIDTH = 0;\nconst PIC_HEIGHT = 0;\nconst PIC_ORIGINAL = true;\nconst PIC_THUMB_FILE_SIZE = 0;\n\n/**\n * PTT 元素发送契约值(NapCat 同款发送契约值,未逐字段校准)。\n * silk v3 协议:下列固定值均为实测调通的 wrapper 发送契约(formatType / voiceType /\n * voiceChangeType / canConvert2Text / playState / autoConvertText / storeID /\n * otherBusinessInfo.aiVoiceType),只抽常量不改值——改值会破坏语音发送。\n */\nconst PTT_FORMAT_TYPE = 1;\nconst PTT_VOICE_TYPE = 1;\nconst PTT_VOICE_CHANGE_TYPE = 0;\nconst PTT_CAN_CONVERT_2_TEXT = true;\nconst PTT_PLAY_STATE = 1;\nconst PTT_AUTO_CONVERT_TEXT = 0;\nconst PTT_STORE_ID = 0;\nconst PTT_AI_VOICE_TYPE = 0;\n/** 假波形数组(占位波形):无真实振幅数据时发送的固定占位,silk v3 协议契约值。 */\nconst PTT_WAVE_AMPLITUDES = [0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17];\n\n/** downloadRichMedia 契约值(2026-09-08 B1 实测:downloadType=2 可用 / thumbSize=0)。 */\nconst DOWNLOAD_TYPE_COMMON = 2;\nconst THUMB_SIZE_NONE = 0;\n/** 语音下载轮询参数(300ms × 34 ≈ 10s;返回 void 只能经重拉元素观察完成)。 */\nconst PTT_POLL_INTERVAL_MS = 300;\nconst PTT_POLL_TIMES = 34;\n\n/** sleep(下载轮询间隔)。 */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/** 读文件 stat(富媒体发送预处理;文件不存在抛 INVALID_PARAM)。 */\nasync function statFile(path: string): Promise<{ size: number }> {\n try {\n return await stat(path);\n } catch {\n throw kernelError(`图片文件不存在: ${path}`, \"INVALID_PARAM\");\n }\n}\n\n/** 计算文件 md5(十六进制小写)。 */\nasync function hashFile(path: string): Promise<string> {\n try {\n const buf = await readFile(path);\n return createHash(\"md5\").update(buf).digest(\"hex\");\n } catch {\n throw kernelError(`图片文件读取失败: ${path}`, \"INVALID_PARAM\");\n }\n}\n\n/**\n * 清洗撤回消息 id(防御外部脏参数)。\n *\n * 调用方(IPC / 协议层 / koishi 适配器)可能传 null、非数组、空字符串等——\n * 直接透传会让裸 TypeError 逃逸,或把脏值一路送到 wrapper.node 才失败\n * (原生层只回「无错误详情」,排查困难)。规则:\n * - 非数组 → INVALID_PARAM\n * - 过滤空字符串 / 纯空白 / 非字符串\n * - 清洗后为空 → INVALID_PARAM\n */\nfunction sanitizeRecallMsgIds(msgIds: string[]): string[] {\n if (!Array.isArray(msgIds)) {\n throw kernelError(\"recallMessage 的 msgIds 必须是数组\", \"INVALID_PARAM\");\n }\n const ids = msgIds.filter((id) => typeof id === \"string\" && id.trim() !== \"\");\n if (ids.length === 0) {\n throw kernelError(\"recallMessage 需要至少一个非空 msgId\", \"INVALID_PARAM\");\n }\n return ids;\n}\n\n/** 消息 API:从 session 拿 msg service,包装成语义化方法。 */\nexport class MsgApi {\n private readonly service: NodeIKernelMsgService;\n /** 消息事件通道(sendMsg 后等 onMsgInfoListUpdate 确认发送结果)。 */\n private readonly channel: MsgEventChannel | null;\n /** NodeQQNTWrapperUtil(富媒体发送 copyFile 用;session 拿不到时可为 null)。 */\n private readonly util: { get(): unknown } | null;\n /** 上次生成 msgId 的时间(单调递增,2026-08-07 防同毫秒并发碰撞)。 */\n private lastMsgTime = 0;\n\n constructor(session: NodeIQQNTWrapperSession, channel?: MsgEventChannel, util?: unknown) {\n const service = session.getMsgService() as unknown as NodeIKernelMsgService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getMsgService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel ?? null;\n // util 传入形态:构造器(NodeQQNTWrapperUtil,含 get())或实例,宽松兼容\n this.util = util !== null && util !== undefined ? (util as { get(): unknown }) : null;\n }\n\n /**\n * 生成 msgId 时间戳(单调递增)。\n * generateMsgUniqueId(chatType, time) 以 time 区分消息——同毫秒并发发送\n * (机器人群发/多会话同时回复)Date.now() 会碰撞,msgId 相同导致 wrapper\n * 拒绝或覆盖。严格单调递增保证进程内唯一。\n */\n private nextMsgTime(): string {\n const now = Date.now();\n if (now > this.lastMsgTime) {\n this.lastMsgTime = now;\n } else {\n this.lastMsgTime += 1;\n }\n return String(this.lastMsgTime);\n }\n\n /**\n * 发送消息:canonical 元素 → NT 发送元素 → sendMsg(NapCat 式)。\n * 返回 NT msgId(雪花 ID)。\n *\n * 2026-08-11 修复(NapCat 式,实测):\n * - sendMsg 第一参传 '0'(固定),msgId 塞 peer.guildId —— 传 msgId 作第一参\n * 时 wrapper 返回 result=5(失败),NapCat 同款调用返回 result=0。\n * - 发送结果以 onMsgInfoListUpdate 事件确认(sendStatus===2 成功):sendMsg\n * 返回值 result 可能为 5 但事件仍成功(异步确认),反之亦然。\n * - ⚠️ 必须先注册事件监听再调 sendMsg(事件可能在 sendMsg 返回前就触发)。\n */\n async sendMessage(target: Peer, elements: CanonicalElement[]): Promise<{ msgId: string }> {\n // NapCat 式富媒体预处理:PIC 元素补 md5/fileName/sourcePath 并放置文件\n // (getRichMediaFilePathForGuild → util.copyFile),否则发送器走未初始化\n // 的 FlashFileUploadService 导致 rich media transfer failed。\n const sendElements = await this.prepareSendElements(elements);\n const msgId = this.service.generateMsgUniqueId(target.chatType, this.nextMsgTime());\n // NapCat 同款:msgId 塞 guildId,第一参 '0'\n const sendPeer: Peer = { ...target, guildId: msgId };\n // 无事件通道(老用法):退化为看返回值\n if (this.channel === null) {\n const raw = await this.service.sendMsg(\"0\", sendPeer, sendElements, new Map());\n unwrapResult(\"sendMsg\", raw);\n return { msgId };\n }\n // 有事件通道:先注册确认监听(NapCat 式,事件可能早于 sendMsg 返回触发),\n // 再调 sendMsg。最终结果以事件 sendStatus 为准(raw.result 非 0 不判失败)。\n const confirm = this.confirmSend(msgId, target);\n // ⚠️ 预消费 confirm 的 rejection(2026-08-22 崩溃根因修复):若下方\n // service.sendMsg 抛错(wrapper 内部异常),confirm 确认监听已注册却无人\n // await,事件回调里 reject 会变成 unhandledRejection,Node 默认抛错退出\n // 直接拖垮整个子进程(实测:sendStatus=0 确认事件触发后进程崩溃、IPC\n // 通道关闭)。先挂 no-op 消费兜底,`await confirm` 仍能正常收到结果\n // (Promise 允许多个消费者),失败语义不变。\n confirm.catch(() => undefined);\n await this.service.sendMsg(\"0\", sendPeer, sendElements, new Map());\n await confirm;\n return { msgId };\n }\n\n /**\n * canonical 元素 → 发送元素,富媒体(PIC/PTT)做 NapCat 式预处理。\n * 2026-08-11 修复(实测):图片发送必须 elementType=2(PIC)+ 完整 picElement\n * (md5HexStr/fileSize/fileName/sourcePath),且文件须经 getRichMediaFilePathForGuild\n * 计算目标路径 + util.copyFile 放置——缺任一环节 sendMsg 返回 rich media transfer failed。\n * 2026-08-12(语音):PTT 同理——只给 filePath 触发 wrapper 内部\n * \"Cannot convert undefined or null to object\"(缺字段转换失败)。\n */\n private async prepareSendElements(elements: CanonicalElement[]): Promise<SendMessageElement[]> {\n const out: SendMessageElement[] = [];\n for (const el of elements) {\n switch (el.type) {\n case \"image\":\n out.push(await this.prepareImageElement(el.path));\n break;\n case \"voice\":\n out.push(await this.preparePttElement(el.path));\n break;\n default:\n out.push(...toSendElements([el]));\n break;\n }\n }\n return out;\n }\n\n /** PIC 元素 NapCat 式预处理:md5 → 目标路径 → copyFile → 完整 picElement。 */\n private async prepareImageElement(path: string): Promise<SendMessageElement> {\n const file = await statFile(path);\n const md5 = await hashFile(path);\n const fileName = basename(path);\n const relPath = await this.placeMediaFile(path, ElementType.PIC, md5, fileName);\n return {\n elementType: ElementType.PIC,\n elementId: \"\",\n picElement: {\n md5HexStr: md5,\n fileSize: String(file.size),\n picWidth: PIC_WIDTH,\n picHeight: PIC_HEIGHT,\n fileName,\n sourcePath: relPath,\n original: PIC_ORIGINAL,\n picType: PIC_TYPE,\n picSubType: PIC_SUB_TYPE,\n fileUuid: \"\",\n fileSubId: \"\",\n thumbFileSize: PIC_THUMB_FILE_SIZE,\n summary: \"\",\n },\n };\n }\n\n /**\n * PTT 元素 NapCat 式预处理:md5 → 目标路径 → copyFile → 完整 pttElement。\n *\n * 2026-08-12(实测依据):只给 filePath 时 wrapper 内部抛\n * \"Cannot convert undefined or null to object\"(缺 md5HexStr/fileSize 等字段),\n * 且发送后进程崩溃重启(supervisor 自动拉起)。完整字段(NapCat 同款,\n * formatType/voiceType/canConvert2Text/waveAmplitudes 等)消除该错误。\n *\n * ⚠️ silk 格式:QQ 语音协议为 silk v3。非 silk → silk 的转码在协议层\n * (adapter,@napuketto/media 的 encodePcmToSilk,2026-08-12 接线)完成,\n * 此处接收的已是 silk 路径,仅做放置。kernel 不 import media(解耦红线)。\n */\n private async preparePttElement(path: string): Promise<SendMessageElement> {\n const file = await statFile(path);\n const md5 = await hashFile(path);\n const fileName = basename(path);\n const relPath = await this.placeMediaFile(path, ElementType.PTT, md5, fileName);\n // 时长估算(NapCat 同款规则:~3KB/s 语音码率,缺 ffprobe 时兜底)\n const duration = Math.max(1, Math.floor(file.size / 1024 / 3));\n return {\n elementType: ElementType.PTT,\n elementId: \"\",\n pttElement: {\n fileName,\n filePath: relPath,\n md5HexStr: md5,\n fileSize: String(file.size),\n duration,\n formatType: PTT_FORMAT_TYPE,\n voiceType: PTT_VOICE_TYPE,\n voiceChangeType: PTT_VOICE_CHANGE_TYPE,\n canConvert2Text: PTT_CAN_CONVERT_2_TEXT,\n waveAmplitudes: PTT_WAVE_AMPLITUDES,\n fileSubId: \"\",\n playState: PTT_PLAY_STATE,\n autoConvertText: PTT_AUTO_CONVERT_TEXT,\n storeID: PTT_STORE_ID,\n otherBusinessInfo: { aiVoiceType: PTT_AI_VOICE_TYPE },\n },\n };\n }\n\n /**\n * 富媒体文件放置(PIC/PTT 共用):md5 + 文件名 → QQ 内部目标路径\n * (getRichMediaFilePathForGuild)→ util.copyFile 放置 → 返回相对数据根路径。\n */\n private async placeMediaFile(\n path: string,\n elementType: ElementType,\n md5: string,\n fileName: string,\n ): Promise<string> {\n const service = this.service;\n const util = this.util;\n // getRichMediaFilePathForGuild:QQ 内部目标路径(纯文件名,相对数据根)。\n // 固定入参 elementSubType / thumbSize / downloadType 为 NapCat 同款发送契约值\n // (未逐字段校准),发送方向固定,改值会破坏富媒体路径计算。\n const relPath = service.getRichMediaFilePathForGuild({\n md5HexStr: md5,\n fileName,\n elementType,\n elementSubType: 0, // 元素子类型(发送固定 0)\n thumbSize: 0, // 缩略图尺寸(发送固定 0,不预生成缩略图)\n needCreate: true,\n downloadType: 1, // 下载类型(发送固定 1)\n file_uuid: \"\",\n });\n if (util !== null) {\n const instance = typeof util.get === \"function\" ? util.get() : util;\n const copy = (instance as Record<string, unknown>)[\"copyFile\"];\n if (typeof copy === \"function\") {\n await (copy as (a: string, b: string) => Promise<unknown>).call(\n instance,\n path,\n relPath,\n );\n }\n }\n return relPath;\n }\n\n /** 注册 onMsgInfoListUpdate 确认监听(返回 Promise,resolve 时发送成功)。 */\n private confirmSend(msgId: string, target: Peer): Promise<void> {\n const channel = this.channel;\n if (channel === null) {\n return Promise.resolve();\n }\n return new Promise((resolve, reject) => {\n const off = channel.on(\"Msg/onMsgInfoListUpdate\", (list) => {\n const mine = list.find((m) => m.guildId === msgId);\n if (mine === undefined) {\n return; // 不是本次消息\n }\n if (\n mine.sendStatus === SEND_STATUS.SUCCESS ||\n mine.sendStatus === SEND_STATUS.SUCCESS_NO_SEQ\n ) {\n off();\n resolve();\n } else if (mine.sendStatus === SEND_STATUS.FAILED) {\n off();\n reject(\n kernelError(`sendMsg 失败: sendStatus=${mine.sendStatus}`, \"SEND_FAILED\"),\n );\n }\n // SENDING:继续等\n });\n // 超时兜底\n setTimeout(() => {\n off();\n reject(\n kernelError(\n `sendMsg 等待确认超时(msgId=${msgId}, target=${JSON.stringify(target)})`,\n \"SEND_FAILED\",\n ),\n );\n }, SEND_CONFIRM_TIMEOUT_MS);\n });\n }\n\n /** 撤回消息(群聊管理员 / 私聊 2 分钟内)。 */\n async recallMessage(target: Peer, msgIds: string[]): Promise<void> {\n const raw = await this.service.recallMsg(target, sanitizeRecallMsgIds(msgIds));\n unwrapResult(\"recallMsg\", raw);\n }\n\n /** 拉取历史消息(msgId 为空从最新拉;count 条,时间倒序)。 */\n async fetchMessages(\n target: Peer,\n opts: { count: number; msgId?: string },\n ): Promise<RawMessage[]> {\n const raw = await this.service.getMsgs(target, opts.msgId ?? \"\", opts.count, false);\n unwrapResult(\"getMsgs\", raw);\n return raw.msgList ?? [];\n }\n\n /** 按 msgId 批量拉取消息(get_msg / 精华消息 / ptt 转文字共用)。 */\n async fetchMsgsByMsgId(target: Peer, ids: string[]): Promise<RawMessage[]> {\n if (ids.length === 0) {\n return [];\n }\n const raw = await this.service.getMsgsByMsgId(target, ids);\n unwrapResult(\"getMsgsByMsgId\", raw);\n return raw.msgList ?? [];\n }\n\n /** 消息表情表态(set_msg_emoji_like;like=true 点赞,false 取消)。 */\n async setMsgEmojiLike(\n target: Peer,\n opts: { msgSeq: string; emojiId: string; emojiType: string; like: boolean },\n ): Promise<void> {\n const raw = await this.service.setMsgEmojiLikes(\n target,\n opts.msgSeq,\n opts.emojiId,\n opts.emojiType,\n opts.like,\n );\n unwrapResult(\"setMsgEmojiLikes\", raw);\n }\n\n /**\n * 语音转文字(fetch_ptt_text)。\n * 流程:按 msgId 拉消息 → 找 PTT 元素 → translatePtt2Text(异步转写)\n * → 再拉一次消息读 pttElement.text。\n */\n async fetchPttText(msgId: string, target: Peer): Promise<string> {\n const msgs = await this.fetchMsgsByMsgId(target, [msgId]);\n const ptt = findPttElement(msgs);\n if (ptt === null) {\n throw kernelError(\"消息中不包含语音\", \"NOT_FOUND\");\n }\n const raw = await this.service.translatePtt2Text(msgId, target, ptt);\n unwrapResult(\"translatePtt2Text\", raw);\n // 转写异步完成:再拉一次拿 text\n const after = await this.fetchMsgsByMsgId(target, [msgId]);\n const text = findPttElement(after)?.pttElement?.text;\n if (text === undefined || text === \"\") {\n throw kernelError(\"获取语音转文字结果失败\", \"UNKNOWN\");\n }\n return text;\n }\n\n /** 标记会话已读。 */\n async markRead(target: Peer): Promise<void> {\n const raw = await this.service.setMsgRead(target);\n unwrapResult(\"setMsgRead\", raw);\n }\n\n /**\n * 语音主动下载(get_record 本地未命中兜底,2026-09-08 B1)。\n * downloadRichMedia 返回 void(实测),完成信号 = 重拉消息元素:\n * 轮询(300ms × 34,约 10s)直到 filePath 非空且 transferStatus 进入\n * 已下载态(2/4);超时但 filePath 已有值则尽力返回(磁盘命中判断留给\n * 调用方),完全无路径抛 NOT_FOUND。\n */\n async downloadPtt(msgId: string, target: Peer): Promise<PttElement> {\n const msgs = await this.fetchMsgsByMsgId(target, [msgId]);\n const el = findPttElement(msgs);\n const ptt = el?.pttElement;\n if (el === null || ptt === undefined) {\n throw kernelError(\"消息中不包含语音\", \"NOT_FOUND\");\n }\n const elemId = el.elementId ?? \"\";\n await this.service.downloadRichMedia({\n msgId,\n elemId,\n chatType: target.chatType,\n downloadType: DOWNLOAD_TYPE_COMMON,\n thumbSize: THUMB_SIZE_NONE,\n });\n for (let i = 0; i < PTT_POLL_TIMES; i++) {\n await sleep(PTT_POLL_INTERVAL_MS);\n const again = await this.fetchMsgsByMsgId(target, [msgId]);\n const latest = findPttElement(again)?.pttElement;\n if (latest === undefined) {\n continue;\n }\n const done = latest.transferStatus === 2 || latest.transferStatus === 4;\n if (latest.filePath !== undefined && latest.filePath !== \"\" && done) {\n return latest;\n }\n if (\n i === PTT_POLL_TIMES - 1 &&\n latest.filePath !== undefined &&\n latest.filePath !== \"\"\n ) {\n return latest;\n }\n }\n throw kernelError(\"语音下载未完成(超时)\", \"TIMEOUT\");\n }\n\n /** 发送输入状态(set_input_status;eventType=1 输入中,0 停止)。 */\n async setInputStatus(target: Peer, eventType: number): Promise<void> {\n await this.service.sendShowInputStatusReq(target.chatType, eventType, target.peerUid);\n }\n\n /**\n * 发送合并转发(send_group/private_forward_msg)。\n * buildMultiForwardMsg 组装 MULTI_FORWARD 元素 → 直接作 sendMsg 元素发送。\n */\n async sendForwardMessage(\n target: Peer,\n sourcePeer: Peer,\n srcMsgIds: string[],\n ): Promise<{ msgId: string }> {\n if (srcMsgIds.length === 0) {\n throw kernelError(\"sendForwardMessage 需要至少一条源消息\", \"INVALID_PARAM\");\n }\n const built = await this.service.buildMultiForwardMsg({\n srcMsgIds,\n srcContact: sourcePeer,\n });\n unwrapResult(\"buildMultiForwardMsg\", built);\n const elements = built.rspInfo?.elements;\n if (elements === undefined || elements.length === 0) {\n throw kernelError(\"合并转发组装失败:无元素\", \"UNKNOWN\");\n }\n const msgId = this.service.generateMsgUniqueId(target.chatType, String(Date.now()));\n const raw = await this.service.sendMsg(msgId, target, elements, new Map());\n unwrapResult(\"sendMsg\", raw);\n return { msgId };\n }\n\n /** 获取合并转发内容(get_forward_msg;resId 取自 multiForwardMsgElement)。 */\n async fetchForwardMessage(peer: Peer, msgId: string): Promise<RawMessage[]> {\n const msgs = await this.fetchMsgsByMsgId(peer, [msgId]);\n const forward = findForwardElement(msgs);\n if (forward === null || forward.resId === \"\") {\n throw kernelError(\"消息不包含合并转发内容\", \"NOT_FOUND\");\n }\n const raw = await this.service.getMultiMsg(peer, msgId, forward.resId);\n unwrapResult(\"getMultiMsg\", raw);\n return raw.msgList ?? [];\n }\n\n /** 单条转发(forward_group/friend_single_msg;srcMsgIds 源、dstPeer 目标)。 */\n async forwardSingleMessage(\n sourcePeer: Peer,\n srcMsgIds: string[],\n dstPeer: Peer,\n ): Promise<void> {\n if (srcMsgIds.length === 0) {\n throw kernelError(\"forwardSingleMessage 需要至少一条源消息\", \"INVALID_PARAM\");\n }\n const raw = await this.service.forwardMsg(srcMsgIds, sourcePeer, [dstPeer], undefined);\n unwrapResult(\"forwardMsg\", raw);\n }\n\n /** 设置在线状态(set_online_status;customStatus 为自定义状态)。 */\n async setOnlineStatus(opts: {\n status: number;\n extStatus: number;\n batteryStatus: number;\n customStatus?: { faceId: string; wording: string; faceType: string };\n }): Promise<void> {\n const raw = await this.service.setStatus(opts);\n unwrapResult(\"setStatus\", raw);\n }\n}\n\n/** 在消息列表中找含 pttElement 的元素(找不到返回 null)。 */\nfunction findPttElement(msgs: RawMessage[]): RawElement | null {\n const [first] = msgs;\n if (first === undefined) {\n return null;\n }\n return first.elements.find((el) => el.pttElement !== undefined) ?? null;\n}\n\n/** 在消息列表中找含 multiForwardMsgElement 的元素(找不到返回 null)。 */\nfunction findForwardElement(\n msgs: RawMessage[],\n): { resId: string; fileName: string; xmlContent: string } | null {\n const [first] = msgs;\n if (first === undefined) {\n return null;\n }\n return (\n first.elements.find((el) => el.multiForwardMsgElement !== undefined)\n ?.multiForwardMsgElement ?? null\n );\n}\n","/**\n * 陌生人信息扁平化纯函数(get_stranger_info 的数据映射部分)\n *\n * 2026-08-08 从 profile.ts 拆出:NAPI 会话依赖(ProfileApi)与纯映射分离,\n * 纯函数可独立单测(stranger-info.test.ts 基线锁定宽松取值行为)。\n *\n * 宽松取值:优先 info(uid 完整详情),字段缺失回退 byUin(uin 初查详情),\n * 两份都缺给默认值——字段缺失是 QQ 详情接口的常态,不能抛错。\n */\nimport type { UserDetailInfoByUin } from \"../types/index.js\";\n\n/** 陌生人信息(get_stranger_info 返回)。 */\nexport interface StrangerInfo {\n user_id: number;\n uid: string;\n nickname: string;\n age: number;\n qid: string;\n qq_level: number;\n sex: \"male\" | \"female\" | \"unknown\";\n long_nick: string;\n reg_time: number;\n is_vip: boolean;\n is_years_vip: boolean;\n vip_level: number;\n remark: string;\n status: number;\n login_days: number;\n}\n\n/** 扁平化后的详情视图(双来源字段级回退的产物,仅含 StrangerInfo 需要的字段)。 */\ninterface MergedDetail {\n coreInfo?: { nick?: string; remark?: string } | undefined;\n baseInfo?: { age?: number; qid?: string; sex?: number; longNick?: string } | undefined;\n vasInfo?: { svipFlag?: boolean; yearVipFlag?: boolean; vipLevel?: number } | undefined;\n commonExt?: { qqLevel?: number; regTime?: number } | undefined;\n status?: number | undefined;\n}\n\n/** 宽松取值:undefined 时给默认值(null 也归默认)。 */\nfunction orDefault<T>(value: T | undefined, fallback: T): T {\n return value ?? fallback;\n}\n\n/** 合并两份详情:优先 info(uid 完整详情),字段缺失回退 byUin(uin 初查详情)。 */\nfunction mergeDetail(info: UserDetailInfoByUin, byUin: UserDetailInfoByUin): MergedDetail {\n const infoSimple = info.detail?.simpleInfo;\n const byUinSimple = byUin.detail?.simpleInfo;\n return {\n coreInfo: pickField(infoSimple, byUinSimple, (s) => s?.coreInfo),\n baseInfo: pickField(infoSimple, byUinSimple, (s) => s?.baseInfo),\n vasInfo: pickField(infoSimple, byUinSimple, (s) => s?.vasInfo),\n commonExt: pickField(info.detail, byUin.detail, (d) => d?.commonExt),\n status: pickField(infoSimple, byUinSimple, (s) => s?.status?.status),\n };\n}\n\n/** 字段级回退:primary 的字段取不到回退 fallback 的同名字段(宽松取值)。 */\nfunction pickField<T, R>(\n primary: T | undefined,\n fallback: T | undefined,\n get: (src: T) => R | undefined,\n): R | undefined {\n const fromPrimary = primary !== undefined ? get(primary) : undefined;\n if (fromPrimary !== undefined) {\n return fromPrimary;\n }\n return fallback !== undefined ? get(fallback) : undefined;\n}\n\n/** 扁平化两份详情 → StrangerInfo(宽松取值,字段缺失给默认)。 */\nexport function flattenStrangerInfo(\n uin: string,\n uid: string,\n byUin: UserDetailInfoByUin,\n info: UserDetailInfoByUin,\n): StrangerInfo {\n const d = mergeDetail(info, byUin);\n return {\n user_id: Number(uin),\n uid,\n nickname: orDefault(d.coreInfo?.nick, \"\"),\n age: orDefault(d.baseInfo?.age, 0),\n qid: orDefault(d.baseInfo?.qid, \"\"),\n qq_level: orDefault(d.commonExt?.qqLevel, 0),\n sex: mapSex(d.baseInfo?.sex),\n long_nick: orDefault(d.baseInfo?.longNick, \"\"),\n reg_time: orDefault(d.commonExt?.regTime, 0),\n is_vip: orDefault(d.vasInfo?.svipFlag, false),\n is_years_vip: orDefault(d.vasInfo?.yearVipFlag, false),\n vip_level: orDefault(d.vasInfo?.vipLevel, 0),\n remark: orDefault(d.coreInfo?.remark, \"\"),\n status: orDefault(d.status, 0),\n login_days: 0,\n };\n}\n\n/** 性别数值 → OB11 字符串(1=男 2=女 其余 unknown)。 */\nfunction mapSex(raw: number | undefined): \"male\" | \"female\" | \"unknown\" {\n if (raw === 1) {\n return \"male\";\n }\n if (raw === 2) {\n return \"female\";\n }\n return \"unknown\";\n}\n","/**\n * ProfileApi:资料语义化 API(ADR-009 统一错误语义,P2-14/P2-15)\n *\n * - setLongNick:set_self_longnick\n * - setNickName:set_qq_profile\n * - setHeader:set_qq_avatar\n * - getStrangerInfo:get_stranger_info(uin → uid → 详情扁平化)\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type { NodeIKernelProfileService, NodeIQQNTWrapperSession } from \"../types/index.js\";\nimport { checkLooseResult } from \"./result.js\";\nimport type { StrangerInfo } from \"./stranger-info.js\";\nimport { flattenStrangerInfo } from \"./stranger-info.js\";\n\n/** 资料 API:从 session 拿 profile service,包装成语义化方法。 */\nexport class ProfileApi {\n private readonly service: NodeIKernelProfileService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getProfileService() as unknown as NodeIKernelProfileService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getProfileService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 设置个性签名(set_self_longnick)。 */\n async setLongNick(longNick: string): Promise<void> {\n const res = (await this.service.setLongNick(longNick)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n checkLooseResult(\"setLongNick\", res);\n }\n\n /** 设置昵称(set_qq_profile)。 */\n async setNickName(nickName: string): Promise<void> {\n const res = (await this.service.setNickName(nickName)) as\n | { result?: unknown; errMsg?: unknown }\n | undefined;\n checkLooseResult(\"setNickName\", res);\n }\n\n /** 设置头像(set_qq_avatar;filePath 为本地路径)。 */\n async setHeader(filePath: string): Promise<void> {\n const raw = await this.service.setHeader(filePath);\n if (raw.result !== 0) {\n throw kernelError(`setHeader 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n }\n\n /** 获取陌生人信息(get_stranger_info;uin 为用户 QQ 号)。 */\n async getStrangerInfo(uin: string): Promise<StrangerInfo> {\n const byUin = await this.service.getUserDetailInfoByUin(uin);\n const detailUid = byUin.detail?.uid;\n let uid = uin;\n if (detailUid !== undefined && detailUid !== \"\") {\n uid = detailUid;\n }\n const info = await this.service.getUserDetailInfo(uid);\n return flattenStrangerInfo(uin, uid, byUin, info);\n }\n}\n","/**\n * ProfileLikeApi:资料点赞语义化 API(ADR-009 统一错误语义,P2-14)\n *\n * sendLike:send_like(sourceId=71 赞来源,doLikeCount 次数)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type { NodeIKernelProfileLikeService, NodeIQQNTWrapperSession } from \"../types/index.js\";\n\n/** 点赞 API:从 session 拿 profile like service,包装成语义化方法。 */\nexport class ProfileLikeApi {\n private readonly service: NodeIKernelProfileLikeService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service =\n session.getProfileLikeService() as unknown as NodeIKernelProfileLikeService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getProfileLikeService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 点赞(send_like;uid 为目标 uid,count 次数)。 */\n async sendLike(uid: string, count = 1): Promise<void> {\n const raw = await this.service.setBuddyProfileLike({\n friendUid: uid,\n sourceId: 71,\n doLikeCount: count,\n doLikeTollCount: 0,\n });\n if (raw.result !== 0) {\n throw kernelError(`点赞失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n }\n}\n","/**\n * RichMediaApi:富媒体语义化 API(ADR-009 统一错误语义,P2-14)\n *\n * - translateWords:translate_en2zh\n * - 群文件:列表 / 空间 / 建夹 / 删除文件 / 删除夹 / 重命名 / 移动 / 转存\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n GetFileListParam,\n GroupFileItemInfo,\n GroupFolderInfo,\n NodeIKernelRichMediaService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\nimport { unwrap } from \"./result.js\";\n\n/** 群文件业务类型(deleteGroupFile/moveGroupFile/renameGroupFile 用,说明书参考)。 */\nconst GROUP_FILE_BIZ_TYPE = 102;\n/** 排序方式(按时间)。 */\nconst SORT_TYPE_TIME = 1;\n/** 排序方向(倒序)。 */\nconst SORT_ORDER_DESC = 2;\n/** 起始下标(根目录)。 */\nconst START_INDEX_ROOT = 0;\n/** 不显示在线文档文件夹。 */\nconst HIDE_ONLINEDOC_FOLDER = 0;\n/** 默认文件数量。 */\nconst DEFAULT_FILE_COUNT = 50;\n/** 空间查询文件数量(单条即可)。 */\nconst SPACE_QUERY_FILE_COUNT = 1;\n\n/** 群文件列表条目(fileInfo/folderInfo 取其一)。 */\nexport interface GroupFileListItem {\n fileInfo?: GroupFileItemInfo;\n folderInfo?: GroupFolderInfo;\n}\n\n/** 群文件系统信息。 */\nexport interface GroupFileSystemInfo {\n fileCount: number;\n limitCount: number;\n usedSpace: number;\n totalSpace: number;\n}\n\n/** 富媒体 API:从 session 拿 rich media service,包装成语义化方法。 */\nexport class RichMediaApi {\n private readonly service: NodeIKernelRichMediaService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service =\n session.getRichMediaService() as unknown as NodeIKernelRichMediaService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getRichMediaService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 英文单词翻译(translate_en2zh)。 */\n async translateWords(words: string[]): Promise<string[]> {\n const raw = await this.service.translateEnWordToZn(words);\n unwrap(\"translateEnWordToZn\", raw.result, raw.errMsg);\n return raw.words ?? [];\n }\n\n /** 群文件列表(get_group_root_files;parent 缺省根目录)。 */\n async getGroupFileList(\n groupCode: string,\n parentFolderId = \"\",\n fileCount = DEFAULT_FILE_COUNT,\n ): Promise<GroupFileListItem[]> {\n const params: GetFileListParam = {\n sortType: SORT_TYPE_TIME,\n fileCount,\n startIndex: START_INDEX_ROOT,\n sortOrder: SORT_ORDER_DESC,\n showOnlinedocFolder: HIDE_ONLINEDOC_FOLDER,\n };\n const raw = await this.service.getGroupFileList(groupCode, params);\n if (raw.result !== 0) {\n unwrap(\"getGroupFileList\", raw.result, raw.errMsg);\n }\n const list = extractFileList(raw.result);\n if (parentFolderId === \"\") {\n return list;\n }\n return list.filter(\n (item) =>\n (item.fileInfo !== undefined && item.fileInfo.parent === parentFolderId) ||\n (item.folderInfo !== undefined && item.folderInfo.folderUid === parentFolderId),\n );\n }\n\n /** 群文件系统信息(get_group_file_system_info;getGroupFileList 的 groupSpaceResult)。 */\n async getGroupFileSystemInfo(groupCode: string): Promise<GroupFileSystemInfo> {\n const params: GetFileListParam = {\n sortType: SORT_TYPE_TIME,\n fileCount: SPACE_QUERY_FILE_COUNT,\n startIndex: START_INDEX_ROOT,\n sortOrder: SORT_ORDER_DESC,\n showOnlinedocFolder: HIDE_ONLINEDOC_FOLDER,\n };\n const raw = await this.service.getGroupFileList(groupCode, params);\n if (raw.result !== 0) {\n unwrap(\"getGroupFileList\", raw.result, raw.errMsg);\n }\n const space = raw.groupSpaceResult;\n if (space === undefined) {\n throw kernelError(\"getGroupFileList 无 groupSpaceResult\", \"UNKNOWN\");\n }\n return {\n fileCount: countGroupFiles(raw.result),\n limitCount: 0,\n usedSpace: space.usedSpace,\n totalSpace: space.totalSpace,\n };\n }\n\n /** 创建群文件夹(create_group_file_folder)。 */\n async createGroupFolder(groupCode: string, folderName: string): Promise<string> {\n const raw = await this.service.createGroupFolder(groupCode, folderName);\n if (raw.result !== 0) {\n unwrap(\"createGroupFolder\", raw.result, raw.errMsg);\n }\n // 返回形状待探测:兼容 folderId / resultWithGroupItem\n const { result } = raw as { result?: unknown };\n if (typeof result === \"string\") {\n return result;\n }\n return \"\";\n }\n\n /** 删除群文件(delete_group_file)。 */\n async deleteGroupFile(groupCode: string, fileIds: string[]): Promise<void> {\n const raw = await this.service.deleteGroupFile(groupCode, [GROUP_FILE_BIZ_TYPE], fileIds);\n if (raw.result !== 0) {\n unwrap(\"deleteGroupFile\", raw.result, raw.errMsg);\n }\n }\n\n /** 删除群文件夹(delete_group_folder)。 */\n async deleteGroupFolder(groupCode: string, folderId: string): Promise<void> {\n const raw = await this.service.deleteGroupFolder(groupCode, folderId);\n if (raw.result !== 0) {\n unwrap(\"deleteGroupFolder\", raw.result, raw.errMsg);\n }\n }\n\n /** 重命名群文件(rename_group_file;参数语义待探测校准)。 */\n async renameGroupFile(groupCode: string, fileId: string, newName: string): Promise<void> {\n await this.service.renameGroupFile(groupCode, GROUP_FILE_BIZ_TYPE, fileId, newName, \"\");\n }\n\n /** 移动群文件(move_group_file;targetFolder 空为根目录)。 */\n async moveGroupFile(\n groupCode: string,\n fileIds: string[],\n targetFolderId: string,\n ): Promise<void> {\n const raw = await this.service.moveGroupFile(\n groupCode,\n [GROUP_FILE_BIZ_TYPE],\n fileIds,\n \"\",\n targetFolderId,\n );\n if (raw.result !== 0) {\n unwrap(\"moveGroupFile\", raw.result, raw.errMsg);\n }\n }\n\n /** 转存群文件(trans_group_file)。 */\n async transGroupFile(groupCode: string, fileId: string): Promise<void> {\n const raw = await this.service.transGroupFile(groupCode, fileId);\n if (raw.result !== 0) {\n unwrap(\"transGroupFile\", raw.result, raw.errMsg);\n }\n }\n}\n\n/** 从 getGroupFileList 返回提取文件列表(兼容数组 / { items } / { fileList })。导出供单测。 */\nexport function extractFileList(result: unknown): GroupFileListItem[] {\n if (Array.isArray(result)) {\n return result as GroupFileListItem[];\n }\n if (result === null || typeof result !== \"object\") {\n return [];\n }\n const obj = result as { items?: unknown; fileList?: unknown; list?: unknown };\n for (const key of [\"items\", \"fileList\", \"list\"] as const) {\n const value = obj[key];\n if (Array.isArray(value)) {\n return value as GroupFileListItem[];\n }\n }\n return [];\n}\n\n/** 统计文件数量(兼容数组 / { items })。 */\nfunction countGroupFiles(result: unknown): number {\n if (Array.isArray(result)) {\n return (result as GroupFileListItem[]).filter((item) => item.fileInfo !== undefined).length;\n }\n return 0;\n}\n","/**\n * TicketApi:票据语义化 API(ADR-009 统一错误语义,P2-13)\n *\n * - getClientKey:TicketService.forceFetchClientKey 解包(get_clientkey)\n * - getCookies:clientKey → ssl.ptlogin2.qq.com/jump 跳转 → 解析 set-cookie 头\n * (fetch 会吞掉 Set-Cookie 头,故用 node:https 手动请求)\n */\nimport { get as httpsGet } from \"node:https\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n ForceFetchClientKeyRetType,\n NodeIKernelTicketService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 跳转请求超时(毫秒)。 */\nconst JUMP_TIMEOUT_MS = 10_000;\n\n/** 票据 API:从 session 拿 ticket service,包装成语义化方法。 */\nexport class TicketApi {\n private readonly service: NodeIKernelTicketService;\n\n constructor(session: NodeIQQNTWrapperSession) {\n const service = session.getTicketService() as unknown as NodeIKernelTicketService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getTicketService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n }\n\n /** 获取 clientKey(get_clientkey)。 */\n async getClientKey(): Promise<ForceFetchClientKeyRetType> {\n const raw = await this.service.forceFetchClientKey(\"\");\n if (raw.result !== 0 || raw.clientKey === \"\") {\n throw kernelError(`forceFetchClientKey 失败: ${raw.errMsg}`, \"UNKNOWN\");\n }\n return raw;\n }\n\n /** 获取指定域名的 cookies(get_cookies;uin 为机器人 QQ 号)。 */\n async getCookies(domain: string, uin: string): Promise<Record<string, string>> {\n const { clientKey } = await this.getClientKey();\n const u1 = `https://${domain}/${uin}/infocenter`;\n const jumpUrl =\n \"https://ssl.ptlogin2.qq.com/jump?ptlang=1033\" +\n `&clientuin=${encodeURIComponent(uin)}` +\n `&clientkey=${encodeURIComponent(clientKey)}` +\n `&u1=${encodeURIComponent(u1)}` +\n \"&keyindex=19\";\n return fetchJumpCookies(jumpUrl);\n }\n\n /** 计算 bkn(get_csrf_token 与 web 接口共用;skey → bkn 哈希)。 */\n static getBkn(skey: string): string {\n let hash = 5381;\n for (let i = 0; i < skey.length; i += 1) {\n const code = skey.charCodeAt(i);\n hash = hash + (hash << 5) + code;\n }\n return (hash & 0x7f_ff_ff_ff).toString();\n }\n}\n\n/** 请求 jump 地址并解析 set-cookie → dict(fetch 吞 Set-Cookie,故用 node:https)。 */\nfunction fetchJumpCookies(url: string): Promise<Record<string, string>> {\n return new Promise((resolve, reject) => {\n const req = httpsGet(\n url,\n {\n headers: {\n \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\",\n },\n },\n (res) => {\n const cookies = parseSetCookieHeaders(res.headers[\"set-cookie\"]);\n res.resume();\n res.on(\"end\", () => resolve(cookies));\n },\n );\n req.setTimeout(JUMP_TIMEOUT_MS, () => {\n req.destroy();\n reject(new Error(\"获取 cookies 超时\"));\n });\n req.on(\"error\", (err) => reject(err));\n });\n}\n\n/** 解析 set-cookie 头数组 → { name: value } dict。 */\nfunction parseSetCookieHeaders(raw: string[] | undefined): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (raw === undefined) {\n return cookies;\n }\n for (const line of raw) {\n const [pair] = line.split(\";\");\n if (pair !== undefined) {\n const eq = pair.indexOf(\"=\");\n if (eq > 0) {\n const name = pair.slice(0, eq).trim();\n const value = pair.slice(eq + 1);\n if (name !== \"\") {\n cookies[name] = value;\n }\n }\n }\n }\n return cookies;\n}\n","/**\n * WebApi:群空间 web 接口(P2-15)\n *\n * qun.qq.com 群空间接口需 Cookie(p_skey/skey)+ bkn(skey 哈希)。\n * 复用 TicketApi.getCookies('qun.qq.com') 取 Cookie,TicketApi.getBkn 算 bkn。\n * 零 NapCat 代码:接口契约(URL/参数/返回形状)是 qun.qq.com 外部系统的事实,\n * 自研描述。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport { TicketApi } from \"./ticket.js\";\n\n/** 群空间域名(Cookie 归属)。 */\nconst QUN_DOMAIN = \"qun.qq.com\";\n\n/** 精华消息页大小(分页拉取)。 */\nconst ESSENCE_PAGE_LIMIT = 50;\n\n/** 精华消息最大分页数。 */\nconst ESSENCE_MAX_PAGES = 20;\n\n/** 精华消息项(digest_list 返回 data.msg_list 成员,说明书参考)。 */\nexport interface EssenceMsgItem {\n msg_seq: number;\n msg_random: number;\n sender_uin: string;\n sender_nick: string;\n add_digest_uin: string;\n add_digest_nick: string;\n add_digest_time: number;\n msg_content: Array<{\n msg_type?: number;\n text?: string;\n image_url?: string;\n }>;\n}\n\n/** 群荣誉项(honorlist 返回列表项,说明书参考)。 */\nexport interface HonorListItem {\n uin: string;\n name: string;\n avatar: string;\n desc: string;\n}\n\n/** 群荣誉信息(getGroupHonorInfo 返回)。 */\nexport interface GroupHonorWebInfo {\n group_id: number;\n current_talkative: HonorListItem | null;\n talkative_list: HonorListItem[];\n performer_list: HonorListItem[];\n legend_list: HonorListItem[];\n emotion_list: HonorListItem[];\n strong_newbie_list: HonorListItem[];\n}\n\n/** 群荣誉类型(1=龙王 2=群聊之火 3=群聊炽热 6=快乐源泉)。 */\nexport const WebHonorType = {\n ALL: 0,\n TALKATIVE: 1,\n PERFORMER: 2,\n LEGEND: 3,\n EMOTION: 6,\n} as const;\nexport type WebHonorType = (typeof WebHonorType)[keyof typeof WebHonorType];\n\n/** 群空间 web API(Cookie 经 TicketApi 注入)。 */\nexport class WebApi {\n private readonly getCookies: (domain: string) => Promise<Record<string, string>>;\n\n constructor(opts: { getCookies: (domain: string) => Promise<Record<string, string>> }) {\n this.getCookies = opts.getCookies;\n }\n\n /** 拉取单页精华消息(失败/retcode 非 0 返回 null)。 */\n private async fetchEssencePage(\n groupCode: string,\n cookie: Record<string, string>,\n bkn: string,\n page: number,\n ): Promise<{ items: EssenceMsgItem[]; isEnd: boolean } | null> {\n const url =\n \"https://qun.qq.com/cgi-bin/group_digest/digest_list?\" +\n new URLSearchParams({\n bkn,\n page_start: String(page * ESSENCE_PAGE_LIMIT),\n page_limit: String(ESSENCE_PAGE_LIMIT),\n group_code: groupCode,\n }).toString();\n const ret = await fetchQunJson(url, cookie);\n if (ret === null || ret.retcode !== 0) {\n return null;\n }\n const list = ret.data?.msg_list;\n return {\n items: Array.isArray(list) ? list.filter((item) => item !== null) : [],\n isEnd: ret.data?.is_end === true,\n };\n }\n\n /** 获取群精华消息列表(get_essence_msg_list;分页拉取至 is_end)。 */\n async getEssenceMsgList(groupCode: string): Promise<EssenceMsgItem[]> {\n const cookie = await this.getCookies(QUN_DOMAIN);\n const bkn = TicketApi.getBkn(cookie[\"skey\"] ?? \"\");\n const out: EssenceMsgItem[] = [];\n for (let page = 0; page < ESSENCE_MAX_PAGES; page += 1) {\n const result = await this.fetchEssencePage(groupCode, cookie, bkn, page);\n if (result === null) {\n break;\n }\n out.push(...result.items);\n if (result.isEnd) {\n break;\n }\n }\n return out;\n }\n\n /** 获取群荣誉信息(get_group_honor_info;type 0=全部)。 */\n async getGroupHonorInfo(groupCode: string, type: WebHonorType): Promise<GroupHonorWebInfo> {\n const cookie = await this.getCookies(QUN_DOMAIN);\n const result: GroupHonorWebInfo = {\n group_id: Number(groupCode),\n current_talkative: null,\n talkative_list: [],\n performer_list: [],\n legend_list: [],\n emotion_list: [],\n strong_newbie_list: [],\n };\n await this.fillHonorLists(result, groupCode, cookie, type);\n return result;\n }\n\n /** 按 type 填充荣誉列表(ALL 拉全部,单类型只拉对应类)。 */\n private async fillHonorLists(\n result: GroupHonorWebInfo,\n groupCode: string,\n cookie: Record<string, string>,\n type: WebHonorType,\n ): Promise<void> {\n for (const target of honorTargets(type)) {\n const list = await this.fetchHonorList(groupCode, cookie, target.kind);\n result[target.listKey] = list;\n if (target.kind === WebHonorType.TALKATIVE) {\n const [first] = list;\n if (first !== undefined) {\n result.current_talkative = first;\n }\n }\n }\n }\n\n /** 拉取单类荣誉列表(honorlist 接口,正则提取 __INITIAL_STATE__)。 */\n private async fetchHonorList(\n groupCode: string,\n cookie: Record<string, string>,\n type: number,\n ): Promise<HonorListItem[]> {\n const url =\n \"https://qun.qq.com/interactive/honorlist?\" +\n new URLSearchParams({ gc: groupCode, type: String(type) }).toString();\n const text = await fetchQunText(url, cookie);\n if (text === null) {\n return [];\n }\n return parseHonorList(text, type);\n }\n}\n\n/** honorlist 页面 __INITIAL_STATE__ 提取。 */\nconst INITIAL_STATE_RE = /window\\.__INITIAL_STATE__=(.*?);/;\n\n/** 从 honorlist 页面提取 __INITIAL_STATE__ → 荣誉列表(解析失败返回空)。 */\nexport function parseHonorList(text: string, type: number): HonorListItem[] {\n const match = INITIAL_STATE_RE.exec(text);\n if (match === null || match[1] === undefined) {\n return [];\n }\n let state: { talkativeList?: unknown; actorList?: unknown };\n try {\n state = JSON.parse(match[1]);\n } catch {\n return [];\n }\n const raw = type === WebHonorType.TALKATIVE ? state.talkativeList : state.actorList;\n if (!Array.isArray(raw)) {\n return [];\n }\n const out: HonorListItem[] = [];\n for (const item of raw) {\n if (item !== null && typeof item === \"object\") {\n const obj = item as {\n uin?: unknown;\n name?: unknown;\n avatar?: unknown;\n desc?: unknown;\n };\n out.push({\n uin: String(obj.uin ?? \"\"),\n name: String(obj.name ?? \"\"),\n avatar: String(obj.avatar ?? \"\"),\n desc: String(obj.desc ?? \"\"),\n });\n }\n }\n return out;\n}\n\n/** 请求 qun.qq.com JSON 接口(带 Cookie;失败返回 null)。 */\nasync function fetchQunJson(\n url: string,\n cookie: Record<string, string>,\n): Promise<{ retcode: number; data?: { msg_list?: EssenceMsgItem[]; is_end?: boolean } } | null> {\n try {\n const res = await fetch(url, { headers: { Cookie: cookieToString(cookie) } });\n if (!res.ok) {\n return null;\n }\n const json = (await res.json()) as {\n retcode?: number;\n data?: { msg_list?: unknown; is_end?: unknown };\n };\n return {\n retcode: json.retcode ?? -1,\n data: json.data as { msg_list?: EssenceMsgItem[]; is_end?: boolean },\n };\n } catch (err) {\n throw kernelError(`qun.qq.com 请求失败: ${String(err)}`, \"UNKNOWN\");\n }\n}\n\n/** 请求 qun.qq.com 文本接口(带 Cookie;失败返回 null)。 */\nasync function fetchQunText(url: string, cookie: Record<string, string>): Promise<string | null> {\n try {\n const res = await fetch(url, { headers: { Cookie: cookieToString(cookie) } });\n if (!res.ok) {\n return null;\n }\n return await res.text();\n } catch (err) {\n throw kernelError(`qun.qq.com 请求失败: ${String(err)}`, \"UNKNOWN\");\n }\n}\n\n/** Cookie dict → 头字符串。 */\nfunction cookieToString(cookie: Record<string, string>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(cookie)) {\n parts.push(`${key}=${value}`);\n }\n return parts.join(\"; \");\n}\n\n/** 荣誉类型 → 目标列表(ALL 拉全部,单类型只拉对应类)。 */\nfunction honorTargets(type: WebHonorType): Array<{\n kind: WebHonorType;\n listKey: \"talkative_list\" | \"performer_list\" | \"legend_list\" | \"emotion_list\";\n}> {\n const all = [\n { kind: WebHonorType.TALKATIVE, listKey: \"talkative_list\" },\n { kind: WebHonorType.PERFORMER, listKey: \"performer_list\" },\n { kind: WebHonorType.LEGEND, listKey: \"legend_list\" },\n { kind: WebHonorType.EMOTION, listKey: \"emotion_list\" },\n ] as const;\n if (type === WebHonorType.ALL) {\n return [...all];\n }\n return all.filter((target) => target.kind === type);\n}\n","/**\n * FriendBridge:好友事件桥(2026-09-08)\n *\n * 职责:把 buddy service 的原生回调(onBuddyReqChange / onBuddyListChange 等)\n * 推入类型化事件通道 `NTEventChannel<BuddyListener, \"Buddy\">`。与 MsgBridge/\n * GroupBridge 同构(每个 Service 只注册一次原生监听,ADR-003)。\n *\n * 方法名证据:wrapper.node(9.9.33-52230)二进制字符串提取;参数形状\n * 待真实事件校准(透传 unknown,订阅方防御性收窄)。\n *\n * 用法:\n * const channel = new NTEventChannel<BuddyListener, \"Buddy\">(\"Buddy\");\n * const bridge = new FriendBridge(session, channel);\n * bridge.register(); // addKernelBuddyListener(普通 JS 对象,NAPI 反射)\n * bridge.unregister(); // 停止时清理\n */\n\nimport type { NTEventChannel } from \"../event-channel.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n BuddyListener,\n NodeIKernelBuddyService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 好友事件通道的固定类型(事件名前缀 \"Buddy\")。 */\nexport type BuddyEventChannel = NTEventChannel<BuddyListener, \"Buddy\">;\n\n/**\n * 好友事件桥:注册原生监听 → 回调 emit 到 channel。\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份。\n */\nexport class FriendBridge {\n private readonly service: NodeIKernelBuddyService;\n private readonly channel: BuddyEventChannel;\n private listenerId: number | null = null;\n\n constructor(session: NodeIQQNTWrapperSession, channel: BuddyEventChannel) {\n const service = session.getBuddyService() as unknown as NodeIKernelBuddyService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getBuddyService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel;\n }\n\n /** 注册原生监听(幂等)。 */\n register(): void {\n if (this.listenerId !== null) {\n return;\n }\n // listener 为普通 JS 对象(NAPI 反射读取方法回调)\n const listener: BuddyListener = {\n onBuddyReqChange: (arg) => this.channel.emit(\"Buddy/onBuddyReqChange\", arg),\n onBuddyListChange: (arg) => this.channel.emit(\"Buddy/onBuddyListChange\", arg),\n onBuddyListChangedV2: (arg) => this.channel.emit(\"Buddy/onBuddyListChangedV2\", arg),\n onBuddyDeleted: (arg) => this.channel.emit(\"Buddy/onBuddyDeleted\", arg),\n };\n this.listenerId = this.service.addKernelBuddyListener(listener);\n }\n\n /** 注销原生监听(幂等)。 */\n unregister(): void {\n if (this.listenerId === null) {\n return;\n }\n this.service.removeKernelBuddyListener(this.listenerId);\n this.listenerId = null;\n }\n}\n","/**\n * GroupBridge:群事件桥(P2-17,2026-08-05)\n *\n * 职责:把 group service 的原生回调(onGroupListUpdate / onMemberListChange 等)\n * 推入类型化事件通道 `NTEventChannel<GroupListener, \"Group\">`。每个 Service 只\n * 注册一次原生监听(ADR-003),缓存维护(GroupCache)与协议层都订阅 channel。\n *\n * 用法:\n * const channel = new NTEventChannel<GroupListener, \"Group\">(\"Group\");\n * const bridge = new GroupBridge(session, channel);\n * bridge.register(); // addKernelGroupListener(普通 JS 对象,NAPI 反射)\n * const cache = new GroupCache({ channel, groupApi });\n * cache.register(); // 订阅 channel 主动维护\n * bridge.unregister(); // 停止时清理\n */\n\nimport type { NTEventChannel } from \"../event-channel.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n GroupListener,\n NodeIKernelGroupService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 群事件通道的固定类型(事件名前缀 \"Group\")。 */\nexport type GroupEventChannel = NTEventChannel<GroupListener, \"Group\">;\n\n/**\n * 群事件桥:注册原生监听 → 回调 emit 到 channel。\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份。\n */\nexport class GroupBridge {\n private readonly service: NodeIKernelGroupService;\n private readonly channel: GroupEventChannel;\n private listenerId: number | null = null;\n\n constructor(session: NodeIQQNTWrapperSession, channel: GroupEventChannel) {\n const service = session.getGroupService() as unknown as NodeIKernelGroupService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getGroupService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel;\n }\n\n /** 注册原生监听(幂等)。 */\n register(): void {\n if (this.listenerId !== null) {\n return;\n }\n // listener 为普通 JS 对象(NAPI 反射读取方法回调)\n const listener: GroupListener = {\n onGroupListInited: (listEmpty) =>\n this.channel.emit(\"Group/onGroupListInited\", listEmpty),\n onGroupListUpdate: (updateType, groupList) =>\n this.channel.emit(\"Group/onGroupListUpdate\", updateType, groupList),\n onGroupDetailInfoChange: (detailInfo) =>\n this.channel.emit(\"Group/onGroupDetailInfoChange\", detailInfo),\n onMemberListChange: (arg) => this.channel.emit(\"Group/onMemberListChange\", arg),\n onMemberInfoChange: (groupCode, dataSource, members) =>\n this.channel.emit(\"Group/onMemberInfoChange\", groupCode, dataSource, members),\n onGroupNotifiesUpdated: (doubt, notifies) =>\n this.channel.emit(\"Group/onGroupNotifiesUpdated\", doubt, notifies),\n onGroupSingleScreenNotifies: (doubt, seq, notifies) =>\n this.channel.emit(\"Group/onGroupSingleScreenNotifies\", doubt, seq, notifies),\n onShutUpMemberListChanged: (groupCode, members) =>\n this.channel.emit(\"Group/onShutUpMemberListChanged\", groupCode, members),\n // 2026-09-10 接线:群精华列表变化(方法名证据见 listeners/group.ts 注释)\n onGroupEssenceListChange: (arg) =>\n this.channel.emit(\"Group/onGroupEssenceListChange\", arg),\n };\n this.listenerId = this.service.addKernelGroupListener(listener);\n }\n\n /** 注销原生监听(幂等)。 */\n unregister(): void {\n if (this.listenerId === null) {\n return;\n }\n this.service.removeKernelGroupListener(this.listenerId);\n this.listenerId = null;\n }\n}\n","/**\n * MsgBridge:消息事件桥(P2-2,2026-08-05)\n *\n * 职责:把 msg service 的原生回调(onRecvMsg 等)推入类型化事件通道\n * `NTEventChannel<MsgListener, \"Msg\">`。每个 Service 只注册一次原生监听\n * (ADR-003),缓存维护与协议翻译都订阅 channel。\n *\n * 用法:\n * const channel = new NTEventChannel<MsgListener, \"Msg\">(\"Msg\");\n * const bridge = new MsgBridge(session, channel);\n * bridge.register(); // addKernelMsgListener(普通 JS 对象,NAPI 反射)\n * // 协议层订阅:channel.on(\"Msg/onRecvMsg\", handler)\n * bridge.unregister(); // 停止时清理\n */\n\nimport type { NTEventChannel } from \"../event-channel.js\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n MsgListener,\n NodeIKernelMsgService,\n NodeIQQNTWrapperSession,\n} from \"../types/index.js\";\n\n/** 消息事件通道的固定类型(事件名前缀 \"Msg\")。 */\nexport type MsgEventChannel = NTEventChannel<MsgListener, \"Msg\">;\n\n/**\n * 消息事件桥:注册原生监听 → 回调 emit 到 channel。\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份。\n */\nexport class MsgBridge {\n private readonly service: NodeIKernelMsgService;\n private readonly channel: MsgEventChannel;\n private listenerId: number | null = null;\n\n constructor(session: NodeIQQNTWrapperSession, channel: MsgEventChannel) {\n const service = session.getMsgService() as unknown as NodeIKernelMsgService | null;\n if (service === null || service === undefined) {\n throw kernelError(\"getMsgService() 返回空(session 未 init)\", \"INVALID_STATE\");\n }\n this.service = service;\n this.channel = channel;\n }\n\n /** 注册原生监听(幂等)。 */\n register(): void {\n if (this.listenerId !== null) {\n return;\n }\n // listener 为普通 JS 对象(NAPI 反射读取方法回调)\n // onRecvMsg 透传原生回调参数(运行时实证为消息数组,2026-08-07)。\n const listener: MsgListener = {\n onRecvMsg: (msgs) => this.channel.emit(\"Msg/onRecvMsg\", msgs),\n onRecvMsgReadReport: (reports) => this.channel.emit(\"Msg/onRecvMsgReadReport\", reports),\n onRecvMsgReceipt: (receipts) => this.channel.emit(\"Msg/onRecvMsgReceipt\", receipts),\n // 2026-08-11 补齐:发送状态更新(sendMsg 结果以此为准,NapCat 同款)\n onMsgInfoListUpdate: (msgs) => this.channel.emit(\"Msg/onMsgInfoListUpdate\", msgs),\n // 2026-09-10 接线:离线文件 / sys msg(方法名证据见 listeners/msg.ts 头注释)\n onRecvOfflineFileMsg: (arg) => this.channel.emit(\"Msg/onRecvOfflineFileMsg\", arg),\n onRecvOnlineFileMsg: (arg) => this.channel.emit(\"Msg/onRecvOnlineFileMsg\", arg),\n onRecvSysMsg: (arg) => this.channel.emit(\"Msg/onRecvSysMsg\", arg),\n };\n this.listenerId = this.service.addKernelMsgListener(listener);\n }\n\n /** 注销原生监听(幂等)。 */\n unregister(): void {\n if (this.listenerId === null) {\n return;\n }\n this.service.removeKernelMsgListener(this.listenerId);\n this.listenerId = null;\n }\n}\n","/**\n * 类型化事件通道(ADR-003)\n *\n * - 事件名约定 `${Service}/${method}`(如 \"Msg/onRecvMsg\"),签名从 Listener 接口编译期推导\n * - 事件名写错 → 编译期报错(类型推导)\n * - `on` 返回 unsubscribe;`waitFor` 注册临时监听 → filter → 超时自动清理(替代魔法字符串方案)\n * - error 兜底:监听器抛异常不打断派发,统一经 `onError` 通知\n *\n * 组合实现而非 extends EventEmitter:EventEmitter.on 返回 `this`,与设计要求的\n * 返回 unsubscribe 冲突;组合更干净,且内核无全局单例(ADR-015 推论)。\n *\n * 类型推导说明:不直接 `ListenerEvents<L, Name>[E]` 索引(泛型下会扩宽到\n * string|number|symbol 索引,索引出 unknown),而是用 Extract 按事件名从\n * `{ name, fn }` 联合精确提取 handler / 参数类型,保证实例化后签名精确。\n */\nimport { EventEmitter } from \"node:events\";\nimport { KernelError } from \"./infra/index.js\";\n\n/** waitFor 默认超时(毫秒)。 */\nconst DEFAULT_WAIT_TIMEOUT_MS = 30_000;\n\n/** Listener 接口形状(宽约束;精确签名由实例化时的具体接口保证)。 */\ntype ListenerShape = Record<string, unknown>;\n\n/** 事件名:`${Service}/${method}`。 */\ntype EventName<L extends ListenerShape, Name extends string> = `${Name}/${keyof L & string}`;\n\n/** 按事件名从 Listener 精确提取 handler 类型(条件类型 infer,泛型安全)。 */\ntype EventHandler<\n L extends ListenerShape,\n Name extends string,\n E extends EventName<L, Name>,\n> = Extract<\n {\n [K in keyof L]: L[K] extends (...args: infer A) => infer R\n ? { name: `${Name}/${K & string}`; fn: (...args: A) => R }\n : never;\n }[keyof L],\n { name: E }\n>[\"fn\"];\n\n/** 按事件名从 Listener 精确提取参数类型。 */\ntype EventArgs<\n L extends ListenerShape,\n Name extends string,\n E extends EventName<L, Name>,\n> = Extract<\n {\n [K in keyof L]: L[K] extends (...args: infer A) => unknown\n ? { name: `${Name}/${K & string}`; args: A }\n : never;\n }[keyof L],\n { name: E }\n>[\"args\"];\n\n/** 从 Listener 接口推导事件映射(文档/调试用):`{ \"Msg/onRecvMsg\": fn }`。 */\nexport type ListenerEvents<L extends ListenerShape, Name extends string> = {\n [K in keyof L as `${Name}/${K & string}`]: L[K];\n};\n\n/**\n * 类型化事件通道。\n *\n * @typeParam L - Listener 接口(探测产物,如 MsgListener)\n * @typeParam Name - Service 名(事件名前缀,如 \"Msg\")\n */\nexport class NTEventChannel<L extends ListenerShape, Name extends string> {\n readonly serviceName: Name;\n\n private readonly emitter = new EventEmitter();\n private readonly errorHandlers = new Set<(err: unknown) => void>();\n private readonly anyHandlers = new Set<(event: string, ...args: unknown[]) => void>();\n\n constructor(serviceName: Name) {\n this.serviceName = serviceName;\n // 单个 Service 多订阅者(缓存维护 + 协议翻译),关闭默认 10 个监听器上限\n this.emitter.setMaxListeners(0);\n }\n\n /**\n * 订阅事件:返回 unsubscribe(只注销该 handler,不影响其他订阅者)。\n * 每个 Service 只注册一次原生监听,缓存维护与协议翻译都订阅这里。\n */\n on<E extends EventName<L, Name>>(event: E, handler: EventHandler<L, Name, E>): () => void {\n this.emitter.addListener(event as string, handler as (...args: unknown[]) => void);\n return () => {\n this.emitter.removeListener(event as string, handler as (...args: unknown[]) => void);\n };\n }\n\n /**\n * 请求-响应桥:注册临时监听 → filter 命中 → resolve;超时自动清理并 reject。\n * 替代魔法字符串事件名方案。\n */\n waitFor<E extends EventName<L, Name>>(\n event: E,\n opts: {\n filter?: (...args: EventArgs<L, Name, E>) => boolean;\n timeout?: number;\n } = {},\n ): Promise<EventArgs<L, Name, E>> {\n const timeoutMs = opts.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;\n return new Promise((resolve, reject) => {\n const handler = (...args: EventArgs<L, Name, E>) => {\n if (opts.filter && !opts.filter(...args)) {\n return; // 不匹配:保持监听,等待下一条\n }\n cleanup();\n resolve(args);\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(new KernelError(`等待事件超时: ${event as string}`, \"TIMEOUT\"));\n }, timeoutMs);\n const cleanup = (): void => {\n clearTimeout(timer);\n this.emitter.removeListener(\n event as string,\n handler as (...args: unknown[]) => void,\n );\n };\n this.emitter.addListener(event as string, handler as (...args: unknown[]) => void);\n });\n }\n\n /**\n * 类型化派发:wrapper-loader 的原生回调经此推入通道。\n * 2026-08-07 修复:原生 EventEmitter.emit 中一个 listener 抛异常会中断后续\n * listener 派发(Node 语义)——改为快照数组逐个 try/catch,保证「单个订阅者\n * 异常不打断派发」(协议翻译层异常不饿死缓存维护等其他订阅者)。\n */\n /**\n * 订阅所有事件(IPC 子进程模式整通道转发用:事件名 `\"${service}/${method}\"`\n * 经 handler 首参透出,args 原样透传)。返回退订函数。\n */\n onAny(handler: (event: string, ...args: unknown[]) => void): () => void {\n this.anyHandlers.add(handler);\n return () => {\n this.anyHandlers.delete(handler);\n };\n }\n\n emit<E extends EventName<L, Name>>(event: E, ...args: EventArgs<L, Name, E>): void {\n // 全事件订阅(IPC 转发):单个订阅者异常不打断派发,统一走 error 兜底\n for (const anyHandler of this.anyHandlers) {\n try {\n anyHandler(event as string, ...args);\n } catch (err) {\n this.notifyError(err);\n }\n }\n const listeners = this.emitter.listeners(event as string);\n for (const listener of listeners) {\n try {\n (listener as (...a: unknown[]) => void)(...args);\n } catch (err) {\n // error 兜底:单个订阅者异常不打断派发,统一通知 onError\n this.notifyError(err);\n }\n }\n }\n\n /** 通知所有错误处理器(单个异常不打断其他处理器)。 */\n private notifyError(err: unknown): void {\n for (const handler of this.errorHandlers) {\n handler(err);\n }\n }\n\n /** 订阅错误通知(监听器抛出的异常 / 其他内部错误)。 */\n onError(handler: (err: unknown) => void): () => void {\n this.errorHandlers.add(handler);\n return () => {\n this.errorHandlers.delete(handler);\n };\n }\n}\n","/**\n * BuddyCache:好友缓存(B2,2026-09-08;仿 GroupCache 的 ADR-008 模式)\n *\n * - 更新:订阅 Buddy/onBuddyListChange 全量快照(T10 实证 = BuddyCategory[],\n * 好友明细在 category.buddyList)维护 uid → BuddyListEntry 快照。\n * - diff:首帧只建 baseline 不发事件(防止启动时对每个好友发 friend_add);\n * 之后快照对比上一帧——新增发布 BuddyCache/onBuddyAdded、消失发布\n * BuddyCache/onBuddyRemoved(归一化事件,协议层翻译保持纯函数)。\n * - 消费:协议翻译层订阅 diff 通道(friend_add notice)或只读快照查询。\n *\n * 无全局单例(ADR-015 推论)——每进程每 session 一份,由装配层持有。\n */\n\nimport type { BuddyEventChannel } from \"../bridge/index.js\";\nimport { NTEventChannel } from \"../event-channel.js\";\nimport type { BuddyCategory, BuddyListEntry } from \"../types/index.js\";\n\n/** BuddyCache diff 事件 listener(归一化「新增/删除」事件)。 */\nexport type BuddyCacheListener = {\n onBuddyAdded: (entry: BuddyListEntry) => void;\n onBuddyRemoved: (entry: BuddyListEntry) => void;\n};\n\n/** BuddyCache diff 事件通道(事件名 BuddyCache/onBuddyAdded 等)。 */\nexport type BuddyCacheEventChannel = NTEventChannel<BuddyCacheListener, \"BuddyCache\">;\n\n/** BuddyCache 构造参数。 */\nexport interface BuddyCacheOptions {\n /** Buddy 事件通道(FriendBridge 注册原生监听后 emit 到这里)。 */\n channel: BuddyEventChannel;\n /** 校准日志(可选:快照形状异常 raw 记录;缺省静默)。 */\n logger?: { warn(obj: unknown, msg?: string): void };\n}\n\n/**\n * 防御性收窄 onBuddyListChange 载荷 → BuddyCategory[](T10 实证形状;\n * 非数组返回 null,调用方打 raw 日志积累校准数据)。\n */\nexport function narrowBuddyCategories(arg: unknown): BuddyCategory[] | null {\n if (!Array.isArray(arg)) {\n return null;\n }\n for (const item of arg) {\n if (item === null || typeof item !== \"object\") {\n return null;\n }\n }\n return arg as BuddyCategory[];\n}\n\n/** 从快照分类拍平好友明细(buddyList 缺失的分类跳过)。 */\nfunction flattenEntries(categories: BuddyCategory[]): BuddyListEntry[] {\n const out: BuddyListEntry[] = [];\n for (const cat of categories) {\n for (const entry of cat.buddyList ?? []) {\n if (typeof entry?.uid === \"string\" && entry.uid !== \"\") {\n out.push(entry);\n }\n }\n }\n return out;\n}\n\n/** 好友缓存:快照维护 + diff 归一化事件。 */\nexport class BuddyCache {\n private readonly channel: BuddyEventChannel;\n private readonly logger: { warn(obj: unknown, msg?: string): void } | undefined;\n\n /** diff 归一化事件通道(协议层订阅 friend_add / IPC 转发消费)。 */\n readonly events: BuddyCacheEventChannel = new NTEventChannel(\"BuddyCache\");\n\n /** 好友快照(uid → entry,onBuddyListChange 全量维护)。 */\n private readonly buddies = new Map<string, BuddyListEntry>();\n\n /** 首帧已建 baseline(此后快照才参与 diff)。 */\n private bootstrapped = false;\n\n private unsubscribes: (() => void) | null = null;\n\n constructor(opts: BuddyCacheOptions) {\n this.channel = opts.channel;\n this.logger = opts.logger;\n }\n\n /** 订阅 Buddy 事件通道(幂等)。 */\n register(): void {\n if (this.unsubscribes !== null) {\n return;\n }\n const unsub = this.channel.on(\"Buddy/onBuddyListChange\", (arg: unknown) => {\n const categories = narrowBuddyCategories(arg);\n if (categories === null) {\n // 未知形状:raw 日志积累校准数据(V2 事件载荷是 boolean,同样落这里)\n this.logger?.warn(\n { raw: JSON.stringify(arg)?.slice(0, 2000) },\n \"BuddyCache: onBuddyListChange 未知载荷形状\",\n );\n return;\n }\n this.applySnapshot(flattenEntries(categories));\n });\n this.unsubscribes = unsub;\n }\n\n /** 退订事件通道(幂等)。 */\n unregister(): void {\n this.unsubscribes?.();\n this.unsubscribes = null;\n this.buddies.clear();\n this.bootstrapped = false;\n }\n\n /** 应用全量快照:首帧建 baseline,之后 diff 发布归一化事件。 */\n private applySnapshot(entries: BuddyListEntry[]): void {\n if (!this.bootstrapped) {\n for (const entry of entries) {\n this.buddies.set(entry.uid, entry);\n }\n this.bootstrapped = true;\n return;\n }\n const next = new Map<string, BuddyListEntry>();\n for (const entry of entries) {\n next.set(entry.uid, entry);\n }\n for (const [uid, entry] of next) {\n if (!this.buddies.has(uid)) {\n this.events.emit(\"BuddyCache/onBuddyAdded\", entry);\n }\n }\n for (const [uid, entry] of this.buddies) {\n if (!next.has(uid)) {\n this.events.emit(\"BuddyCache/onBuddyRemoved\", entry);\n }\n }\n this.buddies.clear();\n for (const [uid, entry] of next) {\n this.buddies.set(uid, entry);\n }\n }\n\n /** 好友快照(只读拷贝)。 */\n listBuddies(): BuddyListEntry[] {\n return [...this.buddies.values()];\n }\n\n /** 单个好友(快照;undefined = 不在缓存)。 */\n getBuddy(uid: string): BuddyListEntry | undefined {\n return this.buddies.get(uid);\n }\n\n /** 快照是否已建(测试/诊断用)。 */\n get baselineReady(): boolean {\n return this.bootstrapped;\n }\n}\n","/**\n * 等待/延迟工具(login 模块共享,2026-08-08 克隆合并)\n */\n\n/** 等待条件满足(轮询,带超时)。 */\nexport function waitFor(\n predicate: () => boolean,\n timeoutMs: number,\n intervalMs = 500,\n): Promise<boolean> {\n return new Promise((resolve) => {\n const started = Date.now();\n const tick = (): void => {\n if (predicate()) {\n resolve(true);\n return;\n }\n if (Date.now() - started > timeoutMs) {\n resolve(false);\n return;\n }\n setTimeout(tick, intervalMs);\n };\n tick();\n });\n}\n\n/** 短延迟。 */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","/**\n * GroupCache:群/成员缓存(ADR-008,P2-17,2026-08-05)\n *\n * - 更新:订阅 GroupEventChannel 主动维护(群列表/群详情/成员事件 → 缓存 upsert)。\n * - 回填:只读接口查询缺失时经 GroupApi 惰性拉取(in-flight 去重防并发重复拉)。\n * - 消费:协议翻译层只读消费,禁止调 API(翻译 = 纯函数)。\n *\n * 无全局单例(ADR-015 推论)——每进程每 session 实例化一份,由装配层持有。\n */\n\nimport type { GroupApi } from \"../apis/index.js\";\nimport type { GroupEventChannel } from \"../bridge/index.js\";\nimport { waitFor } from \"../login/wait.js\";\nimport type {\n Group,\n GroupDetailInfo,\n GroupMember,\n GroupMemberDataSource,\n GroupMemberListChange,\n} from \"../types/index.js\";\nimport { GroupListUpdateType } from \"../types/index.js\";\n\n/** GroupCache 构造参数。 */\nexport interface GroupCacheOptions {\n /** 群事件通道(GroupBridge 注册原生监听后 emit 到这里)。 */\n channel: GroupEventChannel;\n /** 群 API(惰性回填用)。 */\n groupApi: GroupApi;\n}\n\n/**\n * 群/成员缓存:事件主动维护 + 查询惰性回填。\n * 只读接口 getGroupDetail / getMembers / getMember / getGroup(列表项)。\n */\nexport class GroupCache {\n private readonly channel: GroupEventChannel;\n private readonly groupApi: GroupApi;\n\n /** 群列表项(onGroupListUpdate / getGroupList 回填)。 */\n private readonly groupList = new Map<string, Group>();\n /** 群详情(onGroupDetailInfoChange / getGroupInfo 回填)。 */\n private readonly groupDetails = new Map<string, GroupDetailInfo>();\n /** 群成员:groupCode → (uid → GroupMember)。 */\n private readonly members = new Map<string, Map<string, GroupMember>>();\n /** 惰性回填 in-flight 去重。 */\n private readonly inFlight = new Map<string, Promise<unknown>>();\n\n private unsubscribes: (() => void) | null = null;\n\n constructor(opts: GroupCacheOptions) {\n this.channel = opts.channel;\n this.groupApi = opts.groupApi;\n }\n\n /** 订阅群事件通道(幂等)。 */\n register(): void {\n if (this.unsubscribes !== null) {\n return;\n }\n const unsubs: Array<() => void> = [];\n unsubs.push(\n this.channel.on(\"Group/onGroupListInited\", (listEmpty: boolean) => {\n if (listEmpty) {\n this.groupList.clear();\n }\n }),\n );\n unsubs.push(\n this.channel.on(\n \"Group/onGroupListUpdate\",\n (updateType: GroupListUpdateType, groupList: Group[]) => {\n if (updateType === GroupListUpdateType.ALL) {\n this.groupList.clear();\n }\n for (const group of groupList) {\n this.groupList.set(group.groupCode, group);\n }\n },\n ),\n );\n unsubs.push(\n this.channel.on(\"Group/onGroupDetailInfoChange\", (detailInfo: GroupDetailInfo) => {\n this.groupDetails.set(detailInfo.groupCode, detailInfo);\n }),\n );\n unsubs.push(\n this.channel.on(\"Group/onMemberListChange\", (arg: GroupMemberListChange) => {\n this.upsertMembers(arg.sceneId, arg.infos);\n }),\n );\n unsubs.push(\n this.channel.on(\n \"Group/onMemberInfoChange\",\n (\n _groupCode: string,\n _dataSource: GroupMemberDataSource,\n members: Map<string, GroupMember>,\n ) => {\n this.upsertMembers(_groupCode, members);\n },\n ),\n );\n this.unsubscribes = () => {\n for (const unsub of unsubs) {\n unsub();\n }\n };\n }\n\n /** 退订事件通道(幂等)。 */\n unregister(): void {\n this.unsubscribes?.();\n this.unsubscribes = null;\n this.groupList.clear();\n this.groupDetails.clear();\n this.members.clear();\n this.inFlight.clear();\n }\n\n /** 合并成员到该群缓存(uid → GroupMember)。 */\n private upsertMembers(groupCode: string, infos: Map<string, GroupMember>): void {\n let map = this.members.get(groupCode);\n if (map === undefined) {\n map = new Map();\n this.members.set(groupCode, map);\n }\n for (const [uid, member] of infos) {\n map.set(uid, member);\n }\n }\n\n /** 群列表是否已有缓存(同步判断)。 */\n hasGroup(groupCode: string): boolean {\n return this.groupList.has(groupCode) || this.groupDetails.has(groupCode);\n }\n\n /**\n * 群列表快照(只读,缓存由 onGroupListUpdate 事件主动维护)。\n * 群列表数据不走原生 getGroupList 返回值(实测仅 { result, errMsg },\n * 列表经事件推送)——协议/IPC 层消费列表一律经此方法。\n */\n listGroups(): Group[] {\n return [...this.groupList.values()];\n }\n\n /**\n * 群列表快照(缓存为空时主动触发原生刷新,等待事件回填后返回)。\n *\n * 实测(2026-08-08 端到端联调):onGroupListUpdate 仅在登录后首轮同步\n * 推送(MMKV 已有缓存后不再重复推送)——进程内缓存为空时列表读不到。\n * 触发 getGroupList(true) 会重新拉取并推送事件回填缓存。\n * 返回空数组 = 刷新后仍无数据(超时 5s)。\n */\n async listGroupsRefreshed(): Promise<Group[]> {\n if (this.groupList.size > 0) {\n return [...this.groupList.values()];\n }\n await this.groupApi.getGroupList(true);\n await waitFor(() => this.groupList.size > 0, 5_000, 200);\n return [...this.groupList.values()];\n }\n\n /**\n * 群列表项(只读;缺失惰性回填 getGroupList)。\n * 返回 undefined 表示群不存在。\n */\n async getGroup(groupCode: string): Promise<Group | undefined> {\n const cached = this.groupList.get(groupCode);\n if (cached !== undefined) {\n return cached;\n }\n await this.loadGroupList();\n return this.groupList.get(groupCode);\n }\n\n /**\n * 群详情(只读;缺失惰性回填 getGroupInfo)。\n * getGroupInfo 恒返回对象(失败抛 KernelError),直接缓存。\n */\n async getGroupDetail(groupCode: string): Promise<GroupDetailInfo> {\n const cached = this.groupDetails.get(groupCode);\n if (cached !== undefined) {\n return cached;\n }\n const detail = await this.dedupe<GroupDetailInfo>(`detail:${groupCode}`, () =>\n this.groupApi.getGroupInfo(groupCode),\n );\n this.groupDetails.set(groupCode, detail);\n return detail;\n }\n\n /**\n * 群成员列表(只读;缺失惰性回填 getAllMemberList(forceFetch=true))。\n * 返回空数组表示该群无成员(或拉取失败已由 GroupApi 抛 KernelError)。\n */\n async getMembers(groupCode: string): Promise<GroupMember[]> {\n const map = this.members.get(groupCode);\n if (map !== undefined && map.size > 0) {\n return [...map.values()];\n }\n const members = await this.dedupe<GroupMember[]>(`members:${groupCode}`, () =>\n this.groupApi.getGroupMemberList(groupCode, true),\n );\n const merged = new Map<string, GroupMember>();\n for (const member of members) {\n merged.set(member.uid, member);\n }\n this.members.set(groupCode, merged);\n return members;\n }\n\n /**\n * 群成员(只读;缺失回填:先整群拉取,再按 uid 取;仍无则 getMemberInfo 单查)。\n * 返回 undefined 表示成员不在群。\n */\n async getMember(groupCode: string, uid: string): Promise<GroupMember | undefined> {\n const cached = await this.getCachedMember(groupCode, uid);\n if (cached !== undefined) {\n return cached;\n }\n const members = await this.dedupe<GroupMember[]>(`member:${groupCode}:${uid}`, () =>\n this.groupApi.getGroupMemberInfo(groupCode, [uid]),\n );\n const [member] = members;\n if (member === undefined) {\n return;\n }\n const target = this.members.get(groupCode);\n if (target !== undefined) {\n target.set(member.uid, member);\n }\n return member;\n }\n\n /** 读缓存成员(缺失先整群回填一次)。 */\n private async getCachedMember(\n groupCode: string,\n uid: string,\n ): Promise<GroupMember | undefined> {\n let map = this.members.get(groupCode);\n if (map === undefined || map.size === 0) {\n await this.getMembers(groupCode);\n map = this.members.get(groupCode);\n }\n return map?.get(uid);\n }\n\n /** 惰性回填全量群列表(getGroup 缺失时)。 */\n private async loadGroupList(): Promise<void> {\n const list = await this.dedupe<Group[]>(\"group-list\", () =>\n this.groupApi.getGroupList(false),\n );\n for (const group of list) {\n this.groupList.set(group.groupCode, group);\n }\n }\n\n /** 惰性回填并发去重:同 key 并发只拉一次。 */\n private async dedupe<T>(key: string, loader: () => Promise<T>): Promise<T> {\n const existing = this.inFlight.get(key);\n if (existing !== undefined) {\n return await (existing as Promise<T>);\n }\n const task = loader()\n .catch((err: unknown) => {\n throw err;\n })\n .finally(() => {\n this.inFlight.delete(key);\n });\n this.inFlight.set(key, task);\n return await task;\n }\n}\n","/**\n * CoreContext:只读装配根(ADR-015 推论,无全局单例)\n *\n * 把 logger / paths / wrapper 上下文聚合成单一装配根,由装配层(NapukettoCore)\n * 创建并传递给消费方(协议层 adapter、apis、cache)。\n *\n * - 无全局单例:每账号每进程一份,多账号多进程场景天然隔离(ADR-015)。\n * - 只读消费:字段由装配层填充,消费方只读,不直接修改。\n */\nimport type pino from \"pino\";\nimport type { PathWrapper } from \"./infra/index.js\";\nimport type { LoginResult } from \"./login/index.js\";\nimport type { WrapperContext } from \"./wrapper/wrapper-loader.js\";\n\nexport interface CoreContextOptions {\n /** pino logger(console + 可选文件,由装配层创建)。 */\n logger: pino.Logger;\n /** 账号数据目录布局(config/logs/cache)。 */\n paths: PathWrapper;\n}\n\n/**\n * 装配根:持有进程级共享对象。\n * wrapper / login 由 NapukettoCore 装配后填充,未装配前为 null。\n */\nexport interface CoreContext {\n readonly logger: pino.Logger;\n readonly paths: PathWrapper;\n /** QQ wrapper 上下文(attachWrapper 后填充)。 */\n wrapper: WrapperContext | null;\n /** 登录结果(login 成功后填充)。 */\n login: LoginResult | null;\n}\n\n/** 创建装配根(初始 wrapper/login 为 null)。 */\nexport function createCoreContext(opts: CoreContextOptions): CoreContext {\n return {\n logger: opts.logger,\n paths: opts.paths,\n wrapper: null,\n login: null,\n };\n}\n","/**\n * wrapper NAPI 回调适配器实现(普通 JS 对象,2026-08-05 确认)\n *\n * 事实(实测):wrapper.node 的 exports 89 键里没有 NodeI*Adapter/Listener 构造器,\n * engine.initWithDeskTopConfig / session.init 的 adapter 与 listener 参数一律接受\n * **普通 JS 对象**——NAPI 按方法名反射读取回调(自研实现)。\n *\n * 职责:engine / session 交互所需的全部回调占位与监听器工厂。\n * - GlobalAdapter → engine.initWithDeskTopConfig 第二参(onLog / getAppSetting / ...)\n * - DependsAdapter → session.init 第二参(onMSFStatusChange / onMSFSsoError / getGroupCode)\n * - DispatcherAdapter → session.init 第三参(dispatchRequest / dispatchCall / dispatchCallWithJson)\n * - createSessionListener → 日志版 session 监听器(boot 阶段默认,login 模块可覆盖)\n * - createLoginListener → 登录监听器占位(登录器接入时填充)\n *\n * 这些类的实例本身就是「普通 JS 对象」,可被 NAPI 反射——直接 new 传入即可。\n */\n\nimport type {\n IKernelLoginListener,\n NodeIDependsAdapter,\n NodeIDispatcherAdapter,\n NodeIGlobalAdapter,\n NodeIKernelSessionListener,\n} from \"../types/index.js\";\n\n/** NAPI 回调占位(方法存在即可被反射调用,无副作用)。 */\nconst noop = (): void => undefined;\n\n/** NodeIGlobalAdapter 实现(engine.initWithDeskTopConfig 第二参)。 */\nexport class GlobalAdapter implements NodeIGlobalAdapter {\n onLog = noop;\n onGetSrvCalTime = noop;\n onShowErrUITips = noop;\n fixPicImgType = noop;\n getAppSetting = noop;\n onInstallFinished = noop;\n onUpdateGeneralFlag = noop;\n onGetOfflineMsg = noop;\n}\n\n/** NodeIDependsAdapter 实现(session.init 第二参)。 */\nexport class DependsAdapter implements NodeIDependsAdapter {\n onMSFStatusChange = noop;\n onMSFSsoError = noop;\n getGroupCode = noop;\n}\n\n/** NodeIDispatcherAdapter 实现(session.init 第三参)。 */\nexport class DispatcherAdapter implements NodeIDispatcherAdapter {\n dispatchRequest = noop;\n dispatchCall = noop;\n dispatchCallWithJson = noop;\n}\n\n/** 会话监听器选项。 */\nexport interface SessionListenerOptions {\n /** 事件日志回调(默认静默——boot 阶段事件对用户是噪音;调试时可传回调观察)。 */\n onEvent?: (name: string, ...args: unknown[]) => void;\n}\n\n/**\n * 创建会话监听器(默认静默,boot 阶段默认;login 模块接入后可覆盖)。\n * 回调方法必须存在(NAPI 按方法名反射),但输出与否由调用方决定——\n * onNTSessionCreate / onOpentelemetryInit 等对用户无信息量,默认不打 stdout。\n */\nexport function createSessionListener(\n opts: SessionListenerOptions = {},\n): NodeIKernelSessionListener {\n const { onEvent } = opts;\n const emit = (name: string, ...args: unknown[]): void => {\n onEvent?.(name, ...args);\n };\n return {\n onNTSessionCreate: (sessionId) => emit(\"onNTSessionCreate\", sessionId),\n onGProSessionCreate: (sessionId) => emit(\"onGProSessionCreate\", sessionId),\n onSessionInitComplete: (sessionId) => emit(\"onSessionInitComplete\", sessionId),\n onOpentelemetryInit: (info) => emit(\"onOpentelemetryInit\", info),\n onUserOnlineResult: (result) => emit(\"onUserOnlineResult\", result),\n onGetSelfTinyId: (result) => emit(\"onGetSelfTinyId\", result),\n };\n}\n\n/** 创建登录监听器(占位,登录器接入时填充)。 */\nexport function createLoginListener(): IKernelLoginListener {\n return {};\n}\n","/**\n * 登录连接与快速登录(从 lifecycle.ts 拆分,2026-08-08 FTA 优化)\n *\n * - waitForNetworkConnection:等 MSF 网络连接就绪(getMsfStatus() === 3)\n * - ensureLoginConnected:connect() → 等 onLoginConnected(自建宿主必需)\n * - quickLogin:历史账号快速登录(网络异常 1006511 等就绪后重试;\n * T3 单步超时兜底——底层 promise 永久挂起时按登录失败抛出)\n */\n\nimport { kernelError } from \"../infra/index.js\";\nimport { createLoginListener } from \"../wrapper/wrapper-adapters.js\";\nimport type { WrapperContext } from \"../wrapper/wrapper-loader.js\";\nimport { sleep, waitFor } from \"./wait.js\";\n\n/** 登录结果(QR 或快速登录)。 */\nexport interface LoginResult {\n uin: string;\n uid: string;\n nick: string;\n}\n\n/** 登录列表项(getLoginList 返回,说明书参考)。 */\nexport interface LoginAccountInfo {\n uin: string;\n uid?: string;\n nickName?: string;\n isQuickLogin?: boolean;\n}\n\n/** 登录服务形状(getLoginList / quickLoginWithUin / getMsfStatus,自研描述)。 */\ntype LoginServiceShape = {\n getLoginList(): Promise<{ result: number; LocalLoginInfoList: LoginAccountInfo[] }>;\n quickLoginWithUin(uin: string): Promise<{ result: string; loginErrorInfo: { errMsg: string } }>;\n getMsfStatus(): number;\n};\n\n/**\n * 网络状态(MSF)常量。\n * 3 = 已连接(NapCat waitForNetworkConnection 语义,自研描述)。\n */\nconst MSF_STATUS_CONNECTED = 3;\n\n/** 快速登录网络异常错误码(1006511,P2-1 实测:登录前网络未就绪时报此错)。 */\nconst NETWORK_ERROR_CODE = \"1006511\";\n\n/** 登录连接异常错误提示(自建宿主实测:connect 后未缓冲即 quickLogin 报此错)。 */\nconst CONNECTION_ERROR_HINT = \"登录系统连接异常\";\n\n/** 网络重试最大次数(每次重试前等网络就绪)。 */\nconst NETWORK_RETRY_MAX = 3;\n\n/** 网络就绪等待超时(毫秒)。 */\nconst NETWORK_READY_TIMEOUT_MS = 15_000;\n\n/** 网络就绪轮询间隔(毫秒)。 */\nconst NETWORK_READY_POLL_MS = 1000;\n\n/** 连接稳定缓冲(毫秒,p0-kernel-flow 实证值)。 */\nconst CONNECTION_SETTLE_MS = 3000;\n\n/**\n * 快速登录单步超时(毫秒,T3:软重登挂起兜底)。\n * 服务端已有同账号会话时底层 quickLoginWithUin / getLoginList 永不 settle——\n * 裸 await 会让 core.login 的 catch/qrFallback、loader 的 control.release 全部\n * 永久等待。底层 promise 无法取消:超时后悬挂丢弃即可。\n */\nexport const QUICK_LOGIN_TIMEOUT_MS = 20_000;\n\n/** 快速登录超时文案(按登录失败处理;不含网络错误特征,不触发 1006511 重试叠挂)。 */\nexport const QUICK_LOGIN_TIMEOUT_MESSAGE = \"快速登录超时\";\n\n/**\n * 等待网络连接就绪(loginService.getMsfStatus() === 3)。\n * 参考 NapCat waitForNetworkConnection 思路(自研实现):快速登录在 QQ 刚拉起、\n * 网络栈未初始化时报 1006511 网络异常——等待 MSF 连接后再重试。\n * @returns 是否在超时前就绪。\n */\nexport function waitForNetworkConnection(\n ctx: WrapperContext,\n opts: { timeoutMs?: number } = {},\n): Promise<boolean> {\n const raw = ctx.loginService as unknown as LoginServiceShape | null;\n if (raw === null || typeof raw.getMsfStatus !== \"function\") {\n return Promise.resolve(false);\n }\n return waitFor(\n () => {\n try {\n return raw.getMsfStatus() === MSF_STATUS_CONNECTED;\n } catch {\n return false;\n }\n },\n opts.timeoutMs ?? NETWORK_READY_TIMEOUT_MS,\n NETWORK_READY_POLL_MS,\n );\n}\n\n/** 列出历史登录账号(boot.cjs 启动横幅用,「可用快速登录 of QQ」)。 */\nexport async function listLoginAccounts(ctx: WrapperContext): Promise<LoginAccountInfo[]> {\n const raw = ctx.loginService as unknown as LoginServiceShape | null;\n if (raw === null) {\n throw kernelError(\"loginService 无效(缺 getLoginList)\", \"INVALID_STATE\");\n }\n const list = await raw.getLoginList();\n return list.LocalLoginInfoList;\n}\n\n/**\n * 登录服务连接(自建宿主必需,HANDOVER-V6/V10 p0-kernel-flow 实证顺序):\n * connect() → 等 onLoginConnected。不 connect 则快速登录报「登录系统连接异常」。\n *\n * 对齐 p0-kernel-flow:initConfig → getLoginList → connect + onLoginConnected →\n * quickLoginWithUin。QQ 环境(worker/主进程)下可能已连接——connect 幂等,\n * 监听器超时兜底,不影响原流程。\n */\nasync function ensureLoginConnected(\n ctx: WrapperContext,\n opts: { timeoutMs?: number },\n): Promise<void> {\n const raw = ctx.loginService as unknown as {\n connect?: () => void;\n addKernelLoginListener?: (listener: unknown) => number;\n } | null;\n if (raw === null || typeof raw.connect !== \"function\") {\n return; // 无 connect(老版本 / QQ 主进程自连),跳过\n }\n await waitLoginConnected(raw, opts.timeoutMs);\n // onLoginConnected 后网络栈仍在初始化(p0-kernel-flow 实证:connect → onLoginConnected\n // → 3s 缓冲 → quickLoginWithUin 成功;无缓冲则「登录系统连接异常」)。\n await sleep(CONNECTION_SETTLE_MS);\n}\n\n/** 等 onLoginConnected(注册临时监听 + connect + 超时兜底)。 */\nfunction waitLoginConnected(\n raw: {\n connect?: () => void;\n addKernelLoginListener?: (listener: unknown) => number;\n },\n timeoutMs?: number,\n): Promise<void> {\n return new Promise<void>((resolve) => {\n let settled = false;\n const finish = (): void => {\n if (!settled) {\n settled = true;\n resolve();\n }\n };\n tryRegisterConnectListener(raw, finish);\n setTimeout(finish, timeoutMs ?? NETWORK_READY_TIMEOUT_MS);\n try {\n raw.connect?.();\n } catch {\n finish();\n }\n });\n}\n\n/** 注册临时登录连接监听(失败不阻塞连接)。 */\nfunction tryRegisterConnectListener(\n raw: {\n connect?: () => void;\n addKernelLoginListener?: (listener: unknown) => number;\n },\n finish: () => void,\n): void {\n try {\n // 临时监听等 onLoginConnected;保留不 remove——登录后续回调\n // (onQRCodeLoginSucceed 等)仍可能触发,且进程内只登录一次,无泄漏风险。\n const listener = createLoginListener();\n listener.onLoginConnected = () => {\n finish();\n };\n if (typeof raw.addKernelLoginListener === \"function\") {\n raw.addKernelLoginListener(listener);\n }\n } catch {\n // 监听注册失败不阻塞连接\n }\n}\n\n/**\n * 目标账号选择(2026-08-07 修复:显式 uin 不在列表时**报错**而非静默 fallback——\n * 之前会退到第一个可快速登录账号,导致 `-q <uin>` 实际登成别的号)。\n * 导出供单测(login-connect.test.ts)。\n */\nexport function pickLoginTarget(\n items: LoginAccountInfo[],\n uin: string | undefined,\n): LoginAccountInfo {\n if (uin !== undefined) {\n const target = items.find((i) => i.uin === uin);\n if (target === undefined) {\n const available = items.map((i) => i.uin).join(\"、\");\n throw kernelError(`账号 ${uin} 不在登录列表(可用:${available})`, \"NOT_FOUND\");\n }\n return target;\n }\n // 未指定 uin:优先第一个可快速登录账号,否则列表第一个\n const quick = items.find((i) => i.isQuickLogin);\n if (quick !== undefined) {\n return quick;\n }\n const [first] = items;\n if (first === undefined) {\n throw kernelError(\"无可用登录账号\", \"NOT_LOGIN\");\n }\n return first;\n}\n\n/** 单次快速登录尝试的结果(成功带结果,失败带错误消息)。 */\ntype AttemptResult = { ok: true; result: LoginResult } | { ok: false; errMsg: string };\n\n/**\n * 底层 NAPI promise 超时竞速(T3):promise 先 settle 用其结果,超时先到用\n * onTimeout 产物(可抛错 → 整体 reject)。底层 promise 无法取消,超时后\n * 悬挂丢弃——勿做清理动作引发二次问题;败者的迟到 settle 由 race 吞掉。\n */\nfunction raceWithTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n onTimeout: () => T,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const timeout = new Promise<T>((resolve, reject) => {\n timer = setTimeout(() => {\n try {\n resolve(onTimeout());\n } catch (err) {\n reject(err);\n }\n }, timeoutMs);\n });\n return Promise.race([promise, timeout]).finally(() => {\n if (timer !== null) {\n clearTimeout(timer);\n }\n });\n}\n\n/**\n * 带网络重试的快速登录(P2-1)。\n * 重试语义:仅当失败为网络异常(1006511)且未达上限时,等网络就绪后重试。\n * T3:每次尝试包超时竞速,挂起按登录失败处理(超时文案非网络错误 → 不重试)。\n */\nasync function loginWithNetworkRetry(\n ctx: WrapperContext,\n loginService: LoginServiceShape,\n target: LoginAccountInfo,\n opts: { uin?: string; timeoutMs?: number },\n quickTimeoutMs: number,\n): Promise<LoginResult> {\n let lastErrMsg = \"\";\n for (let attempt = 1; attempt <= NETWORK_RETRY_MAX; attempt += 1) {\n const attemptResult = await raceWithTimeout(\n attemptQuickLogin(loginService, target),\n quickTimeoutMs,\n (): AttemptResult => ({ ok: false, errMsg: QUICK_LOGIN_TIMEOUT_MESSAGE }),\n );\n if (attemptResult.ok) {\n return attemptResult.result;\n }\n lastErrMsg = attemptResult.errMsg;\n const shouldRetry = isNetworkError(lastErrMsg) && attempt < NETWORK_RETRY_MAX;\n if (!shouldRetry) {\n break;\n }\n const ready = await waitForNetworkConnection(ctx, {\n timeoutMs: opts.timeoutMs ?? NETWORK_READY_TIMEOUT_MS,\n });\n if (!ready) {\n break;\n }\n }\n throw kernelError(`快速登录失败: ${lastErrMsg}`, \"NOT_LOGIN\");\n}\n\n/** 单次快速登录尝试(成功带结果,失败带错误消息)。 */\nasync function attemptQuickLogin(\n loginService: LoginServiceShape,\n target: LoginAccountInfo,\n): Promise<AttemptResult> {\n const result = await loginService.quickLoginWithUin(target.uin);\n const { errMsg } = result.loginErrorInfo;\n if (errMsg) {\n return { ok: false, errMsg };\n }\n return {\n ok: true,\n result: {\n uin: target.uin,\n uid: target.uid ?? \"\",\n nick: target.nickName ?? \"\",\n },\n };\n}\n\n/** 判定快速登录失败是否为网络异常(重试语义)。导出供单测。 */\nexport function isNetworkError(errMsg: string): boolean {\n return errMsg.includes(NETWORK_ERROR_CODE) || errMsg.includes(CONNECTION_ERROR_HINT);\n}\n\n/** quickLogin 参数(uin 指定账号;timeoutMs 网络就绪等待;quickLoginTimeoutMs 单步超时)。 */\nexport interface QuickLoginOptions {\n /** 指定快速登录账号(缺省遍历历史列表,优先 isQuickLogin)。 */\n uin?: string;\n /** 网络就绪等待超时(毫秒,P2-1 既有语义),默认 15s。 */\n timeoutMs?: number;\n /** 单步快速登录超时(毫秒,T3 挂起兜底),默认 {@link QUICK_LOGIN_TIMEOUT_MS}。 */\n quickLoginTimeoutMs?: number;\n}\n\n/**\n * 快速登录:遍历历史登录列表尝试。\n * P2-1:失败且错误为网络异常(1006511)时,等网络就绪后重试(最多 NETWORK_RETRY_MAX 次)。\n * T3:getLoginList 与每次 quickLoginWithUin 均包超时竞速(默认 20s)——服务端\n * 已有会话时底层 promise 永久挂起,超时按登录失败(「快速登录失败: 快速登录超时」)\n * 抛出,上层 catch/qrFallback 语义自动生效。\n */\nexport async function quickLogin(\n ctx: WrapperContext,\n opts: QuickLoginOptions = {},\n): Promise<LoginResult> {\n const raw = ctx.loginService as unknown as LoginServiceShape | null;\n if (raw === null) {\n throw kernelError(\"loginService 无效(缺 getLoginList)\", \"INVALID_STATE\");\n }\n const loginService = raw;\n const quickTimeoutMs = opts.quickLoginTimeoutMs ?? QUICK_LOGIN_TIMEOUT_MS;\n // 连接登录服务(自建宿主必需:不 connect 则「登录系统连接异常」)\n await ensureLoginConnected(ctx, {\n ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),\n });\n const list = await raceWithTimeout(loginService.getLoginList(), quickTimeoutMs, () => {\n throw kernelError(`快速登录失败: ${QUICK_LOGIN_TIMEOUT_MESSAGE}`, \"NOT_LOGIN\");\n });\n const items = list.LocalLoginInfoList;\n if (items.length === 0) {\n throw kernelError(\"无历史登录账号\", \"NOT_LOGIN\");\n }\n const target = pickLoginTarget(items, opts.uin);\n return loginWithNetworkRetry(ctx, loginService, target, opts, quickTimeoutMs);\n}\n","/**\n * 完整启动生命周期(wrapper.node 契约流程,2026-08-05 实测确认,自研实现)\n *\n * 启动顺序由 wrapper.node 外部契约决定(engine→login→session→startNT),\n * 运行时实测确认,自研实现:\n * 1. engine.initWithDeskTopConfig(appid/qua/版本)\n * 2. loginService.initConfig + addKernelLoginListener\n * 3. getLoginList() → quickLoginWithUin(uin)(或 QR 登录)\n * 4. 登录成功 → genSessionConfig → session.init(config, 3 adapter, listener) → startNT(0)\n * 5. 等 init 完成信号(onOpentelemetryInit(is_init) 为主,onSessionInitComplete 为辅)\n *\n * 关键(2026-08-05 实测修正):\n * - session 用 `new wrapper.NodeIQQNTWrapperSession()` 创建;exports 不含 NodeI*Adapter/Listener\n * 构造器(89 键实测无)——adapter 与 listener 一律传普通 JS 对象(NAPI 反射读取方法回调)。\n *\n * 模块边界(2026-08-05 解耦;2026-08-08 FTA 优化再拆):\n * - 配置装配(buildEngineConfig / buildLoginConfig / buildSessionConfig)→ wrapper-config.ts\n * - NAPI 回调适配器(GlobalAdapter / DependsAdapter / DispatcherAdapter / listener 工厂)→ wrapper-adapters.ts\n * - 登录连接 + 快速登录(waitForNetworkConnection / quickLogin 等)→ login-connect.ts\n * - 本文件只保留 session 流程编排:就绪等待(waitSessionReady)+ session 初始化(initAndStartSession)。\n */\n\nimport { kernelError } from \"../infra/index.js\";\nimport type { NodeIKernelSessionListener, WrapperSessionInitConfig } from \"../types/index.js\";\nimport { DependsAdapter, DispatcherAdapter } from \"../wrapper/wrapper-adapters.js\";\nimport type { WrapperContext } from \"../wrapper/wrapper-loader.js\";\nimport { waitFor } from \"./wait.js\";\n\nexport type { LoginAccountInfo, LoginResult, QuickLoginOptions } from \"./login-connect.js\";\nexport { listLoginAccounts, quickLogin, waitForNetworkConnection } from \"./login-connect.js\";\n\n/** session init 默认超时(毫秒)。 */\nconst DEFAULT_INIT_TIMEOUT_MS = 15_000;\n\n/** session 就绪等待默认超时(毫秒)。 */\nconst SESSION_READY_TIMEOUT_MS = 30_000;\n\n/** session 就绪轮询间隔(毫秒)。 */\nconst SESSION_READY_POLL_MS = 1000;\n\n/**\n * 等待 QQ 自己完成 session 初始化(framework 复用模式,2026-08-05 修正)。\n *\n * 背景:QQ 9.9.31 实测——session 由 QQ 主进程自己创建并 init(登录后)。\n * 我们不自己 init(重复 init 断言 \"implementation of IQQNTWrapperSession is not valid\"),\n * 也不自己 create(登录前 create() 干扰 QQ,实测 QQ 退出 code=0)。\n * 正确做法:等 QQ 的 session 就绪(getMsgService 非 null = 核心 service 已挂载)。\n */\nexport async function waitSessionReady(\n ctx: WrapperContext,\n opts: { timeoutMs?: number },\n): Promise<void> {\n const { session } = ctx;\n if (session === null || session === undefined) {\n throw kernelError(\"session 未创建\", \"INVALID_STATE\");\n }\n const ok = await waitFor(\n () => {\n try {\n const svc = session.getMsgService();\n return svc !== null && svc !== undefined;\n } catch {\n return false;\n }\n },\n opts.timeoutMs ?? SESSION_READY_TIMEOUT_MS,\n SESSION_READY_POLL_MS,\n );\n if (!ok) {\n throw kernelError(\"等待 QQ session 就绪超时(QQ 未完成 init)\", \"TIMEOUT\");\n }\n}\n\n/** 登录连接 + 快速登录(quickLogin 等)已拆到 login-connect.ts(见文件头 re-export)。 */\n\n/** session 初始化(4 参全为普通 JS 对象,等 init 完成信号)。 */\nexport async function initAndStartSession(\n ctx: WrapperContext,\n config: WrapperSessionInitConfig,\n listener: NodeIKernelSessionListener,\n opts: { timeoutMs?: number },\n): Promise<void> {\n const { session } = ctx;\n if (session === null || session === undefined) {\n throw kernelError(\"session 未创建\", \"INVALID_STATE\");\n }\n\n // adapter / listener 全部用普通 JS 对象(实测 exports 89 键无 NodeI*Adapter/Listener\n // 构造器;NAPI 反射读取对象方法回调,自研实现)。\n const depends = new DependsAdapter();\n const dispatcher = new DispatcherAdapter();\n\n const initComplete = watchInitSignal(listener);\n session.init(config, depends, dispatcher, listener);\n startSessionBestEffort(ctx, session);\n\n const ok = await Promise.race([\n initComplete.then(() => true),\n waitFor(() => false, opts.timeoutMs ?? DEFAULT_INIT_TIMEOUT_MS).then(() => false),\n ]);\n if (!ok) {\n throw kernelError(\"session init 超时\", \"TIMEOUT\");\n }\n}\n\n/** 监听 init 完成信号:onOpentelemetryInit(is_init) 为主,onSessionInitComplete(0) 为辅。 */\nfunction watchInitSignal(listener: NodeIKernelSessionListener): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const onOpentelemetry = listener.onOpentelemetryInit;\n listener.onOpentelemetryInit = (info) => {\n if (info.is_init) {\n resolve();\n }\n if (typeof onOpentelemetry === \"function\") {\n onOpentelemetry(info);\n }\n };\n const onInitComplete = listener.onSessionInitComplete;\n listener.onSessionInitComplete = (r) => {\n if (r === 0 || r === \"0\") {\n resolve();\n } else {\n reject(kernelError(`session init 失败: ${String(r)}`, \"UNKNOWN\"));\n }\n if (typeof onInitComplete === \"function\") {\n onInitComplete(r);\n }\n };\n });\n}\n\n/**\n * 启动会话(best effort,失败不致命):\n * **先 init 后 start(2026-08-07 V9 决定性修正)**。\n * 自建宿主实测(HANDOVER-V9,p0-napcat-min):必须先 session.init 再\n * startupSession.start()——顺序颠倒(先 start 后 init)业务 service 不挂载\n * (getMsgService null);init 后用 startNT(非 startupSession.start)也失败。\n * NapCat initializeSession 同款:有 startupSession 用 start(),否则 startNT(0)。\n */\nfunction startSessionBestEffort(\n ctx: WrapperContext,\n session: NonNullable<WrapperContext[\"session\"]>,\n): void {\n if (startupSessionStart(ctx)) {\n return;\n }\n try {\n session.startNT(0);\n } catch {\n try {\n session.startNT();\n } catch {\n // 无 startNT(9.9.31):忽略,等 init 完成信号\n }\n }\n}\n\n/** 有 startupSession 则 start()(失败不致命),返回是否已处理。 */\nfunction startupSessionStart(ctx: WrapperContext): boolean {\n const { startupSession } = ctx;\n if (startupSession === null || typeof startupSession.start !== \"function\") {\n return false;\n }\n try {\n startupSession.start();\n } catch {\n // start 失败不致命,靠 onOpentelemetryInit/onSessionInitComplete 信号判断\n }\n return true;\n}\n","/**\n * login.ts:QR 登录流程编排 + 状态机 + selfInfo(ADR-010,2026-08-05)\n *\n * 流程(QQ wrapper 登录契约,自研实现):\n * loginService.initConfig(core.login 已做)→ addKernelLoginListener → connect()\n * → 指定账号:quickLoginWithUin;否则 getQRCodePicture() 触发二维码\n * → 回调驱动状态机:未登录 → 扫码中 → 已扫码 → 已登录(onQRCodeLoginSucceed)\n * → 二维码过期(errType=1 errCode=3)→ refresh()\n *\n * 不含 UI 渲染(cli 只做二维码渲染/URL 打印)。\n */\nimport { kernelError } from \"../infra/index.js\";\nimport type { IKernelLoginListener } from \"../types/index.js\";\nimport { createLoginListener } from \"../wrapper/wrapper-adapters.js\";\n\n/** 登录状态。 */\nexport type LoginState = \"idle\" | \"waiting_scan\" | \"scanned\" | \"logged_in\" | \"failed\";\n\n/** 二维码数据(png base64 + 解码 url)。 */\nexport interface QrCodeData {\n pngBase64: string;\n qrcodeUrl: string;\n}\n\n/** selfInfo(登录成功后填写)。 */\nexport interface SelfInfo {\n uin: string;\n uid: string;\n nick: string;\n}\n\n/** 快速登录项。 */\nexport interface LoginListItem {\n uin: string;\n uid?: string;\n nickName?: string;\n isQuickLogin?: boolean;\n}\n\n/** loginService 接口(从 WrapperContext.loginService 获取)。 */\ninterface LoginServiceLike {\n addKernelLoginListener(listener: unknown): number;\n removeKernelLoginListener(listenerId: number): void;\n connect(): void;\n getLoginList(): Promise<{ result: number; LocalLoginInfoList: LoginListItem[] }>;\n quickLoginWithUin(uin: string): Promise<{ result: string; loginErrorInfo: { errMsg: string } }>;\n getQRCodePicture(): boolean;\n}\n\n/** 二维码过期错误码(errType=1 errCode=3,实测)。 */\nconst QR_EXPIRED_ERR_TYPE = 1;\nconst QR_EXPIRED_ERR_CODE = 3;\n\n/** 登录超时上限(毫秒,参考项目 60 次 × 2s = 120s,2026-08-13 用户拍板照搬)。 */\nexport const QR_LOGIN_TIMEOUT_MS = 120_000;\n\n/** 登录超时提示(参考项目文案,前端 failed 态展示)。 */\nexport const QR_LOGIN_TIMEOUT_MESSAGE = \"登录超时,请刷新页面重试\";\n\n/** 二维码过期判定。 */\nfunction isQrCodeExpired(errType: number, errCode: number): boolean {\n return errType === QR_EXPIRED_ERR_TYPE && errCode === QR_EXPIRED_ERR_CODE;\n}\n\n/** 二维码 png data URI 前缀剥离正则。 */\nconst DATA_URI_PREFIX_RE = /^data:image\\/\\w+;base64,/;\n\n/** QR 登录会话(状态机 + 回调订阅)。 */\nexport class QrLoginSession {\n private readonly loginService: LoginServiceLike;\n private state: LoginState = \"idle\";\n private listenerId: number | null = null;\n private self: SelfInfo | null = null;\n private readonly qrCodeHandlers = new Set<(qr: QrCodeData) => void>();\n private readonly stateHandlers = new Set<(state: LoginState) => void>();\n /** 超时定时器(refresh 后启动,登录成功/失败/stop 清理)。 */\n private timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n /** 失败原因(超时等,failed 态透出给上层提示文案)。 */\n private failureReasonValue: string | null = null;\n /** 超时上限(毫秒,默认 QR_LOGIN_TIMEOUT_MS)。 */\n private readonly timeoutMs: number;\n\n constructor(loginService: unknown, options: { timeoutMs?: number } = {}) {\n const svc = loginService as LoginServiceLike | null;\n if (svc === null || typeof svc.addKernelLoginListener !== \"function\") {\n throw kernelError(\"loginService 无效(缺 addKernelLoginListener)\", \"INVALID_STATE\");\n }\n this.loginService = svc;\n this.timeoutMs = options.timeoutMs ?? QR_LOGIN_TIMEOUT_MS;\n }\n\n /** 当前状态。 */\n get currentState(): LoginState {\n return this.state;\n }\n\n /** selfInfo(登录成功后有值)。 */\n get selfInfo(): SelfInfo | null {\n return this.self;\n }\n\n /** 失败原因(超时等;failed 态透出给上层提示文案)。 */\n get failureReason(): string | null {\n return this.failureReasonValue;\n }\n\n /** 订阅二维码(png base64 + url)。返回退订函数。 */\n onQrCode(cb: (qr: QrCodeData) => void): () => void {\n this.qrCodeHandlers.add(cb);\n return () => this.qrCodeHandlers.delete(cb);\n }\n\n /** 订阅状态变化。返回退订函数。 */\n onStateChange(cb: (state: LoginState) => void): () => void {\n this.stateHandlers.add(cb);\n return () => this.stateHandlers.delete(cb);\n }\n\n /** 开始登录:注册监听 → connect → 快速登录(指定账号)或触发二维码。 */\n start(opts: { quickUin?: string } = {}): void {\n if (this.state !== \"idle\") {\n return;\n }\n this.listenerId = this.loginService.addKernelLoginListener(this.buildListener());\n this.loginService.connect();\n if (opts.quickUin !== undefined && opts.quickUin !== \"\") {\n // T3:快速登录可能永久挂起(服务端已有同账号会话)——先启动超时计时,\n // 挂起 → 超时 failed。此前挂起时连兜底计时都不启动(定时器只在 refresh\n // 里重启),ready 态软重登完全无兜底。\n this.restartTimeout();\n this.loginService\n .quickLoginWithUin(opts.quickUin)\n .then((result) => {\n // ⚠️ wrapper 契约(attemptQuickLogin 已实证):快速登录失败是\n // resolve 带 loginErrorInfo.errMsg,**不是 reject**——只挂 .catch\n // 会永不触发 → 无凭据环境(WSL 扫码)永不 refresh → 二维码永不\n // 产生,完全静默阻塞(2026-08-13 WSL 扫码卡死根因)。\n if (result.loginErrorInfo.errMsg !== \"\") {\n this.refreshAfterQuickLogin();\n }\n })\n .catch(() => {\n // reject(异常路径)同样回退二维码\n this.refreshAfterQuickLogin();\n });\n } else {\n this.refresh();\n }\n }\n\n /**\n * 快速登录失败回退二维码。仅 idle 态生效:超时 failed 后悬挂 promise 的\n * 迟到结果(含登录成功后 stop 之后的迟到 settle)不得复活会话/重复出码。\n */\n private refreshAfterQuickLogin(): void {\n if (this.state === \"idle\") {\n this.refresh();\n }\n }\n\n /** 刷新二维码(手动 / 过期自动)。每次出码重启超时计时。 */\n refresh(): void {\n this.failureReasonValue = null;\n this.setState(\"waiting_scan\");\n this.loginService.getQRCodePicture();\n this.restartTimeout();\n }\n\n /** 构建登录监听(普通 JS 对象,NAPI 反射)。 */\n private buildListener(): IKernelLoginListener {\n const listener = createLoginListener();\n listener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {\n const realBase64 = pngBase64QrcodeData.replace(DATA_URI_PREFIX_RE, \"\");\n this.emitQrCode({ pngBase64: realBase64, qrcodeUrl });\n };\n listener.onQRCodeLoginPollingStarted = () => {\n this.setState(\"waiting_scan\");\n };\n listener.onQRCodeSessionUserScaned = () => {\n this.setState(\"scanned\");\n };\n listener.onQRCodeLoginSucceed = (result) => {\n this.clearTimeoutTimer();\n this.self = { uin: result.uin, uid: result.uid, nick: result.nick ?? \"\" };\n this.setState(\"logged_in\");\n };\n listener.onQRCodeSessionFailed = (errType, errCode) => {\n if (isQrCodeExpired(errType, errCode)) {\n // 二维码过期 → 自动刷新(2026-08-13 用户拍板保留自动刷新 + 手动刷新按钮)\n this.refresh();\n return;\n }\n this.clearTimeoutTimer();\n this.failureReasonValue = null;\n this.setState(\"failed\");\n };\n listener.onLoginFailed = () => {\n this.clearTimeoutTimer();\n this.failureReasonValue = null;\n this.setState(\"failed\");\n };\n return listener;\n }\n\n /** 停止:注销监听(登录成功后调用,防重复回调)。 */\n stop(): void {\n this.clearTimeoutTimer();\n if (this.listenerId !== null) {\n this.loginService.removeKernelLoginListener(this.listenerId);\n this.listenerId = null;\n }\n }\n\n /** 重启超时计时器:超时未登录 → failed + 超时提示。 */\n private restartTimeout(): void {\n this.clearTimeoutTimer();\n this.timeoutTimer = setTimeout(() => {\n this.timeoutTimer = null;\n this.failureReasonValue = QR_LOGIN_TIMEOUT_MESSAGE;\n this.setState(\"failed\");\n }, this.timeoutMs);\n }\n\n /** 清理超时计时器(幂等)。 */\n private clearTimeoutTimer(): void {\n if (this.timeoutTimer !== null) {\n clearTimeout(this.timeoutTimer);\n this.timeoutTimer = null;\n }\n }\n\n private setState(next: LoginState): void {\n this.state = next;\n for (const cb of this.stateHandlers) {\n cb(next);\n }\n }\n\n private emitQrCode(qr: QrCodeData): void {\n for (const cb of this.qrCodeHandlers) {\n cb(qr);\n }\n }\n}\n","/**\n * QQ 数据根路径解析(P2-1,2026-08-06)\n *\n * 从 `getNTUserDataInfoConfig()` 的返回值中提取 QQ 真实数据根路径:\n * worker(utilityProcess)模式下 loginService 是 new 的,commonPath 必须指向\n * QQ 真实数据路径才能读到历史账号(cli 的 `.napuketto\\default` 读不到,\n * getLoginList 空,P2-1 实测)。\n *\n * 返回值形态可能是 JSON 字符串(对象/数组)或纯路径字符串,此处做宽容解析。\n */\n\nimport { join } from \"node:path\";\n\n/**\n * QQ 全局数据目录(commonPath / desktopGlobalPath = 数据根/nt_qq/global,\n * HANDOVER-V6 自建宿主三要素之三,p0-kernel-flow 实证)。\n *\n * 防御:已含 nt_qq/global 后缀(worker 环境下 getNTUserDataInfoConfig 可能\n * 返回完整路径)则不重复拼接,避免 `nt_qq/global/nt_qq/global` 错位。\n */\nexport function resolveQqGlobalPath(root: string): string {\n const norm = root.replace(/\\\\/g, \"/\");\n if (norm.endsWith(\"/nt_qq/global\")) {\n return root;\n }\n if (norm.endsWith(\"/nt_qq\")) {\n return join(root, \"global\");\n }\n return join(root, \"nt_qq\", \"global\");\n}\n\n/** 从 getNTUserDataInfoConfig 返回值提取数据根(JSON 字符串解析或纯路径)。 */\nexport function extractDataRoot(raw: string): string | null {\n if (raw.startsWith(\"{\") || raw.startsWith(\"[\")) {\n try {\n return findPathInValue(JSON.parse(raw));\n } catch {\n return null;\n }\n }\n return raw;\n}\n\n/** 递归在对象/数组里找含 QQ 数据路径特征的字符串值。 */\nfunction findPathInValue(value: unknown): string | null {\n if (typeof value === \"string\") {\n return isQqDataPath(value) ? value : null;\n }\n if (Array.isArray(value)) {\n return findInArray(value);\n }\n if (typeof value === \"object\" && value !== null) {\n return findInObject(value);\n }\n return null;\n}\n\n/** 判断字符串是否含 QQ 数据路径特征。 */\nfunction isQqDataPath(value: string): boolean {\n return value.includes(\"Tencent Files\") || value.includes(\"nt_qq\");\n}\n\n/** 在数组里递归查找。 */\nfunction findInArray(value: unknown[]): string | null {\n for (const item of value) {\n const found = findPathInValue(item);\n if (found !== null) {\n return found;\n }\n }\n return null;\n}\n\n/** 在对象值里递归查找。 */\nfunction findInObject(value: object): string | null {\n for (const v of Object.values(value)) {\n const found = findPathInValue(v);\n if (found !== null) {\n return found;\n }\n }\n return null;\n}\n","/**\n * wrapper 交互配置装配(纯函数,无状态)\n *\n * 生成 QQ wrapper 交互所需的三份配置:\n * - buildEngineConfig → engine.initWithDeskTopConfig(appid / qua / 版本 / 路径)\n * - buildLoginConfig → loginService.initConfig\n * - buildSessionConfig → session.init(登录成功后调用)\n *\n * 与 lifecycle.ts(流程编排)解耦:本模块只回答「配置长什么样」,不管流程怎么走。\n * 字段为 wrapper.node 外部契约(appid/qua/版本),运行时实测确认,自研描述。\n * appid 唯一来源是 major.node 动态解析(每版本不同);解析失败显式抛 KernelError,\n * 不再静默回退过期 appid(2026-08 硬编码审计:537237765 是 9.9.31 的 appid,该版本\n * 登录服务已被腾讯下线,静默用它会导致扫码「请下载最新版」且极难排查)。\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { hostname, type as osType, version as osVersionString, platform, release } from \"node:os\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n DeviceInfo,\n EnginInitDesktopConfig,\n WrapperSessionInitConfig,\n} from \"../types/index.js\";\nimport { PlatformType as PlatformTypeValue, VendorType } from \"../types/index.js\";\n\n/** app_type=4:桌面端应用类型(engine.initWithDeskTopConfig 契约;移动端为其它值)。 */\nconst APP_TYPE_DESKTOP = 4;\n\n/**\n * 缩略图生成参数(thumb_config,engine 契约):\n * maxSide 最长边像素上限 / minSide 最短边像素下限 / longLimit 长图最长短边比例上限 /\n * density 像素密度倍数。\n */\nconst THUMB_CONFIG = {\n maxSide: 324,\n minSide: 48,\n longLimit: 6,\n density: 2,\n} as const;\n\n/**\n * session.init 的 deviceConfig(wrapper 契约,JSON 字符串字面量):\n * appearance.isSplitViewMode 分屏模式开关,msg 空对象为占位。自建宿主无 UI,保持最小可用。\n */\nconst DEVICE_CONFIG_JSON = '{\"appearance\":{\"isSplitViewMode\":true},\"msg\":{}}';\n\n/** localId=2052:zh-CN(简体中文)locale 码(Windows LCID,deviceInfo.localId 契约)。 */\nconst ZH_CN_LOCALE_ID = 2052;\n\n/** 平台名映射:os.platform() → wrapper devType 契约值(不写死具体构建号)。 */\nconst DEV_TYPE_BY_PLATFORM: Record<string, string> = {\n win32: \"Windows\",\n darwin: \"Mac\",\n linux: \"Linux\",\n android: \"Android\",\n freebsd: \"FreeBSD\",\n openbsd: \"OpenBSD\",\n sunos: \"SunOS\",\n aix: \"AIX\",\n};\n\n/**\n * 系统信息运行时探测(wrapper 契约:platVer / osVersion / devType)。\n * 旧实现写死 \"Windows 10.0.22631\"/\"Windows 10 Pro\"——换机即失真(2026-08 硬编码审计整改)。\n * 探测失败时给通用值兜底,但不写死具体 Windows 构建号。\n */\nfunction systemInfo(): { platVer: string; osVersion: string; devType: string } {\n let devType = \"Unknown\";\n let platVer = \"Unknown\";\n let osVersion = \"Unknown\";\n try {\n devType = DEV_TYPE_BY_PLATFORM[platform()] ?? platform();\n // platVer 契约形如 \"Windows 10.0.22631\" = \"<平台名> <内核版本号>\"。\n platVer = `${devType} ${release()}`;\n // osVersion 优先 os.version()(Windows 返回 \"Windows 11 Pro\" 等),空则用 os.type()。\n osVersion = osVersionString() || osType();\n } catch {\n // 探测失败:保留通用兜底值。\n }\n return { platVer, osVersion, devType };\n}\n\n/** 纯数字串(major.node 提取的 appid 判定)。 */\nconst DIGITS_ONLY_RE = /^\\d+$/;\n\n/**\n * 从 major.node 提取 appid(NapCat parseAppidFromMajorV2 的自研等价实现)。\n * major.node 含 `QQAppId/` 标记后跟数字(腾讯工具链产物,实测确认)。\n * 返回 null 表示解析失败(调用方应显式报错,不再回退硬编码 appid)。\n */\nexport function parseAppidFromMajor(majorPath: string): string | null {\n if (!existsSync(majorPath)) {\n return null;\n }\n let buf: Buffer;\n try {\n buf = readFileSync(majorPath);\n } catch {\n return null;\n }\n return scanAppidMarkers(buf);\n}\n\n/** 扫描 buf 中所有 QQAppId/ 标记,返回第一个纯数字串(无则 null)。 */\nfunction scanAppidMarkers(buf: Buffer): string | null {\n const marker = Buffer.from(\"QQAppId/\", \"utf-8\");\n let pos = 0;\n while (pos < buf.length) {\n const idx = buf.indexOf(marker, pos);\n if (idx === -1) {\n return null;\n }\n const str = readMarkerValue(buf, idx + marker.length);\n if (DIGITS_ONLY_RE.test(str)) {\n return str;\n }\n pos = idx + marker.length + str.length + 1;\n }\n return null;\n}\n\n/** 读取标记后的 C 字符串(到首个 0 字节为止)。 */\nfunction readMarkerValue(buf: Buffer, start: number): string {\n let end = start;\n while (end < buf.length && buf[end] !== 0) {\n end += 1;\n }\n return buf.subarray(start, end).toString(\"utf-8\");\n}\n\n/**\n * 解析 appid / qua(自研,参考 NapCat QQBasicInfoWrapper 思路但独立实现):\n * appid 唯一来源 = major.node 提取(实测 9.9.33-51802 = 537376818)。\n * majorPath 缺省或解析失败时抛 KernelError——不再静默回退过期 appid。\n * majorPath 传 wrapper.node 同目录 major.node。\n */\nexport function resolveAppidQua(\n fullVersion: string,\n majorPath?: string,\n): { appid: string; qua: string } {\n const appid = majorPath !== undefined ? parseAppidFromMajor(majorPath) : null;\n if (appid === null) {\n throw kernelError(\n \"无法从 major.node 解析 appid,请确认 wrapper.node/major.node 完整,或更新 qq-releases.json\",\n \"INVALID_STATE\",\n );\n }\n return {\n appid,\n qua: `V1_WIN_NQ_${fullVersion}_${fullVersion.split(\"-\")[1] ?? \"\"}_GW_B`,\n };\n}\n\n/** 生成 engine 桌面配置(wrapper 契约字段)。majorPath 可选(缺失/解析失败会抛错)。 */\nexport function buildEngineConfig(\n fullVersion: string,\n dataPathGlobal: string,\n majorPath?: string,\n): EnginInitDesktopConfig {\n const { osVersion } = systemInfo();\n const { qua } = resolveAppidQua(fullVersion, majorPath);\n return {\n base_path_prefix: \"\",\n platform_type: PlatformTypeValue.KWINDOWS,\n app_type: APP_TYPE_DESKTOP,\n app_version: fullVersion,\n os_version: osVersion,\n use_xlog: true,\n qua,\n global_path_config: {\n desktopGlobalPath: dataPathGlobal,\n },\n thumb_config: THUMB_CONFIG,\n };\n}\n\n/** 生成登录初始化配置(externalVersion: false 与 NapCat 同款,扫码兼容关键)。 */\nexport function buildLoginConfig(\n appid: string,\n fullVersion: string,\n commonPath: string,\n): Record<string, unknown> {\n const { platVer } = systemInfo();\n return {\n machineId: \"\",\n appid,\n platVer,\n commonPath,\n clientVer: fullVersion,\n hostName: hostname(),\n externalVersion: false,\n };\n}\n\n/** buildSessionConfig 参数。 */\nexport interface SessionConfigOptions {\n appid: string;\n fullVersion: string;\n selfUin: string;\n selfUid: string;\n accountPath: string;\n downloadPath: string;\n /** 设备指纹 guid(来自 loginService.getMachineGuid();缺省空串)。 */\n machineGuid?: string;\n}\n\n/** 生成 session 初始化配置(登录成功后调用)。 */\nexport function buildSessionConfig(options: SessionConfigOptions): WrapperSessionInitConfig {\n const { appid, fullVersion, selfUin, selfUid, accountPath, downloadPath, machineGuid } =\n options;\n const { platVer, osVersion, devType } = systemInfo();\n const deviceInfo: DeviceInfo = {\n // 设备指纹 guid:loginService.getMachineGuid()(wrapper 探测确认,无 getMachineId 方法)。\n guid: machineGuid ?? \"\",\n buildVer: fullVersion,\n localId: ZH_CN_LOCALE_ID,\n devName: hostname(),\n devType,\n vendorName: \"\",\n osVer: osVersion,\n vendorOsName: devType,\n setMute: false,\n vendorType: VendorType.KNOSETONIOS,\n };\n return {\n selfUin,\n selfUid,\n desktopPathConfig: {\n account_path: accountPath,\n },\n clientVer: fullVersion,\n a2: \"\",\n d2: \"\",\n d2Key: \"\",\n machineId: \"\",\n platform: PlatformTypeValue.KWINDOWS,\n platVer,\n appid,\n rdeliveryConfig: {\n appKey: \"\",\n systemId: 0,\n appId: \"\",\n logicEnvironment: \"\",\n platform: PlatformTypeValue.KWINDOWS,\n language: \"\",\n sdkVersion: \"\",\n userId: \"\",\n appVersion: \"\",\n osVersion: \"\",\n bundleId: \"\",\n serverUrl: \"\",\n fixedAfterHitKeys: [\"\"],\n },\n defaultFileDownloadPath: downloadPath,\n deviceInfo,\n deviceConfig: DEVICE_CONFIG_JSON,\n };\n}\n\n/**\n * 读取设备指纹 guid(wrapper.node 字符串分析实证,2026-08-12):\n * NodeIKernelLoginService::getMachineGuid(0 参,返回机器 GUID 字符串)。\n * 无 getMachineId 方法——TODO 里的方法名不存在,等价方法是 getMachineGuid。\n * 失败(无该方法 / 调用抛错 / 返回值非字符串)兜底空串。\n */\nexport function readMachineGuid(loginService: unknown): string {\n try {\n const fn = (loginService as Record<string, unknown> | null | undefined)?.[\"getMachineGuid\"];\n if (typeof fn !== \"function\") {\n return \"\";\n }\n const value: unknown = fn.call(loginService);\n return typeof value === \"string\" ? value : \"\";\n } catch {\n return \"\";\n }\n}\n","/**\n * wrapper 引导(NAPI 范式,2026-08-05 重构,替代旧 koffi 方案)\n *\n * 事实(实测):wrapper.node 不是标准 NAPI self-register 模块,只能在 QQ 定制版\n * Electron 主进程里由 preload 注册。@napuketto/loader 注入 hook DLL 后,boot.cjs\n * 在 QQ 主进程内截获 `process.dlopen` 的 `module.exports`,然后调用本模块初始化。\n *\n * 本模块**不再**使用 koffi / vtable / 内存偏移——所有对象由 QQ 的 NAPI 层自动构建。\n * 业务层拿到的都是真实 JS 对象:engine.initWithDeskTopConfig(config, adapter) 等。\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\nimport { kernelError } from \"../infra/index.js\";\nimport type {\n EnginInitDesktopConfig,\n NodeIKernelSessionListener,\n NodeIQQNTStartupSessionWrapper,\n NodeIQQNTWrapperEngine,\n NodeIQQNTWrapperSession,\n NodeIQQNTWrapperSessionCtor,\n WrapperNodeApi,\n WrapperSessionInitConfig,\n} from \"../types/index.js\";\nimport { PlatformType } from \"../types/index.js\";\nimport { extractDataRoot, resolveQqGlobalPath } from \"./qq-data-path.js\";\nimport {\n createSessionListener,\n DependsAdapter,\n DispatcherAdapter,\n GlobalAdapter,\n} from \"./wrapper-adapters.js\";\n\n// ---------------------------------------------------------------\n// startNapuketto 内部辅助(非 export,boot 装配用)\n// ---------------------------------------------------------------\n\n/** 默认 engine 配置(KWINDOWS + 版本号,字段待首个真实联调确认)。 */\nfunction defaultEngineConfig(env: BootEnv, qqDataPath?: string): EnginInitDesktopConfig {\n return {\n base_path_prefix: env.dataDir ?? \"\",\n platform_type: PlatformType.KWINDOWS,\n app_type: 4,\n app_version: env.qqVersion ?? \"\",\n os_version: process.platform,\n use_xlog: false,\n qua: \"\",\n global_path_config: {\n desktopGlobalPath: qqDataPath ?? env.dataDir ?? \"\",\n },\n thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },\n };\n}\n\n/**\n * Electron 进程类型(Node 的 process 类型无此字段)。\n * - \"browser\":QQ 主进程(V1 模式)\n * - \"utility\":utilityProcess Worker(路线 B,2026-08-06)\n */\nexport function electronProcessType(): string | undefined {\n return (process as unknown as { type?: string }).type;\n}\n\n/**\n * 解析 QQ 用户数据根(登录数据目录,如 `C:\\Users\\<user>\\Documents\\Tencent Files`)。\n * 优先 NodeQQNTWrapperUtil.getNTUserDataInfoConfig()(QQ 官方 API),回退默认位置。\n *\n * 背景(2026-08-06 P2-1 实测):worker(utilityProcess)模式下 loginService 是\n * `new NodeIKernelLoginService()` 新建的,其 initConfig 的 commonPath 必须指向\n * QQ 真实数据路径才能读到历史账号——cli 的 `.napuketto\\default` 数据目录读不到\n * (getLoginList 返回空 → 只能 QR 登录)。V1 主进程模式 QQ 自己 initConfig 过\n * 正确路径,无需此解析。\n */\nexport function resolveQqUserDataRoot(exports: WrapperNodeApi): string | null {\n return extractUserDataRoot(exports) ?? join(homedir(), \"Documents\", \"Tencent Files\");\n}\n\n/** 从 QQ 官方 API 提取数据根(不可用返回 null,调用方回退默认)。 */\nfunction extractUserDataRoot(exports: WrapperNodeApi): string | null {\n try {\n const util = exports.NodeQQNTWrapperUtil as unknown as {\n get?: () => { getNTUserDataInfoConfig?: () => unknown };\n } | null;\n const raw = util?.get?.().getNTUserDataInfoConfig?.();\n if (typeof raw === \"string\" && raw.trim() !== \"\") {\n return extractDataRoot(raw.trim());\n }\n } catch {\n // 回退默认位置\n }\n return null;\n}\n\n// ---------------------------------------------------------------\n// 导出区(useExportsLast:export 全部在文件末尾)\n// ---------------------------------------------------------------\n\n/** QQ 版本信息(登录握手用)。 */\nexport interface QQVersionContext {\n fullVersion: string;\n buildVersion: string;\n}\n\n/** 已引导的 wrapper 上下文(运行在 QQ Electron 主进程内)。 */\nexport interface WrapperContext {\n /** wrapper.node 的 NAPI 顶层导出。 */\n exports: WrapperNodeApi;\n /** engine 单例(get() 已调用)。 */\n engine: NodeIQQNTWrapperEngine;\n /** 版本信息(供登录握手等使用)。 */\n versionInfo: QQVersionContext;\n /** 会话(QQ 拦截捕获 / createSession 后填充)。 */\n session: NodeIQQNTWrapperSession | null;\n /** 启动会话包装(NapCat 同款:StartupSessionWrapper.create(),start() 替代 startNT)。 */\n startupSession: NodeIQQNTStartupSessionWrapper | null;\n /** QQ 的 loginService 实例(拦截 new 捕获,可为 null)。 */\n loginService: unknown;\n}\n\n/**\n * 从 loader 截获的 NAPI exports 创建 wrapper 上下文。\n * 在 QQ 主进程内调用(boot.cjs → kernel 入口)。\n */\nexport function createWrapper(\n exports: WrapperNodeApi,\n versionInfo: QQVersionContext,\n): WrapperContext {\n const engineMaybe = exports.NodeIQQNTWrapperEngine as unknown as {\n get?: () => NodeIQQNTWrapperEngine;\n } | null;\n if (engineMaybe === null || typeof engineMaybe.get !== \"function\") {\n throw kernelError(\n \"wrapper.node exports 无效(缺少 NodeIQQNTWrapperEngine)\",\n \"INVALID_PARAM\",\n );\n }\n const engine = engineMaybe.get();\n if (!engine || typeof engine.initWithDeskTopConfig !== \"function\") {\n throw kernelError(\"NodeIQQNTWrapperEngine.get() 返回对象无效\", \"INVALID_PARAM\");\n }\n return {\n exports,\n engine,\n versionInfo,\n session: null,\n startupSession: null,\n loginService: null,\n };\n}\n\n/** engine 初始化(wrapper 契约:先 engine 后 session)。config 为普通 JS 对象。 */\nexport function initEngine(ctx: WrapperContext, config: EnginInitDesktopConfig): void {\n ctx.engine.initWithDeskTopConfig(config, new GlobalAdapter());\n}\n\n/**\n * 创建会话(2026-08-06 P2-0 实测修正——NapCat 方式自研等价):\n * 1. StartupSessionWrapper.create()(启动会话包装)\n * 2. getNTWrapperSession(\"nt_1\")(QQ 主 session,带完整 cpp_impl)\n * 3. S.create() 回退\n *\n * 背景:`new NodeIQQNTWrapperSession()` 构造对象缺 cpp_impl,session.init 断言\n * \"implementation of IQQNTWrapperSession is not valid\"(P2-0 实测确认)。\n * NapCat 用 StartupSessionWrapper.create() + getNTWrapperSession('nt_1') 解决——\n * 返回带完整实现的实例。worker(utilityProcess)继承 QQ env 后同样有效。\n */\nexport function createSession(ctx: WrapperContext): NodeIQQNTWrapperSession {\n const S = ctx.exports.NodeIQQNTWrapperSession;\n const session =\n createStartupSession(ctx) ?? getNTWrapperSession(S) ?? createNewSession(S) ?? new S();\n ctx.session = session;\n return session;\n}\n\n/** 步骤 1:StartupSessionWrapper.create()(建立启动 session,失败忽略)。 */\nfunction createStartupSession(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n const Ssw = ctx.exports.NodeIQQNTStartupSessionWrapper as unknown as\n | {\n create?: () => NodeIQQNTStartupSessionWrapper;\n }\n | undefined;\n if (Ssw === undefined || typeof Ssw.create !== \"function\") {\n return null;\n }\n try {\n ctx.startupSession = Ssw.create();\n } catch {\n // 可选,失败继续\n }\n return null;\n}\n\n/** 步骤 2:getNTWrapperSession(\"nt_1\")(QQ 主 session,带 cpp_impl)。 */\nfunction getNTWrapperSession(S: NodeIQQNTWrapperSessionCtor): NodeIQQNTWrapperSession | null {\n if (typeof S.getNTWrapperSession !== \"function\") {\n return null;\n }\n try {\n return S.getNTWrapperSession(\"nt_1\");\n } catch {\n return null; // 回退\n }\n}\n\n/** 步骤 3:S.create() 回退。 */\nfunction createNewSession(S: NodeIQQNTWrapperSessionCtor): NodeIQQNTWrapperSession | null {\n if (typeof S.create !== \"function\") {\n return null;\n }\n try {\n return S.create();\n } catch {\n return null;\n }\n}\n\n/** session 初始化(4 参全为 JS 对象,NAPI 自动转换)。 */\nexport function initSession(\n ctx: WrapperContext,\n config: WrapperSessionInitConfig,\n listener: NodeIKernelSessionListener,\n): void {\n const session = ctx.session ?? createSession(ctx);\n session.init(config, new DependsAdapter(), new DispatcherAdapter(), listener);\n}\n\n/**\n * 启动会话(有 startupSession 用 start(),否则 startNT(0))。\n *\n * ⚠️ 2026-08-07 V9 修正:单纯 start() 只在「已 init」的 session 上有效。\n * 自建宿主必须走 initAndStartSession(先 session.init 再 startupSession.start())——\n * 先 start 后 init 业务 service 不挂载(HANDOVER-V9 实测)。本函数保留给\n * 已 init 的 session 补启动用;新路径请用 initAndStartSession。\n */\nexport function startSession(ctx: WrapperContext): void {\n const { session, startupSession } = ctx;\n if (session === null) {\n throw kernelError(\"session 未创建,无法启动\", \"INVALID_STATE\");\n }\n if (startupSession !== null && typeof startupSession.start === \"function\") {\n startupSession.start();\n return;\n }\n session.startNT(0);\n}\n\n/**\n * boot 装配入口:createWrapper → initEngine → session。\n *\n * **session 来源(2026-08-05 修正)**:\n * 1. qqSession(boot.cjs 拦截 `new` 窃取的 QQ 已 init session)——尚未落地\n * 2. createSession(`new NodeIQQNTWrapperSession()`)——实测可用,默认路径\n * (getMainSession 实测为空壳:service 全 null + 缺 startNT,仅 probe 探测参考)\n *\n * 由 kernel NapukettoCore(core.ts)装配调用;loader 自建宿主 bootstrap 也直接调用作回退。\n * 返回 WrapperContext;失败抛 KernelError。\n */\nexport function startNapuketto(options: StartNapukettoOptions): WrapperContext {\n const { env, engineConfig, sessionConfig, wrapperExports, qqSession, qqLoginService } = options;\n const versionInfo: QQVersionContext = {\n fullVersion: env?.qqVersion ?? \"\",\n buildVersion: env?.qqVersion ?? \"\",\n };\n const ctx = createWrapper(wrapperExports, versionInfo);\n const hasCapturedSession = captureSession(ctx, qqSession);\n // session 创建时机:**自建宿主(标准 node)必须在 engine init 之前创建**——\n // p0-kernel-flow 决定性顺序(HANDOVER-V6):SSW.create + getNTWrapperSession(\"nt_1\")\n // 在 engine.initWithDeskTopConfig 前;engine 先建 session 后建 → 登录后\n // session.init 的 onOpentelemetryInit 不触发(2026-08-07 自建宿主实测)。\n // 路线 B(utility)与 QQ 主进程(browser)保持 engine 先建原顺序(已验证)。\n const isSelfHost = electronProcessType() === undefined;\n prepareSession(ctx, hasCapturedSession, isSelfHost);\n initEngine(ctx, resolveEngineConfig(ctx, env, engineConfig));\n resolveLoginService(ctx, qqLoginService);\n // session:路线 B(utility)与 QQ 主进程(browser)在 engine 后创建;\n // 自建宿主已在 engine 前创建(isSelfHost 分支),此处跳过避免重复。\n finishSession(ctx, hasCapturedSession, isSelfHost, sessionConfig);\n return ctx;\n}\n\n/** engine 前 session 准备(自建宿主必须先建,顺序决定性)。 */\nfunction prepareSession(ctx: WrapperContext, hasCaptured: boolean, isSelfHost: boolean): void {\n if (!hasCaptured && isSelfHost) {\n createSession(ctx);\n }\n}\n\n/** engine 后收尾:非自建宿主补建 session;配置了 sessionConfig 则 init+start。 */\nfunction finishSession(\n ctx: WrapperContext,\n hasCaptured: boolean,\n isSelfHost: boolean,\n sessionConfig: WrapperSessionInitConfig | undefined,\n): void {\n if (!hasCaptured && !isSelfHost) {\n createSession(ctx);\n }\n if (sessionConfig !== undefined) {\n initSession(ctx, sessionConfig, createSessionListener());\n startSession(ctx);\n }\n}\n\n/** engine 配置:显式覆盖优先,否则默认(含 QQ 数据根 global 路径解析)。 */\nfunction resolveEngineConfig(\n ctx: WrapperContext,\n env: BootEnv | undefined,\n engineConfig: EnginInitDesktopConfig | undefined,\n): EnginInitDesktopConfig {\n if (engineConfig !== undefined) {\n return engineConfig;\n }\n return defaultEngineConfig(env ?? {}, resolveBootQqGlobalPath(ctx));\n}\n\n/** 捕获 QQ 已 init 的 session(boot.cjs 拦截 `new` 窃取),返回是否捕获成功。 */\nfunction captureSession(\n ctx: WrapperContext,\n qqSession: NodeIQQNTWrapperSession | null | undefined,\n): boolean {\n if (qqSession === undefined || qqSession === null) {\n return false;\n }\n ctx.session = qqSession;\n return true;\n}\n\n/**\n * 解析 QQ 真实数据目录的 global 路径(desktopGlobalPath 三要素之三)。\n * worker(utilityProcess)模式 + 自建宿主(标准 node)下必须指向 QQ 真实\n * 数据目录(数据根/nt_qq/global,HANDOVER-V6),否则登录数据读写错位\n * (P2-1 实测 2026-08-06;getLoginList 空)。QQ 主进程(V1)QQ 自己已配置\n * engine,无需解析。\n */\nfunction resolveBootQqGlobalPath(ctx: WrapperContext): string | undefined {\n if (electronProcessType() === \"browser\") {\n return undefined;\n }\n const root = resolveQqUserDataRoot(ctx.exports);\n if (root === null) {\n return undefined;\n }\n return resolveQqGlobalPath(root);\n}\n\n/**\n * 解析 loginService:优先捕获实例,其次单例 get()(自建宿主/worker 下 QQ 环境\n * 创建的实例才完整——`new` 自建实例读不到登录列表(getLoginList 空),\n * HANDOVER-V6 p0-login3 实证),最后 new 回退。\n */\nfunction resolveLoginService(ctx: WrapperContext, qqLoginService: unknown): void {\n if (qqLoginService !== undefined && qqLoginService !== null) {\n ctx.loginService = qqLoginService as WrapperContext[\"loginService\"];\n return;\n }\n ctx.loginService = getOrCreateLoginService(ctx.exports);\n}\n\n/** 单例 get() 优先,缺失回退 new(都失败返回 null)。 */\nfunction getOrCreateLoginService(exports: WrapperNodeApi): unknown {\n try {\n const loginSvcCtor = exports.NodeIKernelLoginService as unknown as {\n get?: () => unknown;\n } | null;\n if (loginSvcCtor !== null && typeof loginSvcCtor.get === \"function\") {\n return loginSvcCtor.get();\n }\n return new exports.NodeIKernelLoginService();\n } catch {\n return null;\n }\n}\n\n/** startNapuketto 的引导环境(由 loader launcher 注入环境变量,boot.cjs 透传)。 */\nexport interface BootEnv {\n /** QQ 版本(如 \"9.9.31-49919\")。 */\n qqVersion?: string;\n /** 用户数据目录(cfgDir 的父级,用于 desktopPathConfig)。 */\n dataDir?: string;\n /** 账号 uin(可选,提供则一步 init+start)。 */\n selfUin?: string;\n /** 账号 uid(可选)。 */\n selfUid?: string;\n}\n\n/** startNapuketto 选项。 */\nexport interface StartNapukettoOptions {\n /** 截获的 wrapper.node NAPI exports(boot.cjs 传入)。 */\n wrapperExports: WrapperNodeApi;\n /** 引导环境(版本/数据目录/账号)。 */\n env?: BootEnv;\n /** 覆盖 engine 配置(联调用)。 */\n engineConfig?: EnginInitDesktopConfig;\n /** 覆盖 session 配置(联调用,提供则自动 init+start)。 */\n sessionConfig?: WrapperSessionInitConfig;\n /** QQ 已 init 的 session(boot.cjs 拦截 new 捕获)。 */\n qqSession?: NodeIQQNTWrapperSession | null;\n /** QQ 的 loginService 实例(boot.cjs 拦截 new 捕获)。 */\n qqLoginService?: unknown;\n}\n","/**\n * NapukettoCore:装配 + 启动(§9 第 6 项,2026-08-05 实现)\n *\n * 装配:paths(建目录)→ logger → CoreContext。\n * 启动:attachWrapper(wrapperExports → startNapuketto)→ login(快速登录 → session.init)。\n *\n * 典型用法(boot.cjs 截获 exports 后):\n * const core = await NapukettoCore.create({ dataRoot, account });\n * core.attachWrapper(wrapperExports, { qqVersion, dataDir });\n * // appid 由 resolveAppidQua 从 major.node 动态解析(版本绑定,不写死)\n * await core.login({ appid });\n *\n * 与手动拼 startNapuketto/quickLogin/initAndStartSession 等价,但把装配状态\n * (ctx)集中管理,协议层直接消费 core.ctx。\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { type CoreContext, createCoreContext } from \"./context.js\";\nimport {\n createLogger,\n kernelError,\n type LogLevel,\n type PathOptions,\n PathWrapper,\n} from \"./infra/index.js\";\nimport {\n type LoginResult,\n type LoginState,\n type QrCodeData,\n QrLoginSession,\n type QuickLoginOptions,\n quickLogin,\n type SelfInfo,\n} from \"./login/index.js\";\nimport type { NodeIQQNTWrapperSession, WrapperNodeApi } from \"./types/index.js\";\nimport { resolveQqGlobalPath } from \"./wrapper/qq-data-path.js\";\nimport { buildLoginConfig } from \"./wrapper/wrapper-config.js\";\nimport {\n type BootEnv,\n electronProcessType,\n resolveQqUserDataRoot,\n startNapuketto,\n type WrapperContext,\n} from \"./wrapper/wrapper-loader.js\";\n\nexport interface NapukettoCoreOptions {\n /** 数据根 / 账号(透传给 PathWrapper,见 ADR-016)。 */\n paths?: PathOptions;\n /** 日志级别,默认 info。 */\n logLevel?: LogLevel;\n /** 文件日志路径;不传则默认 `logs/napuketto.log`(父目录自动创建)。 */\n logFile?: string;\n /** 是否输出 console,默认 true。 */\n consoleLog?: boolean;\n}\n\n/** 登录进度(IPC 子进程模式经 onLoginProgress 转发;koishi 插件展示二维码/状态用)。 */\nexport interface LoginProgress {\n /** 登录状态(QR 状态机)。 */\n state: LoginState;\n /** 二维码数据(state=waiting_scan 时有)。 */\n qr?: QrCodeData;\n /** 登录成功 selfInfo(state=logged_in 时有)。 */\n selfInfo?: SelfInfo;\n /** 失败原因(state=failed 时有;如「登录超时,请刷新页面重试」)。 */\n message?: string;\n}\n\n/** login 参数。 */\nexport interface CoreLoginOptions {\n /** appid(登录握手,由 resolveAppidQua 从 major.node 动态解析,不写死)。 */\n appid: string;\n /** session.init 超时(毫秒),默认 15s。 */\n initTimeoutMs?: number;\n /** 指定快速登录账号(缺省遍历历史列表)。 */\n quickUin?: string;\n /**\n * 单步快速登录超时(毫秒,T3 软重登挂起兜底),默认 20s。服务端已有同账号\n * 会话时底层 quickLoginWithUin/getLoginList 永久挂起,超时按登录失败抛出\n * (qrFallback 可接)。底层 promise 无法取消:超时后悬挂丢弃。\n */\n quickLoginTimeoutMs?: number;\n /** 快速登录失败时回退 QR 登录(二维码写缓存目录),默认 false。 */\n qrFallback?: boolean;\n /** 强制扫码:跳过快速登录,直接走 QR 登录(koishi 强制扫码 / 切账号)。默认 false。 */\n qrOnly?: boolean;\n /** QR 二维码图片保存路径(默认缓存目录 qrcode.png)。 */\n qrCodePath?: string;\n /** 登录进度回调(QR 阶段:二维码数据 + 状态变化)。子进程 IPC 模式转发用。 */\n onLoginProgress?: (progress: LoginProgress) => void;\n}\n\n/**\n * 核心装配器:持有 CoreContext,负责启动/停止编排。\n * 无全局单例(ADR-015 推论)——每账号每进程实例化一份。\n */\nexport class NapukettoCore {\n /** 装配根(协议层 / apis / cache 的消费入口)。 */\n readonly ctx: CoreContext;\n\n /** QR 登录会话句柄(登录期间非空;登录终态后清空)。refreshQr() 据此可达。 */\n private qrSession: QrLoginSession | null = null;\n\n /** 由 create() 装配,不直接 new。 */\n private constructor(ctx: CoreContext) {\n this.ctx = ctx;\n }\n\n /**\n * 装配:paths(建目录)→ logger → CoreContext。\n * 不加载 wrapper / 不登录——启动阶段由 attachWrapper + login 完成。\n */\n static create(opts: NapukettoCoreOptions = {}): NapukettoCore {\n const paths = new PathWrapper(opts.paths);\n paths.ensure();\n const logger = createLogger({\n level: opts.logLevel ?? \"info\",\n console: opts.consoleLog ?? true,\n file: opts.logFile ?? paths.file(\"logs\", \"napuketto.log\"),\n // base.name 用 pino 保留字段(logger name):pino-pretty 渲染为 (kernel/pid)\n // 元数据头,不占属性行;cli/loader 同款,三来源可区分\n base: { name: \"kernel\" },\n });\n return new NapukettoCore(createCoreContext({ logger, paths }));\n }\n\n /**\n * 装配 wrapper:wrapperExports → startNapuketto(engine.init + session 创建),\n * 结果挂到 ctx.wrapper。在 QQ 主进程内调用(boot.cjs 截获 exports 后)。\n * captured:boot.cjs 拦截 `new` 捕获的 QQ 实例(session/loginService,优先使用)。\n */\n attachWrapper(\n wrapperExports: WrapperNodeApi,\n env?: BootEnv,\n captured?: { qqSession?: NodeIQQNTWrapperSession | null; qqLoginService?: unknown },\n ): WrapperContext {\n let wrapper: WrapperContext;\n if (env === undefined) {\n wrapper = startNapuketto({ wrapperExports, ...captured });\n } else {\n wrapper = startNapuketto({ wrapperExports, env, ...captured });\n }\n this.ctx.wrapper = wrapper;\n this.ctx.logger.info({ version: env?.qqVersion }, \"wrapper 装配完成(engine + session)\");\n return wrapper;\n }\n\n /**\n * 登录 + session 初始化(wrapper 契约,2026-08-05 实测确认):\n * loginService.initConfig → 登录(快速/QR)→ **登录成功后** create session + init\n * → 等 onOpentelemetryInit(is_init) 完成信号。成功后填 ctx.login。\n *\n * 关键(实测):登录前 create()/init 均断言失败(session 未 init / 干扰 QQ);\n * 登录后 QQ 自己 init 并触发 listener.onOpentelemetryInit——我们只需在登录后\n * create + init 并监听该信号。\n */\n async login(opts: CoreLoginOptions): Promise<LoginResult> {\n const { wrapper } = this.ctx;\n if (wrapper === null) {\n throw kernelError(\"wrapper 未装配,无法登录\", \"INVALID_STATE\");\n }\n\n // 1. loginService.initConfig(wrapper 流程:addKernelLoginListener 前)\n this.initLoginConfig(wrapper, opts.appid);\n\n // 强制扫码(qrOnly):跳过快速登录,直接 QR 登录(koishi 强制扫码 / 切账号)。\n if (opts.qrOnly === true) {\n const loginResult = await this.loginByQr(opts);\n this.ctx.login = loginResult;\n return loginResult;\n }\n\n // 2. 登录:快速登录(优先)→ QR 回退。登录成功前不碰 session。\n let loginResult: LoginResult;\n try {\n const quickOpts: QuickLoginOptions = {};\n if (opts.quickUin !== undefined) {\n quickOpts.uin = opts.quickUin;\n }\n if (opts.quickLoginTimeoutMs !== undefined) {\n quickOpts.quickLoginTimeoutMs = opts.quickLoginTimeoutMs;\n }\n loginResult = await quickLogin(wrapper, quickOpts);\n this.ctx.logger.info({ uin: loginResult.uin, uid: loginResult.uid }, \"快速登录成功\");\n } catch (err) {\n if (opts.qrFallback !== true) {\n throw err;\n }\n loginResult = await this.loginByQr(opts);\n }\n\n // 3. 登录成功即返回。QQ 登录后重建 session(Proxy 捕获新实例),由 boot.cjs\n // 登录成功后 setSession 替换,再自行等待 session 就绪(waitSessionReady)。\n // 不在 login 内等待——替换发生在 login 返回之后,这里等不到。\n this.ctx.login = loginResult;\n return loginResult;\n }\n\n /**\n * loginService.initConfig(worker/自建宿主需指定 QQ 真实数据路径)。\n * worker(utilityProcess)模式 + 自建宿主(标准 node)下 loginService 是\n * new 的,commonPath 必须指向 QQ 真实数据目录(数据根/nt_qq/global,\n * HANDOVER-V6 三要素之三)才能读到历史账号——cli 的 `.napuketto\\default`\n * 读不到(getLoginList 空,P2-1 实测 2026-08-06)。\n * QQ 主进程(V1)模式 QQ 自己 initConfig 过正确路径,无需解析。\n */\n private initLoginConfig(wrapper: WrapperContext, appid: string): void {\n const loginService = wrapper.loginService as {\n initConfig?: (config: unknown) => void;\n } | null;\n if (loginService === null || typeof loginService.initConfig !== \"function\") {\n this.ctx.logger.warn(\"loginService 不可用,跳过 initConfig\");\n return;\n }\n const commonPath = resolveCommonPath(this.ctx, wrapper);\n const loginCfg = buildLoginConfig(appid, wrapper.versionInfo.fullVersion, commonPath);\n loginService.initConfig(loginCfg);\n this.ctx.logger.info({ commonPath }, \"loginService.initConfig OK\");\n }\n\n /**\n * 替换当前 session(登录后 Proxy 捕获的 QQ 新实例替换自己 create 的无效实例)。\n * 在 QQ 主进程内由 boot.cjs 调用(登录成功后)。\n */\n setSession(session: NodeIQQNTWrapperSession): void {\n if (this.ctx.wrapper === null) {\n throw kernelError(\"wrapper 未装配,无法设置 session\", \"INVALID_STATE\");\n }\n this.ctx.wrapper.session = session;\n }\n\n /**\n * 手动刷新二维码(QR 登录期间可用)。返回是否成功触发(false = 当前不在扫码态)。\n * koishi 前端「刷新二维码」按钮经 loader IPC `login.refreshQr` 动作直达。\n */\n refreshQr(): boolean {\n if (this.qrSession === null) {\n return false;\n }\n this.qrSession.refresh();\n return true;\n }\n\n /** QR 登录:QrLoginSession 状态机 + 二维码写缓存目录(无 UI,cli 可打印路径)。 */\n private async loginByQr(opts: CoreLoginOptions): Promise<LoginResult> {\n const { wrapper } = this.ctx;\n if (wrapper === null || wrapper.loginService === null) {\n throw kernelError(\"wrapper/loginService 不可用,无法 QR 登录\", \"INVALID_STATE\");\n }\n const session = new QrLoginSession(wrapper.loginService);\n // 暴露句柄:登录期间外部可经 refreshQr() 手动刷新二维码(koishi 前端刷新按钮)。\n this.qrSession = session;\n const qrPath = opts.qrCodePath ?? this.ctx.paths.file(\"cache\", \"qrcode.png\");\n this.subscribeQrProgress(session, opts, qrPath);\n\n try {\n // 启动 QR 登录(注册监听 → connect → getQRCodePicture)\n // ⚠️ 不传 quickUin(2026-08-13):QR 回退路径下快速登录已在 core.login\n // 失败过一次,这里再传会导致 QrLoginSession 二次快速登录——无凭据环境\n // 直接 resolve 带 errMsg,白等一个周期才出二维码。QR 登录应直接出码。\n session.start();\n\n await waitQrLoggedIn(session);\n const self = session.selfInfo;\n if (self === null) {\n throw kernelError(\"QR 登录成功但 selfInfo 为空\", \"INVALID_STATE\");\n }\n return { uin: self.uin, uid: self.uid, nick: self.nick };\n } finally {\n session.stop();\n this.qrSession = null;\n }\n }\n\n /** 订阅 QR 进度:二维码写盘 + 状态机经 onLoginProgress 转发。 */\n private subscribeQrProgress(\n session: QrLoginSession,\n opts: CoreLoginOptions,\n qrPath: string,\n ): void {\n session.onQrCode((qr) => {\n this.ctx.logger.warn(`请扫描二维码登录(保存: ${qrPath} | URL: ${qr.qrcodeUrl})`);\n if (qr.pngBase64 !== \"\") {\n writeQrCodePng(qrPath, qr.pngBase64);\n }\n opts.onLoginProgress?.({ state: \"waiting_scan\", qr });\n });\n session.onStateChange((state) => {\n this.ctx.logger.info({ state }, \"QR 登录状态\");\n const message = state === \"failed\" ? (session.failureReason ?? undefined) : undefined;\n opts.onLoginProgress?.({ state, ...(message !== undefined ? { message } : {}) });\n });\n }\n\n /** 停止:日志收尾(资源清理留给 P2 常驻管理)。 */\n stop(): void {\n this.ctx.logger.info(\"NapukettoCore 停止\");\n }\n}\n\n/** 写二维码 png 到磁盘(base64 解码,供 cli/用户扫码)。 */\nfunction writeQrCodePng(filePath: string, pngBase64: string): void {\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, Buffer.from(pngBase64, \"base64\"));\n } catch {\n // 写失败不阻塞登录(cli 仍可打印 URL)\n }\n}\n\n/** 等待 QR 登录终态:logged_in resolve,failed reject。 */\nfunction waitQrLoggedIn(session: QrLoginSession): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const offState = session.onStateChange((state) => {\n if (state === \"logged_in\") {\n offState();\n resolve();\n } else if (state === \"failed\") {\n offState();\n reject(kernelError(\"QR 登录失败\", \"NOT_LOGIN\"));\n }\n });\n });\n}\n\n/**\n * loginService.initConfig 的 commonPath:\n * worker/自建宿主(非 browser)优先 QQ 真实数据目录(数据根/nt_qq/global),\n * 否则回退账号目录。\n */\nfunction resolveCommonPath(ctx: CoreContext, wrapper: WrapperContext): string {\n if (electronProcessType() !== \"browser\") {\n const root = resolveQqUserDataRoot(wrapper.exports);\n if (root !== null) {\n return resolveQqGlobalPath(root);\n }\n }\n return ctx.paths.accountDir;\n}\n","/**\n * session 解析(2026-08-05,wrapper 机制实测确认后)\n *\n * - getMainSession:startup.create → getSessionIdList → getNTWrapperSession(nt_x)\n * (实测这些返回的 session 是空壳——核心 service 未 init,仅作参考)\n * - 真正可用的 session 是 `new wrapper.NodeIQQNTWrapperSession()` + init(见 lifecycle.ts)\n */\n\nimport type { NodeIQQNTWrapperSession } from \"../types/index.js\";\nimport type { WrapperContext } from \"./wrapper-loader.js\";\n\n/** 从 getSessionIdList 的 Map 提取主 sessionId(nt_ 前缀优先)。 */\nfunction findMainSessionId(ids: Map<unknown, unknown>): string | null {\n return firstStringId(ids, (s) => s.startsWith(\"nt_\")) ?? firstStringId(ids, () => true);\n}\n\n/** 遍历 Map 找第一个满足谓词的字符串 id(值优先,其次键)。 */\nfunction firstStringId(ids: Map<unknown, unknown>, pred: (s: string) => boolean): string | null {\n for (const [k, v] of ids) {\n if (typeof v === \"string\" && pred(v)) {\n return v;\n }\n if (typeof k === \"string\" && pred(k)) {\n return k;\n }\n }\n return null;\n}\n\n/** 通过 getNTWrapperSession 拿主 session(内部辅助)。 */\nfunction resolveMainSession(\n created: { start?: () => void; getSessionIdList?: () => unknown },\n getNTWrapperSession: (id: string) => NodeIQQNTWrapperSession,\n): NodeIQQNTWrapperSession | null {\n created.start?.();\n const ids = typeof created.getSessionIdList === \"function\" ? created.getSessionIdList() : null;\n if (!(ids instanceof Map)) {\n return null;\n }\n const mainId = findMainSessionId(ids);\n if (mainId === null) {\n return null;\n }\n const maybe = getNTWrapperSession(mainId) as NodeIQQNTWrapperSession | null | undefined;\n return maybe !== null && maybe !== undefined && typeof maybe.getMsgService === \"function\"\n ? maybe\n : null;\n}\n\n/**\n * 复用 QQ 已有 session(P1-4:QQ 已登录,直接拿单例避免重复 init)。\n * 优先 `NodeIQQNTWrapperSession.get()`;返回 null 时回退 createSession。\n */\nexport function getExistingSession(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n const got = trySessionGet(ctx);\n if (got !== null) {\n ctx.session = got;\n return got;\n }\n return null;\n}\n\n/** NodeIQQNTWrapperSession.get() 单例(无效/异常返回 null)。 */\nfunction trySessionGet(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n try {\n const S = ctx.exports.NodeIQQNTWrapperSession as unknown as {\n get?: () => NodeIQQNTWrapperSession;\n };\n if (typeof S.get !== \"function\") {\n return null;\n }\n const got = S.get();\n return got && typeof got.getMsgService === \"function\" ? got : null;\n } catch {\n return null; // 复用失败,回退 create\n }\n}\n\n/**\n * 复用 QQ 主 session(P1-4 实测链路):\n * `NodeIQQNTStartupSessionWrapper.create()` → `start()` → `getSessionIdList()`(Map\n * {nt:\"nt_3\", gpro:\"gpro_3\"})→ `NodeIQQNTWrapperSession.getNTWrapperSession(\"nt_3\")`。\n * 注:实测这些 session 是空壳(核心 service null),真正可用的是 new + init(lifecycle.ts)。\n */\nexport function getMainSession(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n try {\n return resolveMainFromStartup(ctx) ?? null;\n } catch {\n return null; // 复用失败,回退 create\n }\n}\n\n/** startup.create → start → getSessionIdList → getNTWrapperSession 链路。 */\nfunction resolveMainFromStartup(ctx: WrapperContext): NodeIQQNTWrapperSession | null {\n const startupRaw = ctx.exports.NodeIQQNTStartupSessionWrapper as unknown as {\n create?: () => { start?: () => void; getSessionIdList?: () => unknown } | null;\n };\n const sessionCtor = ctx.exports.NodeIQQNTWrapperSession as unknown as {\n getNTWrapperSession?: (id: string) => NodeIQQNTWrapperSession;\n };\n if (\n typeof startupRaw.create !== \"function\" ||\n typeof sessionCtor.getNTWrapperSession !== \"function\"\n ) {\n return null;\n }\n const created = startupRaw.create();\n if (!created) {\n return null;\n }\n const getSession = (id: string): NodeIQQNTWrapperSession =>\n (sessionCtor.getNTWrapperSession as (name: string) => NodeIQQNTWrapperSession)(id);\n const session = resolveMainSession(created, getSession);\n if (session !== null) {\n ctx.session = session;\n }\n return session;\n}\n","/**\n * 运行时反射探测的序列化工具(从 probe.ts 拆分,2026-08-08 FTA 优化)\n *\n * - serialize:深度受限序列化(BigInt 转字符串,Map/Set/Promise 展开,循环引用防护)\n * - tryShape:解析 service getter 返回形状\n * - shapeKeyGetters:探测 service 关键数据 getter 返回形状\n */\n\n/** 序列化深度上限(循环引用/深层对象防护)。 */\nconst MAX_SERIALIZE_DEPTH = 4;\n/** 数组序列化条数上限。 */\nconst MAX_ARRAY_ITEMS = 20;\n/** 对象键序列化条数上限。 */\nconst MAX_OBJECT_KEYS = 50;\n\n/** 序列化一个值(BigInt 转字符串,Map/Set/Promise 展开,循环引用防护)。 */\nexport function serialize(value: unknown, depth = 0): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value === \"bigint\") return value.toString();\n if (typeof value === \"function\") return `[function ${value.name ?? \"anon\"}]`;\n if (typeof value !== \"object\") return value;\n if (depth > MAX_SERIALIZE_DEPTH) return \"[depth-limit]\";\n try {\n return serializeContainer(value, depth);\n } catch {\n return \"[unserializable]\";\n }\n}\n\n/** 序列化容器对象(数组 / Map / Set / Promise / 普通对象)。 */\nfunction serializeContainer(value: object, depth: number): unknown {\n if (Array.isArray(value)) {\n return serializeArray(value, depth);\n }\n if (value instanceof Map) {\n return serializeMap(value, depth);\n }\n if (value instanceof Set) {\n return serializeSet(value, depth);\n }\n if (typeof (value as Promise<unknown>).then === \"function\") {\n return \"[Promise]\";\n }\n return serializeObject(value, depth);\n}\n\n/** 数组序列化(条数截断)。 */\nfunction serializeArray(value: unknown[], depth: number): unknown[] {\n return value.slice(0, MAX_ARRAY_ITEMS).map((v) => serialize(v, depth + 1));\n}\n\n/** Map 序列化(键转字符串,条目截断)。 */\nfunction serializeMap(value: Map<unknown, unknown>, depth: number): unknown {\n const out: Record<string, unknown> = {};\n let i = 0;\n for (const [k, v] of value) {\n if (i >= MAX_OBJECT_KEYS) break;\n out[String(k)] = serialize(v, depth + 1);\n i += 1;\n }\n return { kind: \"Map\", size: value.size, entries: out };\n}\n\n/** Set 序列化(值截断)。 */\nfunction serializeSet(value: Set<unknown>, depth: number): unknown {\n return {\n kind: \"Set\",\n size: value.size,\n values: [...value].slice(0, MAX_ARRAY_ITEMS).map((v) => serialize(v, depth + 1)),\n };\n}\n\n/** 普通对象序列化(键截断)。 */\nfunction serializeObject(value: object, depth: number): unknown {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value).slice(0, MAX_OBJECT_KEYS)) {\n out[key] = serialize((value as Record<string, unknown>)[key], depth + 1);\n }\n return out;\n}\n\n/** 尝试解析一个 service getter 的返回值形状(深度受限)。 */\nexport function tryShape(session: unknown, getter: string): unknown {\n try {\n const fn = (session as Record<string, unknown>)[getter];\n if (typeof fn !== \"function\") return null;\n const value = fn.call(session);\n if (value === null || value === undefined) return null;\n return serialize(value);\n } catch {\n return \"[error]\";\n }\n}\n\n/** 数据 getter 方法名模式(探测返回形状用)。 */\nconst DATA_GETTER_RE = /^(get|query|fetch|load)/i;\n\n/** 探测 service 的关键数据 getter 返回形状(取前几个无参调用)。 */\nexport function shapeKeyGetters(\n session: unknown,\n serviceMethods: string[],\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const candidates = serviceMethods.filter(\n (m) => DATA_GETTER_RE.test(m) && !m.endsWith(\"Listener\"),\n );\n for (const m of candidates.slice(0, 8)) {\n out[m] = tryShape(session, m);\n }\n return out;\n}\n","/**\n * 运行时反射探测基础工具(从 probe.ts 拆分,2026-08-08 FTA 优化)\n *\n * - listMethods:反射枚举对象原型链上的方法名(去重、去构造器)\n * - tryCall:安全调用方法拿返回值(失败记录错误,不中断探测)\n */\n\n/** 反射枚举一个对象原型链上的方法名(去重、去构造器)。 */\nexport function listMethods(obj: unknown): string[] {\n if (obj === null || obj === undefined) {\n return [];\n }\n const names = new Set<string>();\n collectPrototypeNames(obj, names);\n collectOwnNames(obj, names);\n return [...names].sort();\n}\n\n/** 收集原型链上的属性名(到 Object/Function.prototype 为止,去构造器)。 */\nfunction collectPrototypeNames(obj: unknown, names: Set<string>): void {\n let proto = Object.getPrototypeOf(obj);\n while (isProbeProto(proto)) {\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (name !== \"constructor\") {\n names.add(name);\n }\n }\n proto = Object.getPrototypeOf(proto);\n }\n}\n\n/** 是否仍需遍历的原型(非 Object/Function.prototype 且非空)。 */\nfunction isProbeProto(proto: object | null): proto is object {\n return proto !== null && proto !== Object.prototype && proto !== Function.prototype;\n}\n\n/** 收集自有属性名(去构造器)。 */\nfunction collectOwnNames(obj: unknown, names: Set<string>): void {\n for (const name of Object.getOwnPropertyNames(obj)) {\n if (name !== \"constructor\") {\n names.add(name);\n }\n }\n}\n\n/** 安全调用:尝试调用方法拿返回值,失败记录错误(不中断探测)。 */\nexport function tryCall(\n obj: unknown,\n name: string,\n): { ok: boolean; value?: unknown; error?: string } {\n try {\n const fn = (obj as Record<string, unknown>)[name];\n if (typeof fn !== \"function\") {\n return { ok: false, error: `not a function (typeof=${typeof fn})` };\n }\n const value = fn.call(obj);\n return { ok: true, value };\n } catch (e) {\n let message = String(e);\n if (e instanceof Error) {\n message = e.message;\n }\n return { ok: false, error: message };\n }\n}\n","/**\n * wrapper exports 探测(从 probe.ts 拆分,2026-08-08 FTA 优化)\n *\n * - probeExportCtors:关键 export 构造器方法面\n * - probeEngineCalls:engine 关键调用\n * - probeLoginService:LoginService 实例(QQ 已登录凭据入口)\n */\n\nimport type { WrapperContext } from \"../wrapper-loader.js\";\nimport { serialize, tryShape } from \"./probe-serialize.js\";\nimport { listMethods, tryCall } from \"./probe-utils.js\";\n\n/** 探测关键 export 构造器方法(get/create/getNTWrapperSession 等)。 */\nexport function probeExportCtors(ctx: WrapperContext): Record<string, unknown> {\n const keys = [\n \"NodeIQQNTWrapperSession\",\n \"NodeIQQNTStartupSessionWrapper\",\n \"NodeQQNTWrapperUtil\",\n \"NodeIKernelLoginService\",\n \"NodeIQQNTWrapperEngine\",\n ] as const;\n const out: Record<string, unknown> = {};\n for (const key of keys) {\n const ctor = (ctx.exports as unknown as Record<string, unknown>)[key];\n if (ctor !== null && ctor !== undefined) {\n out[key] = { methods: listMethods(ctor), ownKeys: Object.getOwnPropertyNames(ctor) };\n }\n }\n return out;\n}\n\n/** 探测 engine 关键调用(getDeviceInfo / getECDHService / getThirdPartySigService)。 */\nexport function probeEngineCalls(ctx: WrapperContext): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const m of ENGINE_CALL_PROBES) {\n out[m] = probeEngineCall(ctx, m);\n }\n return out;\n}\n\n/** 探测单个 engine 方法调用(成功记录方法面/形状,失败记录错误)。 */\nfunction probeEngineCall(ctx: WrapperContext, m: string): unknown {\n const call = tryCall(ctx.engine, m);\n return call.ok\n ? { value: serialize(call.value), methods: call.value ? listMethods(call.value) : [] }\n : { error: call.error ?? \"null/undefined\" };\n}\n\n/** engine 关键调用候选。 */\nconst ENGINE_CALL_PROBES = [\n \"getDeviceInfo\",\n \"getECDHService\",\n \"getThirdPartySigService\",\n \"readyToShow\",\n] as const;\n\n/** 探测 LoginService 实例(QQ 已登录凭据入口)。 */\nexport function probeLoginService(ctx: WrapperContext): Record<string, unknown> | null {\n const ctor = ctx.exports.NodeIKernelLoginService as unknown as {\n get?: () => unknown;\n } | null;\n if (ctor === null || typeof ctor.get !== \"function\") {\n return null;\n }\n const inst = tryCall(ctor, \"get\");\n if (!(inst.ok && inst.value)) {\n return { get: inst.error ?? \"null/undefined\" };\n }\n return {\n methods: listMethods(inst.value),\n ownKeys: Object.getOwnPropertyNames(inst.value).slice(0, 30),\n ...probeLoginGetters(inst.value),\n };\n}\n\n/** 尝试关键登录信息 getter(无参调用,探测形状)。 */\nfunction probeLoginGetters(inst: unknown): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const m of LOGIN_GETTER_PROBES) {\n if (typeof (inst as Record<string, unknown>)[m] === \"function\") {\n out[m] = tryShape(inst, m);\n }\n }\n return out;\n}\n\n/** 登录信息 getter 候选(无参调用,探测形状)。 */\nconst LOGIN_GETTER_PROBES = [\n \"getAccountInfo\",\n \"getLoginInfo\",\n \"getA2\",\n \"getTicket\",\n \"getUin\",\n \"getUid\",\n \"getSelfUin\",\n] as const;\n","/**\n * 运行时反射探测(ADR-006 落地,P1-4)\n *\n * 注入链路打通后(2026-08-05),wrapper exports 是 QQ 已注册的单例。\n * 本模块在 **QQ 主进程内**反射枚举 session / service 的真实方法名与返回形状,\n * 产出 JSON 到 NAPUTO_CFG_DIR/napuketto-probe.json,作为 types/services/ 的权威来源。\n *\n * 用法:boot.cjs 在 NAPUTO_PROBE=1 时于 startNapuketto 后调用 probeRuntime(ctx)。\n * 产物是研究工具数据,类型层据此补全(自研描述,非移植)。\n *\n * 工具拆分(2026-08-08 FTA 优化):方法枚举/安全调用 → probe-utils.ts;\n * 序列化/形状探测 → probe-serialize.ts;exports/service 探测 → probe-services.ts;\n * 本文件只留 session 探测编排(probeStartup/probeSession/probeRuntime)。\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\nimport { getMainSession } from \"../session-resolver.js\";\nimport type { WrapperContext } from \"../wrapper-loader.js\";\nimport { serialize, shapeKeyGetters } from \"./probe-serialize.js\";\nimport { probeEngineCalls, probeExportCtors, probeLoginService } from \"./probe-services.js\";\nimport { listMethods, tryCall } from \"./probe-utils.js\";\n\n/** 枚举候选 sessionId(nt_0..nt_9 / gpro_0..gpro_9)找 QQ 已 init 的 session。 */\nfunction enumerateSessionIds(ctx: WrapperContext): Record<string, unknown> {\n const S = ctx.exports.NodeIQQNTWrapperSession;\n const enumFn = getNTWrapperSessionFn(ctx);\n if (enumFn === null) {\n return {};\n }\n const out: Record<string, unknown> = {};\n for (let i = 0; i < 10; i += 1) {\n probeSessionId(enumFn, S, `nt_${i}`, out);\n probeSessionId(enumFn, S, `gpro_${i}`, out);\n }\n return out;\n}\n\n/** 探测单个 sessionId(getter 存在性 + getMsgService 调用结果)。 */\nfunction probeSessionId(\n enumFn: (...args: unknown[]) => unknown,\n thisCtx: unknown,\n id: string,\n out: Record<string, unknown>,\n): void {\n try {\n const value = enumFn.call(thisCtx, id);\n if (!value) {\n return;\n }\n out[id] = probeSessionRecord(value);\n } catch {\n // 忽略单次失败\n }\n}\n\n/** 探测单个 session 记录(getter 存在性 + msgService 调用结果 + 方法面)。 */\nfunction probeSessionRecord(value: unknown): Record<string, unknown> {\n const rec = value as Record<string, unknown>;\n const msg = tryMsgService(rec, value);\n return {\n hasMsgServiceGetter: typeof rec[\"getMsgService\"] === \"function\",\n msgServiceOk: msg.service !== null,\n msgServiceMethods: msg.service ? listMethods(msg.service).length : 0,\n msgError: msg.error,\n methods: listMethods(value),\n };\n}\n\n/** 尝试调用 getMsgService(失败记录错误,成功返回实例)。 */\nfunction tryMsgService(\n rec: Record<string, unknown>,\n thisCtx: unknown,\n): { service: unknown; error: string | null } {\n try {\n const svc = (rec[\"getMsgService\"] as () => unknown).call(thisCtx);\n return { service: svc ?? null, error: null };\n } catch (e) {\n return { service: null, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** 取 NodeIQQNTWrapperSession.getNTWrapperSession 函数(无则 null)。 */\nfunction getNTWrapperSessionFn(ctx: WrapperContext): ((...args: unknown[]) => unknown) | null {\n const fn = (ctx.exports.NodeIQQNTWrapperSession as unknown as Record<string, unknown>)[\n \"getNTWrapperSession\"\n ];\n return typeof fn === \"function\" ? (fn as (...args: unknown[]) => unknown) : null;\n}\n\n/** 探测 startup 实例:start() + getSessionIdList(),提取 sessionIds(Map 值或键)。 */\nfunction probeStartupChain(created: unknown): {\n start: Record<string, unknown>;\n createdGetSessionIdList: Record<string, unknown>;\n sessionIds: string[];\n} {\n const started = tryCall(created, \"start\");\n const ids = tryCall(created, \"getSessionIdList\");\n return {\n start: started.ok ? { ok: true } : { error: started.error },\n createdGetSessionIdList: ids.ok ? { value: serialize(ids.value) } : { error: ids.error },\n sessionIds: extractSessionIds(ids),\n };\n}\n\n/** 从 getSessionIdList 结果提取 sessionIds(Map 值或键优先)。 */\nfunction extractSessionIds(ids: { ok: boolean; value?: unknown }): string[] {\n if (!(ids.ok && ids.value instanceof Map)) {\n return [];\n }\n const out: string[] = [];\n for (const [k, v] of ids.value) {\n out.push(typeof v === \"string\" ? v : String(k));\n }\n return out;\n}\n\n/** 用真实 sessionId 取主 session(getNTWrapperSession(nt_x))并记录方法面。 */\nfunction probeSessionById(ctx: WrapperContext, id: string): unknown {\n const fn = getNTWrapperSessionFn(ctx);\n if (fn === null) {\n return null;\n }\n try {\n const value = fn.call(ctx.exports.NodeIQQNTWrapperSession, id);\n return value\n ? { methods: listMethods(value), ownKeys: Object.getOwnPropertyNames(value) }\n : null;\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** 兜底:候选名字 getNTWrapperSession(无 Map 数据时)。 */\nfunction probeByNameFallback(ctx: WrapperContext): Record<string, unknown> {\n const candidates = [\"main\", \"primary\", \"session1\", \"default\", \"\"];\n const getResults: Record<string, unknown> = {};\n for (const name of candidates) {\n getResults[name === \"\" ? \"<empty>\" : name] = probeSessionByName(ctx, name);\n }\n return getResults;\n}\n\n/** 探测单个候选名(getNTWrapperSession(name),记录方法面或错误)。 */\nfunction probeSessionByName(ctx: WrapperContext, name: string): unknown {\n try {\n const fn = getNTWrapperSessionFn(ctx);\n if (fn === null) {\n return null;\n }\n const value = fn.call(ctx.exports.NodeIQQNTWrapperSession, name);\n return value\n ? { methods: listMethods(value), ownKeys: Object.getOwnPropertyNames(value) }\n : null;\n } catch (e) {\n return { error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** 探测 startup session 链路:create() → start() → getSessionIdList → getNTWrapperSession。 */\nfunction probeStartup(ctx: WrapperContext): Record<string, unknown> | null {\n const startup = ctx.exports.NodeIQQNTStartupSessionWrapper as unknown as {\n create?: () => unknown;\n } | null;\n if (startup === null || typeof startup.create !== \"function\") {\n return null;\n }\n const out: Record<string, unknown> = { staticMethods: listMethods(startup) };\n const created = tryCall(startup, \"create\");\n out[\"create\"] = created.ok\n ? {\n methods: created.value ? listMethods(created.value) : [],\n value: serialize(created.value),\n }\n : { error: created.error };\n if (created.ok && created.value) {\n Object.assign(out, probeCreatedChain(ctx, created.value));\n }\n out[\"enumSessionIds\"] = enumerateSessionIds(ctx);\n // 无 Map 数据时兜底:候选名字\n if (out[\"sessionIds\"] === undefined) {\n out[\"getNTWrapperSession\"] = probeByNameFallback(ctx);\n }\n return out;\n}\n\n/** 探测 create 后的实例链:start / getSessionIdList / 主 session。 */\nfunction probeCreatedChain(ctx: WrapperContext, created: unknown): Record<string, unknown> {\n const chain = probeStartupChain(created);\n const out: Record<string, unknown> = {\n start: chain.start,\n createdGetSessionIdList: chain.createdGetSessionIdList,\n sessionIds: chain.sessionIds,\n };\n const mainId = findMainProbeId(chain.sessionIds);\n if (mainId !== undefined) {\n out[\"mainSessionId\"] = mainId;\n out[\"mainSession\"] = probeSessionById(ctx, mainId);\n }\n return out;\n}\n\n/** 选主 sessionId(nt_ 前缀优先,否则第一个)。 */\nfunction findMainProbeId(sessionIds: string[]): string | undefined {\n return sessionIds.find((id) => id.startsWith(\"nt_\")) ?? sessionIds[0];\n}\n\n/** 探测 session 与 service 方法(优先复用 QQ 主 session)。 */\nfunction probeSession(ctx: WrapperContext): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const session = pickProbeSession(ctx, out);\n out[\"session\"] = session\n ? { methods: listMethods(session), ownKeys: Object.getOwnPropertyNames(session) }\n : null;\n if (session !== null && session !== undefined) {\n out[\"services\"] = collectServiceGetters(session);\n }\n return out;\n}\n\n/** 选探测目标 session:优先 QQ 主 session(getMainSession),回退 ctx.session。 */\nfunction pickProbeSession(ctx: WrapperContext, out: Record<string, unknown>): unknown {\n try {\n const main = getMainSession(ctx);\n if (main !== null) {\n out[\"sessionSource\"] = \"mainSession(getNTWrapperSession)\";\n return main;\n }\n } catch {\n // fallthrough\n }\n if (ctx.session !== null) {\n out[\"sessionSource\"] = \"own-create\";\n return ctx.session;\n }\n return null;\n}\n\n/** 收集 session 的 get*Service getter 方法面与返回形状。 */\nfunction collectServiceGetters(session: unknown): Record<string, unknown> {\n const getters = listMethods(session).filter(\n (m) => m.startsWith(\"get\") && m.endsWith(\"Service\"),\n );\n const services: Record<string, unknown> = {};\n for (const getter of getters) {\n services[getter] = probeServiceGetter(session, getter);\n }\n return services;\n}\n\n/** 探测单个 getter:成功记录方法面/形状,失败记录错误。 */\nfunction probeServiceGetter(session: unknown, getter: string): unknown {\n const call = tryCall(session, getter);\n if (!(call.ok && call.value)) {\n return { error: call.error ?? \"null/undefined\", ok: call.ok };\n }\n const methods = listMethods(call.value);\n return {\n methods,\n ownKeys: Object.getOwnPropertyNames(call.value).slice(0, 20),\n shapes: shapeKeyGetters(call.value, methods),\n };\n}\n\n/**\n * 探测 wrapper 运行时:dump session 方法与各 service 方法到 JSON。\n * 返回探测结果对象;同时写文件(NAPUTO_CFG_DIR/<file>,默认 napuketto-probe.json)。\n */\nexport function probeRuntime(\n ctx: WrapperContext,\n file = \"napuketto-probe.json\",\n): Record<string, unknown> {\n const result: Record<string, unknown> = {\n timestamp: new Date().toISOString(),\n exports: listMethods(ctx.exports),\n engineMethods: listMethods(ctx.engine),\n exportCtors: probeExportCtors(ctx),\n engineCalls: probeEngineCalls(ctx),\n loginService: probeLoginService(ctx),\n startup: probeStartup(ctx),\n ...probeSession(ctx),\n };\n\n // 写文件\n try {\n const dir = process.env[\"NAPUTO_CFG_DIR\"] ?? process.cwd();\n mkdirSync(dir, { recursive: true });\n const full = join(dir, file);\n writeFileSync(full, JSON.stringify(result, null, 2), \"utf8\");\n result[\"probeFile\"] = full;\n } catch (e) {\n result[\"probeFileError\"] = e instanceof Error ? e.message : String(e);\n }\n return result;\n}\n","/**\n * QQ 安装目录探测(ADR-018)\n *\n * wrapper.node 路径随 QQ 版本变化:`<安装目录>/versions/<版本>/resources/app/wrapper.node`。\n * 加载前必须知道版本(登录握手参数 appid/qua 与版本强相关)。\n *\n * 已实测确认(2026-08-05,QQ 9.9.31-49919):\n * - 目录结构:versions/<9.9.31-49919>/resources/app/wrapper.node\n * - 版本信息在 versions/<版本>/resources/app/package.json(name=qq-chat / version / buildVersion)\n * - wrapper.node 是 C++ ABI 模块(非 N-API),导出 INTSessionShell 等符号\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { kernelError } from \"../infra/index.js\";\n\n/** 从 versions 目录读取版本 package.json(拿 name/version/buildVersion)。 */\nfunction readVersionPackage(\n installDir: string,\n version: string,\n): { name: string; version: string; buildVersion?: string } {\n const pkgPath = join(installDir, \"versions\", version, \"resources\", \"app\", \"package.json\");\n if (!existsSync(pkgPath)) {\n throw kernelError(`QQ 版本目录缺少 package.json: ${pkgPath}`, \"NOT_FOUND\");\n }\n try {\n const raw = JSON.parse(readFileSync(pkgPath, \"utf8\")) as {\n name?: string;\n version?: string;\n buildVersion?: string;\n };\n return {\n name: raw.name ?? \"\",\n version: raw.version ?? \"\",\n ...(raw.buildVersion !== undefined ? { buildVersion: raw.buildVersion } : {}),\n };\n } catch (cause) {\n throw kernelError(`QQ 版本 package.json 解析失败: ${pkgPath}`, \"INVALID_PARAM\", { cause });\n }\n}\n\n/** 解析 wrapper.node 绝对路径(按 ADR-018 目录约定)。 */\nexport function resolveWrapperPath(installDir: string, version: string): string {\n return join(installDir, \"versions\", version, \"resources\", \"app\", \"wrapper.node\");\n}\n\n/**\n * 探测指定版本信息(校验 wrapper.node 存在)。\n * 版本号不匹配目录时抛 NOT_FOUND(明确失败而非静默)。\n */\nexport function resolveQQVersion(installDir: string, version: string): QQVersionInfo {\n const wrapperPath = resolveWrapperPath(installDir, version);\n if (!existsSync(wrapperPath)) {\n throw kernelError(`未找到 wrapper.node: ${wrapperPath}`, \"NOT_FOUND\");\n }\n const pkg = readVersionPackage(installDir, version);\n return {\n fullVersion: pkg.version || version,\n buildVersion: pkg.buildVersion ?? \"\",\n wrapperPath,\n appid: \"\",\n qua: \"\",\n };\n}\n\n/** 扫描 versions 目录,取可加载的版本列表(按目录名倒序,最新在前)。 */\nexport function listQQVersions(installDir: string): string[] {\n const versionsDir = join(installDir, \"versions\");\n if (!existsSync(versionsDir)) {\n return [];\n }\n const dirs = readdirSync(versionsDir).filter((name) => {\n const full = join(versionsDir, name);\n return (\n statSync(full).isDirectory() &&\n existsSync(join(full, \"resources\", \"app\", \"wrapper.node\"))\n );\n });\n return dirs.sort().reverse();\n}\n\n/** QQ 版本信息。 */\nexport interface QQVersionInfo {\n /** 完整版本号,如 \"9.9.31-49919\"。 */\n fullVersion: string;\n /** 构建号,如 \"49919\"。 */\n buildVersion: string;\n /** wrapper.node 绝对路径。 */\n wrapperPath: string;\n /** 登录握手参数(P1 探测脚本产出,暂为空串)。 */\n appid: string;\n /** 登录握手参数(P1 探测脚本产出,暂为空串)。 */\n qua: string;\n}\n"],"mappings":"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"}