@agentunion/fastaun 0.5.8 → 0.5.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 +64 -0
- package/_packed_docs/CHANGELOG.md +64 -0
- package/_packed_docs/agent.md/examples//347/276/244/347/273/204-/345/274/200/345/217/221/345/233/242/351/230/237.md +21 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +6 -5
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +43 -8
- package/_packed_docs/sdk/09-group-rpc-manual.md +18 -3
- package/_packed_docs/sdk/09-message-rpc-manual.md +34 -10
- package/dist/agent-md-schema.js +3 -0
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.js +5 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.js +1 -3
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +5 -2
- package/dist/auth.js +64 -27
- package/dist/auth.js.map +1 -1
- package/dist/client/delivery.d.ts +25 -5
- package/dist/client/delivery.js +355 -70
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +8 -8
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +25 -22
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +12 -0
- package/dist/client/rpc-pipeline.js +182 -32
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.js +55 -21
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.js +108 -38
- package/dist/client.js.map +1 -1
- package/dist/discovery.d.ts +1 -0
- package/dist/discovery.js +3 -0
- package/dist/discovery.js.map +1 -1
- package/dist/events.d.ts +23 -8
- package/dist/events.js +120 -26
- package/dist/events.js.map +1 -1
- package/dist/facades.js +7 -3
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/net.d.ts +3 -0
- package/dist/net.js +13 -0
- package/dist/net.js.map +1 -1
- package/dist/register-flow.js +15 -111
- package/dist/register-flow.js.map +1 -1
- package/dist/transport.d.ts +32 -3
- package/dist/transport.js +758 -33
- 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/_packed_docs//345/217/221/345/270/203/346/212/245/345/221/212-0.5.6.md +0 -260
package/dist/client.js
CHANGED
|
@@ -688,7 +688,7 @@ export class AUNClient {
|
|
|
688
688
|
this._dispatcher.subscribe('_raw.group.state_committed', (data) => this._onGroupStateCommitted(data));
|
|
689
689
|
// 其他事件直接透传
|
|
690
690
|
for (const evt of ['message.ack', 'storage.object_changed', 'stream/created', 'stream/closed']) {
|
|
691
|
-
this._dispatcher.subscribe(`_raw.${evt}`, (data) => this._dispatcher.
|
|
691
|
+
this._dispatcher.subscribe(`_raw.${evt}`, (data) => this._dispatcher.enqueue(evt, data));
|
|
692
692
|
}
|
|
693
693
|
// 服务端主动断开通知:记录日志并标记不重连
|
|
694
694
|
this._dispatcher.subscribe('_raw.gateway.disconnect', (data) => this._onGatewayDisconnect(data));
|
|
@@ -852,6 +852,7 @@ export class AUNClient {
|
|
|
852
852
|
this._peerCache.clear();
|
|
853
853
|
this._certCache.clear();
|
|
854
854
|
this._gatewayUrl = null;
|
|
855
|
+
this._gatewayCandidates = [];
|
|
855
856
|
this._deviceId = aid.deviceId || getDeviceId(nextConfig.aunPath);
|
|
856
857
|
this._slotId = aid.slotId || 'default';
|
|
857
858
|
const debugFlag = nextConfig.debug;
|
|
@@ -1059,7 +1060,12 @@ export class AUNClient {
|
|
|
1059
1060
|
const target = message.slice(agentPrefix.length).trim();
|
|
1060
1061
|
if (!target || target.toLowerCase() !== String(this._aid ?? '').trim().toLowerCase())
|
|
1061
1062
|
return false;
|
|
1062
|
-
|
|
1063
|
+
let agentMd = buildDefaultAgentMd(target);
|
|
1064
|
+
try {
|
|
1065
|
+
agentMd = this._agentMdManager.readContent(target);
|
|
1066
|
+
}
|
|
1067
|
+
catch { }
|
|
1068
|
+
await this._agentMdManager.upload(agentMd);
|
|
1063
1069
|
return true;
|
|
1064
1070
|
}
|
|
1065
1071
|
async _runGroupIdentityOperation(groupId, operation) {
|
|
@@ -1091,6 +1097,10 @@ export class AUNClient {
|
|
|
1091
1097
|
}
|
|
1092
1098
|
this._aidStore = store;
|
|
1093
1099
|
const payload = { ...params };
|
|
1100
|
+
if (payload.group_name === undefined && payload.groupName !== undefined) {
|
|
1101
|
+
payload.group_name = payload.groupName;
|
|
1102
|
+
delete payload.groupName;
|
|
1103
|
+
}
|
|
1094
1104
|
const isNamed = Boolean(String(payload.group_name ?? '').trim());
|
|
1095
1105
|
payload._defer_group_ready_postprocess = true;
|
|
1096
1106
|
if (!isNamed) {
|
|
@@ -1116,7 +1126,21 @@ export class AUNClient {
|
|
|
1116
1126
|
delete postprocessParams._defer_group_ready_postprocess;
|
|
1117
1127
|
return await this._groupState.postprocessResult('group.create', postprocessParams, result);
|
|
1118
1128
|
}
|
|
1119
|
-
const
|
|
1129
|
+
const pendingKey = `create:${String(payload.group_name).trim().toLowerCase()}`;
|
|
1130
|
+
const keystore = store._keystore;
|
|
1131
|
+
let keyPair = null;
|
|
1132
|
+
if (keystore && typeof keystore.loadPendingGroupBind === 'function') {
|
|
1133
|
+
keyPair = keystore.loadPendingGroupBind(pendingKey);
|
|
1134
|
+
}
|
|
1135
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
1136
|
+
keyPair = new CryptoProvider().generateIdentity();
|
|
1137
|
+
if (keystore && typeof keystore.savePendingGroupBind === 'function') {
|
|
1138
|
+
keystore.savePendingGroupBind(pendingKey, keyPair);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
1142
|
+
throw new ValidationError('createGroup: failed to generate or load key pair');
|
|
1143
|
+
}
|
|
1120
1144
|
payload.public_key = keyPair.public_key_der_b64;
|
|
1121
1145
|
payload.curve = keyPair.curve;
|
|
1122
1146
|
const result = await this.call('group.create', payload);
|
|
@@ -1138,6 +1162,9 @@ export class AUNClient {
|
|
|
1138
1162
|
throw new ValidationError(imported.error.message);
|
|
1139
1163
|
}
|
|
1140
1164
|
await this._uploadGroupAgentMd(store, groupAid, payload, group);
|
|
1165
|
+
if (keystore && typeof keystore.clearPendingGroupBind === 'function') {
|
|
1166
|
+
keystore.clearPendingGroupBind(pendingKey);
|
|
1167
|
+
}
|
|
1141
1168
|
const postprocessParams = { ...payload };
|
|
1142
1169
|
delete postprocessParams._defer_group_ready_postprocess;
|
|
1143
1170
|
return await this._groupState.postprocessResult('group.create', postprocessParams, result);
|
|
@@ -1193,11 +1220,15 @@ export class AUNClient {
|
|
|
1193
1220
|
}
|
|
1194
1221
|
this._aidStore = store;
|
|
1195
1222
|
const groupId = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
1223
|
+
const pendingKey = `bind:${groupId}`;
|
|
1196
1224
|
const keystore = store._keystore; // AIDStore._keystore: LocalIdentityStore
|
|
1197
1225
|
let keyPair = null;
|
|
1198
1226
|
// 优先复用 pending 槽位中已暂存的待绑定密钥(崩溃/重试幂等)
|
|
1199
1227
|
if (groupId && keystore && typeof keystore.loadPendingGroupBind === 'function') {
|
|
1200
|
-
keyPair = keystore.loadPendingGroupBind(
|
|
1228
|
+
keyPair = keystore.loadPendingGroupBind(pendingKey);
|
|
1229
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
1230
|
+
keyPair = keystore.loadPendingGroupBind(groupId);
|
|
1231
|
+
}
|
|
1201
1232
|
}
|
|
1202
1233
|
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
1203
1234
|
// 未命中 pending,生成新密钥
|
|
@@ -1209,7 +1240,7 @@ export class AUNClient {
|
|
|
1209
1240
|
};
|
|
1210
1241
|
// 发 RPC 前先落盘暂存,确保崩溃后重试能复用同一密钥
|
|
1211
1242
|
if (groupId && keystore && typeof keystore.savePendingGroupBind === 'function') {
|
|
1212
|
-
keystore.savePendingGroupBind(
|
|
1243
|
+
keystore.savePendingGroupBind(pendingKey, keyPair);
|
|
1213
1244
|
}
|
|
1214
1245
|
}
|
|
1215
1246
|
if (!keyPair) {
|
|
@@ -1244,6 +1275,7 @@ export class AUNClient {
|
|
|
1244
1275
|
await this._uploadGroupAgentMd(store, groupAid, payload, group);
|
|
1245
1276
|
// 落盘成功,清除 pending 槽位
|
|
1246
1277
|
if (groupId && keystore && typeof keystore.clearPendingGroupBind === 'function') {
|
|
1278
|
+
keystore.clearPendingGroupBind(pendingKey);
|
|
1247
1279
|
keystore.clearPendingGroupBind(groupId);
|
|
1248
1280
|
}
|
|
1249
1281
|
return result;
|
|
@@ -1333,7 +1365,21 @@ export class AUNClient {
|
|
|
1333
1365
|
throw new ValidationError(`renewGroupAid: cannot determine old public key for ${groupAid}`);
|
|
1334
1366
|
}
|
|
1335
1367
|
// 生成新密钥
|
|
1336
|
-
const
|
|
1368
|
+
const keystore = store._keystore;
|
|
1369
|
+
const pendingKey = `renew:${groupId}`;
|
|
1370
|
+
let newKeyPair = null;
|
|
1371
|
+
if (keystore && typeof keystore.loadPendingGroupBind === 'function') {
|
|
1372
|
+
newKeyPair = keystore.loadPendingGroupBind(pendingKey);
|
|
1373
|
+
}
|
|
1374
|
+
if (!newKeyPair || !newKeyPair.public_key_der_b64 || !newKeyPair.private_key_pem) {
|
|
1375
|
+
newKeyPair = new CryptoProvider().generateIdentity();
|
|
1376
|
+
if (keystore && typeof keystore.savePendingGroupBind === 'function') {
|
|
1377
|
+
keystore.savePendingGroupBind(pendingKey, newKeyPair);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
if (!newKeyPair || !newKeyPair.public_key_der_b64 || !newKeyPair.private_key_pem) {
|
|
1381
|
+
throw new ValidationError('renewGroupAid: failed to generate or load key pair');
|
|
1382
|
+
}
|
|
1337
1383
|
const newPublicKey = newKeyPair.public_key_der_b64;
|
|
1338
1384
|
const newPrivateKey = newKeyPair.private_key_pem;
|
|
1339
1385
|
const curve = newKeyPair.curve || 'P-256';
|
|
@@ -1389,6 +1435,9 @@ export class AUNClient {
|
|
|
1389
1435
|
throw new ValidationError(`renewGroupAid failed to persist group identity: ${imported.error.message}`);
|
|
1390
1436
|
}
|
|
1391
1437
|
await this._uploadGroupAgentMd(store, returnedGroupAid, payload, group);
|
|
1438
|
+
if (keystore && typeof keystore.clearPendingGroupBind === 'function') {
|
|
1439
|
+
keystore.clearPendingGroupBind(pendingKey);
|
|
1440
|
+
}
|
|
1392
1441
|
return result;
|
|
1393
1442
|
}
|
|
1394
1443
|
async completeGroupTransfer(params = {}, options = {}) {
|
|
@@ -1399,12 +1448,26 @@ export class AUNClient {
|
|
|
1399
1448
|
if (!store) {
|
|
1400
1449
|
throw new ValidationError('completeGroupTransfer requires aidStore');
|
|
1401
1450
|
}
|
|
1402
|
-
const keyPair = new CryptoProvider().generateIdentity();
|
|
1403
1451
|
const payload = { ...params };
|
|
1404
1452
|
const groupId = String(params.group_id ?? '').trim();
|
|
1405
1453
|
if (!groupId) {
|
|
1406
1454
|
throw new ValidationError('completeGroupTransfer requires group_id');
|
|
1407
1455
|
}
|
|
1456
|
+
const pendingKey = `complete:${groupId}`;
|
|
1457
|
+
const keystore = store._keystore;
|
|
1458
|
+
let keyPair = null;
|
|
1459
|
+
if (keystore && typeof keystore.loadPendingGroupBind === 'function') {
|
|
1460
|
+
keyPair = keystore.loadPendingGroupBind(pendingKey);
|
|
1461
|
+
}
|
|
1462
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
1463
|
+
keyPair = new CryptoProvider().generateIdentity();
|
|
1464
|
+
if (keystore && typeof keystore.savePendingGroupBind === 'function') {
|
|
1465
|
+
keystore.savePendingGroupBind(pendingKey, keyPair);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
1469
|
+
throw new ValidationError('completeGroupTransfer: failed to generate or load key pair');
|
|
1470
|
+
}
|
|
1408
1471
|
let groupAid = String(params.group_aid ?? '').trim();
|
|
1409
1472
|
if (!groupAid) {
|
|
1410
1473
|
const info = await this.call('group.get_info', { group_id: groupId, required: ['member'] });
|
|
@@ -1461,6 +1524,9 @@ export class AUNClient {
|
|
|
1461
1524
|
throw new ValidationError(imported.error.message);
|
|
1462
1525
|
}
|
|
1463
1526
|
await this._uploadGroupAgentMd(store, returnedGroupAid, payload, group);
|
|
1527
|
+
if (keystore && typeof keystore.clearPendingGroupBind === 'function') {
|
|
1528
|
+
keystore.clearPendingGroupBind(pendingKey);
|
|
1529
|
+
}
|
|
1464
1530
|
return result;
|
|
1465
1531
|
}
|
|
1466
1532
|
/** 证书公钥校验:确保服务端返回的证书公钥 == 本地公钥(DER base64) */
|
|
@@ -1877,11 +1943,11 @@ export class AUNClient {
|
|
|
1877
1943
|
}
|
|
1878
1944
|
return true;
|
|
1879
1945
|
}
|
|
1880
|
-
async _drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true) {
|
|
1881
|
-
return this._delivery.drainOrderedMessages(ns, beforeSeq, pullResponse, persist);
|
|
1946
|
+
async _drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, source = 'push') {
|
|
1947
|
+
return this._delivery.drainOrderedMessages(ns, beforeSeq, pullResponse, persist, source);
|
|
1882
1948
|
}
|
|
1883
|
-
async _publishOrderedMessage(event, ns, seq, payload) {
|
|
1884
|
-
return this._delivery.publishOrderedMessage(event, ns, seq, payload);
|
|
1949
|
+
async _publishOrderedMessage(event, ns, seq, payload, source = 'push') {
|
|
1950
|
+
return this._delivery.publishOrderedMessage(event, ns, seq, payload, source);
|
|
1885
1951
|
}
|
|
1886
1952
|
async _publishPulledMessage(event, ns, seq, payload, persist = true) {
|
|
1887
1953
|
return this._delivery.publishPulledMessage(event, ns, seq, payload, persist);
|
|
@@ -1926,7 +1992,7 @@ export class AUNClient {
|
|
|
1926
1992
|
}
|
|
1927
1993
|
else {
|
|
1928
1994
|
// data 非对象也透传给用户(兼容旧版)
|
|
1929
|
-
|
|
1995
|
+
this._dispatcher.enqueue('group.changed', data);
|
|
1930
1996
|
}
|
|
1931
1997
|
this._clientLog.debug(`_onRawGroupChanged exit: elapsed=${Date.now() - tStart}ms`);
|
|
1932
1998
|
}
|
|
@@ -1990,7 +2056,7 @@ export class AUNClient {
|
|
|
1990
2056
|
certPem = await this._fetchPeerCert(sigAid, expectedFP || undefined);
|
|
1991
2057
|
}
|
|
1992
2058
|
catch {
|
|
1993
|
-
this._dispatcher.
|
|
2059
|
+
this._dispatcher.enqueue('signature_pending', { aid: sigAid, method });
|
|
1994
2060
|
return false;
|
|
1995
2061
|
}
|
|
1996
2062
|
}
|
|
@@ -2010,19 +2076,19 @@ export class AUNClient {
|
|
|
2010
2076
|
if (!ok) {
|
|
2011
2077
|
this._clientLog.warn(`group event signature verification failed aid=${sigAid} method=${method}`);
|
|
2012
2078
|
// P1-16: 签名失败统一发布事件
|
|
2013
|
-
this._dispatcher.
|
|
2079
|
+
this._dispatcher.enqueue('signature.verification_failed', {
|
|
2014
2080
|
aid: sigAid, method, error: 'ECDSA verification failed',
|
|
2015
|
-
})
|
|
2081
|
+
});
|
|
2016
2082
|
}
|
|
2017
2083
|
return ok;
|
|
2018
2084
|
}
|
|
2019
2085
|
catch (exc) {
|
|
2020
2086
|
this._clientLog.warn(`group event signature verification error: ${formatCaughtError(exc)}`);
|
|
2021
2087
|
// P1-16: 签名失败统一发布事件
|
|
2022
|
-
this._dispatcher.
|
|
2088
|
+
this._dispatcher.enqueue('signature.verification_failed', {
|
|
2023
2089
|
aid: String(cs.aid ?? ''), method: String(cs._method ?? ''),
|
|
2024
2090
|
error: formatCaughtError(exc),
|
|
2025
|
-
})
|
|
2091
|
+
});
|
|
2026
2092
|
return false;
|
|
2027
2093
|
}
|
|
2028
2094
|
}
|
|
@@ -2419,7 +2485,7 @@ export class AUNClient {
|
|
|
2419
2485
|
}
|
|
2420
2486
|
}
|
|
2421
2487
|
else {
|
|
2422
|
-
|
|
2488
|
+
this._dispatcher.enqueue('state_change', statePayload);
|
|
2423
2489
|
}
|
|
2424
2490
|
this._assertReconnectOwner(reconnectOwner);
|
|
2425
2491
|
this._lifecycle.assertConnectionAttemptOwner(connectionOwner);
|
|
@@ -2761,7 +2827,7 @@ export class AUNClient {
|
|
|
2761
2827
|
this._attachV2EnvelopeMetadata(event, e2eeMeta);
|
|
2762
2828
|
this._logMessageDebug('decrypt-fail', 'v2.decrypt', undecryptableEvent, event);
|
|
2763
2829
|
if (emitUndecryptable)
|
|
2764
|
-
|
|
2830
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
2765
2831
|
return null;
|
|
2766
2832
|
}
|
|
2767
2833
|
this._clientLog.debug(`V2 decrypt key lookup ok: seq=${String(msg.seq ?? '')}, group=${groupIdForKeys || '<p2p>'}, ik_len=${ikPriv.byteLength}, spk_len=${spkPriv?.byteLength ?? 0}`);
|
|
@@ -2800,7 +2866,7 @@ export class AUNClient {
|
|
|
2800
2866
|
this._attachV2EnvelopeMetadata(event, e2eeMeta);
|
|
2801
2867
|
this._logMessageDebug('decrypt-fail', 'v2.decrypt', undecryptableEvent, event);
|
|
2802
2868
|
if (emitUndecryptable)
|
|
2803
|
-
|
|
2869
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
2804
2870
|
return null;
|
|
2805
2871
|
}
|
|
2806
2872
|
let plaintext;
|
|
@@ -2829,7 +2895,7 @@ export class AUNClient {
|
|
|
2829
2895
|
this._attachV2EnvelopeMetadata(event, e2eeMeta);
|
|
2830
2896
|
this._logMessageDebug('decrypt-fail', 'v2.decrypt', undecryptableEvent, event);
|
|
2831
2897
|
if (emitUndecryptable)
|
|
2832
|
-
|
|
2898
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
2833
2899
|
return null;
|
|
2834
2900
|
}
|
|
2835
2901
|
if (plaintext === null) {
|
|
@@ -3065,6 +3131,11 @@ export class AUNClient {
|
|
|
3065
3131
|
if (!resolvedAid) {
|
|
3066
3132
|
throw new StateError('gateway discovery requires a loaded AID');
|
|
3067
3133
|
}
|
|
3134
|
+
const discovery = this._discovery;
|
|
3135
|
+
const tokenStore = this._tokenStore;
|
|
3136
|
+
const discoveryIsCurrent = () => (this._discovery === discovery
|
|
3137
|
+
&& this._tokenStore === tokenStore
|
|
3138
|
+
&& !this._closing);
|
|
3068
3139
|
if (this._gatewayCandidates.length > 0)
|
|
3069
3140
|
return [...this._gatewayCandidates];
|
|
3070
3141
|
if (this._gatewayUrl) {
|
|
@@ -3072,9 +3143,9 @@ export class AUNClient {
|
|
|
3072
3143
|
return [...this._gatewayCandidates];
|
|
3073
3144
|
}
|
|
3074
3145
|
try {
|
|
3075
|
-
const loadMetadata =
|
|
3146
|
+
const loadMetadata = tokenStore.loadMetadata;
|
|
3076
3147
|
const metadata = typeof loadMetadata === 'function'
|
|
3077
|
-
? loadMetadata.call(
|
|
3148
|
+
? loadMetadata.call(tokenStore, resolvedAid)
|
|
3078
3149
|
: null;
|
|
3079
3150
|
const cachedList = isJsonObject(metadata) && Array.isArray(metadata.gateway_urls)
|
|
3080
3151
|
? metadata.gateway_urls.map((item) => String(item ?? '').trim()).filter(Boolean)
|
|
@@ -3101,12 +3172,13 @@ export class AUNClient {
|
|
|
3101
3172
|
try {
|
|
3102
3173
|
// discoverAll 是生产路径;若调用方注入了旧版/测试 discovery
|
|
3103
3174
|
// 替身而只提供 discover,则保留单候选兼容,不影响多候选发现。
|
|
3104
|
-
const discovery = this._discovery;
|
|
3105
3175
|
const hasInjectedDiscover = Object.prototype.hasOwnProperty.call(discovery, 'discover')
|
|
3106
3176
|
&& typeof discovery.discover === 'function';
|
|
3107
3177
|
const discovered = hasInjectedDiscover
|
|
3108
3178
|
? [await discovery.discover(url)]
|
|
3109
3179
|
: await discovery.discoverAll(url);
|
|
3180
|
+
if (!discoveryIsCurrent())
|
|
3181
|
+
throw new StateError('gateway discovery superseded by identity change');
|
|
3110
3182
|
const gatewayUrls = [...new Set(discovered.map((item) => String(item ?? '').trim()).filter(Boolean))];
|
|
3111
3183
|
if (gatewayUrls.length === 0)
|
|
3112
3184
|
throw new ValidationError('gateway discovery returned no candidates');
|
|
@@ -3114,9 +3186,9 @@ export class AUNClient {
|
|
|
3114
3186
|
const gateway = gatewayUrls[0];
|
|
3115
3187
|
this._gatewayUrl = gateway;
|
|
3116
3188
|
try {
|
|
3117
|
-
const saveMetadata =
|
|
3189
|
+
const saveMetadata = tokenStore.saveMetadata;
|
|
3118
3190
|
if (typeof saveMetadata === 'function') {
|
|
3119
|
-
saveMetadata.call(
|
|
3191
|
+
saveMetadata.call(tokenStore, resolvedAid, {
|
|
3120
3192
|
gateway_url: gateway,
|
|
3121
3193
|
gateway_urls: gatewayUrls,
|
|
3122
3194
|
gateway_cached_at: Date.now(),
|
|
@@ -3129,6 +3201,8 @@ export class AUNClient {
|
|
|
3129
3201
|
return [...gatewayUrls];
|
|
3130
3202
|
}
|
|
3131
3203
|
catch (err) {
|
|
3204
|
+
if (!discoveryIsCurrent())
|
|
3205
|
+
throw new StateError('gateway discovery superseded by identity change');
|
|
3132
3206
|
lastErr = err;
|
|
3133
3207
|
this._clientLog.warn(`gateway discovery failed: aid=${resolvedAid} url=${url} err=${formatCaughtError(err)}`);
|
|
3134
3208
|
}
|
|
@@ -3363,7 +3437,7 @@ export class AUNClient {
|
|
|
3363
3437
|
if (this._sessionParams !== null && identity.access_token) {
|
|
3364
3438
|
this._sessionParams.access_token = identity.access_token;
|
|
3365
3439
|
}
|
|
3366
|
-
|
|
3440
|
+
this._dispatcher.enqueue('token.refreshed', {
|
|
3367
3441
|
aid: identity.aid,
|
|
3368
3442
|
expires_at: identity.access_token_expires_at,
|
|
3369
3443
|
});
|
|
@@ -3373,7 +3447,7 @@ export class AUNClient {
|
|
|
3373
3447
|
if (exc instanceof AuthError) {
|
|
3374
3448
|
if (authErrorRequiresRelogin(exc)) {
|
|
3375
3449
|
this._clientLog.warn(`token refresh requires relogin, stopping refresh loop and triggering reconnect: ${exc.message}`);
|
|
3376
|
-
|
|
3450
|
+
this._dispatcher.enqueue('token.refresh_exhausted', {
|
|
3377
3451
|
aid: this._identity?.aid ?? null,
|
|
3378
3452
|
consecutive_failures: 1,
|
|
3379
3453
|
last_error: String(exc),
|
|
@@ -3386,7 +3460,7 @@ export class AUNClient {
|
|
|
3386
3460
|
this._tokenRefreshFailures++;
|
|
3387
3461
|
if (this._tokenRefreshFailures >= 3) {
|
|
3388
3462
|
this._clientLog.warn(`token refresh failed ${this._tokenRefreshFailures} consecutive times, stopping refresh loop and triggering reconnect`);
|
|
3389
|
-
|
|
3463
|
+
this._dispatcher.enqueue('token.refresh_exhausted', {
|
|
3390
3464
|
aid: this._identity?.aid ?? null,
|
|
3391
3465
|
consecutive_failures: this._tokenRefreshFailures,
|
|
3392
3466
|
last_error: String(exc),
|
|
@@ -3398,7 +3472,7 @@ export class AUNClient {
|
|
|
3398
3472
|
this._clientLog.debug(`token refresh failed (${this._tokenRefreshFailures}/3), will retry: ${exc}`);
|
|
3399
3473
|
}
|
|
3400
3474
|
else {
|
|
3401
|
-
|
|
3475
|
+
this._dispatcher.enqueue('connection.error', { error: formatCaughtError(exc) });
|
|
3402
3476
|
}
|
|
3403
3477
|
}
|
|
3404
3478
|
scheduleNext();
|
|
@@ -3469,7 +3543,7 @@ export class AUNClient {
|
|
|
3469
3543
|
this._lastDisconnectInfo = { code, reason, detail };
|
|
3470
3544
|
// 透传给应用层订阅者
|
|
3471
3545
|
try {
|
|
3472
|
-
|
|
3546
|
+
this._dispatcher.enqueue('gateway.disconnect', {
|
|
3473
3547
|
code,
|
|
3474
3548
|
reason,
|
|
3475
3549
|
detail,
|
|
@@ -3595,13 +3669,7 @@ export class AUNClient {
|
|
|
3595
3669
|
async _publishReconnectEvent(owner, event, payload) {
|
|
3596
3670
|
if (!this._ownsReconnect(owner))
|
|
3597
3671
|
return false;
|
|
3598
|
-
this.
|
|
3599
|
-
try {
|
|
3600
|
-
await this._dispatcher.publish(event, payload);
|
|
3601
|
-
}
|
|
3602
|
-
finally {
|
|
3603
|
-
this._reconnectEventDispatchDepth -= 1;
|
|
3604
|
-
}
|
|
3672
|
+
this._dispatcher.enqueue(event, payload);
|
|
3605
3673
|
return this._ownsReconnect(owner);
|
|
3606
3674
|
}
|
|
3607
3675
|
async _cancelReconnectAndWait() {
|
|
@@ -3610,6 +3678,8 @@ export class AUNClient {
|
|
|
3610
3678
|
if (!owner && !task && !this._reconnectActive)
|
|
3611
3679
|
return;
|
|
3612
3680
|
owner?.abort();
|
|
3681
|
+
this._discovery?._cancelPending?.();
|
|
3682
|
+
this._auth?._cancelPending?.();
|
|
3613
3683
|
this._stopBackgroundTasks();
|
|
3614
3684
|
try {
|
|
3615
3685
|
await this._transport.close();
|