@agentunion/fastaun 0.5.23 → 0.5.25
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 +62 -0
- package/_packed_docs/CHANGELOG.md +62 -0
- package/_packed_docs/INDEX.md +27 -15
- package/_packed_docs/KITE_DOCS_GUIDE.md +22 -19
- package/_packed_docs/TS/346/263/250/345/206/214/350/207/252/345/212/250/347/273/221/345/256/232Owner/347/232/204E2E/351/252/214/350/257/201/344/273/243/347/240/201.md +196 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +2 -2
- package/_packed_docs/sdk/0.5.24-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +401 -0
- package/_packed_docs/sdk/0.5.24-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +354 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +17 -7
- package/_packed_docs/sdk/INDEX.md +30 -6
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +49 -2
- 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 +123 -72
- 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 +173 -96
- package/dist/agent-md.d.ts +2 -1
- package/dist/agent-md.js +52 -22
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +1 -0
- package/dist/aid-store.js +34 -12
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +44 -10
- package/dist/auth.js.map +1 -1
- package/dist/client/delivery.d.ts +3 -0
- package/dist/client/delivery.js +197 -47
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.js +10 -0
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.js +17 -5
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +10 -10
- package/dist/client.js +158 -119
- package/dist/client.js.map +1 -1
- package/dist/group-fs.js +5 -0
- package/dist/group-fs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/keystore/aid-db.d.ts +1 -8
- package/dist/keystore/aid-db.js +46 -204
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +0 -15
- package/dist/keystore/local-token-store.d.ts +1 -6
- package/dist/keystore/local-token-store.js +0 -15
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/ownership-v21.d.ts +4 -2
- package/dist/ownership-v21.js +86 -42
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.d.ts +30 -59
- package/dist/ownership-workflow.js +260 -1667
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +1 -2
- package/dist/tools/cross-sdk-agent.js +7 -14
- package/dist/tools/cross-sdk-agent.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.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { StorageVFS } from './storage/vfs.js';
|
|
|
18
18
|
import { type JsonObject, type JsonValue, type RpcParams, type RpcResult, ConnectionState } from './types.js';
|
|
19
19
|
import { AID } from './aid.js';
|
|
20
20
|
import { type OwnershipConsensusResult } from './ownership-v21.js';
|
|
21
|
-
import { type OwnershipTaskResult
|
|
21
|
+
import { type OwnershipTaskResult } from './ownership-workflow.js';
|
|
22
22
|
import { type CertificateMaterial } from './org-protocol.js';
|
|
23
23
|
/**
|
|
24
24
|
* 递归排序键的 JSON 序列化(Canonical JSON for AUN)
|
|
@@ -82,8 +82,6 @@ export declare class AUNClient {
|
|
|
82
82
|
private _currentAid;
|
|
83
83
|
private _serviceEventIdentityVersion;
|
|
84
84
|
private _serviceEventIdentityAbort;
|
|
85
|
-
/** ownership 关系只在 SDK 内存中编排;服务端不保存状态机。 */
|
|
86
|
-
private _ownershipRecords;
|
|
87
85
|
private _ownershipType;
|
|
88
86
|
private _ownershipStorage;
|
|
89
87
|
private _ownershipFiles;
|
|
@@ -92,9 +90,10 @@ export declare class AUNClient {
|
|
|
92
90
|
private _getOwnershipFiles;
|
|
93
91
|
private _createOwnershipFiles;
|
|
94
92
|
private _getOwnershipWorkflow;
|
|
95
|
-
private
|
|
96
|
-
private
|
|
97
|
-
private
|
|
93
|
+
private _recoverOwnershipRequest;
|
|
94
|
+
private _restoreIdentityType;
|
|
95
|
+
private _ownershipOperation;
|
|
96
|
+
private _ownershipOperationGuard?;
|
|
98
97
|
private _credentialRenewal?;
|
|
99
98
|
private _credentialRenewalTimer;
|
|
100
99
|
private _credentialRenewalAbort;
|
|
@@ -111,8 +110,6 @@ export declare class AUNClient {
|
|
|
111
110
|
_rememberCredential(credential: JsonObject, material: Record<string, unknown>, certificate?: CertificateMaterial): void;
|
|
112
111
|
private _verifyIncomingServiceEvent;
|
|
113
112
|
private _handleCredentialRenewal;
|
|
114
|
-
private _resumeRegistrationIntent;
|
|
115
|
-
private _resumeOwnership;
|
|
116
113
|
/** 实例级 protected_headers */
|
|
117
114
|
private _instanceProtectedHeaders;
|
|
118
115
|
/** 重连退避时间戳(ms) */
|
|
@@ -124,6 +121,7 @@ export declare class AUNClient {
|
|
|
124
121
|
/** Gateway URL */
|
|
125
122
|
private _gatewayUrl;
|
|
126
123
|
private _gatewayCandidates;
|
|
124
|
+
private _peerGatewayCache;
|
|
127
125
|
/** 是否正在关闭 */
|
|
128
126
|
private _closing;
|
|
129
127
|
private _disconnecting;
|
|
@@ -263,7 +261,6 @@ export declare class AUNClient {
|
|
|
263
261
|
get stream(): StreamFacade;
|
|
264
262
|
/** 当前 AID 值对象 */
|
|
265
263
|
get currentAid(): AID | null;
|
|
266
|
-
private _ownershipKey;
|
|
267
264
|
getType(): string;
|
|
268
265
|
get_type(): string;
|
|
269
266
|
getOwner(): string | null;
|
|
@@ -272,7 +269,10 @@ export declare class AUNClient {
|
|
|
272
269
|
get_ownership_status(): Record<string, unknown>;
|
|
273
270
|
bindOwneraid(ownerAid: string): Promise<OwnershipTaskResult>;
|
|
274
271
|
bindSubaid(subAid: string): Promise<OwnershipTaskResult>;
|
|
275
|
-
|
|
272
|
+
bindLocalSubaid(subClient: AUNClient): Promise<OwnershipTaskResult>;
|
|
273
|
+
transferLocalSubaid(subClient: AUNClient, newOwnerClient: AUNClient): Promise<OwnershipTaskResult>;
|
|
274
|
+
private _createOwnershipOperationClient;
|
|
275
|
+
private _localOwnershipOperation;
|
|
276
276
|
releaseSubaid(subAid: string): Promise<OwnershipTaskResult>;
|
|
277
277
|
transferSubaid(subAid: string, newOwnerAid: string): Promise<OwnershipTaskResult>;
|
|
278
278
|
checkOwnershipConsensus(subAid?: string): Promise<OwnershipConsensusResult>;
|
package/dist/client.js
CHANGED
|
@@ -49,7 +49,7 @@ 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
51
|
import { OwnershipFiles, OwnershipStorageAdapter, ownershipAid, ownershipFailure } from './ownership-v21.js';
|
|
52
|
-
import { OwnershipWorkflow } from './ownership-workflow.js';
|
|
52
|
+
import { OwnershipWorkflow, OWNERSHIP_SDK_MESSAGE_TYPE, parseOwnershipMessage } from './ownership-workflow.js';
|
|
53
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');
|
|
@@ -352,7 +352,9 @@ function createAgentMdManagerForRuntime(opts) {
|
|
|
352
352
|
entitySignerResolver: opts.fetchEntitySigner,
|
|
353
353
|
gatewayResolver: async (aid) => {
|
|
354
354
|
const gatewayUrl = await opts.gateway.resolve(aid);
|
|
355
|
-
opts.
|
|
355
|
+
const localAid = opts.ownerAid();
|
|
356
|
+
if (localAid && aid.slice(aid.indexOf('.') + 1).trim().toLowerCase() === localAid.slice(localAid.indexOf('.') + 1).trim().toLowerCase())
|
|
357
|
+
opts.gateway.set(gatewayUrl);
|
|
356
358
|
return gatewayUrl;
|
|
357
359
|
},
|
|
358
360
|
accessTokenResolver: async (aid, gatewayUrl) => {
|
|
@@ -446,7 +448,7 @@ function createAgentMdManagerForRuntime(opts) {
|
|
|
446
448
|
}
|
|
447
449
|
if (!opts.gateway.get()) {
|
|
448
450
|
try {
|
|
449
|
-
opts.gateway.set(await opts.gateway.resolve(target));
|
|
451
|
+
opts.gateway.set(await opts.gateway.resolve(opts.ownerAid() ?? target));
|
|
450
452
|
}
|
|
451
453
|
catch { /* best effort before cert fetch */ }
|
|
452
454
|
}
|
|
@@ -479,8 +481,6 @@ export class AUNClient {
|
|
|
479
481
|
_currentAid = null;
|
|
480
482
|
_serviceEventIdentityVersion = 0;
|
|
481
483
|
_serviceEventIdentityAbort = new AbortController();
|
|
482
|
-
/** ownership 关系只在 SDK 内存中编排;服务端不保存状态机。 */
|
|
483
|
-
_ownershipRecords = new Map();
|
|
484
484
|
_ownershipType = 'agent';
|
|
485
485
|
_ownershipStorage = null;
|
|
486
486
|
_ownershipFiles = null;
|
|
@@ -489,7 +489,7 @@ export class AUNClient {
|
|
|
489
489
|
return this._ownershipStorage ??= new OwnershipStorageAdapter({ currentAid: () => this._aid, call: (method, params) => this.call(method, params), httpPut: (url, data, headers) => this.storage.lowlevel.httpPut(url, data, headers) });
|
|
490
490
|
}
|
|
491
491
|
_getOwnershipFiles() {
|
|
492
|
-
return this._ownershipFiles ??= this._createOwnershipFiles((subAid) => this._agentMdManager.readOwnershipFresh(subAid));
|
|
492
|
+
return this._ownershipFiles ??= this._createOwnershipFiles((subAid, stage) => this._agentMdManager.readOwnershipFresh(subAid, stage));
|
|
493
493
|
}
|
|
494
494
|
_createOwnershipFiles(readAgentMdFresh) {
|
|
495
495
|
return new OwnershipFiles({
|
|
@@ -507,52 +507,56 @@ export class AUNClient {
|
|
|
507
507
|
}
|
|
508
508
|
_getOwnershipWorkflow() {
|
|
509
509
|
return this._ownershipWorkflow ??= new OwnershipWorkflow({
|
|
510
|
-
currentAid: () => this._aid,
|
|
511
|
-
currentType: () => this.getType(),
|
|
510
|
+
currentAid: () => this._aid, files: this._getOwnershipFiles(),
|
|
511
|
+
currentType: () => this.getType(), assertCurrent: () => this._ownershipOperationGuard?.(),
|
|
512
|
+
recoverRequest: (operationId) => this._recoverOwnershipRequest(operationId),
|
|
512
513
|
send: (to, payload, messageId) => this.call('message.send', { to, encrypt: true, message_id: messageId, payload: payload }),
|
|
513
514
|
emit: (event, payload) => this._dispatcher.enqueue(event, payload),
|
|
514
515
|
});
|
|
515
516
|
}
|
|
516
|
-
|
|
517
|
-
const
|
|
518
|
-
if (
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
517
|
+
async _recoverOwnershipRequest(operationId) {
|
|
518
|
+
const identity = this._aid, version = this._serviceEventIdentityVersion;
|
|
519
|
+
const current = () => { if (this._aid !== identity || this._serviceEventIdentityVersion !== version)
|
|
520
|
+
throw ownershipFailure('conflict', 'ownership request identity changed'); };
|
|
521
|
+
const catalog = await this.call('message.peers', { filter: 'all', limit: 200 });
|
|
522
|
+
current();
|
|
523
|
+
for (const peer of Array.isArray(catalog.peers) ? catalog.peers.slice(0, 200) : []) {
|
|
524
|
+
const sender = String(peer.sender_aid ?? '');
|
|
525
|
+
const history = await this.call('message.history', { sender_aid: sender, before_seq: Number.MAX_SAFE_INTEGER, limit: 200 });
|
|
526
|
+
current();
|
|
527
|
+
for (const row of Array.isArray(history.messages) ? history.messages : []) {
|
|
528
|
+
const envelope = row, content = envelope.payload ?? envelope;
|
|
529
|
+
const raw = decryptedOwnershipJsonText(content) ?? content;
|
|
530
|
+
let value = raw;
|
|
531
|
+
if (typeof raw === 'string') {
|
|
532
|
+
try {
|
|
533
|
+
value = JSON.parse(raw);
|
|
534
|
+
}
|
|
535
|
+
catch {
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (!isJsonObject(value) || value.type !== OWNERSHIP_SDK_MESSAGE_TYPE || value.operation_id !== operationId || !['bind.request', 'transfer.request'].includes(String(value.action)))
|
|
540
|
+
continue;
|
|
541
|
+
const authenticated = String(envelope.from_aid ?? envelope.from ?? envelope.sender_aid ?? '');
|
|
542
|
+
if (authenticated !== sender)
|
|
543
|
+
throw ownershipFailure('forbidden', 'ownership history sender changed');
|
|
544
|
+
return { from: authenticated, payload: parseOwnershipMessage(raw, { authenticated_from_aid: authenticated, local_aid: identity }) };
|
|
545
|
+
}
|
|
528
546
|
}
|
|
529
|
-
|
|
547
|
+
return null;
|
|
530
548
|
}
|
|
531
|
-
|
|
549
|
+
_restoreIdentityType() {
|
|
532
550
|
const aid = String(this._aid ?? '').trim();
|
|
533
551
|
if (!aid)
|
|
534
552
|
return;
|
|
535
553
|
const metadata = this._tokenStore.loadMetadata?.(aid);
|
|
536
|
-
const
|
|
537
|
-
if (
|
|
538
|
-
this._ownershipType = String(
|
|
539
|
-
const records = metadata?.ownership_records;
|
|
540
|
-
if (!records || typeof records !== 'object' || Array.isArray(records))
|
|
541
|
-
return;
|
|
542
|
-
for (const [key, value] of Object.entries(records)) {
|
|
543
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
544
|
-
continue;
|
|
545
|
-
const record = value;
|
|
546
|
-
this._ownershipRecords.set(key, record);
|
|
547
|
-
if (String(record.subject_aid ?? '').toLowerCase() === aid.toLowerCase())
|
|
548
|
-
this._ownershipType = String(record.type ?? this._ownershipType);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
async _resumeOwnershipAfterConnect() {
|
|
552
|
-
await this._checkIdentityAdmissionAfterConnect();
|
|
553
|
-
await this._resumeRegistrationIntent();
|
|
554
|
-
await this._resumeOwnership();
|
|
554
|
+
const legacy = metadata?.registration_intent;
|
|
555
|
+
if (metadata?.identity_type ?? legacy?.type)
|
|
556
|
+
this._ownershipType = String(metadata?.identity_type ?? legacy?.type);
|
|
555
557
|
}
|
|
558
|
+
_ownershipOperation = false;
|
|
559
|
+
_ownershipOperationGuard;
|
|
556
560
|
_credentialRenewal;
|
|
557
561
|
_credentialRenewalTimer = null;
|
|
558
562
|
_credentialRenewalAbort = null;
|
|
@@ -750,18 +754,6 @@ export class AUNClient {
|
|
|
750
754
|
await this._safeAsync(this._getCredentialRenewal().receive(String(envelope.from ?? envelope.from_aid ?? envelope.sender_aid ?? ''), parsed));
|
|
751
755
|
return true;
|
|
752
756
|
}
|
|
753
|
-
async _resumeRegistrationIntent() {
|
|
754
|
-
const intent = this._tokenStore.loadMetadata?.(this._aid)?.registration_intent;
|
|
755
|
-
if (['group', 'org'].includes(String(intent?.type ?? this.getType())))
|
|
756
|
-
return;
|
|
757
|
-
if (intent?.protocol !== '2.1' || intent.pending !== true)
|
|
758
|
-
return;
|
|
759
|
-
await this._getOwnershipWorkflow().resumeRegistration(String(intent.owner), String(intent.operation_id));
|
|
760
|
-
this._tokenStore.saveMetadata?.(this._aid, { registration_intent: { ...intent, pending: false } });
|
|
761
|
-
}
|
|
762
|
-
async _resumeOwnership() {
|
|
763
|
-
await this._getOwnershipWorkflow().resume();
|
|
764
|
-
}
|
|
765
757
|
/** 实例级 protected_headers */
|
|
766
758
|
_instanceProtectedHeaders = null;
|
|
767
759
|
/** 重连退避时间戳(ms) */
|
|
@@ -773,6 +765,7 @@ export class AUNClient {
|
|
|
773
765
|
/** Gateway URL */
|
|
774
766
|
_gatewayUrl = null;
|
|
775
767
|
_gatewayCandidates = [];
|
|
768
|
+
_peerGatewayCache = new Map();
|
|
776
769
|
/** 是否正在关闭 */
|
|
777
770
|
_closing = false;
|
|
778
771
|
_disconnecting = false;
|
|
@@ -957,7 +950,7 @@ export class AUNClient {
|
|
|
957
950
|
net: dnsNet,
|
|
958
951
|
});
|
|
959
952
|
this._aid = initAid;
|
|
960
|
-
this.
|
|
953
|
+
this._restoreIdentityType();
|
|
961
954
|
this._agentMdManager = createAgentMdManagerForRuntime({
|
|
962
955
|
config: () => this._configModel,
|
|
963
956
|
logger: () => this._logger.for('aun_core.agent_md'),
|
|
@@ -1138,9 +1131,6 @@ export class AUNClient {
|
|
|
1138
1131
|
get currentAid() {
|
|
1139
1132
|
return this._currentAid;
|
|
1140
1133
|
}
|
|
1141
|
-
_ownershipKey(subjectAid, ownerAid) {
|
|
1142
|
-
return `${String(subjectAid ?? '').trim().toLowerCase()}|${String(ownerAid ?? '').trim().toLowerCase()}`;
|
|
1143
|
-
}
|
|
1144
1134
|
getType() {
|
|
1145
1135
|
return String(this._ownershipType || 'agent').trim() || 'agent';
|
|
1146
1136
|
}
|
|
@@ -1148,45 +1138,12 @@ export class AUNClient {
|
|
|
1148
1138
|
return this.getType();
|
|
1149
1139
|
}
|
|
1150
1140
|
getOwner() {
|
|
1151
|
-
|
|
1152
|
-
let best = null;
|
|
1153
|
-
let bestActive = false;
|
|
1154
|
-
for (const record of this._ownershipRecords.values()) {
|
|
1155
|
-
if (String(record.subject_aid ?? '').trim().toLowerCase() === aid) {
|
|
1156
|
-
if (String(record.status ?? '').trim().toLowerCase() === 'released')
|
|
1157
|
-
continue;
|
|
1158
|
-
const owner = String(record.owner_aid ?? '').trim();
|
|
1159
|
-
if (!owner)
|
|
1160
|
-
continue;
|
|
1161
|
-
const active = String(record.status ?? '').trim().toLowerCase() === 'active';
|
|
1162
|
-
if (!best || active && !bestActive) {
|
|
1163
|
-
best = owner;
|
|
1164
|
-
bestActive = active;
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
return best;
|
|
1141
|
+
return null;
|
|
1169
1142
|
}
|
|
1170
1143
|
get_owner() {
|
|
1171
1144
|
return this.getOwner();
|
|
1172
1145
|
}
|
|
1173
1146
|
getOwnershipStatus() {
|
|
1174
|
-
const aid = String(this._aid ?? '').trim().toLowerCase();
|
|
1175
|
-
let best = null;
|
|
1176
|
-
let bestActive = false;
|
|
1177
|
-
for (const record of this._ownershipRecords.values()) {
|
|
1178
|
-
const subject = String(record.subject_aid ?? '').trim().toLowerCase();
|
|
1179
|
-
const owner = String(record.owner_aid ?? '').trim().toLowerCase();
|
|
1180
|
-
if (aid !== subject && aid !== owner)
|
|
1181
|
-
continue;
|
|
1182
|
-
const active = String(record.status ?? '').trim().toLowerCase() === 'active';
|
|
1183
|
-
if (!best || active && !bestActive) {
|
|
1184
|
-
best = record;
|
|
1185
|
-
bestActive = active;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
if (best)
|
|
1189
|
-
return Object.fromEntries(Object.entries(best).filter(([key]) => key !== 'subject_identity' && key !== 'escrow_password' && key !== 'escrow_download_url'));
|
|
1190
1147
|
return { subject_aid: this._aid ?? '', owner_aid: null, status: 'legacy_unbound', type: this.getType() };
|
|
1191
1148
|
}
|
|
1192
1149
|
get_ownership_status() {
|
|
@@ -1194,7 +1151,53 @@ export class AUNClient {
|
|
|
1194
1151
|
}
|
|
1195
1152
|
async bindOwneraid(ownerAid) { return this._getOwnershipWorkflow().bindOwneraid(ownerAid); }
|
|
1196
1153
|
async bindSubaid(subAid) { return this._getOwnershipWorkflow().bindSubaid(subAid); }
|
|
1197
|
-
async
|
|
1154
|
+
async bindLocalSubaid(subClient) { return this._localOwnershipOperation(subClient); }
|
|
1155
|
+
async transferLocalSubaid(subClient, newOwnerClient) { return this._localOwnershipOperation(subClient, newOwnerClient); }
|
|
1156
|
+
_createOwnershipOperationClient(slotId) {
|
|
1157
|
+
const aid = this._currentAid;
|
|
1158
|
+
const client = new AUNClient(AID._create({ aid: aid.aid, aunPath: aid.aunPath, certPem: aid.certPem, privateKeyPem: aid.privateKeyPem,
|
|
1159
|
+
certValid: aid.isCertValid(), privateKeyValid: aid.isPrivateKeyValid(), deviceId: this._deviceId, slotId,
|
|
1160
|
+
verifySsl: aid.verifySsl, rootCaPath: aid.rootCaPath, debug: aid.debug, aidStore: aid._aidStore }));
|
|
1161
|
+
client._ownershipOperation = true;
|
|
1162
|
+
client._ownershipType = this.getType();
|
|
1163
|
+
return client;
|
|
1164
|
+
}
|
|
1165
|
+
async _localOwnershipOperation(sub, next) {
|
|
1166
|
+
const sources = [this, sub, ...(next ? [next] : [])];
|
|
1167
|
+
if (sources.some((source) => !(source instanceof AUNClient) || !source._currentAid?.isPrivateKeyValid() || !source._currentAid.isCertValid() || !source._currentAid.privateKeyPem || source._currentAid.aid !== source._aid || source.getType() !== 'agent') || new Set(sources.map((source) => source._aid)).size !== sources.length)
|
|
1168
|
+
throw ownershipFailure('forbidden', 'local ownership requires distinct agent clients with private identities');
|
|
1169
|
+
const identities = sources.map((source) => [source._aid, source._currentAid, source._serviceEventIdentityVersion]);
|
|
1170
|
+
const guard = () => { if (sources.some((source, index) => source._aid !== identities[index][0] || source._currentAid !== identities[index][1] || source._serviceEventIdentityVersion !== identities[index][2]))
|
|
1171
|
+
throw ownershipFailure('conflict', 'local ownership source identity changed'); };
|
|
1172
|
+
for (const source of sources) {
|
|
1173
|
+
guard();
|
|
1174
|
+
const document = await source._agentMdManager.readAgentMdFresh(source._aid);
|
|
1175
|
+
guard();
|
|
1176
|
+
if (!document.exists)
|
|
1177
|
+
throw ownershipFailure('forbidden', 'local ownership requires a published agent document');
|
|
1178
|
+
const verified = await source._getOwnershipFiles().verifyFile(document.content, 'agent', source._aid);
|
|
1179
|
+
guard();
|
|
1180
|
+
if (verified.frontmatter.type !== 'agent')
|
|
1181
|
+
throw ownershipFailure('forbidden', 'local ownership requires verified agent identities');
|
|
1182
|
+
}
|
|
1183
|
+
const slot = `ownership-${crypto.randomUUID()}`, clients = [];
|
|
1184
|
+
try {
|
|
1185
|
+
for (const source of sources) {
|
|
1186
|
+
const client = source._createOwnershipOperationClient(slot);
|
|
1187
|
+
client._ownershipOperationGuard = guard;
|
|
1188
|
+
clients.push(client);
|
|
1189
|
+
}
|
|
1190
|
+
for (const client of clients) {
|
|
1191
|
+
guard();
|
|
1192
|
+
await client.connect({ connection_kind: 'short', background_sync: false, auto_reconnect: false });
|
|
1193
|
+
guard();
|
|
1194
|
+
}
|
|
1195
|
+
return next ? await clients[0]._getOwnershipWorkflow().transferLocalSubaid(clients[1]._getOwnershipWorkflow(), clients[2]._getOwnershipWorkflow()) : await clients[0]._getOwnershipWorkflow().bindLocalSubaid(clients[1]._getOwnershipWorkflow());
|
|
1196
|
+
}
|
|
1197
|
+
finally {
|
|
1198
|
+
await Promise.all(clients.map((client) => client.close()));
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1198
1201
|
async releaseSubaid(subAid) { return this._getOwnershipWorkflow().releaseSubaid(subAid); }
|
|
1199
1202
|
async transferSubaid(subAid, newOwnerAid) { return this._getOwnershipWorkflow().transferSubaid(subAid, newOwnerAid); }
|
|
1200
1203
|
async checkOwnershipConsensus(subAid) {
|
|
@@ -1224,7 +1227,28 @@ export class AUNClient {
|
|
|
1224
1227
|
return false;
|
|
1225
1228
|
const envelope = message;
|
|
1226
1229
|
const content = 'payload' in envelope ? envelope.payload : message;
|
|
1227
|
-
|
|
1230
|
+
const raw = decryptedOwnershipJsonText(content) ?? content, sender = String(envelope.from_aid ?? '');
|
|
1231
|
+
let value = raw;
|
|
1232
|
+
if (typeof value === 'string') {
|
|
1233
|
+
try {
|
|
1234
|
+
value = JSON.parse(value);
|
|
1235
|
+
}
|
|
1236
|
+
catch {
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
if (value && typeof value === 'object' && !Array.isArray(value) && value.type === OWNERSHIP_SDK_MESSAGE_TYPE) {
|
|
1241
|
+
const control = value, request = control.action === 'bind.request' || control.action === 'transfer.request';
|
|
1242
|
+
try {
|
|
1243
|
+
parseOwnershipMessage(raw, { authenticated_from_aid: sender, local_aid: this._aid, ...(request ? {} : { task: { ...control, initiator_aid: this._aid } }) });
|
|
1244
|
+
}
|
|
1245
|
+
catch (error) {
|
|
1246
|
+
if (error instanceof ValidationError && !error.retryable || error instanceof StateError && !error.retryable && ['invalid', 'forbidden'].includes(error.stringCode))
|
|
1247
|
+
return true;
|
|
1248
|
+
throw error;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return this._getOwnershipWorkflow().receive(sender, raw);
|
|
1228
1252
|
}
|
|
1229
1253
|
get hasIdentity() {
|
|
1230
1254
|
return this._currentAid !== null && this.state !== ConnectionState.CLOSED;
|
|
@@ -1339,6 +1363,7 @@ export class AUNClient {
|
|
|
1339
1363
|
this._certCache.clear();
|
|
1340
1364
|
this._gatewayUrl = null;
|
|
1341
1365
|
this._gatewayCandidates = [];
|
|
1366
|
+
this._peerGatewayCache.clear();
|
|
1342
1367
|
this._deviceId = aid.deviceId || getDeviceId(nextConfig.aunPath);
|
|
1343
1368
|
this._slotId = aid.slotId || 'default';
|
|
1344
1369
|
const debugFlag = nextConfig.debug;
|
|
@@ -1416,10 +1441,9 @@ export class AUNClient {
|
|
|
1416
1441
|
this._serviceEventIdentityAbort = new AbortController();
|
|
1417
1442
|
this._ensureReadStateIdentity();
|
|
1418
1443
|
if (previousAid !== this._aid) {
|
|
1419
|
-
this._ownershipRecords.clear();
|
|
1420
1444
|
this._ownershipType = 'agent';
|
|
1421
1445
|
}
|
|
1422
|
-
this.
|
|
1446
|
+
this._restoreIdentityType();
|
|
1423
1447
|
}
|
|
1424
1448
|
setProtectedHeaders(headers) {
|
|
1425
1449
|
if (!headers) {
|
|
@@ -1502,6 +1526,7 @@ export class AUNClient {
|
|
|
1502
1526
|
}
|
|
1503
1527
|
/** 关闭连接 */
|
|
1504
1528
|
async close() {
|
|
1529
|
+
this._peerGatewayCache.clear();
|
|
1505
1530
|
return this._lifecycle.close();
|
|
1506
1531
|
}
|
|
1507
1532
|
/**
|
|
@@ -2647,9 +2672,11 @@ export class AUNClient {
|
|
|
2647
2672
|
on(event, handler) {
|
|
2648
2673
|
const tStart = Date.now();
|
|
2649
2674
|
this._clientLog.debug(`on enter: event=${event}`);
|
|
2675
|
+
const messageEvent = event === 'message.received' || event === 'message.recalled' || event === 'message.batch';
|
|
2676
|
+
const hadMessageConsumer = messageEvent && [...this._dispatcher._snapshotApplicationHandlers(['message.received', 'message.recalled', 'message.batch']).values()].some((handlers) => handlers.length > 0);
|
|
2650
2677
|
const result = this._dispatcher.subscribe(event, handler);
|
|
2651
|
-
if (
|
|
2652
|
-
if (this._p2pPeerPullEnabled)
|
|
2678
|
+
if (messageEvent) {
|
|
2679
|
+
if (this._p2pPeerPullEnabled && !hadMessageConsumer)
|
|
2653
2680
|
this._delivery.refreshPendingPeers();
|
|
2654
2681
|
this._delivery.requestBackgroundPullRecovery();
|
|
2655
2682
|
}
|
|
@@ -4041,7 +4068,8 @@ export class AUNClient {
|
|
|
4041
4068
|
this._clientLog.warn(`schedule post-connect P2P gap fill failed: ${formatCaughtError(exc)}`);
|
|
4042
4069
|
});
|
|
4043
4070
|
}
|
|
4044
|
-
|
|
4071
|
+
if (!this._ownershipOperation)
|
|
4072
|
+
this._safeAsync(this._checkIdentityAdmissionAfterConnect());
|
|
4045
4073
|
this._clientLog.debug(`_connectOnce exit: elapsed=${Date.now() - tStart}ms gateway=${gatewayUrl}, aid=${this._aid ?? ''}`);
|
|
4046
4074
|
}
|
|
4047
4075
|
catch (err) {
|
|
@@ -4680,20 +4708,15 @@ export class AUNClient {
|
|
|
4680
4708
|
return gateways[0];
|
|
4681
4709
|
}
|
|
4682
4710
|
async _resolveOwnershipCertificateGateway(aid) {
|
|
4683
|
-
const issuer = aid.slice(aid.indexOf('.') + 1);
|
|
4684
|
-
if (issuer === this._aid?.slice(this._aid.indexOf('.') + 1))
|
|
4711
|
+
const issuer = aid.slice(aid.indexOf('.') + 1).trim().toLowerCase();
|
|
4712
|
+
if (issuer === this._aid?.slice(this._aid.indexOf('.') + 1).trim().toLowerCase())
|
|
4685
4713
|
return this._resolveGatewayForAid(aid);
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
}
|
|
4692
|
-
catch (error) {
|
|
4693
|
-
lastError = error;
|
|
4694
|
-
}
|
|
4714
|
+
try {
|
|
4715
|
+
return await this._resolveGatewayForAid(aid);
|
|
4716
|
+
}
|
|
4717
|
+
catch (error) {
|
|
4718
|
+
throw ownershipFailure('unavailable', error instanceof Error ? error.message : 'signer Gateway discovery failed', true);
|
|
4695
4719
|
}
|
|
4696
|
-
throw ownershipFailure('unavailable', lastError instanceof Error ? lastError.message : 'signer Gateway discovery failed', true);
|
|
4697
4720
|
}
|
|
4698
4721
|
/** 发现并缓存全部 Gateway 候选,供首连和自动重连故障转移使用。 */
|
|
4699
4722
|
async _resolveGatewayCandidatesForAid(aid) {
|
|
@@ -4701,17 +4724,26 @@ export class AUNClient {
|
|
|
4701
4724
|
if (!resolvedAid) {
|
|
4702
4725
|
throw new StateError('gateway discovery requires a loaded AID');
|
|
4703
4726
|
}
|
|
4727
|
+
const dotIdx = resolvedAid.indexOf('.');
|
|
4728
|
+
const issuerDomain = (dotIdx >= 0 ? resolvedAid.slice(dotIdx + 1) : resolvedAid).trim().toLowerCase();
|
|
4729
|
+
const currentAid = this._aid;
|
|
4730
|
+
const sameIssuer = currentAid !== null && issuerDomain === currentAid.slice(currentAid.indexOf('.') + 1).trim().toLowerCase();
|
|
4704
4731
|
const discovery = this._discovery;
|
|
4705
4732
|
const tokenStore = this._tokenStore;
|
|
4706
4733
|
const discoveryIsCurrent = () => (this._discovery === discovery
|
|
4707
4734
|
&& this._tokenStore === tokenStore
|
|
4708
4735
|
&& !this._closing);
|
|
4709
|
-
if (this._gatewayCandidates.length > 0)
|
|
4736
|
+
if (sameIssuer && this._gatewayCandidates.length > 0)
|
|
4710
4737
|
return [...this._gatewayCandidates];
|
|
4711
|
-
if (this._gatewayUrl) {
|
|
4738
|
+
if (sameIssuer && this._gatewayUrl) {
|
|
4712
4739
|
this._gatewayCandidates = [this._gatewayUrl];
|
|
4713
4740
|
return [...this._gatewayCandidates];
|
|
4714
4741
|
}
|
|
4742
|
+
if (!sameIssuer) {
|
|
4743
|
+
const cached = this._peerGatewayCache.get(issuerDomain);
|
|
4744
|
+
if (cached)
|
|
4745
|
+
return [...cached];
|
|
4746
|
+
}
|
|
4715
4747
|
try {
|
|
4716
4748
|
const loadMetadata = tokenStore.loadMetadata;
|
|
4717
4749
|
const metadata = typeof loadMetadata === 'function'
|
|
@@ -4723,16 +4755,19 @@ export class AUNClient {
|
|
|
4723
4755
|
const cachedGateway = cachedList[0]
|
|
4724
4756
|
?? (isJsonObject(metadata) ? String(metadata.gateway_url ?? '').trim() : '');
|
|
4725
4757
|
if (cachedGateway) {
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4758
|
+
const gatewayUrls = [...new Set([cachedGateway, ...cachedList])];
|
|
4759
|
+
if (sameIssuer) {
|
|
4760
|
+
this._gatewayCandidates = gatewayUrls;
|
|
4761
|
+
this._gatewayUrl = gatewayUrls[0];
|
|
4762
|
+
}
|
|
4763
|
+
else
|
|
4764
|
+
this._peerGatewayCache.set(issuerDomain, gatewayUrls);
|
|
4765
|
+
return [...gatewayUrls];
|
|
4729
4766
|
}
|
|
4730
4767
|
}
|
|
4731
4768
|
catch {
|
|
4732
4769
|
// 缓存读取失败不影响发现流程。
|
|
4733
4770
|
}
|
|
4734
|
-
const dotIdx = resolvedAid.indexOf('.');
|
|
4735
|
-
const issuerDomain = dotIdx >= 0 ? resolvedAid.slice(dotIdx + 1) : resolvedAid;
|
|
4736
4771
|
const portSuffix = this._configModel.discoveryPort ? `:${this._configModel.discoveryPort}` : '';
|
|
4737
4772
|
const aidUrl = `https://${resolvedAid}${portSuffix}/.well-known/aun-gateway`;
|
|
4738
4773
|
const gatewayDomainUrl = `https://gateway.${issuerDomain}${portSuffix}/.well-known/aun-gateway`;
|
|
@@ -4752,9 +4787,13 @@ export class AUNClient {
|
|
|
4752
4787
|
const gatewayUrls = [...new Set(discovered.map((item) => String(item ?? '').trim()).filter(Boolean))];
|
|
4753
4788
|
if (gatewayUrls.length === 0)
|
|
4754
4789
|
throw new ValidationError('gateway discovery returned no candidates');
|
|
4755
|
-
this._gatewayCandidates = gatewayUrls;
|
|
4756
4790
|
const gateway = gatewayUrls[0];
|
|
4757
|
-
|
|
4791
|
+
if (sameIssuer) {
|
|
4792
|
+
this._gatewayCandidates = gatewayUrls;
|
|
4793
|
+
this._gatewayUrl = gateway;
|
|
4794
|
+
}
|
|
4795
|
+
else
|
|
4796
|
+
this._peerGatewayCache.set(issuerDomain, gatewayUrls);
|
|
4758
4797
|
try {
|
|
4759
4798
|
const saveMetadata = tokenStore.saveMetadata;
|
|
4760
4799
|
if (typeof saveMetadata === 'function') {
|