@napuketto/adapter 0.8.4 → 0.9.0

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":"buddy-req-Q7Q4bVuz.mjs","names":[],"sources":["../src/core/uid-uin.ts","../src/core/buddy-req.ts"],"sourcesContent":["/**\n * uid → uin 解析(OB11 / Milky 请求与通知翻译共用,P1 2026-10-30 收敛)\n *\n * uidToUin 批量映射由调用方取得后传入;映射缺省退化为 uid 数值(wrapper 部分回调\n * 直接给 uin),非数字返回 0(调用方按 0 兜底展示)。\n *\n * request 事件翻译上下文与毫秒换算常量随本模块共享(OB11 / Milky request 同构)。\n */\n\n/** 毫秒 → 秒(Unix 时间戳换算)。 */\nexport const MS_TO_SEC = 1000;\n\n/** request 事件翻译上下文(uid→uin 映射,调用方批量转换)。 */\nexport interface RequestTranslateContext {\n selfUin: string;\n uidToUin: Map<string, string>;\n}\n\n/** uid → uin(映射命中取 uin,未命中退化 uid 数值,非数字返回 0)。 */\nexport function resolveUin(uid: string | undefined, uidToUin: Map<string, string>): number {\n if (uid === undefined || uid === \"\") {\n return 0;\n }\n const uin = uidToUin.get(uid);\n if (uin !== undefined) {\n return Number(uin);\n }\n const n = Number(uid);\n return Number.isFinite(n) ? n : 0;\n}\n","/**\n * BuddyReq 防御收窄(OB11 / Milky 共用,P1 2026-10-30 从 onebot11 收敛)\n *\n * onBuddyReqChange 回调参数形状未经真实事件校准(wrapper 字符串证据仅有方法名),\n * 仅接受字段形状可用的对象;未知形状返回 null 由调用方打 raw 日志积累校准数据。\n */\nimport type { BuddyReq } from \"@napuketto/kernel\";\n\n/** BuddyReq 防御性收窄:仅接受字段形状可用的对象。 */\nexport function isBuddyReqLike(value: unknown): value is BuddyReq {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const v = value as Record<string, unknown>;\n return typeof v[\"reqTime\"] === \"string\" && typeof v[\"friendUid\"] === \"string\";\n}\n\n/**\n * onBuddyReqChange 回调参数 → BuddyReq 列表(防御性收窄)。\n * 已知候选形状:BuddyReq[] / { buddyReqs: BuddyReq[] };其余返回 null。\n */\nexport function narrowBuddyReqs(arg: unknown): BuddyReq[] | null {\n if (Array.isArray(arg)) {\n return arg.every(isBuddyReqLike) ? (arg as BuddyReq[]) : null;\n }\n if (typeof arg === \"object\" && arg !== null) {\n const reqs = (arg as Record<string, unknown>)[\"buddyReqs\"];\n if (Array.isArray(reqs) && reqs.every(isBuddyReqLike)) {\n return reqs as BuddyReq[];\n }\n }\n return null;\n}\n"],"mappings":";;;;;;;;;;AAUA,MAAa,YAAY;;AASzB,SAAgB,WAAW,KAAyB,UAAuC;CACvF,IAAI,QAAQ,KAAA,KAAa,QAAQ,IAC7B,OAAO;CAEX,MAAM,MAAM,SAAS,IAAI,GAAG;CAC5B,IAAI,QAAQ,KAAA,GACR,OAAO,OAAO,GAAG;CAErB,MAAM,IAAI,OAAO,GAAG;CACpB,OAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AACpC;;;;ACpBA,SAAgB,eAAe,OAAmC;CAC9D,IAAI,OAAO,UAAU,YAAY,UAAU,MACvC,OAAO;CAEX,MAAM,IAAI;CACV,OAAO,OAAO,EAAE,eAAe,YAAY,OAAO,EAAE,iBAAiB;AACzE;;;;;AAMA,SAAgB,gBAAgB,KAAiC;CAC7D,IAAI,MAAM,QAAQ,GAAG,GACjB,OAAO,IAAI,MAAM,cAAc,IAAK,MAAqB;CAE7D,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;EACzC,MAAM,OAAQ,IAAgC;EAC9C,IAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,cAAc,GAChD,OAAO;CAEf;CACA,OAAO;AACX"}
1
+ {"version":3,"file":"buddy-req-Q7Q4bVuz.mjs","names":[],"sources":["../src/core/uid-uin.ts","../src/core/buddy-req.ts"],"sourcesContent":["/**\n * uid → uin 解析(OB11 / Milky 请求与通知翻译共用,P1 2026-10-30 收敛)\n *\n * uidToUin 批量映射由调用方取得后传入;映射缺省退化为 uid 数值(wrapper 部分回调\n * 直接给 uin),非数字返回 0(调用方按 0 兜底展示)。\n *\n * request 事件翻译上下文与毫秒换算常量随本模块共享(OB11 / Milky request 同构)。\n */\n\n/** 毫秒 → 秒(Unix 时间戳换算)。 */\nexport const MS_TO_SEC = 1000;\n\n/** request 事件翻译上下文(uid→uin 映射,调用方批量转换)。 */\nexport interface RequestTranslateContext {\n selfUin: string;\n uidToUin: Map<string, string>;\n}\n\n/** uid → uin(映射命中取 uin,未命中退化 uid 数值,非数字返回 0)。 */\nexport function resolveUin(uid: string | undefined, uidToUin: ReadonlyMap<string, string>): number {\n if (uid === undefined || uid === \"\") {\n return 0;\n }\n const uin = uidToUin.get(uid);\n if (uin !== undefined) {\n return Number(uin);\n }\n const n = Number(uid);\n return Number.isFinite(n) ? n : 0;\n}\n","/**\n * BuddyReq 防御收窄(OB11 / Milky 共用,P1 2026-10-30 从 onebot11 收敛)\n *\n * onBuddyReqChange 回调参数形状未经真实事件校准(wrapper 字符串证据仅有方法名),\n * 仅接受字段形状可用的对象;未知形状返回 null 由调用方打 raw 日志积累校准数据。\n */\nimport type { BuddyReq } from \"@napuketto/kernel\";\n\n/** BuddyReq 防御性收窄:仅接受字段形状可用的对象。 */\nexport function isBuddyReqLike(value: unknown): value is BuddyReq {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const v = value as Record<string, unknown>;\n return typeof v[\"reqTime\"] === \"string\" && typeof v[\"friendUid\"] === \"string\";\n}\n\n/**\n * onBuddyReqChange 回调参数 → BuddyReq 列表(防御性收窄)。\n * 已知候选形状:BuddyReq[] / { buddyReqs: BuddyReq[] };其余返回 null。\n */\nexport function narrowBuddyReqs(arg: unknown): BuddyReq[] | null {\n if (Array.isArray(arg)) {\n return arg.every(isBuddyReqLike) ? (arg as BuddyReq[]) : null;\n }\n if (typeof arg === \"object\" && arg !== null) {\n const reqs = (arg as Record<string, unknown>)[\"buddyReqs\"];\n if (Array.isArray(reqs) && reqs.every(isBuddyReqLike)) {\n return reqs as BuddyReq[];\n }\n }\n return null;\n}\n"],"mappings":";;;;;;;;;;AAUA,MAAa,YAAY;;AASzB,SAAgB,WAAW,KAAyB,UAA+C;CAC/F,IAAI,QAAQ,KAAA,KAAa,QAAQ,IAC7B,OAAO;CAEX,MAAM,MAAM,SAAS,IAAI,GAAG;CAC5B,IAAI,QAAQ,KAAA,GACR,OAAO,OAAO,GAAG;CAErB,MAAM,IAAI,OAAO,GAAG;CACpB,OAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AACpC;;;;ACpBA,SAAgB,eAAe,OAAmC;CAC9D,IAAI,OAAO,UAAU,YAAY,UAAU,MACvC,OAAO;CAEX,MAAM,IAAI;CACV,OAAO,OAAO,EAAE,eAAe,YAAY,OAAO,EAAE,iBAAiB;AACzE;;;;;AAMA,SAAgB,gBAAgB,KAAiC;CAC7D,IAAI,MAAM,QAAQ,GAAG,GACjB,OAAO,IAAI,MAAM,cAAc,IAAK,MAAqB;CAE7D,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;EACzC,MAAM,OAAQ,IAAgC;EAC9C,IAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,cAAc,GAChD,OAAO;CAEf;CACA,OAAO;AACX"}
@@ -1,2 +1,2 @@
1
- import { a as AdapterRegistry, c as ActionResult, i as ProtocolConfig, l as BaseAction, n as BaseProtocolAdapter, o as ProtocolAdapterLike, r as ProtocolHooks, s as ActionRegistry, t as ensureSilk, u as ErrorCodeMap } from "../index-CDKj-jus.mjs";
2
- export { ActionRegistry, type ActionResult, AdapterRegistry, BaseAction, BaseProtocolAdapter, type ErrorCodeMap, type ProtocolAdapterLike, ProtocolConfig, type ProtocolHooks, ensureSilk };
1
+ import { C as ProtocolAdapterLike, D as ErrorCodeMap, E as BaseAction, S as AdapterRegistry, T as ActionResult, _ as varintToNumber, a as collectStrings, b as ProtocolHooks, c as extractSysMsgEnvelope, d as narrowSysMsgBlobs, f as nodeAt, g as varintOf, h as varintAt, i as SysMsgRecognition, l as firstField, m as toHex, n as SysMsgField, o as decodeProtoTree, p as textOf, r as SysMsgKindRule, s as decodeText, t as SysMsgEnvelope, u as getChildren, v as ensureSilk, w as ActionRegistry, x as ProtocolConfig, y as BaseProtocolAdapter } from "../index-h-tsUu0j.mjs";
2
+ export { ActionRegistry, type ActionResult, AdapterRegistry, BaseAction, BaseProtocolAdapter, type ErrorCodeMap, type ProtocolAdapterLike, ProtocolConfig, type ProtocolHooks, type SysMsgEnvelope, type SysMsgField, type SysMsgKindRule, type SysMsgRecognition, collectStrings, decodeProtoTree, decodeText, ensureSilk, extractSysMsgEnvelope, firstField, getChildren, narrowSysMsgBlobs, nodeAt, textOf, toHex, varintAt, varintOf, varintToNumber };
@@ -1,2 +1,2 @@
1
- import { a as AdapterRegistry, i as BaseAction, n as ProtocolConfig, o as ActionRegistry, r as BaseProtocolAdapter, t as ensureSilk } from "../core-DyXyDUcU.mjs";
2
- export { ActionRegistry, AdapterRegistry, BaseAction, BaseProtocolAdapter, ProtocolConfig, ensureSilk };
1
+ import { _ as BaseProtocolAdapter, a as firstField, b as ActionRegistry, c as nodeAt, d as toHex, f as varintAt, g as ProtocolConfig, h as ensureSilk, i as extractSysMsgEnvelope, m as varintToNumber, n as decodeProtoTree, o as getChildren, p as varintOf, r as decodeText, s as narrowSysMsgBlobs, t as collectStrings, u as textOf, v as BaseAction, y as AdapterRegistry } from "../core-DW5i42XH.mjs";
2
+ export { ActionRegistry, AdapterRegistry, BaseAction, BaseProtocolAdapter, ProtocolConfig, collectStrings, decodeProtoTree, decodeText, ensureSilk, extractSysMsgEnvelope, firstField, getChildren, narrowSysMsgBlobs, nodeAt, textOf, toHex, varintAt, varintOf, varintToNumber };
@@ -0,0 +1,481 @@
1
+ import { ConfigBase } from "@napuketto/kernel";
2
+ import { open } from "node:fs/promises";
3
+ import { encodePcmToSilk } from "@napuketto/media";
4
+ //#region src/core/action-registry.ts
5
+ var ActionRegistry = class {
6
+ actions = /* @__PURE__ */ new Map();
7
+ register(action) {
8
+ this.actions.set(action.name, action);
9
+ }
10
+ get(name) {
11
+ return this.actions.get(name);
12
+ }
13
+ has(name) {
14
+ return this.actions.has(name);
15
+ }
16
+ get names() {
17
+ return [...this.actions.keys()];
18
+ }
19
+ };
20
+ //#endregion
21
+ //#region src/core/adapter-registry.ts
22
+ var AdapterRegistry = class {
23
+ adapters = /* @__PURE__ */ new Map();
24
+ register(adapter) {
25
+ this.adapters.set(adapter.protocol, adapter);
26
+ }
27
+ get(protocol) {
28
+ return this.adapters.get(protocol);
29
+ }
30
+ async startAll() {
31
+ await Promise.all([...this.adapters.values()].map((a) => a.start()));
32
+ }
33
+ async stopAll() {
34
+ await Promise.all([...this.adapters.values()].map((a) => a.stop()));
35
+ }
36
+ };
37
+ //#endregion
38
+ //#region src/core/base-action.ts
39
+ /** 校验失败 retcode:HTTP 入口。 */
40
+ const INVALID_RETCODE_HTTP = 400;
41
+ /** 校验失败 retcode:WS 入口。 */
42
+ const INVALID_RETCODE_WS = 1400;
43
+ /** 动作基类:校验失败 retcode=400(HTTP)/1400(WS)。 */
44
+ var BaseAction = class {
45
+ /** 校验并执行(HTTP 入口)。 */
46
+ handle(payload) {
47
+ return this.run(payload, INVALID_RETCODE_HTTP);
48
+ }
49
+ /** 校验并执行(WS 入口,echo 透传)。 */
50
+ async websocketHandle(payload, echo) {
51
+ const result = await this.run(payload, INVALID_RETCODE_WS);
52
+ if (echo !== void 0) result.echo = echo;
53
+ return result;
54
+ }
55
+ async run(payload, invalidRetcode) {
56
+ const parsed = this.schema.safeParse(payload);
57
+ if (!parsed.success) return this.fail(invalidRetcode, "参数校验失败");
58
+ try {
59
+ return {
60
+ retcode: 0,
61
+ status: "ok",
62
+ data: await this._handle(parsed.data),
63
+ message: ""
64
+ };
65
+ } catch (err) {
66
+ return this.mapError(err);
67
+ }
68
+ }
69
+ mapError(err) {
70
+ if (err instanceof Error && "code" in err) {
71
+ const typed = err;
72
+ return {
73
+ retcode: this.errorCodeMap[typed.code] ?? this.errorCodeMap["UNKNOWN"],
74
+ status: "failed",
75
+ data: null,
76
+ message: err.message
77
+ };
78
+ }
79
+ if (err instanceof Error) return this.fail(this.errorCodeMap["UNKNOWN"], err.message);
80
+ return this.fail(this.errorCodeMap["UNKNOWN"], "未知错误");
81
+ }
82
+ fail(retcode, message) {
83
+ return {
84
+ retcode,
85
+ status: "failed",
86
+ data: null,
87
+ message
88
+ };
89
+ }
90
+ };
91
+ //#endregion
92
+ //#region src/core/base-protocol-adapter.ts
93
+ /**
94
+ * 协议适配器骨架。
95
+ * @typeParam TConfig - 协议配置类型
96
+ */
97
+ var BaseProtocolAdapter = class {
98
+ config;
99
+ hooks;
100
+ broadcaster;
101
+ started = false;
102
+ constructor(opts) {
103
+ this.config = opts.config;
104
+ this.hooks = opts.hooks;
105
+ this.broadcaster = opts.broadcaster;
106
+ }
107
+ /** 启动:加载配置 → 校验 → 协议层初始化。 */
108
+ async start() {
109
+ if (this.started) return;
110
+ const config = await this.config.load();
111
+ await this.hooks.onStart(config);
112
+ this.started = true;
113
+ }
114
+ /** 停止:协议层清理资源。 */
115
+ async stop() {
116
+ if (!this.started) return;
117
+ await this.hooks.onStop();
118
+ this.started = false;
119
+ }
120
+ /** 配置热更新:重新加载 → 校验 → 协议层处理。 */
121
+ async reload() {
122
+ const config = await this.config.load();
123
+ await this.hooks.onReload(config);
124
+ }
125
+ /** 广播协议事件给所有 network 适配器。 */
126
+ broadcastEvent(event) {
127
+ this.broadcaster?.emit(event);
128
+ }
129
+ /** 获取广播器(传输装配等内部使用;未传入返回 undefined)。 */
130
+ getBroadcaster() {
131
+ return this.broadcaster;
132
+ }
133
+ };
134
+ //#endregion
135
+ //#region src/core/config.ts
136
+ /** 协议配置基类:zod schema + 文本读写(JSON/TOML,文件路径由装配层传入)。 */
137
+ var ProtocolConfig = class extends ConfigBase {
138
+ constructor(opts) {
139
+ super({
140
+ path: opts.path,
141
+ schema: opts.schema,
142
+ defaults: opts.defaults,
143
+ seed: opts.seed,
144
+ format: opts.format
145
+ });
146
+ }
147
+ };
148
+ //#endregion
149
+ //#region src/core/media.ts
150
+ /**
151
+ * adapter core 媒体辅助:语音归一化(ADR-011 media 严格解耦的协议层落点)
152
+ *
153
+ * 协议层(onebot11 / satori)发送语音前统一走 ensureSilk:把非 silk 音频
154
+ * 转成 QQ 语音格式(silk v3)。转码依赖 @napuketto/media(encodePcmToSilk),
155
+ * 只发生在 adapter 层;kernel 不 import media(红线),只接收已是 silk 的路径。
156
+ */
157
+ /** 读文件头 8 字节(判断 silk 魔数)。 */
158
+ async function readFileHead(path) {
159
+ const handle = await open(path, "r");
160
+ try {
161
+ const buf = Buffer.alloc(8);
162
+ await handle.read(buf, 0, 8, 0);
163
+ return buf.toString("utf8");
164
+ } finally {
165
+ await handle.close();
166
+ }
167
+ }
168
+ /**
169
+ * 语音转码:非 silk 输入(任意音频格式,经 @napuketto/media 归一化)转 silk。
170
+ * 已是 silk(#!SILK_V3 头)原样返回;转码失败原样返回(由 kernel 发送时兜底)。
171
+ */
172
+ async function ensureSilk(path) {
173
+ try {
174
+ if ((await readFileHead(path)).startsWith("#!SILK")) return path;
175
+ return await encodePcmToSilk(path);
176
+ } catch {
177
+ return path;
178
+ }
179
+ }
180
+ //#endregion
181
+ //#region src/core/sysmsg.ts
182
+ /**
183
+ * sysmsg 解码与识别共享层(2026-11-01 自 onebot11/helper/sysmsg.ts 上移,
184
+ * milky.md §0.8.3/§0.8.6——OB11 与 Milky 共用同一识别机制)
185
+ *
186
+ * Msg/onRecvSysMsg 是群名片/头衔/精华/表情回应等系统事件的 sys msg 总闸,payload =
187
+ * 原始 protobuf 字节(djinni int8 数组透传,实测 `[[10,64,…]]` 形状)。本模块:
188
+ * - decodeProtoTree:手写 protobuf wire-format 解码器(varint/length-delimited,
189
+ * 零依赖,BigInt 保 64 位精度)
190
+ * - narrowSysMsgBlobs:回调参数防御性收窄(未知形状 → null,调用方打 raw 日志)
191
+ * - extractSysMsgEnvelope:按实测样本布局提取信封(逐字段可空)
192
+ * - recognizeSysMsg + SysMsgKindRule<TEvent>:(msgType, subType) → 协议事件。
193
+ * 识别表按协议各自维护(onebot11/helper/sysmsg.ts / milky/helper/sysmsg.ts),
194
+ * 表条目随录制窗口样本扩充;未识别一律走结构化校准日志,不广播。
195
+ *
196
+ * 证据:真实样本(c3 观测窗 2026-09-09 双样本 528/382)+ 批次三轮录制
197
+ * (2026-11-01,732:16 / 732:1 / 732:21 / 528:349/381/382,wire bytes 全量解码,
198
+ * milky.md §0.8.6 识别表)+ wrapper.node OnSysMsg* 处理器分类学(原生分发键 =
199
+ * msg_type/sub_type)。翻译纯函数(ADR-008)。
200
+ */
201
+ /** 递归深度上限(防御:恶意/畸形字节防栈溢出)。 */
202
+ const MAX_DEPTH = 16;
203
+ /** 单层字段数上限。 */
204
+ const MAX_FIELDS = 256;
205
+ /** 单个 varint 最大字节数(64 位 + 1 冗余)。 */
206
+ const MAX_VARINT_BYTES = 10;
207
+ /** 读 varint:成功返回 [值, 下一偏移],越界/超长返回 null。 */
208
+ function readVarint(data, start) {
209
+ let value = 0n;
210
+ let shift = 0n;
211
+ let i = start;
212
+ while (true) {
213
+ if (i >= data.length || i - start >= MAX_VARINT_BYTES) return null;
214
+ const byte = data[i];
215
+ if (byte === void 0) return null;
216
+ i += 1;
217
+ value |= BigInt(byte & 127) << shift;
218
+ if ((byte & 128) === 0) return [value, i];
219
+ shift += 7n;
220
+ }
221
+ }
222
+ /** wire 2:length-delimited 字段(递归 try-parse 子树,失败 → 不透明字节)。 */
223
+ function decodeLengthDelimited(data, afterTag, no, depth) {
224
+ const len = readVarint(data, afterTag);
225
+ if (len === null) return null;
226
+ const payloadStart = len[1];
227
+ const end = payloadStart + Number(len[0]);
228
+ if (end > data.length) return null;
229
+ const slice = data.slice(payloadStart, end);
230
+ return {
231
+ field: {
232
+ no,
233
+ kind: "bytes",
234
+ value: slice,
235
+ children: decodeProtoTree(slice, depth + 1)
236
+ },
237
+ next: end
238
+ };
239
+ }
240
+ /** 解码单个带 tag 字段;畸形(field 0 / group / 截断 / 越界)返回 null。 */
241
+ function decodeTaggedField(data, start, depth) {
242
+ const tag = readVarint(data, start);
243
+ if (tag === null) return null;
244
+ const [tagValue, afterTag] = tag;
245
+ const no = Number(tagValue >> 3n);
246
+ if (no === 0) return null;
247
+ return decodeFieldByWire(data, afterTag, no, Number(tagValue & 7n), depth);
248
+ }
249
+ /** 按 wire type 分派字段解码。 */
250
+ function decodeFieldByWire(data, afterTag, no, wireType, depth) {
251
+ if (wireType === 0) return decodeVarintFieldAt(data, afterTag, no);
252
+ if (wireType === 2) return decodeLengthDelimited(data, afterTag, no, depth);
253
+ return decodeFixedField(data, afterTag, wireType);
254
+ }
255
+ /** wire 0:varint 字段。 */
256
+ function decodeVarintFieldAt(data, afterTag, no) {
257
+ const v = readVarint(data, afterTag);
258
+ if (v === null) return null;
259
+ return {
260
+ field: {
261
+ no,
262
+ kind: "varint",
263
+ value: v[0]
264
+ },
265
+ next: v[1]
266
+ };
267
+ }
268
+ /** wire 1/5(fixed64/32):跳过(QQ NT blob 未观测到,防御性兼容);其余判失败。 */
269
+ function decodeFixedField(data, afterTag, wireType) {
270
+ const width = fixedWireWidth(wireType);
271
+ if (width === null) return null;
272
+ return skipFixedBytes(data, afterTag, width);
273
+ }
274
+ /** fixed 类 wire 的字节宽度(非 fixed → null)。 */
275
+ function fixedWireWidth(wireType) {
276
+ if (wireType === 1) return 8;
277
+ return wireType === 5 ? 4 : null;
278
+ }
279
+ /** 定长跳过(越界判失败)。 */
280
+ function skipFixedBytes(data, start, width) {
281
+ const next = start + width;
282
+ if (next > data.length) return null;
283
+ return {
284
+ field: void 0,
285
+ next
286
+ };
287
+ }
288
+ /**
289
+ * 解码 protobuf wire-format 为递归字段树(不维护字段重复/顺序语义以外的信息)。
290
+ *
291
+ * - wire 0 varint / wire 2 length-delimited:支持;wire 2 递归 try-parse,
292
+ * 子解析失败按不透明字节挂树(ASCII 串偶发误判嵌套无实害——树仅用于观测)。
293
+ * - wire 1/5(fixed64/32):跳过(QQ NT blob 未观测到,防御性兼容)。
294
+ * - wire 3/4(group)、截断、field 0、超限:判失败返回 null。
295
+ */
296
+ function decodeProtoTree(data, depth = 0) {
297
+ if (depth > MAX_DEPTH) return null;
298
+ const fields = [];
299
+ let i = 0;
300
+ while (i < data.length) {
301
+ if (fields.length >= MAX_FIELDS) return null;
302
+ const decoded = decodeTaggedField(data, i, depth);
303
+ if (decoded === null) return null;
304
+ if (decoded.field !== void 0) fields.push(decoded.field);
305
+ i = decoded.next;
306
+ }
307
+ return fields;
308
+ }
309
+ /** 取字段号首个命中的节点(规则实现用,路径导航原语)。 */
310
+ function firstField(tree, no) {
311
+ return tree?.find((f) => f.no === no);
312
+ }
313
+ /** bytes 节点的子树(非 bytes/不透明 → null)。 */
314
+ function getChildren(field) {
315
+ return field?.kind === "bytes" ? field.children : null;
316
+ }
317
+ /** varint → number(超安全整数返回 null,调用方按缺字段处理)。 */
318
+ function varintToNumber(value) {
319
+ return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : null;
320
+ }
321
+ /** UTF-8 严格解码 + 可打印检查(含控制字符判不可解码,返回 null)。 */
322
+ function decodeText(bytes) {
323
+ try {
324
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
325
+ for (const ch of text) {
326
+ const code = ch.codePointAt(0) ?? 0;
327
+ if (code < 32 && code !== 9) return null;
328
+ }
329
+ return text;
330
+ } catch {
331
+ return null;
332
+ }
333
+ }
334
+ /** 递归收集树内全部可打印字符串(校准观测用;先序遍历,空字节段跳过)。 */
335
+ function collectStrings(tree) {
336
+ const out = [];
337
+ for (const f of tree) {
338
+ if (f.kind === "varint") continue;
339
+ const text = decodeText(f.value);
340
+ if (text !== null && text.length > 0) out.push(text);
341
+ if (f.children !== null) out.push(...collectStrings(f.children));
342
+ }
343
+ return out;
344
+ }
345
+ /** 按字段号路径下钻 bytes 子树(每段取首个命中;任一段不成立 → null)。 */
346
+ function nodeAt(tree, path) {
347
+ let node = tree;
348
+ for (const no of path) {
349
+ if (node === null) return null;
350
+ node = getChildren(firstField(node, no));
351
+ }
352
+ return node;
353
+ }
354
+ /** 按路径取 varint 数值(bytes 子树末段的 varint 字段;缺失 → null)。 */
355
+ function varintAt(tree, path) {
356
+ if (path.length === 0) return null;
357
+ const f = firstField(nodeAt(tree, path.slice(0, -1)), path[path.length - 1] ?? -1);
358
+ return f?.kind === "varint" ? varintToNumber(f.value) : null;
359
+ }
360
+ /** 单节点内 varint 字段数值(缺失 → null)。 */
361
+ function varintOf(node, no) {
362
+ const f = firstField(node, no);
363
+ return f?.kind === "varint" ? varintToNumber(f.value) : null;
364
+ }
365
+ /** 单节点内 UTF-8 字符串字段(缺失/不可解码 → null)。 */
366
+ function textOf(node, no) {
367
+ const f = firstField(node, no);
368
+ return f?.kind === "bytes" ? decodeText(f.value) : null;
369
+ }
370
+ /** 单个候选块 → Uint8Array(number[] -128..255 / TypedArray;其余 null)。 */
371
+ function toBlob(item) {
372
+ if (item instanceof Int8Array) return Uint8Array.from(item, (b) => b & 255);
373
+ return blobFromGeneric(item);
374
+ }
375
+ /** Uint8Array / 字节数组形态(Int8Array 已在上层处理)。 */
376
+ function blobFromGeneric(item) {
377
+ if (item instanceof Uint8Array) return new Uint8Array(item);
378
+ if (Array.isArray(item) && item.length > 0) return blobFromArray(item);
379
+ return null;
380
+ }
381
+ /** 字节形态数组 → Uint8Array(含非法元素整体判失败)。 */
382
+ function blobFromArray(item) {
383
+ const out = new Uint8Array(item.length);
384
+ for (let i = 0; i < item.length; i += 1) {
385
+ const byte = signedToUnsigned(item[i]);
386
+ if (byte === null) return null;
387
+ out[i] = byte;
388
+ }
389
+ return out;
390
+ }
391
+ /** 带符号/无符号字节归一(非整数拒绝:浮点 & 0xff 会静默截断成别的字节)。 */
392
+ function signedToUnsigned(b) {
393
+ if (typeof b !== "number" || !Number.isInteger(b) || b < -128 || b > 255) return null;
394
+ return b & 255;
395
+ }
396
+ /**
397
+ * onRecvSysMsg 参数 → sysmsg 字节块列表(防御性收窄)。
398
+ * 接受批数组(实测形状)/ 单块裸数组 / TypedArray;零有效块返回 null
399
+ * (调用方打 raw 日志,与 narrowOfflineFiles 同模式)。
400
+ */
401
+ function narrowSysMsgBlobs(arg) {
402
+ if (Array.isArray(arg)) {
403
+ const blobs = arg.map(toBlob).filter((b) => b !== null);
404
+ if (blobs.length > 0) return blobs;
405
+ const single = toBlob(arg);
406
+ return single === null ? null : [single];
407
+ }
408
+ const single = toBlob(arg);
409
+ return single === null ? null : [single];
410
+ }
411
+ /** 顶层 f1 块的字段(uin/uid 对)。 */
412
+ function extractActorBlock(block) {
413
+ const empty = {
414
+ uin: null,
415
+ uid: null,
416
+ uin2: null,
417
+ uid2: null
418
+ };
419
+ if (block === null) return empty;
420
+ return {
421
+ uin: varintOf(block, 1),
422
+ uid: textOf(block, 2),
423
+ uin2: varintOf(block, 5),
424
+ uid2: textOf(block, 6)
425
+ };
426
+ }
427
+ /** 顶层 f2 块 → 信封数值字段。 */
428
+ function extractEnvelopeBlock(block) {
429
+ const empty = {
430
+ msgType: null,
431
+ subType: null,
432
+ subTypeAlt: null,
433
+ groupCodes: null,
434
+ time: null
435
+ };
436
+ if (block === null) return empty;
437
+ const code4 = varintOf(block, 4);
438
+ const code5 = varintOf(block, 5);
439
+ return {
440
+ msgType: varintOf(block, 1),
441
+ subType: varintOf(block, 2),
442
+ subTypeAlt: varintOf(block, 3),
443
+ groupCodes: code4 !== null && code5 !== null ? [code4, code5] : null,
444
+ time: varintOf(block, 6)
445
+ };
446
+ }
447
+ /** 字段树 → 信封(任何字段缺失/非法 = null,不抛错)。 */
448
+ function extractSysMsgEnvelope(tree) {
449
+ const actor = extractActorBlock(getChildren(firstField(tree, 1)));
450
+ return {
451
+ actorUin: actor.uin,
452
+ actorUid: actor.uid,
453
+ targetUin: actor.uin2,
454
+ targetUid: actor.uid2,
455
+ ...extractEnvelopeBlock(getChildren(firstField(tree, 2))),
456
+ strings: collectStrings(tree)
457
+ };
458
+ }
459
+ /**
460
+ * (msgType, subType) → 识别结果。table / observedUnnamed 由各协议注入
461
+ * (键 = `"${msgType}:${subType}"`,原生分发键)。
462
+ */
463
+ function recognizeSysMsg(msgType, subType, table, observedUnnamed = /* @__PURE__ */ new Set()) {
464
+ if (msgType === null || subType === null) return { kind: "unknown" };
465
+ const rule = table.get(`${msgType}:${subType}`);
466
+ if (rule !== void 0) return {
467
+ kind: "notice",
468
+ rule
469
+ };
470
+ return observedUnnamed.has(`${msgType}:${subType}`) ? { kind: "observed_unnamed" } : { kind: "unknown" };
471
+ }
472
+ /** 字节块 → 小写 hex(校准日志用;超长由调用方截断)。 */
473
+ function toHex(bytes) {
474
+ let out = "";
475
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
476
+ return out;
477
+ }
478
+ //#endregion
479
+ export { BaseProtocolAdapter as _, firstField as a, ActionRegistry as b, nodeAt as c, toHex as d, varintAt as f, ProtocolConfig as g, ensureSilk as h, extractSysMsgEnvelope as i, recognizeSysMsg as l, varintToNumber as m, decodeProtoTree as n, getChildren as o, varintOf as p, decodeText as r, narrowSysMsgBlobs as s, collectStrings as t, textOf as u, BaseAction as v, AdapterRegistry as y };
480
+
481
+ //# sourceMappingURL=core-DW5i42XH.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-DW5i42XH.mjs","names":[],"sources":["../src/core/action-registry.ts","../src/core/adapter-registry.ts","../src/core/base-action.ts","../src/core/base-protocol-adapter.ts","../src/core/config.ts","../src/core/media.ts","../src/core/sysmsg.ts"],"sourcesContent":["/**\n * 动作注册表:按动作名索引(构建后只读)。\n * 泛型化(2026-08-08 克隆合并):OB11 与 Satori 共用同一注册表实现。\n */\nimport type { BaseAction } from \"./base-action.js\";\n\nexport class ActionRegistry<TAction extends { name: string } = BaseAction<unknown, unknown>> {\n private readonly actions = new Map<string, TAction>();\n\n register(action: TAction): void {\n this.actions.set(action.name, action);\n }\n\n get(name: string): TAction | undefined {\n return this.actions.get(name);\n }\n\n has(name: string): boolean {\n return this.actions.has(name);\n }\n\n get names(): string[] {\n return [...this.actions.keys()];\n }\n}\n","/**\n * 适配器注册表:cli 按 enabledProtocols 装配用。\n */\n\n/** 协议适配器基类类型(避免 core 与具体协议循环依赖的轻量接口)。 */\nexport interface ProtocolAdapterLike {\n readonly protocol: string;\n start: () => Promise<void>;\n stop: () => Promise<void>;\n}\n\nexport class AdapterRegistry {\n private readonly adapters = new Map<string, ProtocolAdapterLike>();\n\n register(adapter: ProtocolAdapterLike): void {\n this.adapters.set(adapter.protocol, adapter);\n }\n\n get(protocol: string): ProtocolAdapterLike | undefined {\n return this.adapters.get(protocol);\n }\n\n async startAll(): Promise<void> {\n await Promise.all([...this.adapters.values()].map((a) => a.start()));\n }\n\n async stopAll(): Promise<void> {\n await Promise.all([...this.adapters.values()].map((a) => a.stop()));\n }\n}\n","/**\n * BaseAction:协议动作基类(zod 校验 + 统一错误映射)\n *\n * 各协议动作继承并实现 `_handle`;`handle`(HTTP)/ `websocketHandle`(WS)\n * 统一做 zod 校验 + KernelError → 协议错误码映射。\n */\n\nimport type { KernelErrorCode } from \"@napuketto/kernel\";\nimport type { ZodType } from \"zod\";\n\n/** 校验失败 retcode:HTTP 入口。 */\nconst INVALID_RETCODE_HTTP = 400;\n/** 校验失败 retcode:WS 入口。 */\nconst INVALID_RETCODE_WS = 1400;\n\n/** 动作执行结果(含 retcode,协议错误码由协议层映射表提供)。 */\nexport interface ActionResult<T> {\n retcode: number;\n status: string;\n data: T | null;\n message: string;\n echo?: unknown;\n}\n\n/** 协议错误码映射表:KernelErrorCode → 协议错误码(各协议各自实现,ADR-017)。 */\nexport type ErrorCodeMap = Record<KernelErrorCode, number>;\n\n/** 动作基类:校验失败 retcode=400(HTTP)/1400(WS)。 */\nexport abstract class BaseAction<TPayload, TReturn> {\n abstract readonly name: string;\n abstract readonly schema: ZodType<TPayload>;\n protected abstract readonly errorCodeMap: ErrorCodeMap;\n\n /** 校验并执行(HTTP 入口)。 */\n handle(payload: unknown): Promise<ActionResult<TReturn>> {\n return this.run(payload, INVALID_RETCODE_HTTP);\n }\n\n /** 校验并执行(WS 入口,echo 透传)。 */\n async websocketHandle(payload: unknown, echo?: unknown): Promise<ActionResult<TReturn>> {\n const result = await this.run(payload, INVALID_RETCODE_WS);\n if (echo !== undefined) {\n result.echo = echo;\n }\n return result;\n }\n\n private async run(payload: unknown, invalidRetcode: number): Promise<ActionResult<TReturn>> {\n const parsed = this.schema.safeParse(payload);\n if (!parsed.success) {\n return this.fail(invalidRetcode, \"参数校验失败\");\n }\n try {\n const data = await this._handle(parsed.data);\n return { retcode: 0, status: \"ok\", data, message: \"\" };\n } catch (err) {\n return this.mapError(err);\n }\n }\n\n protected abstract _handle(payload: TPayload): Promise<TReturn>;\n\n private mapError(err: unknown): ActionResult<TReturn> {\n if (err instanceof Error && \"code\" in err) {\n const typed = err as Error & { code: KernelErrorCode };\n //从断言联合类型访问 code,解构会丢失 err 其余属性\n const retcode = this.errorCodeMap[typed.code] ?? this.errorCodeMap[\"UNKNOWN\"];\n return { retcode, status: \"failed\", data: null, message: err.message };\n }\n if (err instanceof Error) {\n return this.fail(this.errorCodeMap[\"UNKNOWN\"], err.message);\n }\n return this.fail(this.errorCodeMap[\"UNKNOWN\"], \"未知错误\");\n }\n\n private fail(retcode: number, message: string): ActionResult<TReturn> {\n return { retcode, status: \"failed\", data: null, message };\n }\n}\n","/**\n * BaseProtocolAdapter:协议适配器骨架(ADR-013,只写一次,各协议薄映射)\n *\n * 生命周期:start(校验配置 → 协议层初始化 → 启动传输)→ stop。\n * 请求分发:协议层把 network 的 onRequest 绑定到 `handleRequest`。\n * 事件广播:协议层翻译 kernel 事件后经 `broadcastEvent` 推给 network。\n */\nimport type { EventBroadcaster } from \"@napuketto/network\";\nimport type { ProtocolConfig } from \"./config.js\";\n\n/** 适配器生命周期钩子:由协议层实现(配置校验、初始化、资源清理)。 */\nexport interface ProtocolHooks {\n /** 配置校验通过后初始化(如建立 kernel 事件订阅)。 */\n onStart: (config: unknown) => Promise<void>;\n /** 停止时清理资源(退订、关闭传输等)。 */\n onStop: () => Promise<void>;\n /** 配置热更新。 */\n onReload: (config: unknown) => Promise<void>;\n}\n\n/**\n * 协议适配器骨架。\n * @typeParam TConfig - 协议配置类型\n */\nexport abstract class BaseProtocolAdapter<TConfig> {\n abstract readonly protocol: string;\n\n protected readonly config: ProtocolConfig<TConfig>;\n protected readonly hooks: ProtocolHooks;\n private readonly broadcaster: EventBroadcaster | undefined;\n private started = false;\n\n constructor(opts: {\n config: ProtocolConfig<TConfig>;\n hooks: ProtocolHooks;\n broadcaster?: EventBroadcaster;\n }) {\n this.config = opts.config;\n this.hooks = opts.hooks;\n this.broadcaster = opts.broadcaster;\n }\n\n /** 启动:加载配置 → 校验 → 协议层初始化。 */\n async start(): Promise<void> {\n if (this.started) {\n return;\n }\n const config = await this.config.load();\n await this.hooks.onStart(config);\n this.started = true;\n }\n\n /** 停止:协议层清理资源。 */\n async stop(): Promise<void> {\n if (!this.started) {\n return;\n }\n await this.hooks.onStop();\n this.started = false;\n }\n\n /** 配置热更新:重新加载 → 校验 → 协议层处理。 */\n async reload(): Promise<void> {\n // ConfigBase.reload() 原为 load() 的别名(已在 kernel 侧作为死代码删除),\n // 热更新语义即重新 load,行为不变。\n const config = await this.config.load();\n await this.hooks.onReload(config);\n }\n\n /** 广播协议事件给所有 network 适配器。 */\n protected broadcastEvent<T>(event: T): void {\n this.broadcaster?.emit(event);\n }\n\n /** 获取广播器(传输装配等内部使用;未传入返回 undefined)。 */\n protected getBroadcaster(): EventBroadcaster | undefined {\n return this.broadcaster;\n }\n}\n","/**\n * 协议配置加载基类:复用 kernel ConfigBase(zod schema 天然满足其校验器接口)。\n */\nimport type { ConfigFormat } from \"@napuketto/kernel\";\nimport { ConfigBase } from \"@napuketto/kernel\";\nimport type { ZodType } from \"zod\";\n\n/** 协议配置基类:zod schema + 文本读写(JSON/TOML,文件路径由装配层传入)。 */\nexport class ProtocolConfig<T> extends ConfigBase<T> {\n constructor(opts: {\n path: string;\n schema: ZodType<T>;\n defaults: T;\n /** 内存初值(全局 TOML 分段装配:seed 存在时 load() 直接用它)。 */\n seed?: T;\n format?: ConfigFormat;\n }) {\n super({\n path: opts.path,\n schema: opts.schema,\n defaults: opts.defaults,\n seed: opts.seed,\n format: opts.format,\n });\n }\n}\n","/**\n * adapter core 媒体辅助:语音归一化(ADR-011 media 严格解耦的协议层落点)\n *\n * 协议层(onebot11 / satori)发送语音前统一走 ensureSilk:把非 silk 音频\n * 转成 QQ 语音格式(silk v3)。转码依赖 @napuketto/media(encodePcmToSilk),\n * 只发生在 adapter 层;kernel 不 import media(红线),只接收已是 silk 的路径。\n */\nimport { open } from \"node:fs/promises\";\nimport { encodePcmToSilk } from \"@napuketto/media\";\n\n/** 读文件头 8 字节(判断 silk 魔数)。 */\nasync function readFileHead(path: string): Promise<string> {\n const handle = await open(path, \"r\");\n try {\n const buf = Buffer.alloc(8);\n await handle.read(buf, 0, 8, 0);\n return buf.toString(\"utf8\");\n } finally {\n await handle.close();\n }\n}\n\n/**\n * 语音转码:非 silk 输入(任意音频格式,经 @napuketto/media 归一化)转 silk。\n * 已是 silk(#!SILK_V3 头)原样返回;转码失败原样返回(由 kernel 发送时兜底)。\n */\nexport async function ensureSilk(path: string): Promise<string> {\n try {\n const header = await readFileHead(path);\n if (header.startsWith(\"#!SILK\")) {\n return path;\n }\n return await encodePcmToSilk(path);\n } catch {\n return path;\n }\n}\n","/**\n * sysmsg 解码与识别共享层(2026-11-01 自 onebot11/helper/sysmsg.ts 上移,\n * milky.md §0.8.3/§0.8.6——OB11 与 Milky 共用同一识别机制)\n *\n * Msg/onRecvSysMsg 是群名片/头衔/精华/表情回应等系统事件的 sys msg 总闸,payload =\n * 原始 protobuf 字节(djinni int8 数组透传,实测 `[[10,64,…]]` 形状)。本模块:\n * - decodeProtoTree:手写 protobuf wire-format 解码器(varint/length-delimited,\n * 零依赖,BigInt 保 64 位精度)\n * - narrowSysMsgBlobs:回调参数防御性收窄(未知形状 → null,调用方打 raw 日志)\n * - extractSysMsgEnvelope:按实测样本布局提取信封(逐字段可空)\n * - recognizeSysMsg + SysMsgKindRule<TEvent>:(msgType, subType) → 协议事件。\n * 识别表按协议各自维护(onebot11/helper/sysmsg.ts / milky/helper/sysmsg.ts),\n * 表条目随录制窗口样本扩充;未识别一律走结构化校准日志,不广播。\n *\n * 证据:真实样本(c3 观测窗 2026-09-09 双样本 528/382)+ 批次三轮录制\n * (2026-11-01,732:16 / 732:1 / 732:21 / 528:349/381/382,wire bytes 全量解码,\n * milky.md §0.8.6 识别表)+ wrapper.node OnSysMsg* 处理器分类学(原生分发键 =\n * msg_type/sub_type)。翻译纯函数(ADR-008)。\n */\n\n// ---------------------------------------------------------------------------\n// wire-format 解码器\n// ---------------------------------------------------------------------------\n\n/** 递归深度上限(防御:恶意/畸形字节防栈溢出)。 */\nconst MAX_DEPTH = 16;\n\n/** 单层字段数上限。 */\nconst MAX_FIELDS = 256;\n\n/** 单个 varint 最大字节数(64 位 + 1 冗余)。 */\nconst MAX_VARINT_BYTES = 10;\n\n/** protobuf 字段树节点(判别联合)。 */\nexport type SysMsgField =\n | {\n readonly no: number;\n readonly kind: \"varint\";\n /** 原始 64 位值(BigInt;安全转换走 varintToNumber)。 */\n readonly value: bigint;\n }\n | {\n readonly no: number;\n readonly kind: \"bytes\";\n readonly value: Uint8Array;\n /** 子树(try-parse 成功时;null = 不透明字节,empty [] = 空消息)。 */\n readonly children: readonly SysMsgField[] | null;\n };\n\n/** 读 varint:成功返回 [值, 下一偏移],越界/超长返回 null。 */\nfunction readVarint(data: Uint8Array, start: number): [bigint, number] | null {\n let value = 0n;\n let shift = 0n;\n let i = start;\n while (true) {\n if (i >= data.length || i - start >= MAX_VARINT_BYTES) {\n return null;\n }\n const byte = data[i];\n if (byte === undefined) {\n return null;\n }\n i += 1;\n value |= BigInt(byte & 0x7f) << shift;\n if ((byte & 0x80) === 0) {\n return [value, i];\n }\n shift += 7n;\n }\n}\n\n/** 单字段解码产物:field = 产出节点(定长跳过类无产出),next = 下一偏移。 */\ninterface DecodedField {\n readonly field: SysMsgField | undefined;\n readonly next: number;\n}\n\n/** wire 2:length-delimited 字段(递归 try-parse 子树,失败 → 不透明字节)。 */\nfunction decodeLengthDelimited(\n data: Uint8Array,\n afterTag: number,\n no: number,\n depth: number,\n): DecodedField | null {\n const len = readVarint(data, afterTag);\n if (len === null) {\n return null;\n }\n const payloadStart = len[1];\n const end = payloadStart + Number(len[0]);\n if (end > data.length) {\n return null;\n }\n const slice = data.slice(payloadStart, end);\n return {\n field: { no, kind: \"bytes\", value: slice, children: decodeProtoTree(slice, depth + 1) },\n next: end,\n };\n}\n\n/** 解码单个带 tag 字段;畸形(field 0 / group / 截断 / 越界)返回 null。 */\nfunction decodeTaggedField(data: Uint8Array, start: number, depth: number): DecodedField | null {\n const tag = readVarint(data, start);\n if (tag === null) {\n return null;\n }\n const [tagValue, afterTag] = tag;\n const no = Number(tagValue >> 3n);\n if (no === 0) {\n return null;\n }\n return decodeFieldByWire(data, afterTag, no, Number(tagValue & 7n), depth);\n}\n\n/** 按 wire type 分派字段解码。 */\nfunction decodeFieldByWire(\n data: Uint8Array,\n afterTag: number,\n no: number,\n wireType: number,\n depth: number,\n): DecodedField | null {\n if (wireType === 0) {\n return decodeVarintFieldAt(data, afterTag, no);\n }\n if (wireType === 2) {\n return decodeLengthDelimited(data, afterTag, no, depth);\n }\n return decodeFixedField(data, afterTag, wireType);\n}\n\n/** wire 0:varint 字段。 */\nfunction decodeVarintFieldAt(data: Uint8Array, afterTag: number, no: number): DecodedField | null {\n const v = readVarint(data, afterTag);\n if (v === null) {\n return null;\n }\n return { field: { no, kind: \"varint\", value: v[0] }, next: v[1] };\n}\n\n/** wire 1/5(fixed64/32):跳过(QQ NT blob 未观测到,防御性兼容);其余判失败。 */\nfunction decodeFixedField(\n data: Uint8Array,\n afterTag: number,\n wireType: number,\n): DecodedField | null {\n const width = fixedWireWidth(wireType);\n if (width === null) {\n return null; // wire 3/4(group)未观测到,整层判失败\n }\n return skipFixedBytes(data, afterTag, width);\n}\n\n/** fixed 类 wire 的字节宽度(非 fixed → null)。 */\nfunction fixedWireWidth(wireType: number): number | null {\n if (wireType === 1) {\n return 8;\n }\n return wireType === 5 ? 4 : null;\n}\n\n/** 定长跳过(越界判失败)。 */\nfunction skipFixedBytes(data: Uint8Array, start: number, width: number): DecodedField | null {\n const next = start + width;\n if (next > data.length) {\n return null;\n }\n return { field: undefined, next };\n}\n\n/**\n * 解码 protobuf wire-format 为递归字段树(不维护字段重复/顺序语义以外的信息)。\n *\n * - wire 0 varint / wire 2 length-delimited:支持;wire 2 递归 try-parse,\n * 子解析失败按不透明字节挂树(ASCII 串偶发误判嵌套无实害——树仅用于观测)。\n * - wire 1/5(fixed64/32):跳过(QQ NT blob 未观测到,防御性兼容)。\n * - wire 3/4(group)、截断、field 0、超限:判失败返回 null。\n */\nexport function decodeProtoTree(data: Uint8Array, depth = 0): readonly SysMsgField[] | null {\n if (depth > MAX_DEPTH) {\n return null;\n }\n const fields: SysMsgField[] = [];\n let i = 0;\n while (i < data.length) {\n if (fields.length >= MAX_FIELDS) {\n return null;\n }\n const decoded = decodeTaggedField(data, i, depth);\n if (decoded === null) {\n return null;\n }\n if (decoded.field !== undefined) {\n fields.push(decoded.field);\n }\n i = decoded.next;\n }\n return fields;\n}\n\n/** 取字段号首个命中的节点(规则实现用,路径导航原语)。 */\nexport function firstField(\n tree: readonly SysMsgField[] | null,\n no: number,\n): SysMsgField | undefined {\n return tree?.find((f) => f.no === no);\n}\n\n/** bytes 节点的子树(非 bytes/不透明 → null)。 */\nexport function getChildren(field: SysMsgField | undefined): readonly SysMsgField[] | null {\n return field?.kind === \"bytes\" ? field.children : null;\n}\n\n/** varint → number(超安全整数返回 null,调用方按缺字段处理)。 */\nexport function varintToNumber(value: bigint): number | null {\n return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : null;\n}\n\n/** UTF-8 严格解码 + 可打印检查(含控制字符判不可解码,返回 null)。 */\nexport function decodeText(bytes: Uint8Array): string | null {\n try {\n const text = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n for (const ch of text) {\n const code = ch.codePointAt(0) ?? 0;\n if (code < 0x20 && code !== 0x09) {\n return null;\n }\n }\n return text;\n } catch {\n return null;\n }\n}\n\n/** 递归收集树内全部可打印字符串(校准观测用;先序遍历,空字节段跳过)。 */\nexport function collectStrings(tree: readonly SysMsgField[]): string[] {\n const out: string[] = [];\n for (const f of tree) {\n if (f.kind === \"varint\") {\n continue;\n }\n const text = decodeText(f.value);\n if (text !== null && text.length > 0) {\n out.push(text);\n }\n if (f.children !== null) {\n out.push(...collectStrings(f.children));\n }\n }\n return out;\n}\n\n/** 按字段号路径下钻 bytes 子树(每段取首个命中;任一段不成立 → null)。 */\nexport function nodeAt(\n tree: readonly SysMsgField[] | null,\n path: readonly number[],\n): readonly SysMsgField[] | null {\n let node = tree;\n for (const no of path) {\n if (node === null) {\n return null;\n }\n node = getChildren(firstField(node, no));\n }\n return node;\n}\n\n/** 按路径取 varint 数值(bytes 子树末段的 varint 字段;缺失 → null)。 */\nexport function varintAt(\n tree: readonly SysMsgField[] | null,\n path: readonly number[],\n): number | null {\n if (path.length === 0) {\n return null;\n }\n const parent = nodeAt(tree, path.slice(0, -1));\n const f = firstField(parent, path[path.length - 1] ?? -1);\n return f?.kind === \"varint\" ? varintToNumber(f.value) : null;\n}\n\n/** 单节点内 varint 字段数值(缺失 → null)。 */\nexport function varintOf(node: readonly SysMsgField[] | null, no: number): number | null {\n const f = firstField(node, no);\n return f?.kind === \"varint\" ? varintToNumber(f.value) : null;\n}\n\n/** 单节点内 UTF-8 字符串字段(缺失/不可解码 → null)。 */\nexport function textOf(node: readonly SysMsgField[] | null, no: number): string | null {\n const f = firstField(node, no);\n return f?.kind === \"bytes\" ? decodeText(f.value) : null;\n}\n\n// ---------------------------------------------------------------------------\n// 回调参数收窄(onRecvSysMsg arg = 带符号字节数组的数组)\n// ---------------------------------------------------------------------------\n\n/** 单个候选块 → Uint8Array(number[] -128..255 / TypedArray;其余 null)。 */\nfunction toBlob(item: unknown): Uint8Array | null {\n if (item instanceof Int8Array) {\n return Uint8Array.from(item, (b) => b & 0xff);\n }\n return blobFromGeneric(item);\n}\n\n/** Uint8Array / 字节数组形态(Int8Array 已在上层处理)。 */\nfunction blobFromGeneric(item: unknown): Uint8Array | null {\n if (item instanceof Uint8Array) {\n return new Uint8Array(item);\n }\n if (Array.isArray(item) && item.length > 0) {\n return blobFromArray(item);\n }\n return null;\n}\n\n/** 字节形态数组 → Uint8Array(含非法元素整体判失败)。 */\nfunction blobFromArray(item: unknown[]): Uint8Array | null {\n const out = new Uint8Array(item.length);\n for (let i = 0; i < item.length; i += 1) {\n const byte = signedToUnsigned(item[i]);\n if (byte === null) {\n return null;\n }\n out[i] = byte;\n }\n return out;\n}\n\n/** 带符号/无符号字节归一(非整数拒绝:浮点 & 0xff 会静默截断成别的字节)。 */\nfunction signedToUnsigned(b: unknown): number | null {\n if (typeof b !== \"number\" || !Number.isInteger(b) || b < -128 || b > 255) {\n return null;\n }\n return b & 0xff;\n}\n\n/**\n * onRecvSysMsg 参数 → sysmsg 字节块列表(防御性收窄)。\n * 接受批数组(实测形状)/ 单块裸数组 / TypedArray;零有效块返回 null\n * (调用方打 raw 日志,与 narrowOfflineFiles 同模式)。\n */\nexport function narrowSysMsgBlobs(arg: unknown): readonly Uint8Array[] | null {\n if (Array.isArray(arg)) {\n // 优先按「批」解释(实测:外层数组内层字节块)\n const blobs = arg.map(toBlob).filter((b): b is Uint8Array => b !== null);\n if (blobs.length > 0) {\n return blobs;\n }\n // 兜底:arg 本身是裸字节数组\n const single = toBlob(arg);\n return single === null ? null : [single];\n }\n const single = toBlob(arg);\n return single === null ? null : [single];\n}\n\n// ---------------------------------------------------------------------------\n// 信封提取(字段号依据 = 实测样本观察,逐字段软失败)\n// ---------------------------------------------------------------------------\n\n/** 实测样本信封(全部可空 = 提取失败不打死整条事件)。 */\nexport interface SysMsgEnvelope {\n /** f1.f1:行为人 uin(观测 = 自身账号;语义待校准)。 */\n actorUin: number | null;\n /** f1.f2:行为人 uid。 */\n actorUid: string | null;\n /** f1.f5:对象 uin(观测与 actor 相等)。 */\n targetUin: number | null;\n /** f1.f6:对象 uid。 */\n targetUid: string | null;\n /** f2.f1:msgType(原生分发键之一)。 */\n msgType: number | null;\n /** f2.f2:subType(原生分发键之一)。 */\n subType: number | null;\n /** f2.f3:备用 subType(观测恒等于 subType)。 */\n subTypeAlt: number | null;\n /** f2.f4/f5:群标识对(观测恒相邻整数,语义待校准)。 */\n groupCodes: readonly [number, number] | null;\n /** f2.f6:事件时间戳(秒)。 */\n time: number | null;\n /** 树内全部可打印字符串(校准观测用,含 base64 文本)。 */\n strings: readonly string[];\n}\n\n/** 顶层 f1 块的字段(uin/uid 对)。 */\nfunction extractActorBlock(block: readonly SysMsgField[] | null): {\n uin: number | null;\n uid: string | null;\n uin2: number | null;\n uid2: string | null;\n} {\n const empty = { uin: null, uid: null, uin2: null, uid2: null };\n if (block === null) {\n return empty;\n }\n return {\n uin: varintOf(block, 1),\n uid: textOf(block, 2),\n uin2: varintOf(block, 5),\n uid2: textOf(block, 6),\n };\n}\n\n/** 顶层 f2 块 → 信封数值字段。 */\nfunction extractEnvelopeBlock(\n block: readonly SysMsgField[] | null,\n): Pick<SysMsgEnvelope, \"msgType\" | \"subType\" | \"subTypeAlt\" | \"groupCodes\" | \"time\"> {\n const empty = { msgType: null, subType: null, subTypeAlt: null, groupCodes: null, time: null };\n if (block === null) {\n return empty;\n }\n const code4 = varintOf(block, 4);\n const code5 = varintOf(block, 5);\n return {\n msgType: varintOf(block, 1),\n subType: varintOf(block, 2),\n subTypeAlt: varintOf(block, 3),\n groupCodes: code4 !== null && code5 !== null ? ([code4, code5] as const) : null,\n time: varintOf(block, 6),\n };\n}\n\n/** 字段树 → 信封(任何字段缺失/非法 = null,不抛错)。 */\nexport function extractSysMsgEnvelope(tree: readonly SysMsgField[]): SysMsgEnvelope {\n const actor = extractActorBlock(getChildren(firstField(tree, 1)));\n return {\n actorUin: actor.uin,\n actorUid: actor.uid,\n targetUin: actor.uin2,\n targetUid: actor.uid2,\n ...extractEnvelopeBlock(getChildren(firstField(tree, 2))),\n strings: collectStrings(tree),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 识别层(协议各自持表;不得猜错还硬广播)\n// ---------------------------------------------------------------------------\n\n/**\n * 单条识别规则:表命中后从字段树提取协议事件体;提取失败 → null(降级校准日志)。\n *\n * - `kind`:协议侧事件种类(OB11 notice_type / Milky event_type)。\n * - `collectUids`(可选):声明提取所需的 uid 集合——调用方批量 uid→uin 后经\n * `uinOf` 解析器传入 extract(OB11 group_card 场景;不需要则不声明)。\n * - `uinOf`:uid → uin 解析器(未收集/解析失败返回 0,协议侧口径自定)。\n */\n/** 规则执行上下文(信封与调用方装配信息,OB11 补 base 事件头用)。 */\nexport interface SysMsgRuleContext {\n /** 机器人自身 uin(数值)。 */\n selfUin: number;\n /** 事件时间(Unix 秒;sysmsg 信封 f2.f6,缺省进程当时)。 */\n time: number;\n}\n\nexport interface SysMsgKindRule<TEvent> {\n readonly kind: string;\n collectUids?(tree: readonly SysMsgField[], env: SysMsgEnvelope): readonly string[];\n extract(\n tree: readonly SysMsgField[],\n env: SysMsgEnvelope,\n uinOf: (uid: string) => number,\n ctx: SysMsgRuleContext,\n ): TEvent | null;\n}\n\n/** 识别结果三态。 */\nexport type SysMsgRecognition<TEvent> =\n | { readonly kind: \"notice\"; readonly rule: SysMsgKindRule<TEvent> }\n | { readonly kind: \"observed_unnamed\" }\n | { readonly kind: \"unknown\" };\n\n/**\n * (msgType, subType) → 识别结果。table / observedUnnamed 由各协议注入\n * (键 = `\"${msgType}:${subType}\"`,原生分发键)。\n */\nexport function recognizeSysMsg<TEvent>(\n msgType: number | null,\n subType: number | null,\n table: ReadonlyMap<string, SysMsgKindRule<TEvent>>,\n observedUnnamed: ReadonlySet<string> = new Set(),\n): SysMsgRecognition<TEvent> {\n if (msgType === null || subType === null) {\n return { kind: \"unknown\" };\n }\n const rule = table.get(`${msgType}:${subType}`);\n if (rule !== undefined) {\n return { kind: \"notice\", rule };\n }\n return observedUnnamed.has(`${msgType}:${subType}`)\n ? { kind: \"observed_unnamed\" }\n : { kind: \"unknown\" };\n}\n\n/** 字节块 → 小写 hex(校准日志用;超长由调用方截断)。 */\nexport function toHex(bytes: Uint8Array): string {\n let out = \"\";\n for (const b of bytes) {\n out += b.toString(16).padStart(2, \"0\");\n }\n return out;\n}\n"],"mappings":"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"}