@agentunion/fastaun 0.5.20 → 0.5.22
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 +104 -0
- package/_packed_docs/CHANGELOG.md +104 -0
- package/_packed_docs/INDEX.md +59 -34
- package/_packed_docs/KITE_DOCS_GUIDE.md +59 -52
- package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/344/270/216/346/215/242owner/347/274/226/346/216/222/346/226/271/346/241/210.md +530 -0
- package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/345/256/214/346/225/264/350/256/276/350/256/241/344/270/216/345/256/236/346/226/275/350/256/241/345/210/222.md +685 -0
- package/_packed_docs/agent.md/working/agent.md-schema/344/270/216/347/255/276/345/217/221/351/252/214/347/255/276/346/265/201/347/250/213/346/276/204/346/270/205.md +349 -0
- package/_packed_docs/agent.md/working//346/234/254/345/234/260Docker/346/265/213/350/257/225Bug/346/270/205/345/215/225.md +40 -0
- package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +307 -67
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +41 -15
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1433 -1392
- package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +30 -8
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +2 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +33 -22
- package/_packed_docs/sdk/0.5.21-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +368 -0
- package/_packed_docs/sdk/0.5.21-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +350 -0
- package/_packed_docs/sdk/0.5.22-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +212 -0
- package/_packed_docs/sdk/0.5.22-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +199 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +4 -1
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +42 -39
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +163 -14
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +48 -47
- package/_packed_docs/sdk/09-group-rpc-manual.md +15 -2
- package/_packed_docs/sdk/09-message-rpc-manual.md +250 -160
- package/_packed_docs/sdk/09-storage-rpc-manual.md +64 -7
- package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +73 -0
- package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +90 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +33 -25
- package/_packed_docs/sdk/INDEX.md +67 -33
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +74 -57
- package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +312 -300
- package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +322 -382
- package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -708
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +28 -20
- package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +190 -97
- package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +158 -127
- package/dist/agent-md-schema.js +6 -4
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +22 -2
- package/dist/agent-md.js +92 -68
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +13 -0
- package/dist/aid-store.js +50 -15
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +22 -2
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +1 -0
- package/dist/cert-utils.js +13 -0
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +87 -1
- package/dist/client/delivery.js +1387 -139
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +3 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -1
- package/dist/client/rpc-pipeline.js +149 -37
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +12 -2
- package/dist/client/v2-e2ee.js +831 -304
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +26 -2
- package/dist/client.js +412 -68
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts +18 -1
- package/dist/events.js +148 -9
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +43 -1
- package/dist/facades.js +363 -7
- package/dist/facades.js.map +1 -1
- package/dist/group-fs.d.ts +1 -0
- package/dist/group-fs.js +37 -19
- package/dist/group-fs.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +6 -1
- package/dist/keystore/aid-db.js +184 -42
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +14 -0
- package/dist/keystore/local-token-store.d.ts +9 -1
- package/dist/keystore/local-token-store.js +79 -14
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +46 -0
- package/dist/org-protocol.js +802 -12
- package/dist/org-protocol.js.map +1 -1
- package/dist/ownership-v21.d.ts +36 -5
- package/dist/ownership-v21.js +168 -52
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.d.ts +24 -2
- package/dist/ownership-workflow.js +682 -41
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/secret-store/file-store.js +84 -0
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.js +2 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/storage/lowlevel.d.ts +6 -0
- package/dist/storage/lowlevel.js +24 -7
- package/dist/storage/lowlevel.js.map +1 -1
- package/dist/storage/read-cache.d.ts +70 -0
- package/dist/storage/read-cache.js +625 -0
- package/dist/storage/read-cache.js.map +1 -0
- package/dist/storage/vfs.d.ts +2 -0
- package/dist/storage/vfs.js +50 -15
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +86 -3
- package/dist/tools/cross-sdk-agent.js +758 -46
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +3 -0
- package/dist/transport.js +104 -46
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/v2/e2ee/encrypt-group.js +1 -0
- package/dist/v2/e2ee/encrypt-group.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +1 -0
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
- package/dist/v2/e2ee/types.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -26,7 +26,7 @@ import { normalizeGroupId } from './group-id.js';
|
|
|
26
26
|
import { validateAIDFormat, validateGroupIDFormat } from './validators.js';
|
|
27
27
|
import { RPCTransport } from './transport.js';
|
|
28
28
|
import { AuthFlow } from './auth.js';
|
|
29
|
-
import { AgentMdManager, buildDefaultAgentMd
|
|
29
|
+
import { AgentMdManager, buildDefaultAgentMd } from './agent-md.js';
|
|
30
30
|
import { GroupIndexMetaCache } from './group-index.js';
|
|
31
31
|
import { SeqTracker } from './seq-tracker.js';
|
|
32
32
|
import { ClientRuntime } from './client/runtime.js';
|
|
@@ -37,7 +37,7 @@ import { LifecycleController } from './client/lifecycle.js';
|
|
|
37
37
|
import { PeerDirectory } from './client/peers.js';
|
|
38
38
|
import { RpcPipeline } from './client/rpc-pipeline.js';
|
|
39
39
|
import { CollabClient } from './collab/client.js';
|
|
40
|
-
import { CredentialFacade, OrgFacade, GroupFacade, MailFacade, MessageFacade, StreamFacade } from './facades.js';
|
|
40
|
+
import { DebugFacade, CredentialFacade, OrgFacade, GroupFacade, MailFacade, MessageFacade, StreamFacade } from './facades.js';
|
|
41
41
|
import { StorageVFS } from './storage/vfs.js';
|
|
42
42
|
import { V2E2EECoordinator } from './client/v2-e2ee.js';
|
|
43
43
|
import { GroupStateCoordinator } from './client/group-state.js';
|
|
@@ -48,9 +48,9 @@ import { isJsonObject, ConnectionState, STATE_TO_PUBLIC, } from './types.js';
|
|
|
48
48
|
import { AID } from './aid.js';
|
|
49
49
|
import { certFingerprint, certMatchesFingerprint, normalizeFingerprintHex, publicKeyMatchesFingerprint } from './cert-utils.js';
|
|
50
50
|
import { validateAgentMdCertificate, validateAgentMdDocument } from './agent-md-schema.js';
|
|
51
|
-
import { OwnershipFiles, OwnershipStorageAdapter, ownershipFailure
|
|
51
|
+
import { OwnershipFiles, OwnershipStorageAdapter, ownershipFailure } from './ownership-v21.js';
|
|
52
52
|
import { OwnershipWorkflow } from './ownership-workflow.js';
|
|
53
|
-
import { canonicalJson, createCredential, createMemberCredential, signMembershipBinding, verifyServiceEvent } from './org-protocol.js';
|
|
53
|
+
import { canonicalJson, createCredential, createMemberCredential, signMembershipBinding, digest, validateMembershipBinding, verifyMembershipBinding, verifyCredentialSignature, createRepresentationProof, verifyServiceEvent, buildRequestBinding, validateRequestBinding, CredentialRenewal } from './org-protocol.js';
|
|
54
54
|
function isPromiseLike(value) {
|
|
55
55
|
return Boolean(value && typeof value.then === 'function');
|
|
56
56
|
}
|
|
@@ -478,6 +478,7 @@ export class AUNClient {
|
|
|
478
478
|
/** 当前 AID 值对象(新 API) */
|
|
479
479
|
_currentAid = null;
|
|
480
480
|
_serviceEventIdentityVersion = 0;
|
|
481
|
+
_serviceEventIdentityAbort = new AbortController();
|
|
481
482
|
/** ownership 关系只在 SDK 内存中编排;服务端不保存状态机。 */
|
|
482
483
|
_ownershipRecords = new Map();
|
|
483
484
|
_ownershipType = 'agent';
|
|
@@ -494,10 +495,10 @@ export class AUNClient {
|
|
|
494
495
|
currentType: () => this.getType(),
|
|
495
496
|
now: () => Math.trunc(Date.now() / 1000),
|
|
496
497
|
readAgentMdFresh: (subAid) => this._agentMdManager.readOwnershipFresh(subAid),
|
|
497
|
-
submitAgentMdRaw: (subAid, raw, condition) => this._agentMdManager.submitOwnershipRaw(subAid, raw, condition),
|
|
498
|
+
submitAgentMdRaw: (subAid, raw, condition, assertActive, stage) => this._agentMdManager.submitOwnershipRaw(subAid, raw, condition, assertActive, stage),
|
|
498
499
|
readOwnerMd: (ownerAid, subAid) => this._getOwnershipStorage().readOwnerMd(ownerAid, subAid),
|
|
499
|
-
writeOwnerMd: (ownerAid, subAid, raw) => this._getOwnershipStorage().writeOwnerMd(ownerAid, subAid, raw),
|
|
500
|
-
deleteOwnerMd: (ownerAid, subAid) => this._getOwnershipStorage().deleteOwnerMd(ownerAid, subAid),
|
|
500
|
+
writeOwnerMd: (ownerAid, subAid, raw, original, assertActive) => this._getOwnershipStorage().writeOwnerMd(ownerAid, subAid, raw, original, assertActive),
|
|
501
|
+
deleteOwnerMd: (ownerAid, subAid, expectedVersion) => this._getOwnershipStorage().deleteOwnerMd(ownerAid, subAid, expectedVersion),
|
|
501
502
|
resolveCertificate: async (aid, fingerprint, purpose) => this._auth.resolveOwnershipCertificate(await this._resolveOwnershipCertificateGateway(aid), aid, fingerprint, purpose),
|
|
502
503
|
});
|
|
503
504
|
}
|
|
@@ -505,7 +506,7 @@ export class AUNClient {
|
|
|
505
506
|
return this._ownershipWorkflow ??= new OwnershipWorkflow({
|
|
506
507
|
currentAid: () => this._aid, store: this._tokenStore, files: this._getOwnershipFiles(),
|
|
507
508
|
currentType: () => this.getType(),
|
|
508
|
-
send: (to, payload) => this.call('message.send', { to, encrypt: true, payload: payload }),
|
|
509
|
+
send: (to, payload, messageId) => this.call('message.send', { to, encrypt: true, message_id: messageId, payload: payload }),
|
|
509
510
|
emit: (event, payload) => this._dispatcher.enqueue(event, payload),
|
|
510
511
|
});
|
|
511
512
|
}
|
|
@@ -549,6 +550,196 @@ export class AUNClient {
|
|
|
549
550
|
await this._resumeRegistrationIntent();
|
|
550
551
|
await this._resumeOwnership();
|
|
551
552
|
}
|
|
553
|
+
_credentialRenewal;
|
|
554
|
+
_credentialRenewalTimer = null;
|
|
555
|
+
_credentialRenewalAbort = null;
|
|
556
|
+
_credentialRenewalScan = Promise.resolve();
|
|
557
|
+
_getCredentialRenewal() {
|
|
558
|
+
return this._credentialRenewal ??= new CredentialRenewal({
|
|
559
|
+
currentAid: () => this._aid ?? '',
|
|
560
|
+
atomicCheckpointSave: () => this._tokenStore instanceof LocalTokenStore,
|
|
561
|
+
load: (aid) => (this._tokenStore._loadMetadataStrict?.(aid) ?? this._tokenStore.loadMetadata?.(aid))?.credential_renewals_v1 ?? {},
|
|
562
|
+
...(typeof this._tokenStore._loadMetadataStrict === 'function' ? {
|
|
563
|
+
loadRevocations: (aid) => this._tokenStore._loadMetadataStrict(aid)?.credential_revocations_v1 ?? {},
|
|
564
|
+
saveRevocations: (aid, state, guard) => {
|
|
565
|
+
if (guard && !guard.current())
|
|
566
|
+
throw new ValidationError('credential revocation synchronization was superseded');
|
|
567
|
+
this._tokenStore.saveMetadata?.(aid, { credential_revocations_v1: state });
|
|
568
|
+
},
|
|
569
|
+
} : {}),
|
|
570
|
+
save: (aid, records, guard) => {
|
|
571
|
+
const version = this._serviceEventIdentityVersion;
|
|
572
|
+
const current = () => version === this._serviceEventIdentityVersion && aid === this._aid && (!guard || guard.current());
|
|
573
|
+
if (!current())
|
|
574
|
+
throw new ValidationError('membership event receiver identity changed');
|
|
575
|
+
const saved = this._tokenStore instanceof LocalTokenStore ? records : Object.fromEntries(Object.entries(records).map(([cid, value]) => {
|
|
576
|
+
const record = { ...value };
|
|
577
|
+
for (const key of ['submitted_at', 'submission_hash', 'registration_submitted_at', 'registration_submission_hash'])
|
|
578
|
+
delete record[key];
|
|
579
|
+
return [cid, record];
|
|
580
|
+
}));
|
|
581
|
+
this._tokenStore.saveMetadata?.(aid, { credential_renewals_v1: saved });
|
|
582
|
+
if (!current())
|
|
583
|
+
throw new ValidationError('membership event receiver identity changed');
|
|
584
|
+
},
|
|
585
|
+
signer: (aid, guard) => this._credentialRenewalSigner(aid, guard),
|
|
586
|
+
certificate: (aid, fingerprint, signedAt) => signedAt === undefined
|
|
587
|
+
? this._fetchServiceEventCertificate(aid, fingerprint) : this._fetchPeerCert(aid, fingerprint, undefined, signedAt),
|
|
588
|
+
call: (method, params, onSent) => {
|
|
589
|
+
if (!onSent)
|
|
590
|
+
return this._rpcPipeline.call(method, params);
|
|
591
|
+
const version = this._serviceEventIdentityVersion, aid = this._aid;
|
|
592
|
+
return this._rpcPipeline['_callWithSent'](method, params, async () => {
|
|
593
|
+
if (version !== this._serviceEventIdentityVersion || aid !== this._aid)
|
|
594
|
+
throw new ValidationError('credential sender identity changed');
|
|
595
|
+
await onSent();
|
|
596
|
+
});
|
|
597
|
+
},
|
|
598
|
+
send: (to, payload) => this.call('message.send', { to, encrypt: true, payload }),
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
_assertCredentialNotRevoked(credential, proofChain) {
|
|
602
|
+
const version = this._serviceEventIdentityVersion, aid = this._aid;
|
|
603
|
+
const current = () => { if (aid !== this._aid || version !== this._serviceEventIdentityVersion)
|
|
604
|
+
throw new ValidationError('credential receiver identity changed'); };
|
|
605
|
+
this._getCredentialRenewal()._assertNotRevoked(credential, proofChain);
|
|
606
|
+
current();
|
|
607
|
+
return current;
|
|
608
|
+
}
|
|
609
|
+
async _credentialRenewalSigner(aid, guard) {
|
|
610
|
+
const check = () => { if (guard && !guard.current())
|
|
611
|
+
throw new ValidationError('credential renewal was superseded'); };
|
|
612
|
+
check();
|
|
613
|
+
const stored = this._aidStore?.load(aid);
|
|
614
|
+
let identity = aid === this._aid && this._identity?.private_key_pem
|
|
615
|
+
? AID._create({ aid, aunPath: this._configModel.aunPath, certPem: String(this._identity.cert ?? this._currentAid?.certPem ?? ''), privateKeyPem: String(this._identity.private_key_pem), certValid: true, privateKeyValid: true })
|
|
616
|
+
: stored?.ok ? stored.data.aid : undefined;
|
|
617
|
+
if (!identity?.isPrivateKeyValid())
|
|
618
|
+
return null;
|
|
619
|
+
const certificate = new crypto.X509Certificate(identity.certPem);
|
|
620
|
+
const end = Date.parse(certificate.validTo), start = Date.parse(certificate.validFrom);
|
|
621
|
+
if (end > Date.now() && end - Date.now() < (end - start) / 2 && this._aidStore) {
|
|
622
|
+
const renewed = await (guard ? this._aidStore._renewCert(aid, guard) : this._aidStore.renewCert(aid));
|
|
623
|
+
check();
|
|
624
|
+
if (!renewed.ok)
|
|
625
|
+
throw new ValidationError(renewed.error.message);
|
|
626
|
+
const loaded = this._aidStore.load(aid);
|
|
627
|
+
if (!loaded.ok)
|
|
628
|
+
throw new ValidationError(loaded.error.message);
|
|
629
|
+
identity = loaded.data.aid;
|
|
630
|
+
if (aid === this._aid && this._identity) {
|
|
631
|
+
this._identity.cert = identity.certPem;
|
|
632
|
+
this._currentAid = identity;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return identity;
|
|
636
|
+
}
|
|
637
|
+
_currentCredential(input) {
|
|
638
|
+
const records = this._tokenStore.loadMetadata?.(this._aid ?? '')?.credential_renewals_v1 ?? {};
|
|
639
|
+
let cid = typeof input === 'string' ? input : String(input.cid);
|
|
640
|
+
const seen = new Set();
|
|
641
|
+
while (records[cid]?.successor_cid && !seen.has(cid)) {
|
|
642
|
+
seen.add(cid);
|
|
643
|
+
cid = String(records[cid].successor_cid);
|
|
644
|
+
}
|
|
645
|
+
return records[cid] ? structuredClone(records[cid]) : null;
|
|
646
|
+
}
|
|
647
|
+
_credentialCurrent(input) {
|
|
648
|
+
return this._getCredentialRenewal().current(input);
|
|
649
|
+
}
|
|
650
|
+
async _resumeCredentialRenewalAfterConnect(guard, syncRevocations = false) {
|
|
651
|
+
if (!this._aid || (guard && !guard.current()))
|
|
652
|
+
return;
|
|
653
|
+
await this._safeAsync(guard ? this._credentialRenewalSigner(this._aid, guard) : this._credentialRenewalSigner(this._aid));
|
|
654
|
+
if (syncRevocations && (!guard || guard.current()))
|
|
655
|
+
await this._safeAsync(this._getCredentialRenewal()._syncRevocations?.(guard) ?? Promise.resolve());
|
|
656
|
+
if (!guard || guard.current())
|
|
657
|
+
await this._getCredentialRenewal().resume(guard);
|
|
658
|
+
}
|
|
659
|
+
_startCredentialRenewal() {
|
|
660
|
+
this._stopCredentialRenewal();
|
|
661
|
+
const aid = this._aid, version = this._serviceEventIdentityVersion;
|
|
662
|
+
if (!aid || !['ready', 'connected'].includes(this._state) || this._closing || this._disconnecting)
|
|
663
|
+
return;
|
|
664
|
+
const abort = new AbortController();
|
|
665
|
+
this._credentialRenewalAbort = abort;
|
|
666
|
+
const current = () => this._credentialRenewalAbort === abort && !abort.signal.aborted && aid === this._aid && version === this._serviceEventIdentityVersion && ['ready', 'connected'].includes(this._state) && !this._closing && !this._disconnecting;
|
|
667
|
+
const guard = { current, signal: abort.signal };
|
|
668
|
+
let nextRevocationSync = Date.now() + 3_600_000;
|
|
669
|
+
const scan = async () => {
|
|
670
|
+
const task = this._credentialRenewalScan.then(async () => {
|
|
671
|
+
if (!current())
|
|
672
|
+
return;
|
|
673
|
+
const syncRevocations = Date.now() >= nextRevocationSync;
|
|
674
|
+
if (syncRevocations)
|
|
675
|
+
nextRevocationSync = Date.now() + 3_600_000;
|
|
676
|
+
await this._resumeCredentialRenewalAfterConnect(guard, syncRevocations);
|
|
677
|
+
});
|
|
678
|
+
this._credentialRenewalScan = task.catch(() => { });
|
|
679
|
+
await this._safeAsync(task);
|
|
680
|
+
if (current())
|
|
681
|
+
this._credentialRenewalTimer = setTimeout(() => { void scan(); }, 60_000);
|
|
682
|
+
};
|
|
683
|
+
void scan();
|
|
684
|
+
}
|
|
685
|
+
_stopCredentialRenewal() {
|
|
686
|
+
this._credentialRenewalAbort?.abort();
|
|
687
|
+
this._credentialRenewalAbort = null;
|
|
688
|
+
if (this._credentialRenewalTimer !== null)
|
|
689
|
+
clearTimeout(this._credentialRenewalTimer);
|
|
690
|
+
this._credentialRenewalTimer = null;
|
|
691
|
+
}
|
|
692
|
+
_rememberCredential(credential, material, certificate) {
|
|
693
|
+
if (!material.long_term && certificate) {
|
|
694
|
+
const cert = new crypto.X509Certificate(typeof certificate === 'string' && !certificate.includes('-----BEGIN CERTIFICATE-----') ? Buffer.from(certificate, 'base64') : certificate);
|
|
695
|
+
const parent = Array.isArray(credential.proof_chain) ? credential.proof_chain[0] : undefined;
|
|
696
|
+
const certificateEnd = Math.floor(Date.parse(cert.validTo) / 1000);
|
|
697
|
+
const defaultEnd = credential.parent != null && isJsonObject(parent) && typeof parent.exp === 'number'
|
|
698
|
+
? Math.min(certificateEnd, Number(parent.exp)) : certificateEnd;
|
|
699
|
+
material = { ...material, long_term: credential.exp === defaultEnd };
|
|
700
|
+
}
|
|
701
|
+
this._safeAsync(this._getCredentialRenewal().remember(credential, material));
|
|
702
|
+
}
|
|
703
|
+
async _verifyIncomingServiceEvent(event, payload) {
|
|
704
|
+
const targetAid = this._identity?.aid ?? '';
|
|
705
|
+
const version = this._serviceEventIdentityVersion;
|
|
706
|
+
const signal = this._serviceEventIdentityAbort.signal;
|
|
707
|
+
const expiresAt = Number(payload?.expires_at);
|
|
708
|
+
const transport = this._transport, connectionGeneration = transport?._connectionGeneration, workerGeneration = transport?._workerGeneration;
|
|
709
|
+
const current = () => version === this._serviceEventIdentityVersion && targetAid === (this._identity?.aid ?? '') && transport === this._transport && connectionGeneration === this._transport?._connectionGeneration && workerGeneration === this._transport?._workerGeneration && Date.now() / 1000 < expiresAt;
|
|
710
|
+
const verified = await verifyServiceEvent(event, payload, { targetAid, resolveEventCertificate: (aid, fingerprint) => this._fetchServiceEventCertificate(aid, fingerprint), resolveCredentialService: this.resolveCredentialService });
|
|
711
|
+
if (!current())
|
|
712
|
+
throw new ValidationError('service event receiver identity changed');
|
|
713
|
+
if (['credential.revoked', 'credential.revocationList'].includes(event.replace(/^event\//, ''))) {
|
|
714
|
+
await this._getCredentialRenewal()._receiveRevocations(payload, { current, signal });
|
|
715
|
+
}
|
|
716
|
+
if (event.replace(/^event\//, '') === 'org.member.changed' && isJsonObject(payload) && Object.hasOwn(payload, 'membership_package')) {
|
|
717
|
+
await this._getCredentialRenewal().importMembershipPackage(payload, { current, signal });
|
|
718
|
+
if (!current())
|
|
719
|
+
throw new ValidationError('service event receiver identity changed');
|
|
720
|
+
}
|
|
721
|
+
return verified;
|
|
722
|
+
}
|
|
723
|
+
async _handleCredentialRenewal(message) {
|
|
724
|
+
if (!isJsonObject(message))
|
|
725
|
+
return false;
|
|
726
|
+
const envelope = message;
|
|
727
|
+
const content = 'payload' in envelope ? envelope.payload : envelope;
|
|
728
|
+
const payload = decryptedOwnershipJsonText(content) ?? content;
|
|
729
|
+
let parsed = payload;
|
|
730
|
+
if (typeof payload === 'string') {
|
|
731
|
+
try {
|
|
732
|
+
parsed = JSON.parse(payload);
|
|
733
|
+
}
|
|
734
|
+
catch {
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (!isJsonObject(parsed) || !['credential.renewal.request', 'credential.renewal.result'].includes(String(parsed.type)))
|
|
739
|
+
return false;
|
|
740
|
+
await this._safeAsync(this._getCredentialRenewal().receive(String(envelope.from ?? envelope.from_aid ?? envelope.sender_aid ?? ''), parsed));
|
|
741
|
+
return true;
|
|
742
|
+
}
|
|
552
743
|
async _resumeRegistrationIntent() {
|
|
553
744
|
const intent = this._tokenStore.loadMetadata?.(this._aid)?.registration_intent;
|
|
554
745
|
if (['group', 'org'].includes(String(intent?.type ?? this.getType())))
|
|
@@ -598,11 +789,13 @@ export class AUNClient {
|
|
|
598
789
|
_defaultConnectDeliveryMode;
|
|
599
790
|
/** peer 证书缓存 */
|
|
600
791
|
_certCache = new Map();
|
|
792
|
+
_certFetchInFlight = new Map();
|
|
601
793
|
_agentMdManager;
|
|
602
794
|
_aidStore;
|
|
603
795
|
_groupIdentityOperations = new Map();
|
|
604
796
|
_groupIndexMetaCache = new GroupIndexMetaCache();
|
|
605
797
|
/** 消息序列号跟踪器(群消息 + P2P 空洞检测) */
|
|
798
|
+
_p2pPeerPullEnabled = true;
|
|
606
799
|
_seqTracker = new SeqTracker();
|
|
607
800
|
_seqTrackerContext = null;
|
|
608
801
|
/** 惰性群同步:已同步过的 group_id 集合 */
|
|
@@ -685,6 +878,7 @@ export class AUNClient {
|
|
|
685
878
|
_storage;
|
|
686
879
|
_collab;
|
|
687
880
|
_message;
|
|
881
|
+
_debug;
|
|
688
882
|
_mail;
|
|
689
883
|
_group;
|
|
690
884
|
_stream;
|
|
@@ -784,7 +978,7 @@ export class AUNClient {
|
|
|
784
978
|
dnsNet,
|
|
785
979
|
});
|
|
786
980
|
this._transport.setProtocolMetaObserver((meta) => this._observeRpcMeta(meta));
|
|
787
|
-
this._transport.setServiceEventVerifier((event, payload) =>
|
|
981
|
+
this._transport.setServiceEventVerifier((event, payload) => this._verifyIncomingServiceEvent(event, payload), () => this._identity?.aid ?? '', () => this._serviceEventIdentityVersion);
|
|
788
982
|
this._transport.setProtocolDisconnectCallback((err, closeCode) => this._handleTransportDisconnect(err, closeCode));
|
|
789
983
|
this._transport._onForegroundDrained = () => this._delivery.onForegroundPullsDrained();
|
|
790
984
|
this._transport._onRpcResponse = () => this._delivery.requestBackgroundPullRecovery();
|
|
@@ -817,7 +1011,8 @@ export class AUNClient {
|
|
|
817
1011
|
this._dispatcher.subscribeProtocol('_raw.message.read_changed', (data) => this._onRawReadChanged(data, 'p2p'));
|
|
818
1012
|
// V2 P2P 推送通知:收到通知后自动走 message.v2.pull 拉取并解密
|
|
819
1013
|
this._dispatcher.subscribeProtocol('_raw.peer.v2.message_received', (data) => {
|
|
820
|
-
const
|
|
1014
|
+
const sender = isJsonObject(data) ? String(data.sender_aid ?? '') : '';
|
|
1015
|
+
const ns = this._p2pPeerPullEnabled ? (sender ? this._delivery.peerNamespace(sender) : '') : this._aid ? `p2p:${this._aid}` : '';
|
|
821
1016
|
const hasInline = isJsonObject(data)
|
|
822
1017
|
&& isJsonObject(data.inline_message);
|
|
823
1018
|
const inlineGeneration = hasInline ? this._delivery.captureInlineAckGeneration() : undefined;
|
|
@@ -896,6 +1091,21 @@ export class AUNClient {
|
|
|
896
1091
|
this._collab = new CollabClient(this);
|
|
897
1092
|
return this._collab;
|
|
898
1093
|
}
|
|
1094
|
+
get debug() {
|
|
1095
|
+
if (!this._debug)
|
|
1096
|
+
this._debug = new DebugFacade(this, async (operation) => {
|
|
1097
|
+
const transport = this._transport;
|
|
1098
|
+
const previous = transport.getTraceMode();
|
|
1099
|
+
transport.setTraceMode('diag');
|
|
1100
|
+
try {
|
|
1101
|
+
return await operation();
|
|
1102
|
+
}
|
|
1103
|
+
finally {
|
|
1104
|
+
transport.setTraceMode(previous);
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
return this._debug;
|
|
1108
|
+
}
|
|
899
1109
|
/** Message facade 入口 */
|
|
900
1110
|
get message() {
|
|
901
1111
|
if (!this._message)
|
|
@@ -974,6 +1184,7 @@ export class AUNClient {
|
|
|
974
1184
|
}
|
|
975
1185
|
async bindOwneraid(ownerAid) { return this._getOwnershipWorkflow().bindOwneraid(ownerAid); }
|
|
976
1186
|
async bindSubaid(subAid) { return this._getOwnershipWorkflow().bindSubaid(subAid); }
|
|
1187
|
+
async resetOwnershipTask() { return this._getOwnershipWorkflow().resetOwnershipTask(); }
|
|
977
1188
|
async releaseSubaid(subAid) { return this._getOwnershipWorkflow().releaseSubaid(subAid); }
|
|
978
1189
|
async transferSubaid(subAid, newOwnerAid) { return this._getOwnershipWorkflow().transferSubaid(subAid, newOwnerAid); }
|
|
979
1190
|
async checkOwnershipConsensus(subAid) { return this._getOwnershipWorkflow().checkOwnershipConsensus(subAid); }
|
|
@@ -1173,7 +1384,7 @@ export class AUNClient {
|
|
|
1173
1384
|
dnsNet,
|
|
1174
1385
|
});
|
|
1175
1386
|
this._transport.setProtocolMetaObserver((meta) => this._observeRpcMeta(meta));
|
|
1176
|
-
this._transport.setServiceEventVerifier((event, payload) =>
|
|
1387
|
+
this._transport.setServiceEventVerifier((event, payload) => this._verifyIncomingServiceEvent(event, payload), () => this._identity?.aid ?? '', () => this._serviceEventIdentityVersion);
|
|
1177
1388
|
this._transport.setProtocolDisconnectCallback((err, closeCode) => this._handleTransportDisconnect(err, closeCode));
|
|
1178
1389
|
this._transport._onForegroundDrained = () => this._delivery.onForegroundPullsDrained();
|
|
1179
1390
|
this._transport._onRpcResponse = () => this._delivery.requestBackgroundPullRecovery();
|
|
@@ -1181,7 +1392,10 @@ export class AUNClient {
|
|
|
1181
1392
|
loadIdentity(aid) {
|
|
1182
1393
|
const previousAid = this._aid;
|
|
1183
1394
|
this._identityRuntime.loadIdentity(aid);
|
|
1395
|
+
this._stopCredentialRenewal();
|
|
1184
1396
|
this._serviceEventIdentityVersion++;
|
|
1397
|
+
this._serviceEventIdentityAbort.abort();
|
|
1398
|
+
this._serviceEventIdentityAbort = new AbortController();
|
|
1185
1399
|
this._ensureReadStateIdentity();
|
|
1186
1400
|
if (previousAid !== this._aid) {
|
|
1187
1401
|
this._ownershipRecords.clear();
|
|
@@ -1289,6 +1503,7 @@ export class AUNClient {
|
|
|
1289
1503
|
? this._ensureReadStateIdentity() : null;
|
|
1290
1504
|
try {
|
|
1291
1505
|
const result = await this._rpcPipeline.call(method, params);
|
|
1506
|
+
await this._safeAsync(this._getCredentialRenewal().observe(method, params ?? {}, result));
|
|
1292
1507
|
if (readIdentity !== null && readIdentity === this._ensureReadStateIdentity())
|
|
1293
1508
|
this._observeReadStateResult(method, result, params ?? {});
|
|
1294
1509
|
return result;
|
|
@@ -1318,6 +1533,13 @@ export class AUNClient {
|
|
|
1318
1533
|
const groupId = String(params.group_id ?? params.groupId ?? params.group_aid ?? params.groupAid ?? fallback).trim();
|
|
1319
1534
|
if (!groupId || !store)
|
|
1320
1535
|
return false;
|
|
1536
|
+
const groupManager = store._agentMdManager ?? this._agentMdManager;
|
|
1537
|
+
const keystore = store._keystore;
|
|
1538
|
+
let pendingBind = keystore?.loadPendingGroupBind?.(`bind:${groupId}`);
|
|
1539
|
+
if (!pendingBind?.public_key_der_b64 || !pendingBind?.private_key_pem)
|
|
1540
|
+
pendingBind = keystore?.loadPendingGroupBind?.(groupId);
|
|
1541
|
+
if (groupManager.pendingUpload(fallback) && (!pendingBind?.public_key_der_b64 || !pendingBind?.private_key_pem))
|
|
1542
|
+
return false;
|
|
1321
1543
|
try {
|
|
1322
1544
|
await this._runGroupIdentityOperation(groupId, () => this.bindGroupAid({ group_id: groupId }, { aidStore: store }));
|
|
1323
1545
|
}
|
|
@@ -1336,6 +1558,8 @@ export class AUNClient {
|
|
|
1336
1558
|
if (!loaded.ok || !loaded.data?.aid?.isPrivateKeyValid())
|
|
1337
1559
|
return false;
|
|
1338
1560
|
const groupManager = store._agentMdManager ?? this._agentMdManager;
|
|
1561
|
+
if (groupManager.pendingUpload(groupAid))
|
|
1562
|
+
return false;
|
|
1339
1563
|
if (String((groupManager._loadRecord?.(groupAid, true) ?? groupManager.loadRecord(groupAid))?.local_draft_content ?? ''))
|
|
1340
1564
|
return false;
|
|
1341
1565
|
await this._runGroupIdentityOperation(groupAid, () => this._uploadGroupAgentMd(store, groupAid, {}, {}));
|
|
@@ -1353,17 +1577,15 @@ export class AUNClient {
|
|
|
1353
1577
|
const aid = this._aid;
|
|
1354
1578
|
if (!aid || !this._currentAid)
|
|
1355
1579
|
return false;
|
|
1356
|
-
const observed = await this.
|
|
1357
|
-
if (observed.exists || String(this._agentMdManager._loadRecord?.(aid, true)?.local_draft_content ?? this._agentMdManager.loadRecord(aid)?.local_draft_content ?? ''))
|
|
1580
|
+
const observed = await this._agentMdManager.readAgentMdFresh(aid);
|
|
1581
|
+
if (observed.exists || String(this._agentMdManager._loadRecord?.(aid, true)?.local_draft_content ?? this._agentMdManager.loadRecord(aid)?.local_draft_content ?? '') || this._agentMdManager.pendingUpload(aid))
|
|
1358
1582
|
return false;
|
|
1359
1583
|
let agentMd = buildDefaultAgentMd(aid, { type: this.getType() });
|
|
1360
1584
|
try {
|
|
1361
1585
|
agentMd = this._agentMdManager.readContent(aid) || agentMd;
|
|
1362
1586
|
}
|
|
1363
1587
|
catch { }
|
|
1364
|
-
|
|
1365
|
-
const raw = signOwnershipFile(this._currentAid, payload.endsWith('\n') || payload.endsWith('\r') ? payload : `${payload}\n`, 'agent');
|
|
1366
|
-
await this._agentMdManager.submitOwnershipRaw(aid, raw, { if_none_match: '*' });
|
|
1588
|
+
await this._agentMdManager.upload(agentMd, { if_none_match: '*' });
|
|
1367
1589
|
return true;
|
|
1368
1590
|
}
|
|
1369
1591
|
async _checkIdentityAdmissionAfterConnect() {
|
|
@@ -1400,6 +1622,9 @@ export class AUNClient {
|
|
|
1400
1622
|
if (!isJsonObject(raw))
|
|
1401
1623
|
continue;
|
|
1402
1624
|
const group = raw;
|
|
1625
|
+
const status = String(group.status ?? '').trim().toLowerCase();
|
|
1626
|
+
if (status && status !== 'active')
|
|
1627
|
+
continue;
|
|
1403
1628
|
const role = String(group.role ?? group.my_role ?? '').trim().toLowerCase();
|
|
1404
1629
|
const groupId = String(group.group_id ?? group.groupId ?? group.group_aid ?? group.groupAid ?? '').trim();
|
|
1405
1630
|
const groupAid = String(group.group_aid ?? group.groupAid ?? groupId).trim();
|
|
@@ -1418,6 +1643,8 @@ export class AUNClient {
|
|
|
1418
1643
|
return;
|
|
1419
1644
|
}
|
|
1420
1645
|
const groupManager = store._agentMdManager ?? this._agentMdManager;
|
|
1646
|
+
if (groupManager.pendingUpload(groupAid))
|
|
1647
|
+
return;
|
|
1421
1648
|
if (String((groupManager._loadRecord?.(groupAid, true) ?? groupManager.loadRecord(groupAid))?.local_draft_content ?? '')) {
|
|
1422
1649
|
await this._agentMdManager.check(groupAid);
|
|
1423
1650
|
return;
|
|
@@ -1427,8 +1654,6 @@ export class AUNClient {
|
|
|
1427
1654
|
await this.bindGroupAid({ group_id: groupId, group_aid: groupAid }, { aidStore: store });
|
|
1428
1655
|
return;
|
|
1429
1656
|
}
|
|
1430
|
-
if (await this._resumeGroupAgentMdUpload(store, groupAid))
|
|
1431
|
-
return;
|
|
1432
1657
|
const checked = await this._agentMdManager.check(groupAid);
|
|
1433
1658
|
if (!checked.remote_found) {
|
|
1434
1659
|
await this._uploadGroupAgentMd(store, groupAid, {}, {});
|
|
@@ -1459,12 +1684,45 @@ export class AUNClient {
|
|
|
1459
1684
|
this._groupIdentityOperations.delete(key);
|
|
1460
1685
|
}
|
|
1461
1686
|
}
|
|
1687
|
+
async _resumeOrgRequest(method, params, target) {
|
|
1688
|
+
const binding = params.request_binding;
|
|
1689
|
+
const now = Math.trunc(Date.now() / 1000);
|
|
1690
|
+
if (!isJsonObject(binding) || now < Number(binding.exp))
|
|
1691
|
+
return params;
|
|
1692
|
+
const credentials = isJsonObject(params.credentials) ? params.credentials : {};
|
|
1693
|
+
const credential = ['member_credential', 'approval_credential', 'audit_credential'].map((field) => credentials[field]).find(isJsonObject) ?? params.credential_template;
|
|
1694
|
+
const cid = isJsonObject(credential) ? String(credential.cid) : '';
|
|
1695
|
+
validateRequestBinding(binding, method, target, params, cid, String(params.nonce), String(params.request_id), Number(binding.iat));
|
|
1696
|
+
let exp = now + 300;
|
|
1697
|
+
for (const material of [credential, ...Object.values(credentials).filter((value) => isJsonObject(value) && value.ver === 'aun-cap/1'), ...[params, params.previous_owner, params.new_owner].filter(isJsonObject).flatMap((value) => ['membership_binding', 'credential_template', 'registration_authorization'].map((field) => value[field]))]) {
|
|
1698
|
+
if (isJsonObject(material)) {
|
|
1699
|
+
if (!Number.isInteger(material.exp) || Number(material.exp) <= now)
|
|
1700
|
+
throw new ValidationError('Org recovery authorization material has expired');
|
|
1701
|
+
exp = Math.min(exp, Number(material.exp));
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
const refreshed = { ...params, request_binding: buildRequestBinding(method, target, params, cid, String(params.nonce), String(params.request_id), now, exp) };
|
|
1705
|
+
delete refreshed.client_signature;
|
|
1706
|
+
if (isJsonObject(credentials.representation_proof)) {
|
|
1707
|
+
if (!this._currentAid || !isJsonObject(credential))
|
|
1708
|
+
throw new ValidationError('Org recovery representation signer is unavailable');
|
|
1709
|
+
refreshed.credentials = { ...credentials, representation_proof: createRepresentationProof(credential, refreshed, refreshed.request_binding, this._currentAid, refreshed.proof_chain) };
|
|
1710
|
+
}
|
|
1711
|
+
return refreshed;
|
|
1712
|
+
}
|
|
1462
1713
|
async createOrg(params = {}, options = {}) {
|
|
1463
1714
|
const store = options.aidStore;
|
|
1464
1715
|
const owner = this._currentAid;
|
|
1465
1716
|
const orgAid = String(params.org_id ?? '').trim();
|
|
1466
1717
|
if (!store || !owner?.isPrivateKeyValid() || !orgAid)
|
|
1467
1718
|
throw new ValidationError('createOrg requires aidStore, org_id and current owner identity');
|
|
1719
|
+
const tokens = store._tokenStore;
|
|
1720
|
+
const inputHash = crypto.createHash('sha256').update(canonicalJson(params)).digest('hex');
|
|
1721
|
+
let checkpoint = (typeof tokens._loadMetadataStrict === 'function' ? tokens._loadMetadataStrict(orgAid) : tokens.loadMetadata(orgAid))?.org_identity_creation;
|
|
1722
|
+
if (checkpoint != null && !isJsonObject(checkpoint))
|
|
1723
|
+
throw ownershipFailure('persistence_unavailable', 'Org creation checkpoint is invalid');
|
|
1724
|
+
if (checkpoint && (checkpoint.owner_aid !== owner.aid || checkpoint.input_hash !== inputHash))
|
|
1725
|
+
throw ownershipFailure('conflict', 'pending Org creation context changed');
|
|
1468
1726
|
this._aidStore = store;
|
|
1469
1727
|
let identity = store.load(orgAid);
|
|
1470
1728
|
if (!identity.ok) {
|
|
@@ -1476,18 +1734,13 @@ export class AUNClient {
|
|
|
1476
1734
|
if (!identity.ok)
|
|
1477
1735
|
throw ownershipFailure('unavailable', identity.error.message, true);
|
|
1478
1736
|
const target = options.serviceAid ?? `org.${orgAid.slice(orgAid.indexOf('.') + 1)}`;
|
|
1479
|
-
const tokens = store._tokenStore;
|
|
1480
|
-
const inputHash = crypto.createHash('sha256').update(canonicalJson(params)).digest('hex');
|
|
1481
|
-
let checkpoint = tokens.loadMetadata(orgAid)?.org_identity_creation;
|
|
1482
|
-
if (checkpoint && (checkpoint.owner_aid !== owner.aid || checkpoint.input_hash !== inputHash))
|
|
1483
|
-
throw ownershipFailure('conflict', 'pending Org creation context changed');
|
|
1484
1737
|
if (!checkpoint) {
|
|
1485
1738
|
const prepared = this.org.prepareRequest('org.lifecycle.create', { ...params, owner_aid: owner.aid }, target, owner, { orgSigner: identity.data.aid });
|
|
1486
1739
|
checkpoint = { owner_aid: owner.aid, input_hash: inputHash, source_params: params, prepared, phase: 'create' };
|
|
1487
1740
|
tokens.saveMetadata(orgAid, { org_identity_creation: checkpoint });
|
|
1488
1741
|
}
|
|
1489
1742
|
if (checkpoint.phase === 'create') {
|
|
1490
|
-
const result = await this.call('org.lifecycle.create', checkpoint.prepared);
|
|
1743
|
+
const result = await this.call('org.lifecycle.create', await this._resumeOrgRequest('org.lifecycle.create', checkpoint.prepared, target));
|
|
1491
1744
|
checkpoint = { ...checkpoint, result, phase: 'upload' };
|
|
1492
1745
|
tokens.saveMetadata(orgAid, { org_identity_creation: checkpoint });
|
|
1493
1746
|
}
|
|
@@ -1501,33 +1754,60 @@ export class AUNClient {
|
|
|
1501
1754
|
if (typeof keyStore?.listIdentities !== 'function' || typeof tokens?.loadMetadata !== 'function')
|
|
1502
1755
|
return;
|
|
1503
1756
|
for (const aid of keyStore.listIdentities()) {
|
|
1504
|
-
const metadata = tokens.loadMetadata(aid);
|
|
1757
|
+
const metadata = typeof tokens._loadMetadataStrict === 'function' ? tokens._loadMetadataStrict(aid) : tokens.loadMetadata(aid);
|
|
1505
1758
|
const creation = metadata?.org_identity_creation;
|
|
1506
1759
|
const transfer = metadata?.org_identity_transfer;
|
|
1760
|
+
if (creation != null && !isJsonObject(creation) || transfer != null && !isJsonObject(transfer))
|
|
1761
|
+
throw ownershipFailure('persistence_unavailable', 'Org identity checkpoint is invalid');
|
|
1507
1762
|
try {
|
|
1508
1763
|
if (creation?.owner_aid === this._aid) {
|
|
1509
1764
|
await this.createOrg(creation.source_params, { aidStore: store });
|
|
1510
1765
|
continue;
|
|
1511
1766
|
}
|
|
1512
|
-
if (transfer?.owner_aid === this._aid) {
|
|
1767
|
+
if (transfer?.owner_aid === this._aid && transfer.phase !== 'completed') {
|
|
1513
1768
|
await this.completeOrgTransfer(transfer.source_params, { aidStore: store });
|
|
1514
1769
|
continue;
|
|
1515
1770
|
}
|
|
1516
|
-
const orgManager = store._agentMdManager;
|
|
1517
|
-
if (String((orgManager?._loadRecord?.(aid, true) ?? orgManager?.loadRecord?.(aid))?.local_draft_content ?? ''))
|
|
1518
|
-
continue;
|
|
1519
|
-
const pending = store._agentMdManager.pendingUpload(aid);
|
|
1520
|
-
if (!pending)
|
|
1521
|
-
continue;
|
|
1522
|
-
const fields = validateAgentMdDocument(pending.content, { expectedAid: aid }).frontmatter;
|
|
1523
|
-
if (fields.type === 'org' && fields.owner === this._aid)
|
|
1524
|
-
await this._resumeGroupAgentMdUpload(store, aid);
|
|
1525
1771
|
}
|
|
1526
1772
|
catch (error) {
|
|
1527
1773
|
this._clientLog.warn(`post-connect Org identity recovery failed: aid=${aid} err=${formatCaughtError(error)}`);
|
|
1528
1774
|
}
|
|
1529
1775
|
}
|
|
1530
1776
|
}
|
|
1777
|
+
async _verifyOrgTransferMaterials(params, ownerAid) {
|
|
1778
|
+
const orgAid = String(params.org_id);
|
|
1779
|
+
const previous = params.previous_owner;
|
|
1780
|
+
const next = params.new_owner;
|
|
1781
|
+
const binding = previous.membership_binding;
|
|
1782
|
+
const nextBinding = next.membership_binding;
|
|
1783
|
+
const credential = previous.credential_template;
|
|
1784
|
+
const ext = isJsonObject(credential.ext) ? credential.ext : {};
|
|
1785
|
+
const capability = Array.isArray(credential.cap) && credential.cap.length === 1 && isJsonObject(credential.cap[0]) ? credential.cap[0] : {};
|
|
1786
|
+
const now = Math.trunc(Date.now() / 1000);
|
|
1787
|
+
if (binding.org_id !== orgAid || nextBinding.org_id !== orgAid || binding.member_aid === ownerAid || nextBinding.member_aid !== ownerAid
|
|
1788
|
+
|| binding.role !== 'member' || nextBinding.role !== 'owner' || digest(previous.terms) !== binding.terms_hash || digest(next.terms) !== nextBinding.terms_hash
|
|
1789
|
+
|| !Number.isSafeInteger(nextBinding.iat) || !Number.isSafeInteger(nextBinding.exp) || Number(nextBinding.iat) > now || Number(nextBinding.exp) <= now
|
|
1790
|
+
|| credential.iss !== orgAid || credential.sub !== binding.member_aid || credential.parent != null || credential.delegate !== false
|
|
1791
|
+
|| ext.credential_type !== 'member' || ext.bound_to_membership !== true || ext.org_id !== orgAid || ext.membership_id !== binding.membership_id
|
|
1792
|
+
|| ext.binding_version !== binding.binding_version || ext.membership_terms_hash !== binding.terms_hash
|
|
1793
|
+
|| capability.ns !== 'org' || capability.action !== 'role.member' || capability.resource !== orgAid)
|
|
1794
|
+
throw ownershipFailure('invalid', 'Org transfer membership materials do not match');
|
|
1795
|
+
validateMembershipBinding(binding);
|
|
1796
|
+
const certificates = new Map();
|
|
1797
|
+
for (const [aid, signature] of [[binding.member_aid, binding.member_signature], [orgAid, binding.org_signature], [orgAid, credential.signature]]) {
|
|
1798
|
+
if (!isJsonObject(signature))
|
|
1799
|
+
throw ownershipFailure('invalid', 'Org transfer membership materials do not match');
|
|
1800
|
+
const fingerprint = String(signature.cert_fingerprint);
|
|
1801
|
+
const certificate = await this._auth.resolveOwnershipCertificate(await this._resolveOwnershipCertificateGateway(String(aid)), String(aid), fingerprint);
|
|
1802
|
+
certificates.set(fingerprint, certificate.cert_pem);
|
|
1803
|
+
}
|
|
1804
|
+
const resolve = (fingerprint) => certificates.get(fingerprint);
|
|
1805
|
+
const checkedAt = Math.trunc(Date.now() / 1000);
|
|
1806
|
+
verifyMembershipBinding(binding, resolve, checkedAt, orgAid);
|
|
1807
|
+
verifyCredentialSignature(credential, resolve, checkedAt, orgAid);
|
|
1808
|
+
if (Number(nextBinding.exp) <= checkedAt)
|
|
1809
|
+
throw ownershipFailure('invalid', 'Org transfer membership materials do not match');
|
|
1810
|
+
}
|
|
1531
1811
|
async completeOrgTransfer(params = {}, options = {}) {
|
|
1532
1812
|
const store = options.aidStore;
|
|
1533
1813
|
const owner = this._currentAid;
|
|
@@ -1545,9 +1825,19 @@ export class AUNClient {
|
|
|
1545
1825
|
const tokenStore = store._tokenStore;
|
|
1546
1826
|
const pendingKey = `org-transfer:${orgAid}:${transferId}`;
|
|
1547
1827
|
const inputHash = crypto.createHash('sha256').update(canonicalJson(params)).digest('hex');
|
|
1548
|
-
let pending = tokenStore.loadMetadata(orgAid)?.org_identity_transfer;
|
|
1828
|
+
let pending = (typeof tokenStore._loadMetadataStrict === 'function' ? tokenStore._loadMetadataStrict(orgAid) : tokenStore.loadMetadata(orgAid))?.org_identity_transfer;
|
|
1829
|
+
if (pending != null && !isJsonObject(pending))
|
|
1830
|
+
throw ownershipFailure('persistence_unavailable', 'Org transfer checkpoint is invalid');
|
|
1831
|
+
if (pending?.phase === 'completed' && pending.transfer_id !== transferId)
|
|
1832
|
+
pending = undefined;
|
|
1549
1833
|
if (pending && (pending.transfer_id !== transferId || pending.owner_aid !== owner.aid || pending.input_hash !== inputHash))
|
|
1550
1834
|
throw ownershipFailure('conflict', 'pending Org transfer context changed');
|
|
1835
|
+
if (pending?.phase === 'completed') {
|
|
1836
|
+
localStore.clearPendingGroupBind(pendingKey);
|
|
1837
|
+
return pending.result;
|
|
1838
|
+
}
|
|
1839
|
+
if (!pending)
|
|
1840
|
+
await this._verifyOrgTransferMaterials(params, owner.aid);
|
|
1551
1841
|
let keyPair = localStore.loadPendingGroupBind(pendingKey);
|
|
1552
1842
|
if (!keyPair) {
|
|
1553
1843
|
if (pending)
|
|
@@ -1568,7 +1858,7 @@ export class AUNClient {
|
|
|
1568
1858
|
tokenStore.saveMetadata(orgAid, { org_identity_transfer: pending });
|
|
1569
1859
|
}
|
|
1570
1860
|
if (pending.phase === 'rekey') {
|
|
1571
|
-
const result = await this.call('org.lifecycle.transferAccept', pending.rekey_params);
|
|
1861
|
+
const result = await this.call('org.lifecycle.transferAccept', await this._resumeOrgRequest('org.lifecycle.transferAccept', pending.rekey_params, target));
|
|
1572
1862
|
pending = { ...pending, phase: 'finalize', rekey_result: result };
|
|
1573
1863
|
tokenStore.saveMetadata(orgAid, { org_identity_transfer: pending });
|
|
1574
1864
|
}
|
|
@@ -1604,9 +1894,9 @@ export class AUNClient {
|
|
|
1604
1894
|
pending = { ...pending, finalize_params: this.org.prepareRequest('org.lifecycle.transferAccept', final, target, owner, { orgSigner: orgIdentity }) };
|
|
1605
1895
|
tokenStore.saveMetadata(orgAid, { org_identity_transfer: pending });
|
|
1606
1896
|
}
|
|
1607
|
-
const result = await this.call('org.lifecycle.transferAccept', pending.finalize_params);
|
|
1897
|
+
const result = await this.call('org.lifecycle.transferAccept', await this._resumeOrgRequest('org.lifecycle.transferAccept', pending.finalize_params, target));
|
|
1608
1898
|
await this._uploadOrgAgentMd(store, orgAid, pending.source_params);
|
|
1609
|
-
tokenStore.saveMetadata(orgAid, { org_identity_transfer:
|
|
1899
|
+
tokenStore.saveMetadata(orgAid, { org_identity_transfer: { transfer_id: transferId, owner_aid: owner.aid, input_hash: inputHash, phase: 'completed', result } });
|
|
1610
1900
|
localStore.clearPendingGroupBind(pendingKey);
|
|
1611
1901
|
return result;
|
|
1612
1902
|
}
|
|
@@ -1859,7 +2149,13 @@ export class AUNClient {
|
|
|
1859
2149
|
entityIdentityGetter: (aid, type) => this._freshEntityCertificate(aid, type),
|
|
1860
2150
|
entitySignerResolver: (aid, fingerprint) => this._freshEntitySigner(aid, fingerprint),
|
|
1861
2151
|
gatewayResolver: (aid) => identityStore._resolveGateway(aid),
|
|
1862
|
-
accessTokenResolver: (
|
|
2152
|
+
accessTokenResolver: async () => {
|
|
2153
|
+
const state = identityStore._tokenStore?.loadInstanceState?.(identity.aid, store.deviceId, store.slotId) ?? {};
|
|
2154
|
+
const token = AuthFlow._getCachedAccessToken(state);
|
|
2155
|
+
if (token && String(state.refresh_token ?? '') && !this._auth._validateCachedCredentials(identityStore._authIdentityFromAid(identity)))
|
|
2156
|
+
return token;
|
|
2157
|
+
return identityStore._uploadAgentMdToken(identity, await identityStore._resolveGateway(identity.aid));
|
|
2158
|
+
},
|
|
1863
2159
|
aidValidator: (aid) => identityStore._registerFlow.validateAidName(aid),
|
|
1864
2160
|
});
|
|
1865
2161
|
return await manager.resumePendingUpload(groupAid);
|
|
@@ -2334,6 +2630,11 @@ export class AUNClient {
|
|
|
2334
2630
|
const tStart = Date.now();
|
|
2335
2631
|
this._clientLog.debug(`on enter: event=${event}`);
|
|
2336
2632
|
const result = this._dispatcher.subscribe(event, handler);
|
|
2633
|
+
if (event === 'message.received' || event === 'message.recalled' || event === 'message.batch') {
|
|
2634
|
+
if (this._p2pPeerPullEnabled)
|
|
2635
|
+
this._delivery.refreshPendingPeers();
|
|
2636
|
+
this._delivery.requestBackgroundPullRecovery();
|
|
2637
|
+
}
|
|
2337
2638
|
this._clientLog.debug(`on exit: elapsed=${Date.now() - tStart}ms event=${event}`);
|
|
2338
2639
|
return result;
|
|
2339
2640
|
}
|
|
@@ -2663,7 +2964,10 @@ export class AUNClient {
|
|
|
2663
2964
|
if (event === 'message.received') {
|
|
2664
2965
|
return this._handleOwnershipMessage(payload).then((handled) => {
|
|
2665
2966
|
if (!handled)
|
|
2666
|
-
return this.
|
|
2967
|
+
return this._handleCredentialRenewal(payload).then((renewed) => {
|
|
2968
|
+
if (!renewed)
|
|
2969
|
+
return this._delivery.publishAppEvent(event, payload, source, deliveryContext);
|
|
2970
|
+
});
|
|
2667
2971
|
return undefined;
|
|
2668
2972
|
});
|
|
2669
2973
|
}
|
|
@@ -3094,22 +3398,36 @@ export class AUNClient {
|
|
|
3094
3398
|
this._clientLog.debug(`_fetchPeerCert exit: elapsed=${Date.now() - tStart}ms aid=${aid} (cache_hit)`);
|
|
3095
3399
|
return cached.certPem;
|
|
3096
3400
|
}
|
|
3097
|
-
const
|
|
3098
|
-
|
|
3099
|
-
|
|
3401
|
+
const inFlightKey = `${cacheKey}:timeout:${timeoutMs}`;
|
|
3402
|
+
const pending = this._certFetchInFlight.get(inFlightKey);
|
|
3403
|
+
if (pending)
|
|
3404
|
+
return await pending;
|
|
3405
|
+
const request = (async () => {
|
|
3406
|
+
const gatewayUrl = this._gatewayUrl;
|
|
3407
|
+
if (!gatewayUrl) {
|
|
3408
|
+
throw new ValidationError('gateway url unavailable for e2ee cert fetch');
|
|
3409
|
+
}
|
|
3410
|
+
const peerGatewayUrl = AUNClient._resolvePeerGatewayUrl(gatewayUrl, aid);
|
|
3411
|
+
const certPem = await _httpGetText(AUNClient._buildCertUrl(peerGatewayUrl, aid, certFingerprint), this._configModel.verifySsl, timeoutMs);
|
|
3412
|
+
const validated = await this._validateAndCachePeerCert({
|
|
3413
|
+
aid,
|
|
3414
|
+
certPem,
|
|
3415
|
+
certFingerprint,
|
|
3416
|
+
source: 'fetch',
|
|
3417
|
+
signedAt,
|
|
3418
|
+
agentMdRead,
|
|
3419
|
+
});
|
|
3420
|
+
this._clientLog.debug(`_fetchPeerCert exit: elapsed=${Date.now() - tStart}ms aid=${aid} (fetched)`);
|
|
3421
|
+
return validated;
|
|
3422
|
+
})();
|
|
3423
|
+
this._certFetchInFlight.set(inFlightKey, request);
|
|
3424
|
+
try {
|
|
3425
|
+
return await request;
|
|
3426
|
+
}
|
|
3427
|
+
finally {
|
|
3428
|
+
if (this._certFetchInFlight.get(inFlightKey) === request)
|
|
3429
|
+
this._certFetchInFlight.delete(inFlightKey);
|
|
3100
3430
|
}
|
|
3101
|
-
const peerGatewayUrl = AUNClient._resolvePeerGatewayUrl(gatewayUrl, aid);
|
|
3102
|
-
const certPem = await _httpGetText(AUNClient._buildCertUrl(peerGatewayUrl, aid, certFingerprint), this._configModel.verifySsl, timeoutMs);
|
|
3103
|
-
const validated = await this._validateAndCachePeerCert({
|
|
3104
|
-
aid,
|
|
3105
|
-
certPem,
|
|
3106
|
-
certFingerprint,
|
|
3107
|
-
source: 'fetch',
|
|
3108
|
-
signedAt,
|
|
3109
|
-
agentMdRead,
|
|
3110
|
-
});
|
|
3111
|
-
this._clientLog.debug(`_fetchPeerCert exit: elapsed=${Date.now() - tStart}ms aid=${aid} (fetched)`);
|
|
3112
|
-
return validated;
|
|
3113
3431
|
}
|
|
3114
3432
|
catch (err) {
|
|
3115
3433
|
this._clientLog.debug(`_fetchPeerCert exit (error): elapsed=${Date.now() - tStart}ms aid=${aid} err=${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -3685,7 +4003,7 @@ export class AUNClient {
|
|
|
3685
4003
|
}
|
|
3686
4004
|
/** 后台 Promise 统一兜底,避免事件回调里的异步异常变成未处理拒绝。 */
|
|
3687
4005
|
_safeAsync(promise) {
|
|
3688
|
-
promise.catch((exc) => {
|
|
4006
|
+
return promise.then(() => undefined).catch((exc) => {
|
|
3689
4007
|
this._clientLog.warn(`background task exception: ${formatCaughtError(exc)}`);
|
|
3690
4008
|
});
|
|
3691
4009
|
}
|
|
@@ -3880,7 +4198,7 @@ export class AUNClient {
|
|
|
3880
4198
|
return await this._v2E2EE.ackGroupV2(groupId, upToSeq, groupAid);
|
|
3881
4199
|
}
|
|
3882
4200
|
/** 解密单条 V2 pull 消息。缺 sender IK 时先入 pending,后台补齐后重试。 */
|
|
3883
|
-
async _decryptV2Message(msg, allowPending = true, emitUndecryptable = true, rotateKeys = true, observeAgentMd = true, deferStatus, deferKeyFetch = false, source = 'pull') {
|
|
4201
|
+
async _decryptV2Message(msg, allowPending = true, emitUndecryptable = true, rotateKeys = true, observeAgentMd = true, deferStatus, deferKeyFetch = false, source = 'pull', deferredEffects) {
|
|
3884
4202
|
const session = this._v2Session;
|
|
3885
4203
|
if (!session)
|
|
3886
4204
|
return null;
|
|
@@ -3896,8 +4214,12 @@ export class AUNClient {
|
|
|
3896
4214
|
return null;
|
|
3897
4215
|
}
|
|
3898
4216
|
const e2eeMeta = this._v2E2eeMeta(envelope);
|
|
3899
|
-
if (observeAgentMd)
|
|
3900
|
-
|
|
4217
|
+
if (observeAgentMd) {
|
|
4218
|
+
if (deferredEffects)
|
|
4219
|
+
deferredEffects.push(() => this._agentMdManager.observeEnvelope(envelope));
|
|
4220
|
+
else
|
|
4221
|
+
this._agentMdManager.observeEnvelope(envelope);
|
|
4222
|
+
}
|
|
3901
4223
|
let spkId = '';
|
|
3902
4224
|
let recipientKeySource = '';
|
|
3903
4225
|
if (isJsonObject(envelope.recipient)) {
|
|
@@ -3976,8 +4298,12 @@ export class AUNClient {
|
|
|
3976
4298
|
};
|
|
3977
4299
|
this._attachV2EnvelopeMetadata(event, e2eeMeta);
|
|
3978
4300
|
this._logMessageDebug('decrypt-fail', 'v2.decrypt', undecryptableEvent, event);
|
|
3979
|
-
if (emitUndecryptable)
|
|
3980
|
-
|
|
4301
|
+
if (emitUndecryptable) {
|
|
4302
|
+
if (deferredEffects)
|
|
4303
|
+
deferredEffects.push(() => { this._dispatcher.enqueue(undecryptableEvent, event); });
|
|
4304
|
+
else
|
|
4305
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
4306
|
+
}
|
|
3981
4307
|
return null;
|
|
3982
4308
|
}
|
|
3983
4309
|
this._clientLog.debug(`V2 decrypt key lookup ok: seq=${String(msg.seq ?? '')}, group=${groupIdForKeys || '<p2p>'}, ik_len=${ikPriv.byteLength}, spk_len=${spkPriv?.byteLength ?? 0}`);
|
|
@@ -4015,8 +4341,12 @@ export class AUNClient {
|
|
|
4015
4341
|
};
|
|
4016
4342
|
this._attachV2EnvelopeMetadata(event, e2eeMeta);
|
|
4017
4343
|
this._logMessageDebug('decrypt-fail', 'v2.decrypt', undecryptableEvent, event);
|
|
4018
|
-
if (emitUndecryptable)
|
|
4019
|
-
|
|
4344
|
+
if (emitUndecryptable) {
|
|
4345
|
+
if (deferredEffects)
|
|
4346
|
+
deferredEffects.push(() => { this._dispatcher.enqueue(undecryptableEvent, event); });
|
|
4347
|
+
else
|
|
4348
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
4349
|
+
}
|
|
4020
4350
|
return null;
|
|
4021
4351
|
}
|
|
4022
4352
|
let plaintext;
|
|
@@ -4044,16 +4374,24 @@ export class AUNClient {
|
|
|
4044
4374
|
};
|
|
4045
4375
|
this._attachV2EnvelopeMetadata(event, e2eeMeta);
|
|
4046
4376
|
this._logMessageDebug('decrypt-fail', 'v2.decrypt', undecryptableEvent, event);
|
|
4047
|
-
if (emitUndecryptable)
|
|
4048
|
-
|
|
4377
|
+
if (emitUndecryptable) {
|
|
4378
|
+
if (deferredEffects)
|
|
4379
|
+
deferredEffects.push(() => { this._dispatcher.enqueue(undecryptableEvent, event); });
|
|
4380
|
+
else
|
|
4381
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
4382
|
+
}
|
|
4049
4383
|
return null;
|
|
4050
4384
|
}
|
|
4051
4385
|
if (plaintext === null) {
|
|
4052
4386
|
this._clientLog.debug(`V2 decrypt returned null plaintext: seq=${String(msg.seq ?? '')}, group=${groupIdForKeys || '<p2p>'}`);
|
|
4053
4387
|
return null;
|
|
4054
4388
|
}
|
|
4055
|
-
if (rotateKeys)
|
|
4056
|
-
|
|
4389
|
+
if (rotateKeys) {
|
|
4390
|
+
if (deferredEffects)
|
|
4391
|
+
deferredEffects.push(() => this._scheduleV2KeyRotationForMessage(msg, envelope));
|
|
4392
|
+
else
|
|
4393
|
+
this._scheduleV2KeyRotationForMessage(msg, envelope);
|
|
4394
|
+
}
|
|
4057
4395
|
const e2ee = this._v2E2eeMeta(envelope);
|
|
4058
4396
|
let result = {
|
|
4059
4397
|
message_id: String(msg.message_id ?? ''),
|
|
@@ -4149,6 +4487,8 @@ export class AUNClient {
|
|
|
4149
4487
|
if (protectedHeaders && Object.keys(protectedHeaders).length > 0) {
|
|
4150
4488
|
meta.protected_headers = protectedHeaders;
|
|
4151
4489
|
}
|
|
4490
|
+
if (typeof envelope.need_render === 'boolean')
|
|
4491
|
+
meta.need_render = envelope.need_render;
|
|
4152
4492
|
const payloadType = String(envelope.payload_type ?? protectedHeaders?.payload_type ?? '').trim();
|
|
4153
4493
|
if (payloadType) {
|
|
4154
4494
|
meta.payload_type = payloadType;
|
|
@@ -4164,6 +4504,8 @@ export class AUNClient {
|
|
|
4164
4504
|
return meta;
|
|
4165
4505
|
}
|
|
4166
4506
|
_attachV2EnvelopeMetadata(message, meta) {
|
|
4507
|
+
if (typeof meta.need_render === 'boolean')
|
|
4508
|
+
message.need_render = meta.need_render;
|
|
4167
4509
|
const payloadType = typeof meta.payload_type === 'string' ? meta.payload_type.trim() : '';
|
|
4168
4510
|
if (payloadType)
|
|
4169
4511
|
message.payload_type = payloadType;
|
|
@@ -4521,6 +4863,7 @@ export class AUNClient {
|
|
|
4521
4863
|
// ── 内部:后台任务 ────────────────────────────────────────
|
|
4522
4864
|
/** 启动所有后台任务 */
|
|
4523
4865
|
_startBackgroundTasks() {
|
|
4866
|
+
this._startCredentialRenewal();
|
|
4524
4867
|
// WebSocket Ping/Pong 由 Gateway 与传输层处理;SDK 不发送 RPC 心跳。
|
|
4525
4868
|
// 短连接生命周期短,不需要 token 刷新。
|
|
4526
4869
|
if (this._sessionOptions.connection_kind !== 'short') {
|
|
@@ -4530,6 +4873,7 @@ export class AUNClient {
|
|
|
4530
4873
|
}
|
|
4531
4874
|
/** 停止所有后台任务 */
|
|
4532
4875
|
_stopBackgroundTasks() {
|
|
4876
|
+
this._stopCredentialRenewal();
|
|
4533
4877
|
if (this._heartbeatTimer !== null) {
|
|
4534
4878
|
clearInterval(this._heartbeatTimer);
|
|
4535
4879
|
this._heartbeatTimer = null;
|