@agentunion/fastaun 0.5.36 → 0.5.37
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 +21 -7
- package/_packed_docs/CHANGELOG.md +21 -7
- package/_packed_docs/sdk/0.5.36-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +2 -2
- package/_packed_docs/sdk/0.5.36-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +2 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +6 -6
- package/dist/agent-md.js +1 -0
- package/dist/agent-md.js.map +1 -1
- package/dist/client/control-messages.js +4 -2
- package/dist/client/control-messages.js.map +1 -1
- package/dist/client/delivery.d.ts +1 -0
- package/dist/client/delivery.js +45 -6
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/lifecycle.js +1 -1
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +1 -0
- package/dist/client/rpc-pipeline.js +17 -0
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.js +11 -7
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.js +71 -44
- package/dist/client.js.map +1 -1
- package/dist/pubsub/binding.d.ts +2 -6
- package/dist/pubsub/binding.js +58 -61
- package/dist/pubsub/binding.js.map +1 -1
- package/dist/secret-store/file-store.js +8 -1
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +1 -1
- package/dist/udp-standby.js +3 -2
- package/dist/udp-standby.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -231,9 +231,32 @@ function clampReconnectDelayMs(value, fallback, upper = RECONNECT_MAX_BASE_DELAY
|
|
|
231
231
|
return Math.min(Math.max(ms, RECONNECT_MIN_BASE_DELAY_MS), upper);
|
|
232
232
|
}
|
|
233
233
|
function reconnectSleepDelayMs(baseDelay, maxBaseDelay) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
return Math.max(1, Math.random() * Math.min(baseDelay, maxBaseDelay));
|
|
235
|
+
}
|
|
236
|
+
const reconnectSlots = new Map();
|
|
237
|
+
async function acquireReconnectSlot(aid, wait, pause) {
|
|
238
|
+
const issuer = aid.slice(aid.indexOf('.') + 1).toLowerCase();
|
|
239
|
+
let slots = reconnectSlots.get(issuer);
|
|
240
|
+
if (!slots) {
|
|
241
|
+
slots = { active: new Set(), next: 0 };
|
|
242
|
+
reconnectSlots.set(issuer, slots);
|
|
243
|
+
}
|
|
244
|
+
while (slots.active.size >= 2)
|
|
245
|
+
await wait(Promise.race(slots.active));
|
|
246
|
+
let done;
|
|
247
|
+
const ticket = new Promise(resolve => { done = resolve; });
|
|
248
|
+
slots.active.add(ticket);
|
|
249
|
+
const release = () => { slots.active.delete(ticket); done(); };
|
|
250
|
+
const delay = Math.min(500, Math.max(0, slots.next - Date.now()));
|
|
251
|
+
slots.next = Date.now() + delay + 250 + Math.random() * 250;
|
|
252
|
+
try {
|
|
253
|
+
await pause(delay);
|
|
254
|
+
return release;
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
release();
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
237
260
|
}
|
|
238
261
|
/** peer 证书缓存 TTL(1 小时) */
|
|
239
262
|
const PEER_CERT_CACHE_TTL = 3600;
|
|
@@ -743,7 +766,7 @@ export class AUNClient {
|
|
|
743
766
|
if (current())
|
|
744
767
|
this._credentialRenewalTimer = setTimeout(() => { void scan(); }, 60_000);
|
|
745
768
|
};
|
|
746
|
-
void scan();
|
|
769
|
+
this._credentialRenewalTimer = setTimeout(() => { void scan(); }, 30_000 + Math.random() * 30_000);
|
|
747
770
|
}
|
|
748
771
|
_stopCredentialRenewal() {
|
|
749
772
|
this._credentialRenewalAbort?.abort();
|
|
@@ -2475,28 +2498,43 @@ export class AUNClient {
|
|
|
2475
2498
|
tokens.saveMetadata(orgAid, { org_identity_creation: null });
|
|
2476
2499
|
return checkpoint.result;
|
|
2477
2500
|
}
|
|
2478
|
-
_schedulePostConnectRecovery() {
|
|
2501
|
+
_schedulePostConnectRecovery(backgroundSync = this._sessionOptions.background_sync !== false) {
|
|
2479
2502
|
this._postConnectRecoveryAbort?.abort();
|
|
2480
2503
|
if (this._postConnectRecoveryTimer !== null)
|
|
2481
2504
|
clearTimeout(this._postConnectRecoveryTimer);
|
|
2482
2505
|
this._postConnectRecoveryTimer = null;
|
|
2483
2506
|
const store = this._aidStore;
|
|
2484
|
-
if (this._sessionOptions.background_sync === false)
|
|
2485
|
-
return;
|
|
2486
2507
|
const abort = new AbortController();
|
|
2487
2508
|
this._postConnectRecoveryAbort = abort;
|
|
2488
2509
|
const aid = this._aid, version = this._serviceEventIdentityVersion, connection = this._entityOwnershipConnection;
|
|
2489
2510
|
const current = () => !abort.signal.aborted && aid === this._aid && version === this._serviceEventIdentityVersion && connection === this._entityOwnershipConnection && ['ready', 'connected'].includes(this._state) && !this._closing && !this._disconnecting;
|
|
2511
|
+
const tasks = [];
|
|
2512
|
+
if (!this._ownershipOperation)
|
|
2513
|
+
tasks.push(() => this._checkIdentityAdmissionAfterConnect());
|
|
2514
|
+
tasks.push(() => this._reconcileReadStates());
|
|
2515
|
+
if (backgroundSync) {
|
|
2516
|
+
tasks.push(() => this._recoverGroupInvites());
|
|
2517
|
+
if (this._v2Session && this._sessionOptions.connection_kind !== 'short')
|
|
2518
|
+
tasks.push(() => this._v2AutoConfirmPendingProposals());
|
|
2519
|
+
tasks.push(() => this._delivery.restoreMessageClassPulls(), () => this._fillP2pGap());
|
|
2520
|
+
if (store && !this._ownershipOperation)
|
|
2521
|
+
tasks.push(() => this._resumeOrgIdentityAfterConnect(store, current));
|
|
2522
|
+
}
|
|
2523
|
+
const run = async () => {
|
|
2524
|
+
for (let index = 0; index < tasks.length; index++) {
|
|
2525
|
+
if (!current())
|
|
2526
|
+
return;
|
|
2527
|
+
const pending = tasks[index]();
|
|
2528
|
+
this._safeAsync(pending);
|
|
2529
|
+
await pending.catch(() => { });
|
|
2530
|
+
if (index + 1 < tasks.length)
|
|
2531
|
+
await this._sleep(1000 + Math.random() * 4000, abort.signal);
|
|
2532
|
+
}
|
|
2533
|
+
};
|
|
2490
2534
|
this._postConnectRecoveryTimer = setTimeout(() => {
|
|
2491
2535
|
this._postConnectRecoveryTimer = null;
|
|
2492
|
-
if (
|
|
2493
|
-
|
|
2494
|
-
this._safeAsync(this._delivery.restoreMessageClassPulls());
|
|
2495
|
-
void this._fillP2pGap().catch((exc) => {
|
|
2496
|
-
this._clientLog.warn(`schedule post-connect P2P gap fill failed: ${formatCaughtError(exc)}`);
|
|
2497
|
-
});
|
|
2498
|
-
if (store && !this._ownershipOperation)
|
|
2499
|
-
this._safeAsync(this._resumeOrgIdentityAfterConnect(store, current));
|
|
2536
|
+
if (current())
|
|
2537
|
+
this._safeAsync(run());
|
|
2500
2538
|
}, 30_000 + Math.random() * 30_000);
|
|
2501
2539
|
this._postConnectRecoveryTimer.unref?.();
|
|
2502
2540
|
}
|
|
@@ -4990,18 +5028,11 @@ export class AUNClient {
|
|
|
4990
5028
|
}
|
|
4991
5029
|
this._lifecycle.assertConnectionAttemptOwner(connectionOwner);
|
|
4992
5030
|
this._startBackgroundTasks();
|
|
4993
|
-
this._safeAsync(this._reconcileReadStates());
|
|
4994
5031
|
const connectionKind = String(params.connection_kind ?? 'long');
|
|
4995
5032
|
const isShortConnection = connectionKind === 'short';
|
|
4996
5033
|
const hasExplicitBackgroundSync = Object.prototype.hasOwnProperty.call(params, 'background_sync');
|
|
4997
5034
|
const backgroundSyncEnabled = this._sessionOptions.background_sync !== false
|
|
4998
5035
|
&& (!isShortConnection || hasExplicitBackgroundSync);
|
|
4999
|
-
if (backgroundSyncEnabled) {
|
|
5000
|
-
// 连接建立/恢复后先从事实列表恢复定向邀请提示;失败不影响连接本身。
|
|
5001
|
-
this._safeAsync(this._recoverGroupInvites().catch((exc) => {
|
|
5002
|
-
this._clientLog.debug(`group invite recovery failed: ${formatCaughtError(exc)}`);
|
|
5003
|
-
}));
|
|
5004
|
-
}
|
|
5005
5036
|
if (!isShortConnection) {
|
|
5006
5037
|
await this._v2E2EE.onConnected({ backgroundSync: backgroundSyncEnabled });
|
|
5007
5038
|
this._assertReconnectOwner(reconnectOwner);
|
|
@@ -5016,13 +5047,7 @@ export class AUNClient {
|
|
|
5016
5047
|
// 群消息按惰性触发,不在此处主动 pull
|
|
5017
5048
|
this._entityOwnershipConnection++;
|
|
5018
5049
|
this._entityOwnershipChecks.clear();
|
|
5019
|
-
|
|
5020
|
-
this._safeAsync(this._checkIdentityAdmissionAfterConnect());
|
|
5021
|
-
if (backgroundSyncEnabled)
|
|
5022
|
-
this._schedulePostConnectRecovery();
|
|
5023
|
-
if (!isShortConnection && backgroundSyncEnabled && this._v2Session) {
|
|
5024
|
-
this._safeAsync(this._v2AutoConfirmPendingProposals());
|
|
5025
|
-
}
|
|
5050
|
+
this._schedulePostConnectRecovery(backgroundSyncEnabled);
|
|
5026
5051
|
this._activateUdpStandby();
|
|
5027
5052
|
this._clientLog.debug(`_connectOnce exit: elapsed=${Date.now() - tStart}ms gateway=${gatewayUrl}, aid=${this._aid ?? ''}`);
|
|
5028
5053
|
}
|
|
@@ -6463,8 +6488,17 @@ export class AUNClient {
|
|
|
6463
6488
|
reconnect: async () => {
|
|
6464
6489
|
if (this._closing || this._disconnecting)
|
|
6465
6490
|
throw new ConnectionError('client closed');
|
|
6466
|
-
this.
|
|
6467
|
-
await this.
|
|
6491
|
+
await this._sleep(reconnectSleepDelayMs(1000, RECONNECT_MAX_BASE_DELAY_MS));
|
|
6492
|
+
const release = await acquireReconnectSlot(this._aid ?? '', task => task, ms => this._sleep(ms));
|
|
6493
|
+
try {
|
|
6494
|
+
if (this._closing || this._disconnecting || this._udpStandby?.state === 'offline')
|
|
6495
|
+
throw new ConnectionError('client closed');
|
|
6496
|
+
this._state = 'standby';
|
|
6497
|
+
await this._lifecycle.connect(this._udpStandbyConnectOptions);
|
|
6498
|
+
}
|
|
6499
|
+
finally {
|
|
6500
|
+
release();
|
|
6501
|
+
}
|
|
6468
6502
|
},
|
|
6469
6503
|
});
|
|
6470
6504
|
this._udpStandby.activate(this._udpStandbyPolicy);
|
|
@@ -6592,9 +6626,10 @@ export class AUNClient {
|
|
|
6592
6626
|
next_retry_at: this._nextRetryAt,
|
|
6593
6627
|
}))
|
|
6594
6628
|
return;
|
|
6629
|
+
let releaseReconnectSlot;
|
|
6595
6630
|
try {
|
|
6596
|
-
// 抖动随当前 base 增长且不超过 max_base,避免首次重连被放大到几十秒。
|
|
6597
6631
|
await this._sleep(Math.max(0, this._nextRetryAt - Date.now()), reconnectAbort?.signal);
|
|
6632
|
+
releaseReconnectSlot = await acquireReconnectSlot(this._aid ?? '', task => this._lifecycle.waitForAttempt(task, undefined, reconnectAbort ?? undefined), ms => this._sleep(ms, reconnectAbort?.signal));
|
|
6598
6633
|
if (!this._ownsReconnect(reconnectAbort))
|
|
6599
6634
|
return;
|
|
6600
6635
|
this._state = 'reconnecting';
|
|
@@ -6603,17 +6638,6 @@ export class AUNClient {
|
|
|
6603
6638
|
attempt,
|
|
6604
6639
|
}))
|
|
6605
6640
|
return;
|
|
6606
|
-
// /health 仅作旁路诊断,不能阻止实际 WebSocket 重连。
|
|
6607
|
-
if (this._gatewayUrl) {
|
|
6608
|
-
void this._discovery.checkHealth(this._gatewayUrl, 5_000)
|
|
6609
|
-
.then((healthy) => {
|
|
6610
|
-
if (!healthy)
|
|
6611
|
-
this._clientLog.warn(`reconnect health advisory failed: gateway=${this._gatewayUrl}`);
|
|
6612
|
-
})
|
|
6613
|
-
.catch((exc) => {
|
|
6614
|
-
this._clientLog.debug(`reconnect health advisory error: ${formatCaughtError(exc)}`);
|
|
6615
|
-
});
|
|
6616
|
-
}
|
|
6617
6641
|
await this._transport.close();
|
|
6618
6642
|
if (!this._ownsReconnect(reconnectAbort))
|
|
6619
6643
|
return;
|
|
@@ -6650,7 +6674,7 @@ export class AUNClient {
|
|
|
6650
6674
|
this._sessionParams.access_token = '';
|
|
6651
6675
|
}
|
|
6652
6676
|
}
|
|
6653
|
-
const candidates = await this._lifecycle.waitForAttempt(this._resolveGatewayCandidatesForAid(this._aid,
|
|
6677
|
+
const candidates = await this._lifecycle.waitForAttempt(this._resolveGatewayCandidatesForAid(this._aid, this._migrationPending || this._migrationRevision.size > 0 || attempt % 3 === 2, reconnectAbort ?? undefined), undefined, reconnectAbort ?? undefined);
|
|
6654
6678
|
if (!this._ownsReconnect(reconnectAbort))
|
|
6655
6679
|
return;
|
|
6656
6680
|
this._sessionParams.gateways = [...candidates];
|
|
@@ -6710,6 +6734,9 @@ export class AUNClient {
|
|
|
6710
6734
|
}
|
|
6711
6735
|
delay = Math.min(delay * 2, maxBaseDelay);
|
|
6712
6736
|
}
|
|
6737
|
+
finally {
|
|
6738
|
+
releaseReconnectSlot?.();
|
|
6739
|
+
}
|
|
6713
6740
|
}
|
|
6714
6741
|
}
|
|
6715
6742
|
/** 每轮重连从当前地址之后开始轮转,避免永远黏在失效的首个 Gateway。 */
|