@agentunion/fastaun-browser 0.3.6 → 0.4.0

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.
Files changed (69) hide show
  1. package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/345/256/236/346/226/275/350/256/241/345/210/222.md +596 -0
  2. package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/350/256/276/350/256/241/346/226/271/346/241/210_v3.md +1633 -0
  3. package/_packed_docs/INDEX.md +17 -11
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +11 -10
  5. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +134 -158
  6. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +11 -7
  7. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +98 -119
  8. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +147 -374
  9. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +153 -153
  10. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +163 -1383
  11. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +71 -91
  12. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +76 -63
  13. package/_packed_docs/sdk/09-custody-api-manual.md +7 -6
  14. package/_packed_docs/sdk/09-meta-rpc-manual.md +13 -14
  15. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +37 -49
  16. package/_packed_docs/sdk/INDEX.md +72 -98
  17. package/_packed_docs/sdk/README.md +85 -266
  18. package/dist/aid-store.d.ts +64 -0
  19. package/dist/aid-store.d.ts.map +1 -0
  20. package/dist/aid-store.js +855 -0
  21. package/dist/aid-store.js.map +1 -0
  22. package/dist/aid.d.ts +50 -0
  23. package/dist/aid.d.ts.map +1 -0
  24. package/dist/aid.js +106 -0
  25. package/dist/aid.js.map +1 -0
  26. package/dist/auth.js +1 -1
  27. package/dist/auth.js.map +1 -1
  28. package/dist/bundle.js +1626 -1885
  29. package/dist/cert-utils.d.ts +26 -0
  30. package/dist/cert-utils.d.ts.map +1 -0
  31. package/dist/cert-utils.js +221 -0
  32. package/dist/cert-utils.js.map +1 -0
  33. package/dist/client.d.ts +89 -60
  34. package/dist/client.d.ts.map +1 -1
  35. package/dist/client.js +558 -154
  36. package/dist/client.js.map +1 -1
  37. package/dist/error-codes.d.ts +25 -0
  38. package/dist/error-codes.d.ts.map +1 -0
  39. package/dist/error-codes.js +31 -0
  40. package/dist/error-codes.js.map +1 -0
  41. package/dist/errors.d.ts +4 -0
  42. package/dist/errors.d.ts.map +1 -1
  43. package/dist/errors.js +4 -0
  44. package/dist/errors.js.map +1 -1
  45. package/dist/index.d.ts +6 -6
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +5 -5
  48. package/dist/index.js.map +1 -1
  49. package/dist/keystore/index.d.ts +1 -1
  50. package/dist/keystore/index.d.ts.map +1 -1
  51. package/dist/result.d.ts +19 -0
  52. package/dist/result.d.ts.map +1 -0
  53. package/dist/result.js +10 -0
  54. package/dist/result.js.map +1 -0
  55. package/dist/transport.d.ts +3 -0
  56. package/dist/transport.d.ts.map +1 -1
  57. package/dist/transport.js +16 -1
  58. package/dist/transport.js.map +1 -1
  59. package/dist/types.d.ts +13 -2
  60. package/dist/types.d.ts.map +1 -1
  61. package/dist/types.js +22 -0
  62. package/dist/types.js.map +1 -1
  63. package/dist/v2/e2ee/encrypt-p2p.js +1 -1
  64. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
  65. package/dist/version.d.ts +2 -0
  66. package/dist/version.d.ts.map +1 -0
  67. package/dist/version.js +5 -0
  68. package/dist/version.js.map +1 -0
  69. package/package.json +1 -1
@@ -0,0 +1,855 @@
1
+ import { AID } from './aid.js';
2
+ import { CryptoProvider, pemToArrayBuffer, importPrivateKeyEcdsa, importCertPublicKeyEcdsa, ecdsaSignDer, ecdsaVerifyDer } from './crypto.js';
3
+ import * as codes from './error-codes.js';
4
+ import { publicKeyDerB64 } from './cert-utils.js';
5
+ import { AuthFlow } from './auth.js';
6
+ import { GatewayDiscovery } from './discovery.js';
7
+ import { IdentityConflictError, ValidationError } from './errors.js';
8
+ import { IndexedDBKeyStore } from './keystore/indexeddb.js';
9
+ import { getDeviceId, normalizeInstanceId } from './config.js';
10
+ import { resultErr, resultOk } from './result.js';
11
+ // ── 证书 DER 解析工具 ────────────────────────────────────────────
12
+ function _derReadLength(data, offset) {
13
+ if (offset >= data.length)
14
+ return null;
15
+ const first = data[offset];
16
+ if (first < 0x80)
17
+ return { value: first, lenBytes: 1 };
18
+ const n = first & 0x7f;
19
+ if (n === 0 || n > 4)
20
+ return null;
21
+ let v = 0;
22
+ for (let i = 0; i < n; i++)
23
+ v = (v << 8) | data[offset + 1 + i];
24
+ return { value: v, lenBytes: 1 + n };
25
+ }
26
+ function _derSkipTlv(data, offset) {
27
+ const len = _derReadLength(data, offset + 1);
28
+ if (!len)
29
+ return null;
30
+ return offset + 1 + len.lenBytes + len.value;
31
+ }
32
+ /**
33
+ * 从证书 PEM 中提取有效期 [notBefore, notAfter](Unix ms)。
34
+ * 仅支持 UTCTime(0x17)和 GeneralizedTime(0x18)。
35
+ */
36
+ function parseCertValidity(certPem) {
37
+ try {
38
+ const der = new Uint8Array(pemToArrayBuffer(certPem));
39
+ // SEQUENCE (cert) → SEQUENCE (tbs) → skip version/serial/sigAlg/issuer → SEQUENCE (validity)
40
+ const cert = _derSkipTlv(der, -1); // 先找 cert SEQUENCE 起点
41
+ // 直接从 offset=0 开始解析
42
+ const certLen = _derReadLength(der, 1);
43
+ if (!certLen || der[0] !== 0x30)
44
+ return null;
45
+ const tbsStart = 1 + certLen.lenBytes;
46
+ if (der[tbsStart] !== 0x30)
47
+ return null;
48
+ const tbsLen = _derReadLength(der, tbsStart + 1);
49
+ if (!tbsLen)
50
+ return null;
51
+ let pos = tbsStart + 1 + tbsLen.lenBytes;
52
+ // 跳过 [0] version(可选)
53
+ if (der[pos] === 0xa0) {
54
+ const e = _derSkipTlv(der, pos);
55
+ if (e === null)
56
+ return null;
57
+ pos = e;
58
+ }
59
+ // 跳过 serialNumber
60
+ {
61
+ const e = _derSkipTlv(der, pos);
62
+ if (e === null)
63
+ return null;
64
+ pos = e;
65
+ }
66
+ // 跳过 signature AlgId
67
+ {
68
+ const e = _derSkipTlv(der, pos);
69
+ if (e === null)
70
+ return null;
71
+ pos = e;
72
+ }
73
+ // 跳过 issuer
74
+ {
75
+ const e = _derSkipTlv(der, pos);
76
+ if (e === null)
77
+ return null;
78
+ pos = e;
79
+ }
80
+ // 现在 pos 指向 validity SEQUENCE
81
+ if (der[pos] !== 0x30)
82
+ return null;
83
+ const valLen = _derReadLength(der, pos + 1);
84
+ if (!valLen)
85
+ return null;
86
+ pos = pos + 1 + valLen.lenBytes;
87
+ function parseTime(offset) {
88
+ const tag = der[offset];
89
+ const len = _derReadLength(der, offset + 1);
90
+ if (!len)
91
+ return null;
92
+ const raw = new TextDecoder().decode(der.slice(offset + 1 + len.lenBytes, offset + 1 + len.lenBytes + len.value));
93
+ if (tag === 0x17) {
94
+ // UTCTime: YYMMDDHHMMSSZ
95
+ const y = parseInt(raw.slice(0, 2), 10);
96
+ const year = y >= 50 ? 1900 + y : 2000 + y;
97
+ return Date.UTC(year, parseInt(raw.slice(2, 4), 10) - 1, parseInt(raw.slice(4, 6), 10), parseInt(raw.slice(6, 8), 10), parseInt(raw.slice(8, 10), 10), parseInt(raw.slice(10, 12), 10));
98
+ }
99
+ else if (tag === 0x18) {
100
+ // GeneralizedTime: YYYYMMDDHHMMSSZ
101
+ return Date.UTC(parseInt(raw.slice(0, 4), 10), parseInt(raw.slice(4, 6), 10) - 1, parseInt(raw.slice(6, 8), 10), parseInt(raw.slice(8, 10), 10), parseInt(raw.slice(10, 12), 10), parseInt(raw.slice(12, 14), 10));
102
+ }
103
+ return null;
104
+ }
105
+ const notBefore = parseTime(pos);
106
+ if (notBefore === null)
107
+ return null;
108
+ const nbLen = _derReadLength(der, pos + 1);
109
+ if (!nbLen)
110
+ return null;
111
+ const notAfterOffset = pos + 1 + nbLen.lenBytes + nbLen.value;
112
+ const notAfter = parseTime(notAfterOffset);
113
+ if (notAfter === null)
114
+ return null;
115
+ return { notBefore, notAfter };
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ /**
122
+ * 从证书 PEM 中提取 subject CN。
123
+ * 返回 null 表示无法解析(不视为错误,跳过 CN 校验)。
124
+ */
125
+ function parseCertCN(certPem) {
126
+ try {
127
+ const der = new Uint8Array(pemToArrayBuffer(certPem));
128
+ if (der[0] !== 0x30)
129
+ return null;
130
+ const certLen = _derReadLength(der, 1);
131
+ if (!certLen)
132
+ return null;
133
+ const tbsStart = 1 + certLen.lenBytes;
134
+ if (der[tbsStart] !== 0x30)
135
+ return null;
136
+ const tbsLen = _derReadLength(der, tbsStart + 1);
137
+ if (!tbsLen)
138
+ return null;
139
+ let pos = tbsStart + 1 + tbsLen.lenBytes;
140
+ if (der[pos] === 0xa0) {
141
+ const e = _derSkipTlv(der, pos);
142
+ if (e === null)
143
+ return null;
144
+ pos = e;
145
+ }
146
+ {
147
+ const e = _derSkipTlv(der, pos);
148
+ if (e === null)
149
+ return null;
150
+ pos = e;
151
+ } // serial
152
+ {
153
+ const e = _derSkipTlv(der, pos);
154
+ if (e === null)
155
+ return null;
156
+ pos = e;
157
+ } // sigAlg
158
+ // issuer SEQUENCE
159
+ if (der[pos] !== 0x30)
160
+ return null;
161
+ const issuerLen = _derReadLength(der, pos + 1);
162
+ if (!issuerLen)
163
+ return null;
164
+ const issuerEnd = pos + 1 + issuerLen.lenBytes + issuerLen.value;
165
+ pos = pos + 1 + issuerLen.lenBytes;
166
+ // 跳过 issuer,找 subject(在 validity 之后)
167
+ // 先跳过 issuer
168
+ pos = issuerEnd;
169
+ // 跳过 validity
170
+ {
171
+ const e = _derSkipTlv(der, pos);
172
+ if (e === null)
173
+ return null;
174
+ pos = e;
175
+ }
176
+ // 现在 pos 指向 subject SEQUENCE
177
+ if (der[pos] !== 0x30)
178
+ return null;
179
+ const subjectLen = _derReadLength(der, pos + 1);
180
+ if (!subjectLen)
181
+ return null;
182
+ const subjectEnd = pos + 1 + subjectLen.lenBytes + subjectLen.value;
183
+ pos = pos + 1 + subjectLen.lenBytes;
184
+ // 遍历 subject RDN SET
185
+ while (pos < subjectEnd) {
186
+ if (der[pos] !== 0x31)
187
+ break; // SET
188
+ const setLen = _derReadLength(der, pos + 1);
189
+ if (!setLen)
190
+ break;
191
+ const setEnd = pos + 1 + setLen.lenBytes + setLen.value;
192
+ let rpos = pos + 1 + setLen.lenBytes;
193
+ while (rpos < setEnd) {
194
+ if (der[rpos] !== 0x30)
195
+ break; // SEQUENCE
196
+ const seqLen = _derReadLength(der, rpos + 1);
197
+ if (!seqLen)
198
+ break;
199
+ const seqEnd = rpos + 1 + seqLen.lenBytes + seqLen.value;
200
+ let apos = rpos + 1 + seqLen.lenBytes;
201
+ // OID
202
+ if (der[apos] !== 0x06) {
203
+ rpos = seqEnd;
204
+ continue;
205
+ }
206
+ const oidLen = _derReadLength(der, apos + 1);
207
+ if (!oidLen) {
208
+ rpos = seqEnd;
209
+ continue;
210
+ }
211
+ const oidBytes = der.slice(apos + 1 + oidLen.lenBytes, apos + 1 + oidLen.lenBytes + oidLen.value);
212
+ // CN OID: 2.5.4.3 = 55 04 03
213
+ const isCN = oidBytes.length === 3 && oidBytes[0] === 0x55 && oidBytes[1] === 0x04 && oidBytes[2] === 0x03;
214
+ apos = apos + 1 + oidLen.lenBytes + oidLen.value;
215
+ if (isCN && apos < seqEnd) {
216
+ const valLen = _derReadLength(der, apos + 1);
217
+ if (valLen) {
218
+ return new TextDecoder().decode(der.slice(apos + 1 + valLen.lenBytes, apos + 1 + valLen.lenBytes + valLen.value));
219
+ }
220
+ }
221
+ rpos = seqEnd;
222
+ }
223
+ pos = setEnd;
224
+ }
225
+ return null;
226
+ }
227
+ catch {
228
+ return null;
229
+ }
230
+ }
231
+ function normalizeSlotId(slotId) {
232
+ const value = String(slotId ?? 'default').trim();
233
+ return value || 'default';
234
+ }
235
+ function issuerFromAid(aid) {
236
+ const target = String(aid ?? '').trim();
237
+ const dotIdx = target.indexOf('.');
238
+ return dotIdx >= 0 ? target.slice(dotIdx + 1) : target;
239
+ }
240
+ function validateRegisterAidName(aid) {
241
+ if (!aid)
242
+ throw new ValidationError("AIDStore.register requires 'aid'");
243
+ const name = aid.includes('.') ? aid.split('.')[0] : aid;
244
+ if (!/^[a-z0-9_][a-z0-9_-]{3,63}$/.test(name)) {
245
+ throw new ValidationError(`Invalid AID name '${name}': must be 4-64 characters, only [a-z0-9_-], cannot start with '-'`);
246
+ }
247
+ if (name.startsWith('guest')) {
248
+ throw new ValidationError("AID name must not start with 'guest'");
249
+ }
250
+ }
251
+ function gatewayHttpUrl(gatewayUrl, path) {
252
+ const parsed = new URL(gatewayUrl);
253
+ parsed.protocol = parsed.protocol === 'wss:' ? 'https:' : 'http:';
254
+ parsed.pathname = path;
255
+ parsed.search = '';
256
+ parsed.hash = '';
257
+ return parsed.toString();
258
+ }
259
+ function pkiCertUrl(gatewayUrl, aid) {
260
+ return gatewayHttpUrl(gatewayUrl, `/pki/cert/${encodeURIComponent(aid)}`);
261
+ }
262
+ function agentMdUrl(aid, gatewayUrl, discoveryPort) {
263
+ const scheme = String(gatewayUrl ?? '').trim().toLowerCase().startsWith('ws://') ? 'http' : 'https';
264
+ let host = String(aid ?? '').trim();
265
+ if (discoveryPort && !host.includes(':'))
266
+ host = `${host}:${discoveryPort}`;
267
+ return `${scheme}://${host}/agent.md`;
268
+ }
269
+ async function fetchWithTimeout(input, init = {}, timeoutMs = 30000) {
270
+ const controller = new AbortController();
271
+ const timer = globalThis.setTimeout(() => controller.abort(), timeoutMs);
272
+ try {
273
+ return await fetch(input, { ...init, signal: controller.signal });
274
+ }
275
+ finally {
276
+ globalThis.clearTimeout(timer);
277
+ }
278
+ }
279
+ function resultError(result) {
280
+ return result.ok ? null : result.error;
281
+ }
282
+ export class AIDStore {
283
+ aunPath;
284
+ deviceId;
285
+ slotId;
286
+ _encryptionSeed;
287
+ _keystore;
288
+ _auth;
289
+ _crypto;
290
+ _discovery;
291
+ _verifySsl;
292
+ _discoveryPort;
293
+ _gatewayCache = new Map();
294
+ _agentMdCache = new Map();
295
+ constructor(opts) {
296
+ this.aunPath = String(opts.aunPath ?? '');
297
+ this._encryptionSeed = String(opts.encryptionSeed ?? '');
298
+ this.deviceId = opts.deviceId
299
+ ? normalizeInstanceId(opts.deviceId, 'deviceId', { allowEmpty: true })
300
+ : getDeviceId();
301
+ this.slotId = normalizeSlotId(opts.slotId);
302
+ this._verifySsl = opts.verifySsl ?? true;
303
+ this._discoveryPort = opts.discoveryPort ?? null;
304
+ this._keystore = new IndexedDBKeyStore({ encryptionSeed: this._encryptionSeed || undefined });
305
+ this._crypto = new CryptoProvider();
306
+ this._discovery = new GatewayDiscovery();
307
+ this._auth = new AuthFlow({
308
+ keystore: this._keystore,
309
+ crypto: this._crypto,
310
+ deviceId: this.deviceId,
311
+ slotId: this.slotId,
312
+ rootCaPem: opts.rootCaPem ?? null,
313
+ verifySsl: this._verifySsl,
314
+ });
315
+ }
316
+ close() {
317
+ const close = this._keystore.close;
318
+ if (typeof close === 'function')
319
+ close.call(this._keystore);
320
+ }
321
+ async load(aid) {
322
+ const target = String(aid ?? '').trim();
323
+ const certPem = await this._keystore.loadCert(target);
324
+ if (!certPem)
325
+ return resultErr(codes.CERT_NOT_FOUND, `certificate not found for aid: ${target}`);
326
+ // 证书基础解析
327
+ let certPub = '';
328
+ try {
329
+ certPub = publicKeyDerB64(certPem);
330
+ if (!certPub)
331
+ return resultErr(codes.CERT_PARSE_ERROR, `certificate parse failed for aid: ${target}`);
332
+ }
333
+ catch (exc) {
334
+ return resultErr(codes.CERT_PARSE_ERROR, `certificate parse failed for aid: ${target}`, exc);
335
+ }
336
+ // 有效期检查(对齐 Python cert_time_error)
337
+ const validity = parseCertValidity(certPem);
338
+ if (validity) {
339
+ const now = Date.now();
340
+ if (now > validity.notAfter) {
341
+ return resultErr(codes.CERT_EXPIRED, `certificate expired for aid: ${target}`);
342
+ }
343
+ if (now < validity.notBefore) {
344
+ return resultErr(codes.CERT_NOT_YET_VALID, `certificate not yet valid for aid: ${target}`);
345
+ }
346
+ }
347
+ // CN 校验(对齐 Python cert_common_name 检查)
348
+ const cn = parseCertCN(certPem);
349
+ if (cn && cn !== target) {
350
+ return resultErr(codes.CERT_CHAIN_BROKEN, `certificate CN mismatch: expected ${target}, got ${cn}`);
351
+ }
352
+ // 加载私钥
353
+ let keyPair;
354
+ try {
355
+ keyPair = await this._keystore.loadKeyPair(target);
356
+ }
357
+ catch (exc) {
358
+ return resultErr(codes.PRIVATE_KEY_PARSE_ERROR, `private key load failed for aid: ${target}`, exc);
359
+ }
360
+ if (!keyPair?.private_key_pem) {
361
+ return resultOk({
362
+ aid: await AID.create({
363
+ aid: target,
364
+ aunPath: this.aunPath,
365
+ certPem,
366
+ privateKeyPem: null,
367
+ certValid: true,
368
+ privateKeyValid: false,
369
+ deviceId: this.deviceId,
370
+ slotId: this.slotId,
371
+ }),
372
+ });
373
+ }
374
+ // 私钥解析 + 配对自检(对齐 Python sign/verify probe)
375
+ try {
376
+ const privateKey = await importPrivateKeyEcdsa(String(keyPair.private_key_pem));
377
+ // public_key_der_b64 字段存在时做快速比对
378
+ if (keyPair.public_key_der_b64 && keyPair.public_key_der_b64 !== certPub) {
379
+ return resultErr(codes.KEYPAIR_MISMATCH, `keypair public key mismatch for aid: ${target}`);
380
+ }
381
+ // sign/verify 自检:用私钥签名探针,用证书公钥验证
382
+ const probe = new TextEncoder().encode('aun-aidstore-private-key-self-test');
383
+ const sig = await ecdsaSignDer(privateKey, probe);
384
+ const pubKey = await importCertPublicKeyEcdsa(certPem);
385
+ const ok = await ecdsaVerifyDer(pubKey, sig, probe);
386
+ if (!ok) {
387
+ return resultErr(codes.KEYPAIR_MISMATCH, `private key does not match certificate for aid: ${target}`);
388
+ }
389
+ }
390
+ catch (exc) {
391
+ return resultErr(codes.PRIVATE_KEY_PARSE_ERROR, `private key parse failed for aid: ${target}`, exc);
392
+ }
393
+ return resultOk({
394
+ aid: await AID.create({
395
+ aid: target,
396
+ aunPath: this.aunPath,
397
+ certPem,
398
+ privateKeyPem: String(keyPair.private_key_pem),
399
+ certValid: true,
400
+ privateKeyValid: true,
401
+ deviceId: this.deviceId,
402
+ slotId: this.slotId,
403
+ }),
404
+ });
405
+ }
406
+ async list() {
407
+ try {
408
+ const aids = await this._keystore.listIdentities();
409
+ const identities = [];
410
+ for (const aid of aids.sort()) {
411
+ const loaded = await this.load(aid);
412
+ if (loaded.ok && loaded.data.aid.isPrivateKeyValid()) {
413
+ identities.push({
414
+ aid: loaded.data.aid.aid,
415
+ certFingerprint: loaded.data.aid.certFingerprint,
416
+ certNotAfter: loaded.data.aid.certNotAfter,
417
+ certIssuer: loaded.data.aid.certIssuer,
418
+ });
419
+ }
420
+ }
421
+ return resultOk({ identities });
422
+ }
423
+ catch (exc) {
424
+ return resultErr('LIST_IDENTITIES_FAILED', String(exc), exc);
425
+ }
426
+ }
427
+ async changeSeed(oldSeed, newSeed) {
428
+ try {
429
+ const changed = await this._keystore.changeSeed?.(oldSeed, newSeed);
430
+ this._encryptionSeed = String(newSeed ?? '');
431
+ return resultOk({ changed: true, count: changed?.privateKeysMigrated ?? changed?.migrated ?? 0 });
432
+ }
433
+ catch (exc) {
434
+ return resultErr(codes.PRIVATE_KEY_PARSE_ERROR, String(exc), exc);
435
+ }
436
+ }
437
+ async register(aid) {
438
+ const target = String(aid ?? '').trim();
439
+ try {
440
+ validateRegisterAidName(target);
441
+ const gatewayUrl = await this._resolveGateway(target);
442
+ await this._auth.registerAid(gatewayUrl, target);
443
+ return resultOk({ registered: true });
444
+ }
445
+ catch (exc) {
446
+ if (exc instanceof IdentityConflictError) {
447
+ return resultErr(codes.IDENTITY_CONFLICT, String(exc), exc);
448
+ }
449
+ if (exc instanceof ValidationError) {
450
+ return resultErr(codes.INVALID_AID_FORMAT, String(exc), exc);
451
+ }
452
+ const message = exc instanceof Error ? exc.message : String(exc);
453
+ if (/network|connect|timeout|abort/i.test(message)) {
454
+ return resultErr(codes.NETWORK_ERROR, message, exc);
455
+ }
456
+ return resultErr(codes.SERVER_ERROR, message, exc);
457
+ }
458
+ }
459
+ async exists(aid) {
460
+ const target = String(aid ?? '').trim();
461
+ try {
462
+ const gatewayUrl = await this._resolveGateway(target);
463
+ const response = await fetchWithTimeout(pkiCertUrl(gatewayUrl, target), { method: 'HEAD' }, 10000);
464
+ if (response.status === 200)
465
+ return resultOk({ exists: true });
466
+ if (response.status === 404)
467
+ return resultOk({ exists: false });
468
+ return resultErr(codes.NETWORK_ERROR, `unexpected PKI HEAD status ${response.status}`);
469
+ }
470
+ catch (exc) {
471
+ return resultErr(codes.NETWORK_ERROR, String(exc), exc);
472
+ }
473
+ }
474
+ async resolve(aid, opts) {
475
+ const target = String(aid ?? '').trim();
476
+ const forceRefresh = !!opts?.forceRefresh;
477
+ const skipAgentMd = !!opts?.skipAgentMd;
478
+ try {
479
+ let peer;
480
+ let certFromCache = false;
481
+ const cached = await this.load(target);
482
+ if (cached.ok && !forceRefresh) {
483
+ peer = cached.data.aid;
484
+ certFromCache = true;
485
+ }
486
+ else {
487
+ const gatewayUrl = await this._resolveGateway(target);
488
+ const certPem = await this._auth.fetchPeerCert(gatewayUrl, target);
489
+ if (!certPem)
490
+ return resultErr(codes.CERT_NOT_FOUND, `certificate not found for aid: ${target}`);
491
+ await this._keystore.saveCert(target, certPem);
492
+ const reloaded = await this.load(target);
493
+ if (!reloaded.ok)
494
+ return reloaded;
495
+ peer = reloaded.data.aid;
496
+ }
497
+ const source = { certFromCache, cert_from_cache: certFromCache, agentMdFetched: false, agent_md_fetched: false };
498
+ if (skipAgentMd)
499
+ return resultOk({ aid: peer, source });
500
+ const agentMd = await this.fetchAgentMd(target);
501
+ if (!agentMd.ok)
502
+ return agentMd;
503
+ source.agentMdFetched = true;
504
+ source.agent_md_fetched = true;
505
+ return resultOk({ aid: peer, agentMd: agentMd.data, agent_md: agentMd.data, source });
506
+ }
507
+ catch (exc) {
508
+ return resultErr(codes.NETWORK_ERROR, String(exc), exc);
509
+ }
510
+ }
511
+ async fetchAgentMd(aid) {
512
+ const target = String(aid ?? '').trim();
513
+ try {
514
+ const gatewayUrl = await this._resolveGateway(target);
515
+ const cached = this._agentMdCache.get(target) ?? {};
516
+ const headers = { Accept: 'text/markdown' };
517
+ if (cached.etag)
518
+ headers['If-None-Match'] = cached.etag;
519
+ if (cached.lastModified)
520
+ headers['If-Modified-Since'] = cached.lastModified;
521
+ const response = await fetchWithTimeout(agentMdUrl(target, gatewayUrl, this._discoveryPort), {
522
+ method: 'GET',
523
+ headers,
524
+ }, 30000);
525
+ let content = '';
526
+ if (response.status === 304 && cached.content) {
527
+ content = cached.content;
528
+ }
529
+ else if (response.status === 404) {
530
+ return resultErr(codes.AGENTMD_NOT_FOUND, `agent.md not found for aid: ${target}`);
531
+ }
532
+ else if (!response.ok) {
533
+ return resultErr(codes.NETWORK_ERROR, `download agent.md failed: HTTP ${response.status}`);
534
+ }
535
+ else {
536
+ content = await response.text();
537
+ }
538
+ let peer;
539
+ const loaded = await this.load(target);
540
+ if (loaded.ok) {
541
+ peer = loaded.data.aid;
542
+ }
543
+ else {
544
+ const resolved = await this.resolve(target, { skipAgentMd: true });
545
+ if (!resolved.ok)
546
+ return resolved;
547
+ peer = resolved.data.aid;
548
+ }
549
+ const verified = await peer.verifyAgentMd(content);
550
+ if (!verified.ok)
551
+ return verified;
552
+ const signature = verified.data;
553
+ const verification = { status: signature.status };
554
+ if (signature.reason)
555
+ verification.reason = signature.reason;
556
+ const etag = String(response.headers?.get('ETag') ?? response.headers?.get('etag') ?? cached.etag ?? '').trim();
557
+ const lastModified = String(response.headers?.get('Last-Modified') ?? response.headers?.get('last-modified') ?? cached.lastModified ?? '').trim();
558
+ this._agentMdCache.set(target, { content, etag, lastModified, updatedAt: Date.now() });
559
+ return resultOk({
560
+ aid: target,
561
+ content,
562
+ verification,
563
+ signature,
564
+ certPem: peer.certPem,
565
+ cert_pem: peer.certPem,
566
+ etag,
567
+ lastModified,
568
+ last_modified: lastModified,
569
+ status: response.status,
570
+ });
571
+ }
572
+ catch (exc) {
573
+ return resultErr(codes.NETWORK_ERROR, String(exc), exc);
574
+ }
575
+ }
576
+ async headAgentMd(aid) {
577
+ const target = String(aid ?? '').trim();
578
+ try {
579
+ const gatewayUrl = await this._resolveGateway(target);
580
+ const response = await fetchWithTimeout(agentMdUrl(target, gatewayUrl, this._discoveryPort), {
581
+ method: 'HEAD',
582
+ headers: { Accept: 'text/markdown' },
583
+ }, 15000);
584
+ if (response.status === 404) {
585
+ return resultErr(codes.AGENTMD_NOT_FOUND, `agent.md not found for aid: ${target}`);
586
+ }
587
+ if (!response.ok) {
588
+ return resultErr(codes.NETWORK_ERROR, `head agent.md failed: HTTP ${response.status}`);
589
+ }
590
+ const etag = String(response.headers?.get('ETag') ?? response.headers?.get('etag') ?? '').trim();
591
+ const lastModified = String(response.headers?.get('Last-Modified') ?? response.headers?.get('last-modified') ?? '').trim();
592
+ const contentLength = Number.parseInt(String(response.headers?.get('Content-Length') ?? response.headers?.get('content-length') ?? '0'), 10) || 0;
593
+ const cached = this._agentMdCache.get(target) ?? {};
594
+ this._agentMdCache.set(target, { ...cached, etag, lastModified, updatedAt: Date.now() });
595
+ return resultOk({
596
+ aid: target,
597
+ found: true,
598
+ etag,
599
+ lastModified,
600
+ last_modified: lastModified,
601
+ contentLength,
602
+ content_length: contentLength,
603
+ status: response.status,
604
+ });
605
+ }
606
+ catch (exc) {
607
+ return resultErr(codes.NETWORK_ERROR, String(exc), exc);
608
+ }
609
+ }
610
+ async checkAgentMd(aid, ttlDays = 1) {
611
+ const target = String(aid ?? '').trim();
612
+ const cached = this._agentMdCache.get(target) ?? {};
613
+ const head = await this.headAgentMd(target);
614
+ let remote;
615
+ if (!head.ok) {
616
+ if (head.error.code === codes.AGENTMD_NOT_FOUND) {
617
+ remote = { aid: target, found: false, etag: '', last_modified: '', content_length: 0, status: 404 };
618
+ }
619
+ else {
620
+ return head;
621
+ }
622
+ }
623
+ else {
624
+ remote = head.data;
625
+ }
626
+ const localEtag = String(cached.etag ?? '').trim();
627
+ const localFound = !!cached.content;
628
+ const remoteFound = !!remote.found;
629
+ const remoteEtag = String(remote.etag ?? '').trim();
630
+ const needsUpdate = remoteFound && (!localFound || (!!remoteEtag && remoteEtag !== localEtag));
631
+ return resultOk({
632
+ aid: target,
633
+ localFound,
634
+ local_found: localFound,
635
+ remoteFound,
636
+ remote_found: remoteFound,
637
+ localEtag,
638
+ local_etag: localEtag,
639
+ remoteEtag,
640
+ remote_etag: remoteEtag,
641
+ lastModified: String(remote.lastModified ?? remote.last_modified ?? ''),
642
+ last_modified: String(remote.last_modified ?? remote.lastModified ?? ''),
643
+ needsUpdate,
644
+ needs_update: needsUpdate,
645
+ ttlDays,
646
+ ttl_days: ttlDays,
647
+ });
648
+ }
649
+ async diagnose(aid) {
650
+ const target = String(aid ?? '').trim();
651
+ const loaded = await this.load(target);
652
+ const remote = await this.exists(target);
653
+ const remoteError = resultError(remote);
654
+ const localError = resultError(loaded);
655
+ const localAid = loaded.ok ? loaded.data.aid : null;
656
+ const localCert = !!localAid?.isCertValid();
657
+ const localPrivateKey = !!localAid?.isPrivateKeyValid();
658
+ const localValid = localCert && localPrivateKey;
659
+ const remoteRegistered = !!(remote.ok && remote.data.exists);
660
+ const suggestions = [];
661
+ if (!localPrivateKey)
662
+ suggestions.push('load or register a local identity with a valid private key');
663
+ if (!remoteRegistered)
664
+ suggestions.push('register the AID before using it on the network');
665
+ if (remoteError)
666
+ suggestions.push(`remote registration check failed: ${remoteError.message}`);
667
+ const status = localPrivateKey && remoteRegistered
668
+ ? 'ready'
669
+ : remoteRegistered
670
+ ? 'registered_remote'
671
+ : remote.ok
672
+ ? 'available'
673
+ : 'unknown';
674
+ return resultOk({
675
+ aid: target,
676
+ status,
677
+ localValid,
678
+ local_valid: localValid,
679
+ remoteRegistered,
680
+ remote_registered: remoteRegistered,
681
+ suggestions,
682
+ local: { cert: localCert, private_key: localPrivateKey, error: localError },
683
+ remote: { checked: remote.ok, exists: remote.ok ? remoteRegistered : null, error: remoteError },
684
+ });
685
+ }
686
+ async renewCert(aid) {
687
+ const target = String(aid ?? '').trim();
688
+ const loaded = await this.load(target);
689
+ if (!loaded.ok || !loaded.data.aid.isPrivateKeyValid()) {
690
+ return resultErr(codes.PRIVATE_KEY_REQUIRED, `private key required for aid: ${target}`);
691
+ }
692
+ try {
693
+ const identity = await this._auth.loadIdentityOrNone(target);
694
+ if (!identity?.cert || !identity.private_key_pem) {
695
+ return resultErr(codes.PRIVATE_KEY_REQUIRED, `private key required for aid: ${target}`);
696
+ }
697
+ const gatewayUrl = await this._resolveGateway(target);
698
+ const clientNonce = this._crypto.newClientNonce();
699
+ const phase1 = await this._auth
700
+ ._shortRpc(gatewayUrl, 'auth.aid_login1', {
701
+ aid: target,
702
+ cert: identity.cert,
703
+ client_nonce: clientNonce,
704
+ });
705
+ const [signature, clientTime] = await this._crypto.signLoginNonce(identity.private_key_pem, String(phase1.nonce ?? ''));
706
+ const response = await this._auth
707
+ ._shortRpc(gatewayUrl, 'auth.renew_cert', {
708
+ aid: target,
709
+ request_id: String(phase1.request_id ?? ''),
710
+ nonce: String(phase1.nonce ?? ''),
711
+ client_time: clientTime,
712
+ signature,
713
+ });
714
+ const certPem = String(response.cert ?? response.cert_pem ?? '').trim();
715
+ if (!certPem)
716
+ return resultErr(codes.CERT_RENEWAL_FAILED, 'server response missing certificate');
717
+ await this._keystore.saveCert(target, certPem);
718
+ const refreshed = await this.load(target);
719
+ if (!refreshed.ok)
720
+ return resultErr(codes.CERT_RENEWAL_FAILED, 'renewed certificate reload failed');
721
+ return resultOk({
722
+ renewed: true,
723
+ newFingerprint: refreshed.data.aid.certFingerprint,
724
+ new_fingerprint: refreshed.data.aid.certFingerprint,
725
+ });
726
+ }
727
+ catch (exc) {
728
+ return resultErr(codes.CERT_RENEWAL_FAILED, String(exc), exc);
729
+ }
730
+ }
731
+ async rekey(aid) {
732
+ const target = String(aid ?? '').trim();
733
+ const loaded = await this.load(target);
734
+ if (!loaded.ok || !loaded.data.aid.isPrivateKeyValid()) {
735
+ return resultErr(codes.PRIVATE_KEY_REQUIRED, `private key required for aid: ${target}`);
736
+ }
737
+ try {
738
+ const identity = await this._auth.loadIdentityOrNone(target);
739
+ if (!identity?.cert || !identity.private_key_pem) {
740
+ return resultErr(codes.PRIVATE_KEY_REQUIRED, `private key required for aid: ${target}`);
741
+ }
742
+ const gatewayUrl = await this._resolveGateway(target);
743
+ const newIdentity = await this._crypto.generateIdentity();
744
+ const clientNonce = this._crypto.newClientNonce();
745
+ const phase1 = await this._auth
746
+ ._shortRpc(gatewayUrl, 'auth.aid_login1', {
747
+ aid: target,
748
+ cert: identity.cert,
749
+ client_nonce: clientNonce,
750
+ });
751
+ const signPayload = `${String(phase1.nonce ?? '')}${newIdentity.public_key_der_b64}`;
752
+ const signResult = await loaded.data.aid.sign(signPayload);
753
+ if (!signResult.ok)
754
+ return resultErr(codes.REKEY_FAILED, signResult.error.message);
755
+ const response = await this._auth
756
+ ._shortRpc(gatewayUrl, 'auth.rekey', {
757
+ aid: target,
758
+ request_id: String(phase1.request_id ?? ''),
759
+ nonce: String(phase1.nonce ?? ''),
760
+ new_public_key: newIdentity.public_key_der_b64,
761
+ signature: signResult.data.signature,
762
+ });
763
+ const certPem = String(response.cert ?? response.cert_pem ?? '').trim();
764
+ if (!certPem)
765
+ return resultErr(codes.REKEY_FAILED, 'server response missing certificate');
766
+ const nextIdentity = {
767
+ aid: target,
768
+ private_key_pem: newIdentity.private_key_pem,
769
+ public_key_der_b64: newIdentity.public_key_der_b64,
770
+ curve: newIdentity.curve,
771
+ cert: certPem,
772
+ };
773
+ await this._keystore.saveIdentity(target, nextIdentity);
774
+ const refreshed = await this.load(target);
775
+ if (!refreshed.ok)
776
+ return resultErr(codes.REKEY_FAILED, 'rekeyed identity reload failed');
777
+ return resultOk({
778
+ rekeyed: true,
779
+ newFingerprint: refreshed.data.aid.certFingerprint,
780
+ new_fingerprint: refreshed.data.aid.certFingerprint,
781
+ });
782
+ }
783
+ catch (exc) {
784
+ return resultErr(codes.REKEY_FAILED, String(exc), exc);
785
+ }
786
+ }
787
+ async _resolveGateway(aid) {
788
+ const target = String(aid ?? '').trim();
789
+ if (!target)
790
+ throw new ValidationError('AIDStore requires non-empty aid to resolve gateway');
791
+ const issuerDomain = issuerFromAid(target);
792
+ const cached = this._gatewayCache.get(issuerDomain);
793
+ if (cached)
794
+ return cached;
795
+ const persisted = await this._loadCachedGatewayUrl(target);
796
+ if (persisted) {
797
+ this._gatewayCache.set(issuerDomain, persisted);
798
+ return persisted;
799
+ }
800
+ const port = this._discoveryPort ? `:${this._discoveryPort}` : '';
801
+ const candidates = [
802
+ `https://${target}${port}/.well-known/aun-gateway`,
803
+ `https://gateway.${issuerDomain}${port}/.well-known/aun-gateway`,
804
+ ];
805
+ let lastError = null;
806
+ for (const url of candidates) {
807
+ try {
808
+ const discovered = await this._discovery.discover(url);
809
+ this._gatewayCache.set(issuerDomain, discovered);
810
+ await this._persistGatewayUrl(target, discovered);
811
+ return discovered;
812
+ }
813
+ catch (exc) {
814
+ lastError = exc;
815
+ }
816
+ }
817
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
818
+ }
819
+ async _loadCachedGatewayUrl(aid) {
820
+ const getMetadata = this._keystore.getMetadata;
821
+ if (typeof getMetadata !== 'function')
822
+ return '';
823
+ try {
824
+ const raw = String(await getMetadata.call(this._keystore, aid, 'gateway_url') ?? '').trim();
825
+ if (!raw)
826
+ return '';
827
+ if (raw.startsWith('"') && raw.endsWith('"')) {
828
+ try {
829
+ const parsed = JSON.parse(raw);
830
+ if (typeof parsed === 'string')
831
+ return parsed.trim();
832
+ }
833
+ catch {
834
+ return raw;
835
+ }
836
+ }
837
+ return raw;
838
+ }
839
+ catch {
840
+ return '';
841
+ }
842
+ }
843
+ async _persistGatewayUrl(aid, gatewayUrl) {
844
+ const setMetadata = this._keystore.setMetadata;
845
+ if (typeof setMetadata !== 'function' || !gatewayUrl)
846
+ return;
847
+ try {
848
+ await setMetadata.call(this._keystore, aid, 'gateway_url', gatewayUrl);
849
+ }
850
+ catch {
851
+ // 缓存失败不影响主流程。
852
+ }
853
+ }
854
+ }
855
+ //# sourceMappingURL=aid-store.js.map