@agentunion/fastaun 0.5.27 → 0.5.29
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 +55 -0
- package/_packed_docs/CHANGELOG.md +55 -0
- package/_packed_docs/INDEX.md +23 -3
- package/_packed_docs/KITE_DOCS_GUIDE.md +15 -11
- package/_packed_docs/sdk/0.5.28-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +228 -0
- package/_packed_docs/sdk/0.5.28-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +146 -0
- package/_packed_docs/sdk/0.5.29-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +180 -0
- package/_packed_docs/sdk/0.5.29-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +164 -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 -90
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +6 -4
- package/_packed_docs/sdk/INDEX.md +14 -4
- 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 +1 -1
- 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 +1 -1
- package/_packed_docs//346/216/247/345/210/266/346/266/210/346/201/257/344/270/216/350/256/276/345/244/207/350/203/275/345/212/233/345/215/217/345/225/206/346/226/271/346/241/210.md +426 -0
- package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/344/273/243/347/240/201/345/256/241/346/237/245/351/227/256/351/242/230/346/270/205/345/215/225.md +279 -229
- package/_packed_docs//351/243/216/346/216/247/345/223/215/345/272/224/344/270/216SDK/345/215/217/345/220/214/346/226/271/346/241/210.md +381 -0
- package/dist/agent-md-changes.d.ts +30 -0
- package/dist/agent-md-changes.js +168 -0
- package/dist/agent-md-changes.js.map +1 -0
- package/dist/agent-md-schema.d.ts +1 -0
- package/dist/agent-md-schema.js +29 -12
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +9 -0
- package/dist/agent-md.js +107 -36
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +14 -1
- package/dist/aid-store.js +163 -61
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +10 -0
- package/dist/auth.js +44 -4
- package/dist/auth.js.map +1 -1
- package/dist/client/control-messages.d.ts +31 -0
- package/dist/client/control-messages.js +496 -0
- package/dist/client/control-messages.js.map +1 -0
- package/dist/client/delivery.js +12 -3
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +2 -3
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +8 -0
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.js +76 -13
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +3 -0
- package/dist/client/v2-e2ee.js +13 -4
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +27 -1
- package/dist/client.js +348 -38
- package/dist/client.js.map +1 -1
- package/dist/discovery.d.ts +3 -0
- package/dist/discovery.js +53 -2
- package/dist/discovery.js.map +1 -1
- package/dist/events.js +14 -1
- package/dist/events.js.map +1 -1
- package/dist/ownership-v21.d.ts +2 -1
- package/dist/ownership-v21.js +17 -3
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.js +9 -5
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/risk-control.d.ts +68 -0
- package/dist/risk-control.js +344 -0
- package/dist/risk-control.js.map +1 -0
- package/dist/semantic-facade.js +4 -2
- package/dist/semantic-facade.js.map +1 -1
- package/dist/semantic-protocol.js +2 -0
- package/dist/semantic-protocol.js.map +1 -1
- package/dist/storage/lowlevel.d.ts +1 -0
- package/dist/storage/lowlevel.js +2 -2
- package/dist/storage/lowlevel.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +1 -1
- package/dist/tools/cross-sdk-agent.js +16 -34
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +21 -1
- package/dist/transport.js +199 -20
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -8
package/dist/aid-store.js
CHANGED
|
@@ -9,6 +9,7 @@ import { join } from 'node:path';
|
|
|
9
9
|
import { homedir } from 'node:os';
|
|
10
10
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { AID } from './aid.js';
|
|
12
|
+
import { RiskController } from './risk-control.js';
|
|
12
13
|
import * as codes from './error-codes.js';
|
|
13
14
|
import { certCommonName, certMatchesFingerprint, certTimeError, parseAgentMdTailSignature, publicKeyMatchesFingerprint, signBytes, signDocument, verifyDocument, verifySignatureWithCert } from './cert-utils.js';
|
|
14
15
|
import { certFingerprint as _certFingerprint } from './cert-utils.js';
|
|
@@ -20,11 +21,12 @@ import { GatewayDiscovery } from './discovery.js';
|
|
|
20
21
|
import { DnsResilientNet } from './net.js';
|
|
21
22
|
import { AUNLogger } from './logger.js';
|
|
22
23
|
import { getDeviceId, normalizeSlotId } from './config.js';
|
|
23
|
-
import { AUNError, ClientSignatureError, IdentityConflictError, NotFoundError, ValidationError } from './errors.js';
|
|
24
|
+
import { AUNError, ClientSignatureError, IdentityConflictError, NotFoundError, StateError, ValidationError } from './errors.js';
|
|
24
25
|
import { AgentMdManager, buildDefaultAgentMd } from './agent-md.js';
|
|
25
26
|
import { parseFileFrontmatter, validateAgentMdCertificate, validateAgentMdDocument, validateAgentMdReadDocument } from './agent-md-schema.js';
|
|
26
27
|
import { LocalTokenStore } from './keystore/local-token-store.js';
|
|
27
28
|
import { AuthFlow } from './auth.js';
|
|
29
|
+
import { isJsonObject } from './types.js';
|
|
28
30
|
import { ownershipFailure } from './ownership-v21.js';
|
|
29
31
|
import { validateAIDFormat } from './validators.js';
|
|
30
32
|
function resultError(r) {
|
|
@@ -98,6 +100,66 @@ export class AIDStore {
|
|
|
98
100
|
_agentMdManager;
|
|
99
101
|
_ownershipInflight = new WeakMap();
|
|
100
102
|
_peerAuth;
|
|
103
|
+
_riskControllers = new Map();
|
|
104
|
+
_liveRiskControllers = new Map();
|
|
105
|
+
_riskLoads = new Map();
|
|
106
|
+
_riskPersistedStates = new Map();
|
|
107
|
+
_ownedRiskControllers = new Set();
|
|
108
|
+
setRiskController(aid, controller) { this._liveRiskControllers.set(aid, controller); }
|
|
109
|
+
async _riskForAid(aid) {
|
|
110
|
+
const live = await this._liveRiskControllers.get(aid)?.();
|
|
111
|
+
if (live)
|
|
112
|
+
return live;
|
|
113
|
+
const existing = this._riskControllers.get(aid);
|
|
114
|
+
const stored = this._tokenStore._loadMetadataStrict(aid)?.[`risk_state_v1:${this.deviceId}`];
|
|
115
|
+
const raw = stored ? (typeof stored === 'string' ? stored : JSON.stringify(stored)) : '';
|
|
116
|
+
if (existing && this._riskPersistedStates.get(aid) === raw)
|
|
117
|
+
return existing;
|
|
118
|
+
const loading = this._riskLoads.get(aid);
|
|
119
|
+
if (loading)
|
|
120
|
+
return await loading;
|
|
121
|
+
const task = this._loadRiskForAid(aid, raw);
|
|
122
|
+
this._riskLoads.set(aid, task);
|
|
123
|
+
try {
|
|
124
|
+
return await task;
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
if (this._riskLoads.get(aid) === task)
|
|
128
|
+
this._riskLoads.delete(aid);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async _loadRiskForAid(aid, raw) {
|
|
132
|
+
const controller = new RiskController({ persist: async (state) => {
|
|
133
|
+
const saved = JSON.stringify(state);
|
|
134
|
+
this._tokenStore.saveMetadata(aid, { ...(this._tokenStore._loadMetadataStrict(aid) ?? {}), [`risk_state_v1:${this.deviceId}`]: state });
|
|
135
|
+
this._riskPersistedStates.set(aid, saved);
|
|
136
|
+
} });
|
|
137
|
+
this._riskPersistedStates.set(aid, raw);
|
|
138
|
+
if (raw) {
|
|
139
|
+
const state = JSON.parse(raw);
|
|
140
|
+
if (!isJsonObject(state))
|
|
141
|
+
throw new StateError('persisted risk state is unavailable', { stringCode: 'risk_state_unavailable' });
|
|
142
|
+
await controller.restorePinned(state, { aid, device_id: this.deviceId, slot_id: this.slotId, connection_id: '' });
|
|
143
|
+
}
|
|
144
|
+
const previous = this._riskControllers.get(aid);
|
|
145
|
+
if (previous && this._ownedRiskControllers.has(previous)) {
|
|
146
|
+
previous.close();
|
|
147
|
+
this._ownedRiskControllers.delete(previous);
|
|
148
|
+
}
|
|
149
|
+
this._riskControllers.set(aid, controller);
|
|
150
|
+
this._ownedRiskControllers.add(controller);
|
|
151
|
+
return controller;
|
|
152
|
+
}
|
|
153
|
+
async _riskFetchForAid(aid, url, init, perform, scope) {
|
|
154
|
+
const controller = await this._riskForAid(aid);
|
|
155
|
+
const release = controller.acquire(scope);
|
|
156
|
+
try {
|
|
157
|
+
return await perform(url, init);
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
release();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
101
163
|
async sign(aid, input, options = {}) {
|
|
102
164
|
try {
|
|
103
165
|
if (!['text', 'file'].includes(options.inputType ?? 'text') || !['raw', 'document'].includes(options.format ?? 'raw'))
|
|
@@ -262,32 +324,41 @@ export class AIDStore {
|
|
|
262
324
|
});
|
|
263
325
|
}
|
|
264
326
|
close() {
|
|
327
|
+
for (const controller of this._ownedRiskControllers)
|
|
328
|
+
controller.close();
|
|
329
|
+
this._ownedRiskControllers.clear();
|
|
265
330
|
this._keystore.close();
|
|
266
331
|
this._tokenStore.close();
|
|
267
332
|
const net = this._net;
|
|
268
333
|
if (typeof net.close === 'function')
|
|
269
334
|
net.close();
|
|
270
335
|
}
|
|
271
|
-
|
|
336
|
+
_riskPeerAuth(actorAid) {
|
|
337
|
+
const auth = new AuthFlow({ tokenStore: this._tokenStore, crypto: this._crypto, deviceId: this.deviceId, slotId: this.slotId, verifySsl: this._verifySsl, rootCaPath: this._rootCaPath, logger: this._log.for('aun_core.auth'), net: this._net });
|
|
338
|
+
auth.setHttpHandler((url, init, perform) => this._riskFetchForAid(actorAid, url, init, perform, 'http:gateway:pki'));
|
|
339
|
+
return auth;
|
|
340
|
+
}
|
|
341
|
+
async _resolveAgentMdPeerCertificate(target, certFingerprint, signedAt, actorAid) {
|
|
342
|
+
const auth = actorAid ? this._riskPeerAuth(actorAid) : this._peerAuth;
|
|
272
343
|
const fp = String(certFingerprint ?? '').trim().toLowerCase();
|
|
273
344
|
const cachedCert = this._keystore.loadCert(target, fp || undefined);
|
|
274
345
|
if (cachedCert && (!fp || certMatchesFingerprint(cachedCert, fp) || publicKeyMatchesFingerprint(cachedCert, fp))) {
|
|
275
346
|
if (signedAt !== undefined)
|
|
276
|
-
await this._verifyDownloadedPeerCertificate(await this._resolveGateway(target), target, cachedCert, fp, signedAt);
|
|
347
|
+
await this._verifyDownloadedPeerCertificate(await this._resolveGateway(target, actorAid), target, cachedCert, fp, signedAt, auth);
|
|
277
348
|
return AID._create({ aid: target, aunPath: this.aunPath, certPem: cachedCert, privateKeyPem: null, certValid: true, privateKeyValid: false, deviceId: this.deviceId, slotId: this.slotId, verifySsl: this._verifySsl, rootCaPath: this._rootCaPath, debug: this._debug });
|
|
278
349
|
}
|
|
279
350
|
if (fp) {
|
|
280
|
-
const gatewayUrl = await this._resolveGateway(target);
|
|
281
|
-
const certPem = await this._net.httpGetText(pkiCertUrl(gatewayUrl, target, fp), 10_000);
|
|
282
|
-
await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp, signedAt);
|
|
351
|
+
const gatewayUrl = await this._resolveGateway(target, actorAid);
|
|
352
|
+
const certPem = actorAid ? await auth._fetchRiskText(pkiCertUrl(gatewayUrl, target, fp)) : await this._net.httpGetText(pkiCertUrl(gatewayUrl, target, fp), 10_000);
|
|
353
|
+
await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, fp, signedAt, auth);
|
|
283
354
|
this._keystore.saveCert(target, certPem, _certFingerprint(certPem), { makeActive: false });
|
|
284
355
|
return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
|
|
285
356
|
}
|
|
286
|
-
const gatewayUrl = await this._resolveGateway(target);
|
|
287
|
-
const certPem = await this._registerFlow.fetchPeerCert(gatewayUrl, target);
|
|
357
|
+
const gatewayUrl = await this._resolveGateway(target, actorAid);
|
|
358
|
+
const certPem = actorAid ? await auth._fetchRiskText(pkiCertUrl(gatewayUrl, target)) : await this._registerFlow.fetchPeerCert(gatewayUrl, target);
|
|
288
359
|
if (!certPem)
|
|
289
360
|
throw new Error(`certificate not found for aid: ${target}`);
|
|
290
|
-
await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, undefined, signedAt);
|
|
361
|
+
await this._verifyDownloadedPeerCertificate(gatewayUrl, target, certPem, undefined, signedAt, auth);
|
|
291
362
|
this._keystore.saveCert(target, certPem);
|
|
292
363
|
return AID._create({ aid: target, aunPath: this.aunPath, certPem, privateKeyPem: null, certValid: true, privateKeyValid: false });
|
|
293
364
|
}
|
|
@@ -412,6 +483,11 @@ export class AIDStore {
|
|
|
412
483
|
}
|
|
413
484
|
// ── 联网方法 ──────────────────────────────────────────────────
|
|
414
485
|
async register(aid, options) {
|
|
486
|
+
if (['group', 'org'].includes(String(options?.type ?? '').trim()))
|
|
487
|
+
return resultErr(codes.INVALID_AID_FORMAT, 'group/org must use their dedicated creation flow');
|
|
488
|
+
return this._registerIdentity(aid, options);
|
|
489
|
+
}
|
|
490
|
+
async _registerIdentity(aid, options) {
|
|
415
491
|
const target = String(aid ?? '').trim();
|
|
416
492
|
this._log.for('aun_core.aid_store').debug(`register enter: aid=${target || '-'}`);
|
|
417
493
|
try {
|
|
@@ -420,8 +496,8 @@ export class AIDStore {
|
|
|
420
496
|
const entity = ['group', 'org'].includes(type);
|
|
421
497
|
if (entity && !owner)
|
|
422
498
|
throw new ValidationError('group/org registration requires owner');
|
|
423
|
-
if (!type || type === 'human' && owner)
|
|
424
|
-
throw new ValidationError('register requires type; human forbids owner');
|
|
499
|
+
if (!type || type === 'human' && owner || type !== 'human' && !owner)
|
|
500
|
+
throw new ValidationError('register requires type; human forbids owner; other types require owner');
|
|
425
501
|
if (owner)
|
|
426
502
|
validateAIDFormat(owner, 'owner');
|
|
427
503
|
const exttype = String(options?.exttype ?? '').trim();
|
|
@@ -437,9 +513,11 @@ export class AIDStore {
|
|
|
437
513
|
curve: result.curve,
|
|
438
514
|
});
|
|
439
515
|
if (!entity) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
516
|
+
if (type === 'human') {
|
|
517
|
+
const initialized = await this._createInitialRegistrationAgentMd(target, type, exttype);
|
|
518
|
+
if (!initialized.ok)
|
|
519
|
+
return initialized;
|
|
520
|
+
}
|
|
443
521
|
if (owner) {
|
|
444
522
|
await this._bindRegistrationOwner(target, owner);
|
|
445
523
|
}
|
|
@@ -469,6 +547,7 @@ export class AIDStore {
|
|
|
469
547
|
const client = new AUNClient(AID._create({ aid: target, aunPath: this.aunPath, certPem: identity.certPem, privateKeyPem: identity.privateKeyPem,
|
|
470
548
|
certValid: identity.isCertValid(), privateKeyValid: identity.isPrivateKeyValid(), deviceId: this.deviceId, slotId: `ownership-${randomUUID()}`,
|
|
471
549
|
verifySsl: this._verifySsl, rootCaPath: this._rootCaPath, debug: this._debug, aidStore: this }));
|
|
550
|
+
client._ownershipOperation = true;
|
|
472
551
|
try {
|
|
473
552
|
await client.connect({ connection_kind: 'short', background_sync: false, auto_reconnect: false });
|
|
474
553
|
const result = await client.bindOwneraid(owner);
|
|
@@ -588,48 +667,44 @@ export class AIDStore {
|
|
|
588
667
|
const record = this._agentMdManager.loadRecord(target);
|
|
589
668
|
if (data.agent_md?.etag !== undefined && data.agent_md.etag !== String(record?.remote_etag ?? ''))
|
|
590
669
|
throw new Error('displayed agent.md ETag does not match the current observation');
|
|
591
|
-
const cached = record?.ownership_snapshot;
|
|
592
670
|
const content = data.agent_md?.content ?? record?.content;
|
|
593
671
|
const hash = typeof content === 'string' ? AgentMdManager.contentEtag(content).slice(1, -1) : '';
|
|
594
672
|
const bypass = opts.ownershipTtlMs !== undefined && opts.ownershipTtlMs <= 0;
|
|
595
|
-
|
|
596
|
-
|
|
673
|
+
const observedHash = typeof record?.content === 'string' ? AgentMdManager.contentEtag(record.content).slice(1, -1) : '';
|
|
674
|
+
const observation = { root: this._agentMdManager.root, etag: String(record?.remote_etag ?? ''), version: Number(record?.observation_version ?? 0), content_hash: observedHash };
|
|
675
|
+
const key = JSON.stringify([target, observation, hash]);
|
|
676
|
+
let flights = this._ownershipInflight.get(opts.ownership);
|
|
677
|
+
if (!flights)
|
|
678
|
+
this._ownershipInflight.set(opts.ownership, flights = new Map());
|
|
679
|
+
let pending = bypass ? undefined : flights.get(key);
|
|
680
|
+
if (!pending) {
|
|
681
|
+
pending = (async () => {
|
|
682
|
+
const checker = opts.ownership;
|
|
683
|
+
const checked = bypass && typeof checker._checkOwnershipConsensusFresh === 'function'
|
|
684
|
+
? await checker._checkOwnershipConsensusFresh.call(opts.ownership, target)
|
|
685
|
+
: await opts.ownership.checkOwnershipConsensus(target);
|
|
686
|
+
if (['consensus', 'single_sided', 'released', 'unbound'].includes(checked.status) && (checked.sub_aid !== target || checked.agent_md_etag !== observation.etag || checked.agent_md_sha256 !== hash || observedHash !== hash))
|
|
687
|
+
throw new Error('ownership snapshot does not match the observed agent.md');
|
|
688
|
+
const snapshot = { ...JSON.parse(JSON.stringify(checked)), checked_at: Date.now(), cached: false };
|
|
689
|
+
delete snapshot.owner_md;
|
|
690
|
+
delete snapshot.owner_md_sha256;
|
|
691
|
+
if (!this._agentMdManager.cacheOwnershipSnapshot(target, snapshot, observation))
|
|
692
|
+
throw new Error('agent.md observation changed during ownership check');
|
|
693
|
+
return snapshot;
|
|
694
|
+
})();
|
|
695
|
+
if (!bypass)
|
|
696
|
+
flights.set(key, pending);
|
|
597
697
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
this._ownershipInflight.set(opts.ownership, flights = new Map());
|
|
605
|
-
let pending = bypass ? undefined : flights.get(key);
|
|
606
|
-
if (!pending) {
|
|
607
|
-
pending = (async () => {
|
|
608
|
-
const checker = opts.ownership;
|
|
609
|
-
const checked = bypass && typeof checker._checkOwnershipConsensusFresh === 'function'
|
|
610
|
-
? await checker._checkOwnershipConsensusFresh.call(opts.ownership, target)
|
|
611
|
-
: await opts.ownership.checkOwnershipConsensus(target);
|
|
612
|
-
if (['consensus', 'single_sided', 'released', 'unbound'].includes(checked.status) && (checked.sub_aid !== target || checked.agent_md_etag !== observation.etag || checked.agent_md_sha256 !== hash || observedHash !== hash))
|
|
613
|
-
throw new Error('ownership snapshot does not match the observed agent.md');
|
|
614
|
-
const snapshot = { ...JSON.parse(JSON.stringify(checked)), checked_at: Date.now(), cached: false };
|
|
615
|
-
if (!this._agentMdManager.cacheOwnershipSnapshot(target, snapshot, observation))
|
|
616
|
-
throw new Error('agent.md observation changed during ownership check');
|
|
617
|
-
return snapshot;
|
|
618
|
-
})();
|
|
619
|
-
if (!bypass)
|
|
620
|
-
flights.set(key, pending);
|
|
621
|
-
}
|
|
622
|
-
try {
|
|
623
|
-
data.ownership = JSON.parse(JSON.stringify(await pending));
|
|
624
|
-
}
|
|
625
|
-
finally {
|
|
626
|
-
if (flights.get(key) === pending)
|
|
627
|
-
flights.delete(key);
|
|
628
|
-
}
|
|
698
|
+
try {
|
|
699
|
+
data.ownership = JSON.parse(JSON.stringify(await pending));
|
|
700
|
+
}
|
|
701
|
+
finally {
|
|
702
|
+
if (flights.get(key) === pending)
|
|
703
|
+
flights.delete(key);
|
|
629
704
|
}
|
|
630
705
|
}
|
|
631
706
|
catch (error) {
|
|
632
|
-
data.ownership = { status: 'unavailable', sub_aid: target, owner_aid: null, relation_version: 0, lock: 'unlocked', agent_md_etag: '', agent_md_sha256: '',
|
|
707
|
+
data.ownership = { status: 'unavailable', sub_aid: target, owner_aid: null, relation_version: 0, lock: 'unlocked', agent_md_etag: '', agent_md_sha256: '', code: 'unavailable', reason: error instanceof Error ? error.message : String(error), retryable: true, checked_at: Date.now(), cached: false };
|
|
633
708
|
}
|
|
634
709
|
return resultOk(data);
|
|
635
710
|
}
|
|
@@ -649,16 +724,16 @@ export class AIDStore {
|
|
|
649
724
|
return agentMdFailure(exc);
|
|
650
725
|
}
|
|
651
726
|
}
|
|
652
|
-
async _verifyDownloadedPeerCertificate(gatewayUrl, aid, certPem, certFingerprint = '', signedAt) {
|
|
727
|
+
async _verifyDownloadedPeerCertificate(gatewayUrl, aid, certPem, certFingerprint = '', signedAt, auth = this._peerAuth) {
|
|
653
728
|
const expectedFp = String(certFingerprint ?? '').trim().toLowerCase();
|
|
654
729
|
if (expectedFp && !certMatchesFingerprint(certPem, expectedFp) && !(signedAt !== undefined && publicKeyMatchesFingerprint(certPem, expectedFp))) {
|
|
655
730
|
throw new ValidationError(`certificate fingerprint mismatch for aid: ${aid}`);
|
|
656
731
|
}
|
|
657
732
|
if (signedAt === undefined)
|
|
658
|
-
await
|
|
733
|
+
await auth.verifyPeerCertificate(gatewayUrl, certPem, aid);
|
|
659
734
|
else {
|
|
660
735
|
validateAgentMdCertificate(certPem, aid, signedAt, { requireCurrentValidity: false, readCompatibility: true });
|
|
661
|
-
await
|
|
736
|
+
await auth._verifyAuthCertChain(gatewayUrl, new X509Certificate(certPem), aid, signedAt);
|
|
662
737
|
}
|
|
663
738
|
}
|
|
664
739
|
_authIdentityFromAid(aid) {
|
|
@@ -670,6 +745,7 @@ export class AIDStore {
|
|
|
670
745
|
};
|
|
671
746
|
}
|
|
672
747
|
async _uploadAgentMdToken(aid, gatewayUrl) {
|
|
748
|
+
const risk = await this._riskForAid(aid.aid);
|
|
673
749
|
const auth = new AuthFlow({
|
|
674
750
|
tokenStore: this._tokenStore,
|
|
675
751
|
crypto: this._crypto,
|
|
@@ -681,6 +757,8 @@ export class AIDStore {
|
|
|
681
757
|
logger: this._log.for('aun_core.auth'),
|
|
682
758
|
net: this._net,
|
|
683
759
|
});
|
|
760
|
+
auth.setRequestAdmission((request) => risk.acquire(request));
|
|
761
|
+
auth.setHttpHandler((url, init, perform) => this._riskFetchForAid(aid.aid, url, init, perform, 'http:gateway:pki'));
|
|
684
762
|
auth.setIdentity(this._authIdentityFromAid(aid));
|
|
685
763
|
const result = await auth.authenticate(gatewayUrl, { aid: aid.aid });
|
|
686
764
|
const token = String(result.access_token ?? result.token ?? result.kite_token ?? '').trim();
|
|
@@ -689,6 +767,9 @@ export class AIDStore {
|
|
|
689
767
|
return token;
|
|
690
768
|
}
|
|
691
769
|
async uploadAgentMd(aid, content, options = {}) {
|
|
770
|
+
return this._uploadAgentMd(aid, content, options);
|
|
771
|
+
}
|
|
772
|
+
async _uploadAgentMd(aid, content, options, onCommitted) {
|
|
692
773
|
const target = String(aid ?? '').trim();
|
|
693
774
|
try {
|
|
694
775
|
this._registerFlow.validateAidName(target);
|
|
@@ -700,7 +781,7 @@ export class AIDStore {
|
|
|
700
781
|
const current = loaded.data.aid;
|
|
701
782
|
const pending = content === undefined || content === null ? this._agentMdManager.pendingUpload(target) : null;
|
|
702
783
|
const raw = pending?.content ?? content ?? (String((this._agentMdManager._loadRecord?.(target, true) ?? this._agentMdManager.loadRecord(target))?.local_draft_content ?? '') || this._agentMdManager.readContent(target));
|
|
703
|
-
const parsed = validateAgentMdDocument(raw, { expectedAid: target, validateOwnership: false });
|
|
784
|
+
const parsed = validateAgentMdDocument(raw, { expectedAid: target, validateOwnership: false, forUpload: true });
|
|
704
785
|
const meta = parsed.frontmatter;
|
|
705
786
|
const entity = ['group', 'org'].includes(String(meta.type));
|
|
706
787
|
const uploaderTarget = String(options.uploaderAid ?? options.uploader_aid ?? (entity ? meta.owner : target)).trim();
|
|
@@ -727,22 +808,40 @@ export class AIDStore {
|
|
|
727
808
|
uploadViaGatewayHost: true,
|
|
728
809
|
ownerAidGetter: () => entity ? uploaderTarget : target,
|
|
729
810
|
currentAidGetter: () => entity ? uploader : current,
|
|
811
|
+
onCommitted: onCommitted ? (value, notice) => onCommitted(value, notice, manager) : (async (value, notice) => {
|
|
812
|
+
const { AUNClient } = await import('./client.js');
|
|
813
|
+
const client = new AUNClient(AID._create({ aid: uploader.aid, aunPath: this.aunPath, certPem: uploader.certPem, privateKeyPem: uploader.privateKeyPem,
|
|
814
|
+
certValid: uploader.isCertValid(), privateKeyValid: uploader.isPrivateKeyValid(), deviceId: this.deviceId, slotId: `agent-md-${randomUUID()}`,
|
|
815
|
+
verifySsl: this._verifySsl, rootCaPath: this._rootCaPath, debug: this._debug, aidStore: this }));
|
|
816
|
+
client._ownershipOperation = true;
|
|
817
|
+
try {
|
|
818
|
+
await client.connect({ connection_kind: 'short', background_sync: false, auto_reconnect: false });
|
|
819
|
+
await client._getAgentMdChanges().committed(value, notice, manager);
|
|
820
|
+
}
|
|
821
|
+
finally {
|
|
822
|
+
await client.close();
|
|
823
|
+
}
|
|
824
|
+
}),
|
|
730
825
|
entityIdentityGetter: options.entityIdentityGetter ?? (async (value, type) => {
|
|
731
|
-
const gateway = await this._resolveGateway(value);
|
|
732
|
-
const material = await this.
|
|
826
|
+
const gateway = await this._resolveGateway(value, uploaderTarget);
|
|
827
|
+
const material = await this._riskPeerAuth(uploaderTarget).resolveCurrentEntityCertificate(gateway, value, type ?? 'org');
|
|
733
828
|
const certPem = material.cert_pem;
|
|
734
829
|
return AID._create({ aid: value, aunPath: this.aunPath, certPem, certValid: true, privateKeyPem: null, privateKeyValid: false });
|
|
735
830
|
}),
|
|
736
831
|
entitySignerResolver: async (value, fingerprint) => {
|
|
737
|
-
const material = await this.
|
|
832
|
+
const material = await this._riskPeerAuth(uploaderTarget).resolveOwnershipCertificate(await this._resolveGateway(value, uploaderTarget), value, fingerprint, 'signing');
|
|
738
833
|
return AID._create({ aid: value, aunPath: this.aunPath, certPem: material.cert_pem, certValid: true, privateKeyPem: null, privateKeyValid: false });
|
|
739
834
|
},
|
|
740
835
|
entityOwnerResolver: options.entityOwnerResolver,
|
|
741
|
-
peerResolver: (value, fingerprint, signedAt) => this._resolveAgentMdPeerCertificate(value, fingerprint, signedAt),
|
|
742
|
-
gatewayResolver: (value) => this._resolveGateway(value),
|
|
743
|
-
accessTokenResolver: async () => this._uploadAgentMdToken(uploader, await this._resolveGateway(uploaderTarget)),
|
|
836
|
+
peerResolver: (value, fingerprint, signedAt) => this._resolveAgentMdPeerCertificate(value, fingerprint, signedAt, uploaderTarget),
|
|
837
|
+
gatewayResolver: (value) => this._resolveGateway(value, uploaderTarget),
|
|
838
|
+
accessTokenResolver: async () => this._uploadAgentMdToken(uploader, await this._resolveGateway(uploaderTarget, uploaderTarget)),
|
|
744
839
|
aidValidator: (value) => this._registerFlow.validateAidName(value),
|
|
745
840
|
});
|
|
841
|
+
manager.setHttpHandler((url, init, perform) => {
|
|
842
|
+
const headers = new Headers(init.headers);
|
|
843
|
+
return this._riskFetchForAid(uploaderTarget, url, init, perform, headers.has('X-AUN-Ownership-Check') || headers.has('X-AUN-Ownership-Stage') ? 'http:nameservice:ownership' : 'http:nameservice:agent_md');
|
|
844
|
+
});
|
|
746
845
|
if (pending) {
|
|
747
846
|
await manager.resumePendingUpload(target, current);
|
|
748
847
|
const record = manager.loadRecord(target);
|
|
@@ -1011,7 +1110,7 @@ export class AIDStore {
|
|
|
1011
1110
|
return Promise.resolve(this.importGroupIdentity(aidOrOptions, options));
|
|
1012
1111
|
}
|
|
1013
1112
|
// ── 内部辅助 ──────────────────────────────────────────────────
|
|
1014
|
-
async _resolveGateway(aid) {
|
|
1113
|
+
async _resolveGateway(aid, actorAid) {
|
|
1015
1114
|
const target = String(aid ?? '').trim();
|
|
1016
1115
|
if (!target)
|
|
1017
1116
|
throw new ValidationError('AIDStore requires non-empty aid to resolve gateway');
|
|
@@ -1024,10 +1123,13 @@ export class AIDStore {
|
|
|
1024
1123
|
`https://${target}/.well-known/aun-gateway`,
|
|
1025
1124
|
`https://gateway.${issuerDomain}/.well-known/aun-gateway`,
|
|
1026
1125
|
];
|
|
1126
|
+
const discovery = actorAid ? new GatewayDiscovery({ verifySsl: this._verifySsl, logger: this._log.for('aun_core.discovery'), net: this._net }) : this._discovery;
|
|
1127
|
+
if (actorAid)
|
|
1128
|
+
discovery.setHttpHandler((url, init, perform) => this._riskFetchForAid(actorAid, url, init, perform, 'http:discovery:gateway'));
|
|
1027
1129
|
let lastError = null;
|
|
1028
1130
|
for (const url of candidates) {
|
|
1029
1131
|
try {
|
|
1030
|
-
const discovered = await
|
|
1132
|
+
const discovered = await discovery.discover(url);
|
|
1031
1133
|
this._gatewayCache.set(issuerDomain, discovered);
|
|
1032
1134
|
return discovered;
|
|
1033
1135
|
}
|