@agentunion/fastaun-browser 0.4.8 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/_packed_docs/CHANGELOG.md +17 -0
  3. package/_packed_docs/INDEX.md +15 -9
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +8 -7
  5. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +5 -4
  6. package/_packed_docs/sdk/INDEX.md +15 -9
  7. package/_packed_docs/sdk/README.md +3 -2
  8. package/dist/bundle.js +529 -395
  9. package/dist/client/delivery.js +15 -15
  10. package/dist/client/delivery.js.map +1 -1
  11. package/dist/client/group-state.d.ts.map +1 -1
  12. package/dist/client/group-state.js +13 -21
  13. package/dist/client/group-state.js.map +1 -1
  14. package/dist/client/identity.d.ts.map +1 -1
  15. package/dist/client/identity.js +5 -11
  16. package/dist/client/identity.js.map +1 -1
  17. package/dist/client/lifecycle.d.ts +2 -0
  18. package/dist/client/lifecycle.d.ts.map +1 -1
  19. package/dist/client/lifecycle.js +86 -26
  20. package/dist/client/lifecycle.js.map +1 -1
  21. package/dist/client/rpc-pipeline.d.ts +4 -1
  22. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  23. package/dist/client/rpc-pipeline.js +131 -0
  24. package/dist/client/rpc-pipeline.js.map +1 -1
  25. package/dist/client/runtime.d.ts +84 -0
  26. package/dist/client/runtime.d.ts.map +1 -1
  27. package/dist/client/runtime.js +234 -0
  28. package/dist/client/runtime.js.map +1 -1
  29. package/dist/client/v2-e2ee.d.ts.map +1 -1
  30. package/dist/client/v2-e2ee.js +8 -15
  31. package/dist/client/v2-e2ee.js.map +1 -1
  32. package/dist/client.d.ts +1 -33
  33. package/dist/client.d.ts.map +1 -1
  34. package/dist/client.js +3 -307
  35. package/dist/client.js.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -146,7 +146,6 @@ function reconnectSleepDelaySeconds(baseDelay, maxBaseDelay) {
146
146
  }
147
147
  /** 对端证书缓存 TTL(秒) */
148
148
  const PEER_CERT_CACHE_TTL = 3600;
149
- const PEER_PREKEYS_CACHE_TTL = 3600;
150
149
  /**
151
150
  * 将 WebSocket URL 转为对应的 HTTP URL
152
151
  */
@@ -928,66 +927,11 @@ export class AUNClient {
928
927
  }
929
928
  /** 断开连接但保留本地状态,可再次 connect */
930
929
  async disconnect() {
931
- const tStart = Date.now();
932
- this._clientLog.debug(`disconnect enter: state=${this._state}`);
933
- if (this._closing) {
934
- this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=closing`);
935
- return;
936
- }
937
- if (![
938
- ConnectionState.AUTHENTICATED,
939
- ConnectionState.CONNECTING,
940
- ConnectionState.READY,
941
- ConnectionState.RETRY_BACKOFF,
942
- ConnectionState.RECONNECTING,
943
- ConnectionState.CONNECTION_FAILED,
944
- ].includes(this.state)) {
945
- this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=not_connected`);
946
- return;
947
- }
948
- this._saveSeqTrackerState();
949
- this._stopBackgroundTasks();
950
- if (this._reconnectAbort) {
951
- this._reconnectAbort.abort();
952
- this._reconnectAbort = null;
953
- this._reconnectActive = false;
954
- }
955
- await this._transport.close();
956
- this._state = 'standby';
957
- await this._dispatcher.publish('state_change', { state: this._publicState(this._state) });
958
- this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms`);
930
+ return this._lifecycle.disconnect();
959
931
  }
960
932
  /** 关闭连接 */
961
933
  async close() {
962
- const tStart = Date.now();
963
- this._clientLog.debug(`close enter: state=${this._state}`);
964
- this._closing = true;
965
- this._saveSeqTrackerState();
966
- this._stopBackgroundTasks();
967
- // 取消进行中的重连
968
- if (this._reconnectAbort) {
969
- this._reconnectAbort.abort();
970
- this._reconnectAbort = null;
971
- this._reconnectActive = false;
972
- }
973
- if (this._state === 'idle' || this._state === 'closed') {
974
- this._state = 'closed';
975
- this._resetSeqTrackingState();
976
- this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms reason=already_idle`);
977
- return;
978
- }
979
- // 关闭前通知服务端主动退出(best-effort,失败不阻塞)
980
- try {
981
- await this._transport.call('auth.logout', {});
982
- }
983
- catch {
984
- // auth.logout 失败不影响关闭流程
985
- }
986
- await this._transport.close();
987
- this._state = 'closed';
988
- await this._dispatcher.publish('state_change', { state: this._publicState(this._state) });
989
- this._resetSeqTrackingState();
990
- this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
934
+ return this._lifecycle.close();
991
935
  }
992
936
  // ── RPC ───────────────────────────────────────────
993
937
  /**
@@ -997,136 +941,7 @@ export class AUNClient {
997
941
  * 自动解密 message.pull/group.pull、Group E2EE 生命周期编排。
998
942
  */
999
943
  async call(method, params) {
1000
- const tStart = Date.now();
1001
- this._clientLog.debug(`call enter: method=${method}`);
1002
- try {
1003
- const result = await this._callImpl(method, params);
1004
- this._clientLog.debug(`call exit: elapsed=${Date.now() - tStart}ms method=${method}`);
1005
- return result;
1006
- }
1007
- catch (err) {
1008
- this._clientLog.debug(`call exit (error): elapsed=${Date.now() - tStart}ms method=${method} err=${err instanceof Error ? err.message : String(err)}`);
1009
- throw err;
1010
- }
1011
- }
1012
- async _callImpl(method, params) {
1013
- const p = this._rpcPipeline.preflight(method, params).params;
1014
- // 自动加密:message.send 默认加密(encrypt 默认 true)— V2-only
1015
- if (method === 'message.send') {
1016
- const encrypt = p.encrypt !== undefined ? p.encrypt : true;
1017
- delete p.encrypt;
1018
- if (encrypt) {
1019
- await this._ensureV2SessionReady('message.send', 'V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)');
1020
- this._clientLog.debug('call route: message.send → V2 encrypted send');
1021
- return await this._sendV2(String(p.to ?? ''), p.payload ?? {}, {
1022
- messageId: String(p.message_id ?? '') || undefined,
1023
- timestamp: p.timestamp,
1024
- protectedHeaders: this._protectedHeadersFromParams(p),
1025
- context: isJsonObject(p.context) ? p.context : undefined,
1026
- });
1027
- }
1028
- // encrypt=false:明文走通用 RPC 路径;protected_headers/headers 是信封元数据,加密与否都保留
1029
- this._maybeAppendEchoTraceSend(p);
1030
- }
1031
- // 自动加密:group.send 默认加密(encrypt 默认 true)— V2-only
1032
- if (method === 'group.send') {
1033
- const encrypt = p.encrypt !== undefined ? p.encrypt : true;
1034
- delete p.encrypt;
1035
- if (encrypt) {
1036
- await this._ensureV2SessionReady('group.send', 'V2 session not initialized; encrypted group.send requires V2 (V1 E2EE removed)');
1037
- this._clientLog.debug('call route: group.send → V2 encrypted send');
1038
- return await this._sendGroupV2(String(p.group_id ?? ''), p.payload ?? {}, {
1039
- messageId: String(p.message_id ?? '') || undefined,
1040
- timestamp: p.timestamp,
1041
- protectedHeaders: this._protectedHeadersFromParams(p),
1042
- context: isJsonObject(p.context) ? p.context : undefined,
1043
- });
1044
- }
1045
- this._maybeAppendEchoTraceSend(p);
1046
- }
1047
- if (method === 'group.thought.put') {
1048
- const encrypt = p.encrypt !== undefined ? p.encrypt : true;
1049
- delete p.encrypt;
1050
- if (encrypt) {
1051
- await this._ensureV2SessionReady('group.thought.put', 'V2 session not initialized; encrypted group.thought.put requires V2 (V1 E2EE removed)');
1052
- this._clientLog.debug('call route: group.thought.put → V2 encrypted put');
1053
- return this._putGroupThoughtEncryptedV2(p);
1054
- }
1055
- }
1056
- if (method === 'message.thought.put') {
1057
- const encrypt = p.encrypt !== undefined ? p.encrypt : true;
1058
- delete p.encrypt;
1059
- if (encrypt) {
1060
- await this._ensureV2SessionReady('message.thought.put', 'V2 session not initialized; encrypted message.thought.put requires V2 (V1 E2EE removed)');
1061
- this._clientLog.debug('call route: message.thought.put → V2 encrypted put');
1062
- return this._putMessageThoughtEncryptedV2(p);
1063
- }
1064
- }
1065
- // Pull Gate:序列化同一 key 的 pull 操作,防止并发重复拉取
1066
- const pullGateKey = this._pullGateKeyForCall(method, p);
1067
- if (pullGateKey) {
1068
- return await this._runPullSerialized(pullGateKey, async () => {
1069
- return await this._callImplInner(method, p);
1070
- });
1071
- }
1072
- return await this._callImplInner(method, p);
1073
- }
1074
- /**
1075
- * _callImpl 的内层:pull gate 之后的实际 RPC 分发逻辑。
1076
- * 拆分出来以便 pull gate 包裹整个操作。
1077
- */
1078
- async _callImplInner(method, p) {
1079
- // message.pull:V2-only,按需初始化后走 V2 pull
1080
- if (method === 'message.pull') {
1081
- await this._ensureV2SessionReady('message.pull');
1082
- this._clientLog.debug('call route: message.pull → V2 pull');
1083
- const messages = await this._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, { force: p.force === true });
1084
- return { messages };
1085
- }
1086
- // message.ack:V2-only,按需初始化后走 V2 ack
1087
- if (method === 'message.ack') {
1088
- await this._ensureV2SessionReady('message.ack');
1089
- this._clientLog.debug('call route: message.ack → V2 ack');
1090
- return await this._ackV2(Number(p.seq ?? p.up_to_seq ?? 0) || undefined);
1091
- }
1092
- // group.pull:V2-only,按需初始化后走 V2 pull
1093
- if (method === 'group.pull' && p.group_id) {
1094
- await this._ensureV2SessionReady('group.pull');
1095
- this._clientLog.debug('call route: group.pull → V2 pull');
1096
- const hasExplicitAfterSeq = 'after_seq' in p || 'after_message_seq' in p;
1097
- const cursorParams = this._explicitGroupCursorParams(p);
1098
- const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
1099
- const pullOpts = {};
1100
- if (hasExplicitAfterSeq)
1101
- pullOpts.explicitAfterSeq = true;
1102
- if (Object.keys(cursorParams).length > 0)
1103
- pullOpts.cursorParams = cursorParams;
1104
- if (!ownsCursor)
1105
- pullOpts.ownsCursor = false;
1106
- const messages = await this._pullGroupV2(String(p.group_id), Number(p.after_seq ?? p.after_message_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, Object.keys(pullOpts).length > 0 ? pullOpts : undefined);
1107
- return { messages };
1108
- }
1109
- // group.ack_messages:V2-only,按需初始化后走 V2 ack
1110
- if (method === 'group.ack_messages' && p.group_id) {
1111
- await this._ensureV2SessionReady('group.ack_messages');
1112
- this._clientLog.debug('call route: group.ack_messages → V2 ack');
1113
- const cursorParams = this._explicitGroupCursorParams(p);
1114
- const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
1115
- if (!ownsCursor) {
1116
- return await this._rawGroupAckMessages(p);
1117
- }
1118
- return await this._ackGroupV2(String(p.group_id), Number(p.seq ?? p.msg_seq ?? p.up_to_seq ?? 0) || undefined);
1119
- }
1120
- // 关键操作自动附加客户端签名
1121
- await this._rpcPipeline.applyClientSignature(method, p);
1122
- // P1-23: 非幂等方法使用更长超时
1123
- const callTimeout = NON_IDEMPOTENT_METHODS.has(method) ? NON_IDEMPOTENT_TIMEOUT : undefined;
1124
- let result = callTimeout
1125
- ? await this._transport.call(method, p, callTimeout)
1126
- : await this._transport.call(method, p);
1127
- result = await this._rpcPipeline.postprocessResult(method, p, result);
1128
- // ── Group E2EE 自动编排已移除(V2-only:由 group.v2.bootstrap 驱动)────────
1129
- return result;
944
+ return await this._rpcPipeline.call(method, params);
1130
945
  }
1131
946
  async _callRawV2Rpc(method, params) {
1132
947
  const p = { ...(params ?? {}) };
@@ -1170,10 +985,6 @@ export class AUNClient {
1170
985
  _onRawMessageReceived(data) {
1171
986
  this._delivery.onRawMessageReceived(data);
1172
987
  }
1173
- /** 实际处理推送消息的异步任务(V2-only:明文消息直接透传,V2 加密消息走 _onV2PushNotification) */
1174
- async _processAndPublishMessage(data) {
1175
- return this._delivery.processAndPublishMessage(data);
1176
- }
1177
988
  /** 处理群组消息推送:re-publish(V2 加密消息走 V2 push 路径) */
1178
989
  _onRawGroupMessageCreated(data) {
1179
990
  return this._delivery.onRawGroupMessageCreated(data);
@@ -1182,43 +993,19 @@ export class AUNClient {
1182
993
  async _onRawGroupV2MessageCreated(data) {
1183
994
  return this._delivery.onRawGroupV2MessageCreated(data);
1184
995
  }
1185
- /**
1186
- * 处理群组推送消息的异步任务(V2-only:明文消息直接透传)。
1187
- *
1188
- * 带 payload 的事件(消息推送):直接 re-publish。
1189
- * 不带 payload 的事件(通知):自动 pull 最新消息。
1190
- */
1191
- async _processAndPublishGroupMessage(data) {
1192
- return this._delivery.processAndPublishGroupMessage(data);
1193
- }
1194
996
  async _publishEncryptedPushMessage(normalEvent, undecryptableEvent, ns, seq, msg, group) {
1195
997
  return await this._v2E2EE.publishEncryptedPushMessage(normalEvent, undecryptableEvent, ns, seq, msg, group);
1196
998
  }
1197
999
  async _decryptV2PushMessage(data) {
1198
1000
  return await this._v2E2EE.decryptV2PushMessage(data);
1199
1001
  }
1200
- /** 后台补齐群消息空洞 */
1201
- async _fillGroupGap(groupId) {
1202
- return this._delivery.fillGroupGap(groupId);
1203
- }
1204
- /** 后台补齐群事件空洞 */
1205
- async _fillGroupEventGap(groupId) {
1206
- return this._delivery.fillGroupEventGap(groupId);
1207
- }
1208
1002
  /** 后台补齐 P2P 消息空洞 */
1209
1003
  async _fillP2pGap() {
1210
1004
  return this._delivery.fillP2pGap();
1211
1005
  }
1212
- /** 只按硬上限裁剪 published guard,不能按 contiguousSeq 清理。 */
1213
- _prunePushedSeqs(ns) {
1214
- this._delivery.prunePushedSeqs(ns);
1215
- }
1216
1006
  _markPublishedSeq(ns, seq) {
1217
1007
  this._delivery.markPublishedSeq(ns, seq);
1218
1008
  }
1219
- _attachCurrentInstanceContext(payload) {
1220
- return this._delivery.attachCurrentInstanceContext(payload);
1221
- }
1222
1009
  async _publishAppEvent(event, payload) {
1223
1010
  await this._delivery.publishAppEvent(event, payload);
1224
1011
  }
@@ -1261,9 +1048,6 @@ export class AUNClient {
1261
1048
  const trace = `${this._echoTimestamp()} [AUN-SDK.receive] aid=${this._aid ?? '-'} conn_uptime=${uptime}s`;
1262
1049
  msg.payload = { ...payload, text: payload.text + '\n' + trace };
1263
1050
  }
1264
- _messageTargetsCurrentInstance(message) {
1265
- return this._delivery.messageTargetsCurrentInstance(message);
1266
- }
1267
1051
  async _drainOrderedMessages(ns, beforeSeq) {
1268
1052
  await this._delivery.drainOrderedMessages(ns, beforeSeq);
1269
1053
  }
@@ -1420,82 +1204,6 @@ export class AUNClient {
1420
1204
  const digest = await crypto.subtle.digest('SHA-256', certBytes);
1421
1205
  return 'sha256:' + Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');
1422
1206
  }
1423
- /**
1424
- * 从 X.509 DER 证书中提取 SubjectPublicKeyInfo 并计算其 SHA-256 指纹。
1425
- * 返回 "sha256:<hex>",提取失败返回空串。
1426
- * 用于 H7 指纹校验(DER 证书指纹 OR SPKI 指纹任一匹配)。
1427
- */
1428
- async _spkiFingerprint(certPem) {
1429
- try {
1430
- const der = new Uint8Array(pemToArrayBuffer(certPem));
1431
- // X.509: Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue }
1432
- // tbsCertificate ::= SEQUENCE { version?, serialNumber, signature, issuer, validity, subject, subjectPublicKeyInfo, ... }
1433
- // 逐级解析 SEQUENCE 并定位 SPKI (第 7 个或第 6 个子元素,取决于 version 是否 [0] explicit)。
1434
- const readLen = (buf, pos) => {
1435
- const first = buf[pos];
1436
- if (first < 0x80)
1437
- return { len: first, next: pos + 1 };
1438
- const n = first & 0x7f;
1439
- let len = 0;
1440
- for (let i = 0; i < n; i++)
1441
- len = (len << 8) | buf[pos + 1 + i];
1442
- return { len, next: pos + 1 + n };
1443
- };
1444
- // 外层 SEQUENCE
1445
- if (der[0] !== 0x30)
1446
- return '';
1447
- const outer = readLen(der, 1);
1448
- // tbsCertificate SEQUENCE 起点
1449
- const tbsStart = outer.next;
1450
- if (der[tbsStart] !== 0x30)
1451
- return '';
1452
- const tbsLen = readLen(der, tbsStart + 1);
1453
- let p = tbsLen.next;
1454
- const tbsEnd = tbsLen.next + tbsLen.len;
1455
- // 跳过 [0] EXPLICIT Version(可选)
1456
- if (der[p] === 0xa0) {
1457
- const lv = readLen(der, p + 1);
1458
- p = lv.next + lv.len;
1459
- }
1460
- // 跳过 serialNumber (INTEGER)
1461
- if (der[p] !== 0x02)
1462
- return '';
1463
- let lv = readLen(der, p + 1);
1464
- p = lv.next + lv.len;
1465
- // 跳过 signature (SEQUENCE)
1466
- if (der[p] !== 0x30)
1467
- return '';
1468
- lv = readLen(der, p + 1);
1469
- p = lv.next + lv.len;
1470
- // 跳过 issuer (SEQUENCE)
1471
- if (der[p] !== 0x30)
1472
- return '';
1473
- lv = readLen(der, p + 1);
1474
- p = lv.next + lv.len;
1475
- // 跳过 validity (SEQUENCE)
1476
- if (der[p] !== 0x30)
1477
- return '';
1478
- lv = readLen(der, p + 1);
1479
- p = lv.next + lv.len;
1480
- // 跳过 subject (SEQUENCE)
1481
- if (der[p] !== 0x30)
1482
- return '';
1483
- lv = readLen(der, p + 1);
1484
- p = lv.next + lv.len;
1485
- // subjectPublicKeyInfo (SEQUENCE) — 连同 tag+length+value 全部即 SPKI DER
1486
- if (der[p] !== 0x30 || p >= tbsEnd)
1487
- return '';
1488
- const spkiStart = p;
1489
- const spkiLV = readLen(der, p + 1);
1490
- const spkiEnd = spkiLV.next + spkiLV.len;
1491
- const spkiDer = der.subarray(spkiStart, spkiEnd);
1492
- const digest = await crypto.subtle.digest('SHA-256', spkiDer);
1493
- return 'sha256:' + Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');
1494
- }
1495
- catch {
1496
- return '';
1497
- }
1498
- }
1499
1207
  async _decryptGroupThoughts(result) {
1500
1208
  return await this._v2E2EE.decryptGroupThoughts(result);
1501
1209
  }
@@ -2046,12 +1754,6 @@ export class AUNClient {
2046
1754
  _validateMessageRecipient(toAid) {
2047
1755
  this._rpcPipeline.validateMessageRecipient(toAid);
2048
1756
  }
2049
- _validateOutboundCall(method, params) {
2050
- this._rpcPipeline.validateOutboundCall(method, params);
2051
- }
2052
- _injectMessageCursorContext(method, params) {
2053
- this._rpcPipeline.injectMessageCursorContext(method, params);
2054
- }
2055
1757
  // ── 内部:断线重连 ────────────────────────────────
2056
1758
  /** 不重连 close code 集合:认证失败/权限错误/被踢等,重连无意义 */
2057
1759
  static _NO_RECONNECT_CODES = new Set([4001, 4003, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015]);
@@ -2454,9 +2156,6 @@ export class AUNClient {
2454
2156
  _scheduleV2SenderIKPending(args) {
2455
2157
  return this._v2E2EE.scheduleSenderIKPending(args);
2456
2158
  }
2457
- async _resolveV2SenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
2458
- return await this._v2E2EE.resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey);
2459
- }
2460
2159
  /**
2461
2160
  * V2 P2P 加密发送(推测性:用缓存 bootstrap 直接发,失败刷新重试一次)。
2462
2161
  *
@@ -2826,9 +2525,6 @@ export class AUNClient {
2826
2525
  && (!slotId || slotId === (this._slotId ?? ''));
2827
2526
  }
2828
2527
  // ── Pull Gate(序列化同一 key 的并发 pull)──────────────────
2829
- _pullGateKeyForCall(method, params) {
2830
- return this._rpcPipeline.pullGateKeyForCall(method, params);
2831
- }
2832
2528
  async _runPullSerialized(key, operation) {
2833
2529
  return await this._rpcPipeline.runPullSerialized(key, operation);
2834
2530
  }