@agentunion/fastaun 0.5.35 → 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 +40 -0
- package/_packed_docs/CHANGELOG.md +40 -0
- package/_packed_docs/INDEX.md +10 -3
- 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 +130 -0
- 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 +176 -0
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +6 -6
- package/_packed_docs/sdk/INDEX.md +15 -10
- package/dist/agent-md.d.ts +2 -1
- package/dist/agent-md.js +34 -6
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.js +5 -1
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.js +5 -2
- package/dist/auth.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 +2 -0
- package/dist/client/delivery.js +111 -40
- 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 +3 -0
- package/dist/client/rpc-pipeline.js +45 -3
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.js +16 -8
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.js +90 -51
- package/dist/client.js.map +1 -1
- package/dist/ownership-v21.d.ts +2 -1
- package/dist/ownership-v21.js +19 -9
- package/dist/ownership-v21.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/pubsub/channel.js +3 -3
- package/dist/pubsub/channel.js.map +1 -1
- package/dist/pubsub/client.js +3 -4
- package/dist/pubsub/client.js.map +1 -1
- package/dist/pubsub/service.js +1 -1
- package/dist/pubsub/service.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.d.ts +0 -1
- package/dist/udp-standby.js +10 -13
- 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();
|
|
@@ -1415,25 +1438,30 @@ export class AUNClient {
|
|
|
1415
1438
|
const observation = { root: manager.root, etag: observedEtag, version: Number(record?.observation_version ?? 0), content_hash: typeof record?.content === 'string' ? ownershipSha256(record.content) : '' };
|
|
1416
1439
|
const cache = this._ownershipConsensusCache ??= new Map();
|
|
1417
1440
|
const cached = cache.get(target);
|
|
1418
|
-
if (
|
|
1441
|
+
if (cached?.result.status === 'consensus' && cached.observedVersion === observation.version && record?.remote_status !== 'missing'
|
|
1442
|
+
&& (!record?.remote_owner_md_sha256 || record.remote_owner_md_sha256 === cached.result.owner_md_sha256)
|
|
1419
1443
|
&& (observedEtag === cached.observedEtag || observedEtag === cached.result.agent_md_etag)
|
|
1420
1444
|
&& (typeof record?.content !== 'string' || ownershipSha256(record.content) === cached.result.agent_md_sha256)) {
|
|
1421
1445
|
cached.observedEtag = observedEtag;
|
|
1422
1446
|
return { ...cached.result };
|
|
1423
1447
|
}
|
|
1424
|
-
if (!
|
|
1448
|
+
if (!cached) {
|
|
1425
1449
|
const local = manager.localOwnershipConsensus(target, record);
|
|
1426
1450
|
if (local) {
|
|
1427
|
-
cache.set(target, { observedEtag, result: local });
|
|
1451
|
+
cache.set(target, { observedEtag, observedVersion: observation.version, result: local });
|
|
1428
1452
|
return { ...local };
|
|
1429
1453
|
}
|
|
1430
1454
|
}
|
|
1431
1455
|
cache.delete(target);
|
|
1432
1456
|
const result = { status: 'unavailable', sub_aid: target, owner_aid: null, relation_version: 0, lock: 'unlocked', agent_md_etag: '', agent_md_sha256: '' };
|
|
1433
|
-
const entry = { observedEtag, result: { ...result } };
|
|
1457
|
+
const entry = { observedEtag, observedVersion: observation.version, result: { ...result } };
|
|
1434
1458
|
cache.set(target, entry);
|
|
1459
|
+
let consensusContent;
|
|
1435
1460
|
try {
|
|
1436
1461
|
const snapshot = await manager.readOwnershipFresh(target);
|
|
1462
|
+
consensusContent = snapshot.content;
|
|
1463
|
+
if (snapshot.owner_md_sha256)
|
|
1464
|
+
result.owner_md_sha256 = snapshot.owner_md_sha256;
|
|
1437
1465
|
Object.assign(result, { relation_version: snapshot.relation_version, lock: snapshot.lock, agent_md_etag: snapshot.etag,
|
|
1438
1466
|
agent_md_sha256: snapshot.exists ? ownershipSha256(snapshot.content) : '' });
|
|
1439
1467
|
if (snapshot.ownership_status)
|
|
@@ -1454,11 +1482,15 @@ export class AUNClient {
|
|
|
1454
1482
|
result.retryable = result.status === 'unavailable';
|
|
1455
1483
|
}
|
|
1456
1484
|
if (this._agentMdManager === manager && this._ownershipConsensusCache === cache && cache.get(target) === entry) {
|
|
1457
|
-
entry.result = { ...result };
|
|
1458
1485
|
try {
|
|
1459
|
-
manager.cacheOwnershipSnapshot(target, result, observation)
|
|
1486
|
+
if (manager.cacheOwnershipSnapshot(target, result, observation, consensusContent))
|
|
1487
|
+
entry.result = { ...result };
|
|
1488
|
+
else
|
|
1489
|
+
cache.delete(target);
|
|
1490
|
+
}
|
|
1491
|
+
catch {
|
|
1492
|
+
cache.delete(target);
|
|
1460
1493
|
}
|
|
1461
|
-
catch { }
|
|
1462
1494
|
}
|
|
1463
1495
|
return result;
|
|
1464
1496
|
}
|
|
@@ -2118,6 +2150,7 @@ export class AUNClient {
|
|
|
2118
2150
|
throw ownershipFailure('conflict', 'entity lifecycle ownership action changed');
|
|
2119
2151
|
const ownership = { statement: declaration.statement, action, ...(transfer ? { transfer: { new_owner_aid: ownershipAid(newOwner, 'new_owner_aid'), target_relation_version: declaration.statement.relation_version + 1, expires_at: Number(params.expires_at ?? Math.floor(Date.now() / 1000) + 3600) } } : {}) };
|
|
2120
2152
|
const raw = signOwnershipFile(owner, `---\nowner_aid: ${owner.aid}\nsub_aid: ${target}\nownership: ${JSON.stringify(ownership)}\n---\n`, 'owner');
|
|
2153
|
+
await this._getOwnershipFiles().verifyFile(raw, 'owner', target, owner.aid, true);
|
|
2121
2154
|
await storage.writeOwnerMd(owner.aid, target, raw, original, assertCurrent);
|
|
2122
2155
|
return async (error) => {
|
|
2123
2156
|
if (!(error instanceof AUNError) || error instanceof TimeoutError || error instanceof ConnectionError || error.retryable || isJsonObject(error.data) && error.data.committed === true || ![-32602, 4000, 4030, 403].includes(error.code) && !['INVALID_ARGUMENT', 'ROLE_ACCESS_DENIED', 'MEMBER_NOT_FOUND', 'permission_denied', 'member_banned', 'member_blocked'].includes(error.stringCode))
|
|
@@ -2263,12 +2296,14 @@ export class AUNClient {
|
|
|
2263
2296
|
else {
|
|
2264
2297
|
const payload = `---\nowner_aid: ${owner.aid}\nsub_aid: ${aid}\nownership: ${JSON.stringify({ statement, action: 'declare' })}\n---\n`;
|
|
2265
2298
|
const raw = signOwnershipFile(owner, payload, 'owner');
|
|
2299
|
+
await this._getOwnershipFiles().verifyFile(raw, 'owner', aid, owner.aid, true);
|
|
2266
2300
|
assertCurrent();
|
|
2267
2301
|
await storage.writeOwnerMd(owner.aid, aid, raw, original, assertCurrent);
|
|
2268
2302
|
}
|
|
2269
2303
|
const source = content ?? (observed.exists ? observed.content : buildDefaultAgentMd(aid, { type, name: String(entity.name ?? aid) }));
|
|
2270
2304
|
const updated = updateOwnershipAgentMd(source, { owner: owner.aid, ownership: { statement } }).replace(/^entity_cert_fingerprint:.*(?:\r?\n|$)/m, '');
|
|
2271
2305
|
const signed = signOwnershipFile(local.data.aid, updated, 'agent');
|
|
2306
|
+
await this._getOwnershipFiles().verifyFile(signed, 'agent', aid, undefined, true);
|
|
2272
2307
|
if (content === undefined) {
|
|
2273
2308
|
if (!previous || previous.owner_aid !== owner.aid || previous.relation_version !== version) {
|
|
2274
2309
|
assertCurrent();
|
|
@@ -2463,28 +2498,43 @@ export class AUNClient {
|
|
|
2463
2498
|
tokens.saveMetadata(orgAid, { org_identity_creation: null });
|
|
2464
2499
|
return checkpoint.result;
|
|
2465
2500
|
}
|
|
2466
|
-
_schedulePostConnectRecovery() {
|
|
2501
|
+
_schedulePostConnectRecovery(backgroundSync = this._sessionOptions.background_sync !== false) {
|
|
2467
2502
|
this._postConnectRecoveryAbort?.abort();
|
|
2468
2503
|
if (this._postConnectRecoveryTimer !== null)
|
|
2469
2504
|
clearTimeout(this._postConnectRecoveryTimer);
|
|
2470
2505
|
this._postConnectRecoveryTimer = null;
|
|
2471
2506
|
const store = this._aidStore;
|
|
2472
|
-
if (this._sessionOptions.background_sync === false)
|
|
2473
|
-
return;
|
|
2474
2507
|
const abort = new AbortController();
|
|
2475
2508
|
this._postConnectRecoveryAbort = abort;
|
|
2476
2509
|
const aid = this._aid, version = this._serviceEventIdentityVersion, connection = this._entityOwnershipConnection;
|
|
2477
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
|
+
};
|
|
2478
2534
|
this._postConnectRecoveryTimer = setTimeout(() => {
|
|
2479
2535
|
this._postConnectRecoveryTimer = null;
|
|
2480
|
-
if (
|
|
2481
|
-
|
|
2482
|
-
this._safeAsync(this._delivery.restoreMessageClassPulls());
|
|
2483
|
-
void this._fillP2pGap().catch((exc) => {
|
|
2484
|
-
this._clientLog.warn(`schedule post-connect P2P gap fill failed: ${formatCaughtError(exc)}`);
|
|
2485
|
-
});
|
|
2486
|
-
if (store && !this._ownershipOperation)
|
|
2487
|
-
this._safeAsync(this._resumeOrgIdentityAfterConnect(store, current));
|
|
2536
|
+
if (current())
|
|
2537
|
+
this._safeAsync(run());
|
|
2488
2538
|
}, 30_000 + Math.random() * 30_000);
|
|
2489
2539
|
this._postConnectRecoveryTimer.unref?.();
|
|
2490
2540
|
}
|
|
@@ -4978,18 +5028,11 @@ export class AUNClient {
|
|
|
4978
5028
|
}
|
|
4979
5029
|
this._lifecycle.assertConnectionAttemptOwner(connectionOwner);
|
|
4980
5030
|
this._startBackgroundTasks();
|
|
4981
|
-
this._safeAsync(this._reconcileReadStates());
|
|
4982
5031
|
const connectionKind = String(params.connection_kind ?? 'long');
|
|
4983
5032
|
const isShortConnection = connectionKind === 'short';
|
|
4984
5033
|
const hasExplicitBackgroundSync = Object.prototype.hasOwnProperty.call(params, 'background_sync');
|
|
4985
5034
|
const backgroundSyncEnabled = this._sessionOptions.background_sync !== false
|
|
4986
5035
|
&& (!isShortConnection || hasExplicitBackgroundSync);
|
|
4987
|
-
if (backgroundSyncEnabled) {
|
|
4988
|
-
// 连接建立/恢复后先从事实列表恢复定向邀请提示;失败不影响连接本身。
|
|
4989
|
-
this._safeAsync(this._recoverGroupInvites().catch((exc) => {
|
|
4990
|
-
this._clientLog.debug(`group invite recovery failed: ${formatCaughtError(exc)}`);
|
|
4991
|
-
}));
|
|
4992
|
-
}
|
|
4993
5036
|
if (!isShortConnection) {
|
|
4994
5037
|
await this._v2E2EE.onConnected({ backgroundSync: backgroundSyncEnabled });
|
|
4995
5038
|
this._assertReconnectOwner(reconnectOwner);
|
|
@@ -5004,13 +5047,7 @@ export class AUNClient {
|
|
|
5004
5047
|
// 群消息按惰性触发,不在此处主动 pull
|
|
5005
5048
|
this._entityOwnershipConnection++;
|
|
5006
5049
|
this._entityOwnershipChecks.clear();
|
|
5007
|
-
|
|
5008
|
-
this._safeAsync(this._checkIdentityAdmissionAfterConnect());
|
|
5009
|
-
if (backgroundSyncEnabled)
|
|
5010
|
-
this._schedulePostConnectRecovery();
|
|
5011
|
-
if (!isShortConnection && backgroundSyncEnabled && this._v2Session) {
|
|
5012
|
-
this._safeAsync(this._v2AutoConfirmPendingProposals());
|
|
5013
|
-
}
|
|
5050
|
+
this._schedulePostConnectRecovery(backgroundSyncEnabled);
|
|
5014
5051
|
this._activateUdpStandby();
|
|
5015
5052
|
this._clientLog.debug(`_connectOnce exit: elapsed=${Date.now() - tStart}ms gateway=${gatewayUrl}, aid=${this._aid ?? ''}`);
|
|
5016
5053
|
}
|
|
@@ -6451,8 +6488,17 @@ export class AUNClient {
|
|
|
6451
6488
|
reconnect: async () => {
|
|
6452
6489
|
if (this._closing || this._disconnecting)
|
|
6453
6490
|
throw new ConnectionError('client closed');
|
|
6454
|
-
this.
|
|
6455
|
-
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
|
+
}
|
|
6456
6502
|
},
|
|
6457
6503
|
});
|
|
6458
6504
|
this._udpStandby.activate(this._udpStandbyPolicy);
|
|
@@ -6580,9 +6626,10 @@ export class AUNClient {
|
|
|
6580
6626
|
next_retry_at: this._nextRetryAt,
|
|
6581
6627
|
}))
|
|
6582
6628
|
return;
|
|
6629
|
+
let releaseReconnectSlot;
|
|
6583
6630
|
try {
|
|
6584
|
-
// 抖动随当前 base 增长且不超过 max_base,避免首次重连被放大到几十秒。
|
|
6585
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));
|
|
6586
6633
|
if (!this._ownsReconnect(reconnectAbort))
|
|
6587
6634
|
return;
|
|
6588
6635
|
this._state = 'reconnecting';
|
|
@@ -6591,17 +6638,6 @@ export class AUNClient {
|
|
|
6591
6638
|
attempt,
|
|
6592
6639
|
}))
|
|
6593
6640
|
return;
|
|
6594
|
-
// /health 仅作旁路诊断,不能阻止实际 WebSocket 重连。
|
|
6595
|
-
if (this._gatewayUrl) {
|
|
6596
|
-
void this._discovery.checkHealth(this._gatewayUrl, 5_000)
|
|
6597
|
-
.then((healthy) => {
|
|
6598
|
-
if (!healthy)
|
|
6599
|
-
this._clientLog.warn(`reconnect health advisory failed: gateway=${this._gatewayUrl}`);
|
|
6600
|
-
})
|
|
6601
|
-
.catch((exc) => {
|
|
6602
|
-
this._clientLog.debug(`reconnect health advisory error: ${formatCaughtError(exc)}`);
|
|
6603
|
-
});
|
|
6604
|
-
}
|
|
6605
6641
|
await this._transport.close();
|
|
6606
6642
|
if (!this._ownsReconnect(reconnectAbort))
|
|
6607
6643
|
return;
|
|
@@ -6638,7 +6674,7 @@ export class AUNClient {
|
|
|
6638
6674
|
this._sessionParams.access_token = '';
|
|
6639
6675
|
}
|
|
6640
6676
|
}
|
|
6641
|
-
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);
|
|
6642
6678
|
if (!this._ownsReconnect(reconnectAbort))
|
|
6643
6679
|
return;
|
|
6644
6680
|
this._sessionParams.gateways = [...candidates];
|
|
@@ -6698,6 +6734,9 @@ export class AUNClient {
|
|
|
6698
6734
|
}
|
|
6699
6735
|
delay = Math.min(delay * 2, maxBaseDelay);
|
|
6700
6736
|
}
|
|
6737
|
+
finally {
|
|
6738
|
+
releaseReconnectSlot?.();
|
|
6739
|
+
}
|
|
6701
6740
|
}
|
|
6702
6741
|
}
|
|
6703
6742
|
/** 每轮重连从当前地址之后开始轮转,避免永远黏在失效的首个 Gateway。 */
|