@agentunion/fastaun 0.4.8 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/_packed_docs/CHANGELOG.md +35 -0
- package/_packed_docs/INDEX.md +43 -20
- package/_packed_docs/KITE_DOCS_GUIDE.md +22 -15
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +244 -16
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +113 -27
- package/_packed_docs/sdk/09-group-rpc-manual.md +97 -0
- package/_packed_docs/sdk/09-proxy-rpc-manual.md +231 -0
- package/_packed_docs/sdk/09-storage-rpc-manual.md +117 -4
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +18 -13
- package/_packed_docs/sdk/INDEX.md +26 -14
- package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +214 -0
- package/_packed_docs/sdk/README.md +9 -6
- package/dist/client/delivery.d.ts +5 -0
- package/dist/client/delivery.js +183 -5
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +13 -21
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/identity.js +5 -11
- package/dist/client/identity.js.map +1 -1
- package/dist/client/lifecycle.d.ts +2 -0
- package/dist/client/lifecycle.js +92 -26
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +2 -1
- package/dist/client/rpc-pipeline.js +153 -0
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/runtime.d.ts +84 -0
- package/dist/client/runtime.js +234 -0
- package/dist/client/runtime.js.map +1 -1
- package/dist/client/v2-e2ee.js +15 -15
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +22 -21
- package/dist/client.js +91 -247
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/keystore/local-identity-store.js +10 -0
- package/dist/keystore/local-identity-store.js.map +1 -1
- package/dist/secret-store/file-store.js +35 -2
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/service-proxy.d.ts +197 -0
- package/dist/service-proxy.js +1387 -0
- package/dist/service-proxy.js.map +1 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +45 -0
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -114,6 +114,7 @@ const DEFAULT_SESSION_OPTIONS = {
|
|
|
114
114
|
const RECONNECT_MIN_BASE_DELAY_MS = 1_000;
|
|
115
115
|
const RECONNECT_MAX_BASE_DELAY_MS = 64_000;
|
|
116
116
|
const TOKEN_REFRESH_CHECK_INTERVAL_MS = 30_000;
|
|
117
|
+
const MAX_NOTIFY_PAYLOAD_SIZE = 64 * 1024;
|
|
117
118
|
// 心跳间隔下/上限(秒)。0 = 关闭心跳;负值视为 0;其余值 clamp 到 [10, 600]。
|
|
118
119
|
// 服务端通过 hello.heartbeat_interval 与 meta.ping pong 中的同名字段下发。
|
|
119
120
|
const HEARTBEAT_MIN_INTERVAL_SECONDS = 10;
|
|
@@ -135,7 +136,7 @@ const NON_IDEMPOTENT_METHODS = new Set([
|
|
|
135
136
|
'group.kick', 'group.remove_member', 'group.leave', 'group.dissolve',
|
|
136
137
|
'group.update_name', 'group.update_avatar', 'group.update_announcement',
|
|
137
138
|
'group.update_settings',
|
|
138
|
-
'storage.
|
|
139
|
+
'storage.create_upload_session', 'storage.complete_upload', 'storage.delete_object',
|
|
139
140
|
'auth.create_aid', 'auth.renew_cert', 'auth.rekey',
|
|
140
141
|
'message.thought.put', 'group.thought.put',
|
|
141
142
|
'group.add_member',
|
|
@@ -407,6 +408,8 @@ export class AUNClient {
|
|
|
407
408
|
_seqTrackerContext = null;
|
|
408
409
|
/** 惰性群同步:已同步过的 group_id 集合 */
|
|
409
410
|
_groupSynced = new Set();
|
|
411
|
+
/** 群撤回去重:group_id|sorted(message_ids)|recalled_at -> 时间戳,保证应用层只回调一次 */
|
|
412
|
+
_groupRecallSeen = new Map();
|
|
410
413
|
/** 补洞去重:已完成/进行中的 key -> 开始时间戳,防止重复 pull 同一区间 */
|
|
411
414
|
_gapFillDone = new Map();
|
|
412
415
|
/** pull gate:按消费单元串行化 public pull / gap fill / push auto-pull。 */
|
|
@@ -584,6 +587,8 @@ export class AUNClient {
|
|
|
584
587
|
this._dispatcher.subscribe('_raw.group.v2.message_created', (data) => this._safeAsync(this._onRawGroupV2MessageCreated(data)));
|
|
585
588
|
// 群组消息推送:自动解密后 re-publish
|
|
586
589
|
this._dispatcher.subscribe('_raw.group.message_created', (data) => this._onRawGroupMessageCreated(data));
|
|
590
|
+
// 群组消息撤回推送:在线 push 通道,与 pull 双 tombstone 兜底互补(SDK 去重只回调一次)
|
|
591
|
+
this._dispatcher.subscribe('_raw.group.message_recalled', (data) => this._safeAsync(this._onRawGroupMessageRecalled(data)));
|
|
587
592
|
// 群组变更事件:拦截处理成员变更触发的 epoch 轮换,然后透传
|
|
588
593
|
this._dispatcher.subscribe('_raw.group.changed', (data) => this._onRawGroupChanged(data));
|
|
589
594
|
// V2 epoch 轮换事件:清 bootstrap 缓存并触发 SPK rotation
|
|
@@ -836,72 +841,14 @@ export class AUNClient {
|
|
|
836
841
|
}
|
|
837
842
|
/** 关闭连接 */
|
|
838
843
|
async close() {
|
|
839
|
-
|
|
840
|
-
this._clientLog.debug(`close enter: state=${this._state}, aid=${this._aid ?? ''}`);
|
|
841
|
-
try {
|
|
842
|
-
this._closing = true;
|
|
843
|
-
this._saveSeqTrackerState();
|
|
844
|
-
this._stopBackgroundTasks();
|
|
845
|
-
this._stopReconnect();
|
|
846
|
-
if (this.state === ConnectionState.NO_IDENTITY || this.state === ConnectionState.CLOSED) {
|
|
847
|
-
const closableStore = this._tokenStore;
|
|
848
|
-
closableStore.close?.();
|
|
849
|
-
this._state = 'closed';
|
|
850
|
-
this._logger.close();
|
|
851
|
-
this._resetSeqTrackingState();
|
|
852
|
-
this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms (was no_identity/closed)`);
|
|
853
|
-
return;
|
|
854
|
-
}
|
|
855
|
-
await this._transport.close();
|
|
856
|
-
const closableStore = this._tokenStore;
|
|
857
|
-
closableStore.close?.();
|
|
858
|
-
this._state = 'closed';
|
|
859
|
-
this._logger.close();
|
|
860
|
-
await this._dispatcher.publish('state_change', { state: this._publicState(this._state) });
|
|
861
|
-
this._resetSeqTrackingState();
|
|
862
|
-
this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
|
|
863
|
-
}
|
|
864
|
-
catch (err) {
|
|
865
|
-
this._clientLog.debug(`close exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
866
|
-
throw err;
|
|
867
|
-
}
|
|
844
|
+
return this._lifecycle.close();
|
|
868
845
|
}
|
|
869
846
|
/**
|
|
870
847
|
* 断开连接但不关闭客户端(可重新 connect,对齐 Python disconnect)。
|
|
871
848
|
* disconnect 是可恢复的:停止心跳、关闭 WebSocket,但不清理 keystore 等状态。
|
|
872
849
|
*/
|
|
873
850
|
async disconnect() {
|
|
874
|
-
|
|
875
|
-
this._clientLog.debug(`disconnect enter: state=${this._state}, aid=${this._aid ?? ''}, closing=${this._closing}`);
|
|
876
|
-
try {
|
|
877
|
-
// 若 close() 已在执行中,跳过 disconnect 避免竞态
|
|
878
|
-
if (this._closing) {
|
|
879
|
-
this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms (closing)`);
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
if (![
|
|
883
|
-
ConnectionState.AUTHENTICATED,
|
|
884
|
-
ConnectionState.CONNECTING,
|
|
885
|
-
ConnectionState.READY,
|
|
886
|
-
ConnectionState.RETRY_BACKOFF,
|
|
887
|
-
ConnectionState.RECONNECTING,
|
|
888
|
-
ConnectionState.CONNECTION_FAILED,
|
|
889
|
-
].includes(this.state)) {
|
|
890
|
-
this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms (state=${this._state})`);
|
|
891
|
-
return;
|
|
892
|
-
}
|
|
893
|
-
this._saveSeqTrackerState();
|
|
894
|
-
this._stopBackgroundTasks();
|
|
895
|
-
this._stopReconnect();
|
|
896
|
-
await this._transport.close();
|
|
897
|
-
this._state = 'standby';
|
|
898
|
-
await this._dispatcher.publish('state_change', { state: this._publicState(this._state) });
|
|
899
|
-
this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms`);
|
|
900
|
-
}
|
|
901
|
-
catch (err) {
|
|
902
|
-
this._clientLog.debug(`disconnect exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
903
|
-
throw err;
|
|
904
|
-
}
|
|
851
|
+
return this._lifecycle.disconnect();
|
|
905
852
|
}
|
|
906
853
|
// ── RPC ───────────────────────────────────────────────────
|
|
907
854
|
/**
|
|
@@ -909,154 +856,91 @@ export class AUNClient {
|
|
|
909
856
|
* 自动处理内部方法限制、E2EE 加解密、客户端签名等。
|
|
910
857
|
*/
|
|
911
858
|
async call(method, params) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
859
|
+
return await this._rpcPipeline.call(method, params);
|
|
860
|
+
}
|
|
861
|
+
static _notifyParamsSizeOk(params) {
|
|
862
|
+
return new TextEncoder().encode(JSON.stringify(params)).length <= MAX_NOTIFY_PAYLOAD_SIZE;
|
|
863
|
+
}
|
|
864
|
+
static _validateNotifyEventMethod(method) {
|
|
865
|
+
const normalized = String(method ?? '').trim();
|
|
866
|
+
if (!normalized.startsWith('event/app.') || normalized.length <= 'event/app.'.length) {
|
|
867
|
+
throw new ValidationError('routed notify method must be event/app.*');
|
|
868
|
+
}
|
|
869
|
+
return normalized;
|
|
870
|
+
}
|
|
871
|
+
static _normalizeNotifyTtl(value) {
|
|
872
|
+
if (value === undefined || value === null)
|
|
873
|
+
return undefined;
|
|
874
|
+
const ttl = Number(value);
|
|
875
|
+
if (!Number.isInteger(ttl)) {
|
|
876
|
+
throw new ValidationError('ttl_ms must be an integer');
|
|
877
|
+
}
|
|
878
|
+
if (ttl < 0 || ttl > 60000) {
|
|
879
|
+
throw new ValidationError('ttl_ms must be between 0 and 60000');
|
|
880
|
+
}
|
|
881
|
+
return ttl;
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* 发送轻量在线通知,不走离线存储、seq/pull 或 ack。
|
|
885
|
+
*/
|
|
886
|
+
async notify(method, params, options = {}) {
|
|
887
|
+
if (params !== undefined && params !== null && !isJsonObject(params)) {
|
|
888
|
+
throw new ValidationError('notify params must be an object');
|
|
889
|
+
}
|
|
890
|
+
const payload = { ...(params ?? {}) };
|
|
891
|
+
if (!AUNClient._notifyParamsSizeOk(payload)) {
|
|
892
|
+
throw new ValidationError('notify payload is too large');
|
|
893
|
+
}
|
|
894
|
+
const targetAid = String(options.to ?? '').trim();
|
|
895
|
+
const targetGroupId = String(options.group_id ?? options.groupId ?? '').trim();
|
|
896
|
+
const targetDeviceId = String(options.device_id ?? options.deviceId ?? '').trim();
|
|
897
|
+
const targetSlotId = String(options.slot_id ?? options.slotId ?? '').trim();
|
|
898
|
+
const ttl = AUNClient._normalizeNotifyTtl(options.ttl_ms ?? options.ttlMs);
|
|
899
|
+
if (targetAid && targetGroupId) {
|
|
900
|
+
throw new ValidationError('notify() cannot set both to and group_id');
|
|
901
|
+
}
|
|
902
|
+
if (targetSlotId && !targetDeviceId) {
|
|
903
|
+
throw new ValidationError('slot_id requires device_id for notify target');
|
|
904
|
+
}
|
|
905
|
+
if (targetAid) {
|
|
906
|
+
const eventMethod = AUNClient._validateNotifyEventMethod(method);
|
|
907
|
+
const target = { type: 'aid', aid: targetAid };
|
|
908
|
+
if (targetDeviceId)
|
|
909
|
+
target.device_id = targetDeviceId;
|
|
910
|
+
if (targetSlotId)
|
|
911
|
+
target.slot_id = targetSlotId;
|
|
912
|
+
const routeParams = {
|
|
913
|
+
target,
|
|
914
|
+
deliver: { method: eventMethod, params: payload },
|
|
922
915
|
};
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const lockedParams = { ...p, _pull_gate_locked: true };
|
|
934
|
-
if (rpcBackground)
|
|
935
|
-
lockedParams._rpc_background = true;
|
|
936
|
-
const result = await this._runPullSerialized(pullGateKey, async () => this.call(method, lockedParams));
|
|
937
|
-
return result;
|
|
938
|
-
}
|
|
939
|
-
// 自动加密:message.send 默认加密(encrypt 默认 true)— V2-only
|
|
940
|
-
if (method === 'message.send') {
|
|
941
|
-
const encrypt = p.encrypt ?? true;
|
|
942
|
-
delete p.encrypt;
|
|
943
|
-
if (encrypt) {
|
|
944
|
-
return await runWithRpcPriority(() => this._sendV2(String(p.to ?? ''), p.payload, {
|
|
945
|
-
messageId: String(p.message_id ?? '') || undefined,
|
|
946
|
-
timestamp: p.timestamp,
|
|
947
|
-
protectedHeaders: this._protectedHeadersFromParams(p),
|
|
948
|
-
context: isJsonObject(p.context) ? p.context : undefined,
|
|
949
|
-
}));
|
|
950
|
-
}
|
|
951
|
-
// encrypt=false:明文走通用 RPC 路径;protected_headers/headers 是信封元数据,加密与否都保留
|
|
952
|
-
this._maybeAppendEchoTraceSend(p);
|
|
953
|
-
}
|
|
954
|
-
// 自动加密:group.send 默认加密(encrypt 默认 true)— V2-only
|
|
955
|
-
if (method === 'group.send') {
|
|
956
|
-
const encrypt = p.encrypt ?? true;
|
|
957
|
-
delete p.encrypt;
|
|
958
|
-
if (encrypt) {
|
|
959
|
-
return await runWithRpcPriority(() => this._sendGroupV2(String(p.group_id ?? ''), p.payload, {
|
|
960
|
-
messageId: String(p.message_id ?? '') || undefined,
|
|
961
|
-
timestamp: p.timestamp,
|
|
962
|
-
protectedHeaders: this._protectedHeadersFromParams(p),
|
|
963
|
-
context: isJsonObject(p.context) ? p.context : undefined,
|
|
964
|
-
}));
|
|
965
|
-
}
|
|
966
|
-
this._maybeAppendEchoTraceSend(p);
|
|
967
|
-
}
|
|
968
|
-
if (method === 'group.thought.put') {
|
|
969
|
-
const encrypt = p.encrypt ?? true;
|
|
970
|
-
delete p.encrypt;
|
|
971
|
-
if (encrypt) {
|
|
972
|
-
const v2Error = 'V2 session not initialized; encrypted group.thought.put requires V2 (V1 E2EE removed)';
|
|
973
|
-
if (!this._v2Session || !String(p.group_id ?? '').trim()) {
|
|
974
|
-
throw new StateError(v2Error);
|
|
975
|
-
}
|
|
976
|
-
return await runWithRpcPriority(() => this._putGroupThoughtEncryptedV2(p));
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
if (method === 'message.thought.put') {
|
|
980
|
-
const encrypt = p.encrypt ?? true;
|
|
981
|
-
delete p.encrypt;
|
|
982
|
-
if (encrypt) {
|
|
983
|
-
await this._ensureV2SessionReady('message.thought.put', 'V2 session not initialized; encrypted message.thought.put requires V2 (V1 E2EE removed)');
|
|
984
|
-
return await runWithRpcPriority(() => this._putMessageThoughtEncryptedV2(p));
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
// V2-only:兼容入口名只作为 SDK 内部适配层存在,底层绝不能降级发 legacy RPC。
|
|
988
|
-
if (method === 'message.pull' || method === 'message.v2.pull') {
|
|
989
|
-
await this._ensureV2SessionReady('message.pull');
|
|
990
|
-
const skipAutoAck = p._skip_auto_ack === true || p.skip_auto_ack === true;
|
|
991
|
-
const force = p.force === true;
|
|
992
|
-
const afterSeq = Number(p.after_seq ?? 0) || 0;
|
|
993
|
-
const limit = Number(p.limit ?? 50) || 50;
|
|
994
|
-
const messages = skipAutoAck
|
|
995
|
-
? await runWithRpcPriority(() => this._pullV2(afterSeq, limit, { skipAutoAck: true, gateLocked: true, force }))
|
|
996
|
-
: await runWithRpcPriority(() => this._pullV2(afterSeq, limit, { gateLocked: true, force }));
|
|
997
|
-
return { messages };
|
|
998
|
-
}
|
|
999
|
-
if (method === 'message.ack' || method === 'message.v2.ack') {
|
|
1000
|
-
await this._ensureV2SessionReady('message.ack');
|
|
1001
|
-
return await runWithRpcPriority(() => this._ackV2(Number(p.seq ?? p.up_to_seq ?? 0) || undefined));
|
|
1002
|
-
}
|
|
1003
|
-
if (method === 'group.pull' || method === 'group.v2.pull') {
|
|
1004
|
-
if (!String(p.group_id ?? '').trim()) {
|
|
1005
|
-
throw new ValidationError('group.pull requires group_id');
|
|
1006
|
-
}
|
|
1007
|
-
await this._ensureV2SessionReady('group.pull');
|
|
1008
|
-
const hasExplicitAfterSeq = 'after_seq' in p || 'after_message_seq' in p;
|
|
1009
|
-
const cursorParams = this._explicitGroupCursorParams(p);
|
|
1010
|
-
const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
|
|
1011
|
-
const pullOpts = { gateLocked: true };
|
|
1012
|
-
if (hasExplicitAfterSeq)
|
|
1013
|
-
pullOpts.explicitAfterSeq = true;
|
|
1014
|
-
if (Object.keys(cursorParams).length > 0)
|
|
1015
|
-
pullOpts.cursorParams = cursorParams;
|
|
1016
|
-
if (!ownsCursor)
|
|
1017
|
-
pullOpts.ownsCursor = false;
|
|
1018
|
-
const messages = await runWithRpcPriority(() => this._pullGroupV2(String(p.group_id), Number(p.after_seq ?? p.after_message_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, pullOpts));
|
|
1019
|
-
return { messages };
|
|
1020
|
-
}
|
|
1021
|
-
if (method === 'group.ack_messages' || method === 'group.v2.ack') {
|
|
1022
|
-
if (!String(p.group_id ?? '').trim()) {
|
|
1023
|
-
throw new ValidationError('group.ack_messages requires group_id');
|
|
1024
|
-
}
|
|
1025
|
-
await this._ensureV2SessionReady('group.ack_messages');
|
|
1026
|
-
const cursorParams = this._explicitGroupCursorParams(p);
|
|
1027
|
-
const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
|
|
1028
|
-
if (method === 'group.ack_messages' && !ownsCursor) {
|
|
1029
|
-
return await runWithRpcPriority(() => this._rawGroupAckMessages(p));
|
|
1030
|
-
}
|
|
1031
|
-
return await runWithRpcPriority(() => this._ackGroupV2(String(p.group_id), Number(p.seq ?? p.msg_seq ?? p.up_to_seq ?? 0) || undefined));
|
|
1032
|
-
}
|
|
1033
|
-
if (method === 'message.pull') {
|
|
1034
|
-
delete p._skip_auto_ack;
|
|
1035
|
-
delete p.skip_auto_ack;
|
|
1036
|
-
}
|
|
1037
|
-
delete p._group_cursor_params;
|
|
1038
|
-
// 关键操作自动附加客户端签名
|
|
1039
|
-
this._rpcPipeline.applyClientSignature(method, p);
|
|
1040
|
-
// P1-23: 非幂等方法使用更长超时
|
|
1041
|
-
const callTimeout = NON_IDEMPOTENT_METHODS.has(method) ? NON_IDEMPOTENT_TIMEOUT_MS : undefined;
|
|
1042
|
-
if (method === 'group.thought.get' || method === 'message.thought.get') {
|
|
1043
|
-
this._clientLog.debug(`thought.get transport call start: method=${method}, params=${this._debugJson(this._messageEnvelopeFieldsForDebug(p))}`);
|
|
916
|
+
if (ttl !== undefined)
|
|
917
|
+
routeParams.ttl_ms = ttl;
|
|
918
|
+
await this._transport.notify('notification/route', routeParams);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (targetGroupId) {
|
|
922
|
+
const eventMethod = AUNClient._validateNotifyEventMethod(method);
|
|
923
|
+
const normalizedGroupId = normalizeGroupId(targetGroupId);
|
|
924
|
+
if (!normalizedGroupId) {
|
|
925
|
+
throw new ValidationError('group_id is required for group notify');
|
|
1044
926
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
this._clientLog.debug(`call exit: method=${method} elapsed=${Date.now() - tStart}ms`);
|
|
1054
|
-
return result;
|
|
927
|
+
const routeParams = {
|
|
928
|
+
group_id: normalizedGroupId,
|
|
929
|
+
deliver: { method: eventMethod, params: payload },
|
|
930
|
+
};
|
|
931
|
+
if (ttl !== undefined)
|
|
932
|
+
routeParams.ttl_ms = ttl;
|
|
933
|
+
await this._transport.notify('notification/group.route', routeParams);
|
|
934
|
+
return;
|
|
1055
935
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
throw err;
|
|
936
|
+
if (targetDeviceId || targetSlotId) {
|
|
937
|
+
throw new ValidationError('device_id and slot_id require to');
|
|
1059
938
|
}
|
|
939
|
+
const directMethod = String(method ?? '').trim();
|
|
940
|
+
if (!directMethod.startsWith('notification/')) {
|
|
941
|
+
throw new ValidationError('direct notify method must start with notification/');
|
|
942
|
+
}
|
|
943
|
+
await this._transport.notify(directMethod, payload);
|
|
1060
944
|
}
|
|
1061
945
|
// ── 事件 ──────────────────────────────────────────────────
|
|
1062
946
|
/** 订阅事件 */
|
|
@@ -1120,35 +1004,17 @@ export class AUNClient {
|
|
|
1120
1004
|
async _onRawMessageReceived(data) {
|
|
1121
1005
|
return this._delivery.onRawMessageReceived(data);
|
|
1122
1006
|
}
|
|
1123
|
-
/** 实际处理推送消息的异步任务 */
|
|
1124
|
-
async _processAndPublishMessage(data) {
|
|
1125
|
-
return this._delivery.processAndPublishMessage(data);
|
|
1126
|
-
}
|
|
1127
1007
|
/** 处理群组消息推送:自动解密后 re-publish */
|
|
1128
1008
|
async _onRawGroupMessageCreated(data) {
|
|
1129
1009
|
return this._delivery.onRawGroupMessageCreated(data);
|
|
1130
1010
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
*
|
|
1134
|
-
* 带 payload 的事件(消息推送):解密后 re-publish。
|
|
1135
|
-
* 不带 payload 的事件(通知):自动 pull 最新消息,逐条解密后 re-publish。
|
|
1136
|
-
*/
|
|
1137
|
-
async _processAndPublishGroupMessage(data) {
|
|
1138
|
-
return this._delivery.processAndPublishGroupMessage(data);
|
|
1139
|
-
}
|
|
1140
|
-
/** 后台补齐群消息空洞 */
|
|
1141
|
-
async _fillGroupGap(groupId) {
|
|
1142
|
-
return this._delivery.fillGroupGap(groupId);
|
|
1011
|
+
async _onRawGroupMessageRecalled(data) {
|
|
1012
|
+
return this._delivery.onRawGroupMessageRecalled(data);
|
|
1143
1013
|
}
|
|
1144
1014
|
/** 后台补齐 P2P 消息空洞 */
|
|
1145
1015
|
async _fillP2pGap() {
|
|
1146
1016
|
return this._delivery.fillP2pGap();
|
|
1147
1017
|
}
|
|
1148
|
-
/** 只按硬上限裁剪 published guard,不能按 contiguousSeq 清理。 */
|
|
1149
|
-
_prunePushedSeqs(ns) {
|
|
1150
|
-
this._delivery.prunePushedSeqs(ns);
|
|
1151
|
-
}
|
|
1152
1018
|
_recordPendingP2pPull(ns, seq) {
|
|
1153
1019
|
if (!ns || seq <= 0)
|
|
1154
1020
|
return;
|
|
@@ -1184,9 +1050,6 @@ export class AUNClient {
|
|
|
1184
1050
|
_markPublishedSeq(ns, seq) {
|
|
1185
1051
|
this._delivery.markPublishedSeq(ns, seq);
|
|
1186
1052
|
}
|
|
1187
|
-
_attachCurrentInstanceContext(payload) {
|
|
1188
|
-
return this._delivery.attachCurrentInstanceContext(payload);
|
|
1189
|
-
}
|
|
1190
1053
|
_publishAppEvent(event, payload, source = 'direct') {
|
|
1191
1054
|
return this._delivery.publishAppEvent(event, payload, source);
|
|
1192
1055
|
}
|
|
@@ -1260,18 +1123,12 @@ export class AUNClient {
|
|
|
1260
1123
|
_logMessageDebug(stage, source, event, message, opts = {}) {
|
|
1261
1124
|
this._delivery.logMessageDebug(stage, source, event, message, opts);
|
|
1262
1125
|
}
|
|
1263
|
-
_messageTargetsCurrentInstance(message) {
|
|
1264
|
-
return this._delivery.messageTargetsCurrentInstance(message);
|
|
1265
|
-
}
|
|
1266
1126
|
_tryAcquirePullGate(key) {
|
|
1267
1127
|
return this._rpcPipeline.tryAcquirePullGate(key);
|
|
1268
1128
|
}
|
|
1269
1129
|
_releasePullGate(key, token) {
|
|
1270
1130
|
this._rpcPipeline.releasePullGate(key, token);
|
|
1271
1131
|
}
|
|
1272
|
-
_pullGateKeyForCall(method, params) {
|
|
1273
|
-
return this._rpcPipeline.pullGateKeyForCall(method, params);
|
|
1274
|
-
}
|
|
1275
1132
|
_isPullResponseProcessing(key) {
|
|
1276
1133
|
if (!key)
|
|
1277
1134
|
return false;
|
|
@@ -1396,10 +1253,6 @@ export class AUNClient {
|
|
|
1396
1253
|
this._seqTracker.onMessageSeq(ns, seq);
|
|
1397
1254
|
return this._seqTracker.getContiguousSeq(ns) !== before;
|
|
1398
1255
|
}
|
|
1399
|
-
/** 后台补齐群事件空洞 */
|
|
1400
|
-
async _fillGroupEventGap(groupId) {
|
|
1401
|
-
return this._delivery.fillGroupEventGap(groupId);
|
|
1402
|
-
}
|
|
1403
1256
|
_extractGroupIdFromResult(result) {
|
|
1404
1257
|
const group = isJsonObject(result.group) ? result.group : null;
|
|
1405
1258
|
const gid = group ? String(group.group_id ?? '') : '';
|
|
@@ -2086,9 +1939,6 @@ export class AUNClient {
|
|
|
2086
1939
|
_scheduleV2SenderIKPending(args) {
|
|
2087
1940
|
return this._v2E2EE.scheduleSenderIKPending(args);
|
|
2088
1941
|
}
|
|
2089
|
-
async _resolveV2SenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
|
|
2090
|
-
return await this._v2E2EE.resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey);
|
|
2091
|
-
}
|
|
2092
1942
|
/**
|
|
2093
1943
|
* 构造 V2 P2P envelope;message.send 与 message.thought.put 共用。
|
|
2094
1944
|
*/
|
|
@@ -2784,12 +2634,6 @@ export class AUNClient {
|
|
|
2784
2634
|
_validateMessageRecipient(toAid) {
|
|
2785
2635
|
this._rpcPipeline.validateMessageRecipient(toAid);
|
|
2786
2636
|
}
|
|
2787
|
-
_validateOutboundCall(method, params) {
|
|
2788
|
-
this._rpcPipeline.validateOutboundCall(method, params);
|
|
2789
|
-
}
|
|
2790
|
-
_injectMessageCursorContext(method, params) {
|
|
2791
|
-
this._rpcPipeline.injectMessageCursorContext(method, params);
|
|
2792
|
-
}
|
|
2793
2637
|
/** 启动 V2 缓存清理后台任务 */
|
|
2794
2638
|
_startV2MaintenanceTasks() {
|
|
2795
2639
|
if (this._cacheCleanupTimer === null) {
|