@agentunion/fastaun-browser 0.4.4 → 0.4.6

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 (73) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/_packed_docs/CHANGELOG.md +41 -0
  3. package/_packed_docs/INDEX.md +2 -2
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
  5. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +73 -84
  6. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +16 -15
  7. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +2 -2
  8. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +22 -5
  9. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +42 -26
  10. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +2 -2
  11. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +61 -35
  12. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
  13. package/_packed_docs/sdk/09-message-rpc-manual.md +6 -6
  14. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +6 -4
  15. package/_packed_docs/sdk/INDEX.md +2 -2
  16. package/_packed_docs/sdk/README.md +3 -3
  17. package/dist/agent-md.d.ts +111 -0
  18. package/dist/agent-md.d.ts.map +1 -0
  19. package/dist/agent-md.js +656 -0
  20. package/dist/agent-md.js.map +1 -0
  21. package/dist/aid-store.d.ts +8 -40
  22. package/dist/aid-store.d.ts.map +1 -1
  23. package/dist/aid-store.js +89 -172
  24. package/dist/aid-store.js.map +1 -1
  25. package/dist/auth.d.ts +8 -13
  26. package/dist/auth.d.ts.map +1 -1
  27. package/dist/auth.js +37 -130
  28. package/dist/auth.js.map +1 -1
  29. package/dist/bundle.js +6540 -6033
  30. package/dist/client.d.ts +8 -65
  31. package/dist/client.d.ts.map +1 -1
  32. package/dist/client.js +193 -762
  33. package/dist/client.js.map +1 -1
  34. package/dist/index.d.ts +4 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/keystore/index.d.ts +49 -22
  39. package/dist/keystore/index.d.ts.map +1 -1
  40. package/dist/keystore/index.js +6 -1
  41. package/dist/keystore/index.js.map +1 -1
  42. package/dist/keystore/indexeddb-identity-store.d.ts +59 -0
  43. package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -0
  44. package/dist/keystore/indexeddb-identity-store.js +489 -0
  45. package/dist/keystore/indexeddb-identity-store.js.map +1 -0
  46. package/dist/keystore/indexeddb-shared.d.ts +76 -0
  47. package/dist/keystore/indexeddb-shared.d.ts.map +1 -0
  48. package/dist/keystore/indexeddb-shared.js +382 -0
  49. package/dist/keystore/indexeddb-shared.js.map +1 -0
  50. package/dist/keystore/indexeddb-token-store.d.ts +119 -0
  51. package/dist/keystore/indexeddb-token-store.d.ts.map +1 -0
  52. package/dist/keystore/indexeddb-token-store.js +1086 -0
  53. package/dist/keystore/indexeddb-token-store.js.map +1 -0
  54. package/dist/keystore/indexeddb.d.ts +11 -1
  55. package/dist/keystore/indexeddb.d.ts.map +1 -1
  56. package/dist/keystore/indexeddb.js +167 -18
  57. package/dist/keystore/indexeddb.js.map +1 -1
  58. package/dist/register-flow.d.ts +53 -0
  59. package/dist/register-flow.d.ts.map +1 -0
  60. package/dist/register-flow.js +401 -0
  61. package/dist/register-flow.js.map +1 -0
  62. package/dist/v2/session/keystore.d.ts +5 -0
  63. package/dist/v2/session/keystore.d.ts.map +1 -1
  64. package/dist/v2/session/keystore.js +29 -0
  65. package/dist/v2/session/keystore.js.map +1 -1
  66. package/dist/version.d.ts +1 -1
  67. package/dist/version.js +1 -1
  68. package/package.json +1 -1
  69. package/_packed_docs/0.4.0_/345/267/256/345/274/202/346/240/270/345/256/236/345/206/263/347/255/226/350/256/260/345/275/225.md +0 -302
  70. package/_packed_docs/AUN_SDK_0.4.0_/350/256/276/350/256/241/345/257/271/346/257/224/345/210/206/346/236/220.md +0 -194
  71. package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/345/256/236/346/226/275/350/256/241/345/210/222.md +0 -596
  72. 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 +0 -1698
  73. package/_packed_docs/python-sdk-v2-only-changelog.md +0 -189
@@ -0,0 +1,656 @@
1
+ import { AID } from './aid.js';
2
+ import { AUNError, ClientSignatureError, NotFoundError, StateError, ValidationError } from './errors.js';
3
+ import { isJsonObject } from './types.js';
4
+ const DEFAULT_HTTP_TIMEOUT_MS = 30_000;
5
+ const HEAD_HTTP_TIMEOUT_MS = 15_000;
6
+ const noopLogger = {
7
+ error: () => { },
8
+ warn: () => { },
9
+ info: () => { },
10
+ debug: () => { },
11
+ };
12
+ async function fetchWithTimeout(input, init, timeoutMs = DEFAULT_HTTP_TIMEOUT_MS) {
13
+ const controller = new AbortController();
14
+ const timer = globalThis.setTimeout(() => controller.abort(), timeoutMs);
15
+ try {
16
+ return await fetch(input, { ...init, signal: controller.signal });
17
+ }
18
+ catch (error) {
19
+ if (controller.signal.aborted) {
20
+ throw new AUNError(`agent.md request timed out after ${timeoutMs}ms`);
21
+ }
22
+ throw error;
23
+ }
24
+ finally {
25
+ globalThis.clearTimeout(timer);
26
+ }
27
+ }
28
+ function headerValue(headers, name) {
29
+ return String(headers?.get(name) ?? headers?.get(name.toLowerCase()) ?? '').trim();
30
+ }
31
+ function agentMdHttpScheme(gatewayUrl) {
32
+ return String(gatewayUrl ?? '').trim().toLowerCase().startsWith('ws://') ? 'http' : 'https';
33
+ }
34
+ function isRecord(value) {
35
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
36
+ }
37
+ export class AgentMdManager {
38
+ _aunPath;
39
+ _agentMdPath;
40
+ _tokenStore;
41
+ _log;
42
+ _ownerAidGetter;
43
+ _currentAidGetter;
44
+ _gatewayResolver;
45
+ _peerResolver;
46
+ _accessTokenResolver;
47
+ _aidValidator;
48
+ _cache = new Map();
49
+ _memoryStorage = new Map();
50
+ _fetchInflight = new Set();
51
+ _lock = Promise.resolve();
52
+ _localAgentMdEtag = '';
53
+ _remoteAgentMdEtag = '';
54
+ constructor(opts) {
55
+ this._aunPath = String(opts.aunPath ?? '');
56
+ this._agentMdPath = this._defaultRoot();
57
+ this._tokenStore = opts.tokenStore;
58
+ this._log = opts.logger ?? noopLogger;
59
+ this._ownerAidGetter = opts.ownerAidGetter;
60
+ this._currentAidGetter = opts.currentAidGetter;
61
+ this._gatewayResolver = opts.gatewayResolver;
62
+ this._peerResolver = opts.peerResolver;
63
+ this._accessTokenResolver = opts.accessTokenResolver;
64
+ this._aidValidator = opts.aidValidator;
65
+ }
66
+ get root() {
67
+ return this._agentMdPath || this._defaultRoot();
68
+ }
69
+ setAunPath(aunPath) {
70
+ this._aunPath = String(aunPath ?? '');
71
+ this.setRoot(null);
72
+ }
73
+ setRoot(root) {
74
+ this._agentMdPath = String(root ?? '').trim() || this._defaultRoot();
75
+ this._cache.clear();
76
+ this._memoryStorage.clear();
77
+ this._fetchInflight.clear();
78
+ this._localAgentMdEtag = '';
79
+ this._remoteAgentMdEtag = '';
80
+ return this._agentMdPath;
81
+ }
82
+ async upload(content) {
83
+ const target = this._ownerAid();
84
+ const current = this._currentAid();
85
+ if (!target || !current) {
86
+ throw new ValidationError('uploadAgentMd requires local AID');
87
+ }
88
+ if (!current.isPrivateKeyValid()) {
89
+ throw new StateError('uploadAgentMd requires loaded AID with a valid private key');
90
+ }
91
+ if (content !== undefined && content !== null) {
92
+ const text = String(content ?? '');
93
+ if (text.length === 0)
94
+ throw new ValidationError('uploadAgentMd requires non-empty content');
95
+ await this.saveRecord(target, {
96
+ content: text,
97
+ local_etag: await AgentMdManager.contentEtag(text),
98
+ fetched_at: Date.now(),
99
+ });
100
+ }
101
+ const localContent = await this.readContent(target);
102
+ if (localContent === null || localContent.length === 0) {
103
+ throw new ValidationError('uploadAgentMd requires local agent.md content');
104
+ }
105
+ const signedResult = await current.signAgentMd(localContent);
106
+ if (!signedResult.ok) {
107
+ throw new ClientSignatureError(signedResult.error.message || 'agent.md signing failed');
108
+ }
109
+ const signed = signedResult.data.signed;
110
+ const result = await this._uploadHttp(target, signed);
111
+ const localEtag = await AgentMdManager.contentEtag(signed);
112
+ const remoteEtag = String(result.etag ?? '').trim();
113
+ await this.saveRecord(target, {
114
+ content: signed,
115
+ local_etag: localEtag,
116
+ remote_etag: remoteEtag || undefined,
117
+ last_modified: String(result.last_modified ?? result.lastModified ?? '').trim(),
118
+ fetched_at: Date.now(),
119
+ checked_at: Date.now(),
120
+ remote_status: remoteEtag ? 'found' : 'unknown',
121
+ last_error: '',
122
+ });
123
+ return result;
124
+ }
125
+ async download(aid, timeoutMs = DEFAULT_HTTP_TIMEOUT_MS) {
126
+ const target = this._safeAid(String(aid ?? this._ownerAid() ?? '').trim());
127
+ const contentResult = await this._downloadHttp(target, timeoutMs);
128
+ const peer = await this._resolvePeer(target);
129
+ const verified = await peer.verifyAgentMd(contentResult.content);
130
+ if (!verified.ok)
131
+ throw new AUNError(verified.error.message);
132
+ const signature = { ...verified.data };
133
+ const statusText = String(signature.status ?? 'invalid');
134
+ const reason = String(signature.reason ?? '').trim();
135
+ const verification = { status: statusText };
136
+ if (reason)
137
+ verification.reason = reason;
138
+ const localEtag = await AgentMdManager.contentEtag(contentResult.content);
139
+ const saved = await this.saveRecord(target, {
140
+ content: contentResult.content,
141
+ local_etag: localEtag,
142
+ remote_etag: contentResult.etag || undefined,
143
+ last_modified: contentResult.last_modified || undefined,
144
+ fetched_at: Date.now(),
145
+ checked_at: Date.now(),
146
+ remote_status: 'found',
147
+ verify_status: statusText,
148
+ verify_error: reason,
149
+ last_error: '',
150
+ });
151
+ let inSync = null;
152
+ if (target === this._ownerAid()) {
153
+ const remote = contentResult.etag || String(saved.remote_etag ?? '');
154
+ inSync = localEtag && remote ? localEtag === remote : false;
155
+ }
156
+ return {
157
+ aid: target,
158
+ content: contentResult.content,
159
+ verification,
160
+ signature,
161
+ cert_pem: peer.certPem,
162
+ etag: contentResult.etag,
163
+ last_modified: contentResult.last_modified,
164
+ status: contentResult.status,
165
+ in_sync: inSync,
166
+ saved_to: this._logicalPath(this._contentKey(target)),
167
+ save_error: null,
168
+ };
169
+ }
170
+ async check(aid, ttlDays = 1) {
171
+ const target = this._safeAid(String(aid ?? this._ownerAid() ?? '').trim());
172
+ const before = await this.loadRecord(target) ?? {};
173
+ const localEtag = String(before.local_etag ?? '').trim();
174
+ const localFound = !!(Object.keys(before).length > 0 && (String(before.content ?? '') || localEtag));
175
+ const remoteEtagCached = String(before.remote_etag ?? before.etag ?? '').trim();
176
+ const lastModifiedCached = String(before.last_modified ?? '').trim();
177
+ const checkedAtCached = Number(before.checked_at ?? 0) || 0;
178
+ const cacheFresh = AgentMdManager.checkedAtFresh(checkedAtCached, ttlDays) || AgentMdManager.lastModifiedFresh(lastModifiedCached, ttlDays);
179
+ if (localFound && localEtag && remoteEtagCached && localEtag === remoteEtagCached && cacheFresh) {
180
+ return {
181
+ aid: target,
182
+ local_found: true,
183
+ remote_found: true,
184
+ local_etag: localEtag,
185
+ remote_etag: remoteEtagCached,
186
+ in_sync: true,
187
+ needs_update: false,
188
+ last_modified: lastModifiedCached,
189
+ status: 200,
190
+ cached: true,
191
+ verify_status: String(before.verify_status ?? ''),
192
+ verify_error: String(before.verify_error ?? ''),
193
+ ttl_days: Number(ttlDays) || 0,
194
+ };
195
+ }
196
+ const now = Date.now();
197
+ let remote;
198
+ try {
199
+ remote = await this._head(target);
200
+ }
201
+ catch (err) {
202
+ await this.saveRecord(target, { checked_at: now, remote_status: 'error', last_error: err instanceof Error ? err.message : String(err) });
203
+ throw err;
204
+ }
205
+ const remoteFound = !!remote.found;
206
+ const remoteEtag = String(remote.etag ?? '').trim();
207
+ const lastModified = String(remote.last_modified ?? '').trim();
208
+ const saved = await this.saveRecord(target, {
209
+ remote_etag: remoteFound ? remoteEtag : '',
210
+ last_modified: lastModified,
211
+ checked_at: now,
212
+ remote_status: remoteFound ? 'found' : 'missing',
213
+ last_error: '',
214
+ });
215
+ const inSync = !!(localFound && remoteFound && localEtag && remoteEtag && localEtag === remoteEtag);
216
+ return {
217
+ aid: target,
218
+ local_found: localFound,
219
+ remote_found: remoteFound,
220
+ local_etag: localEtag,
221
+ remote_etag: remoteEtag,
222
+ in_sync: inSync,
223
+ needs_update: !!(remoteFound && !inSync),
224
+ last_modified: lastModified,
225
+ status: Number(remote.status ?? (remoteFound ? 200 : 404)),
226
+ cached: false,
227
+ verify_status: String(saved.verify_status ?? before.verify_status ?? ''),
228
+ verify_error: String(saved.verify_error ?? before.verify_error ?? ''),
229
+ ttl_days: Number(ttlDays) || 0,
230
+ };
231
+ }
232
+ async observeMeta(aid, etag = '', lastModified = '', source = '') {
233
+ const target = String(aid ?? '').trim();
234
+ const remoteEtag = String(etag ?? '').trim();
235
+ const remoteLastModified = String(lastModified ?? '').trim();
236
+ if (!target || (!remoteEtag && !remoteLastModified))
237
+ return;
238
+ let before = this._cache.get(target);
239
+ if (!before || typeof before !== 'object')
240
+ before = await this.loadRecord(target) ?? {};
241
+ const same = (!remoteEtag || String(before.remote_etag ?? '').trim() === remoteEtag) &&
242
+ (!remoteLastModified || String(before.last_modified ?? '').trim() === remoteLastModified);
243
+ let record = { ...before };
244
+ if (!same || Object.keys(before).length === 0) {
245
+ const fields = {
246
+ observed_at: Date.now(),
247
+ remote_status: 'found',
248
+ };
249
+ if (remoteEtag)
250
+ fields.remote_etag = remoteEtag;
251
+ if (remoteLastModified)
252
+ fields.last_modified = remoteLastModified;
253
+ record = await this.saveRecord(target, fields) || record;
254
+ }
255
+ if (target === this._ownerAid() && remoteEtag)
256
+ this._remoteAgentMdEtag = remoteEtag;
257
+ await this._scheduleFetchIfMissing(target, record, source);
258
+ this._log.debug(`agent.md meta observed: aid=${target} etag=${remoteEtag || '-'} last_modified=${remoteLastModified || '-'} source=${source || '-'}`);
259
+ }
260
+ async observeRpcMeta(meta, ownerAid) {
261
+ if (!isJsonObject(meta))
262
+ return;
263
+ const owner = String(ownerAid ?? this._ownerAid() ?? '').trim();
264
+ const etag = String(meta.agent_md_etag ?? '').trim();
265
+ if (etag && owner) {
266
+ this._remoteAgentMdEtag = etag;
267
+ await this.observeMeta(owner, etag, '', 'rpc.self');
268
+ }
269
+ const etags = meta.agent_md_etags;
270
+ if (!isJsonObject(etags))
271
+ return;
272
+ for (const key of ['requester', 'peer', 'receiver', 'target', 'to', 'sender', 'from']) {
273
+ const item = etags[key];
274
+ if (!isJsonObject(item))
275
+ continue;
276
+ await this.observeMeta(String(item.aid ?? ''), String(item.etag ?? ''), String(item.last_modified ?? item.lastModified ?? ''), `rpc.${key}`);
277
+ }
278
+ }
279
+ async observeEnvelope(envelope) {
280
+ if (!isJsonObject(envelope))
281
+ return;
282
+ if (!isJsonObject(envelope.agent_md))
283
+ return;
284
+ const agentMd = envelope.agent_md;
285
+ if (!isJsonObject(agentMd.sender))
286
+ return;
287
+ const sender = agentMd.sender;
288
+ let senderAid = String(sender.aid ?? '').trim();
289
+ if (!senderAid) {
290
+ const aad = isJsonObject(envelope.aad) ? envelope.aad : {};
291
+ senderAid = String(aad.from ?? envelope.from ?? '').trim();
292
+ }
293
+ await this.observeMeta(senderAid, String(sender.etag ?? '').trim(), String(sender.last_modified ?? sender.lastModified ?? '').trim(), 'envelope');
294
+ }
295
+ eventSnapshot() {
296
+ if (!this._localAgentMdEtag && !this._remoteAgentMdEtag)
297
+ return null;
298
+ return { local_etag: this._localAgentMdEtag, remote_etag: this._remoteAgentMdEtag };
299
+ }
300
+ async readContent(aid) {
301
+ return await this._readStorage(this._contentKey(aid));
302
+ }
303
+ async writeContent(aid, content) {
304
+ await this._writeStorage(this._contentKey(aid), String(content ?? ''));
305
+ }
306
+ async loadRecord(aid) {
307
+ const target = String(aid ?? '').trim();
308
+ if (!target)
309
+ return null;
310
+ try {
311
+ const loaded = await this._withLock(async () => {
312
+ const record = await this._readRecordUnlocked(target);
313
+ const next = Object.keys(record).length > 0 ? { ...record, aid: target } : { aid: target };
314
+ try {
315
+ const content = await this.readContent(target);
316
+ if (content !== null) {
317
+ next.content = content;
318
+ next.local_etag = await AgentMdManager.contentEtag(content);
319
+ }
320
+ else {
321
+ const metaRaw = await this._readStorage(this._metaKey(target));
322
+ if (metaRaw !== null)
323
+ this._log.warn(`agent.md content read failed: aid=${target}`);
324
+ }
325
+ }
326
+ catch (err) {
327
+ this._log.warn(`agent.md content read failed: aid=${target} err=${err instanceof Error ? err.message : String(err)}`);
328
+ }
329
+ return next;
330
+ });
331
+ if (Object.keys(loaded).length <= 1)
332
+ return null;
333
+ this._cache.set(target, { ...loaded });
334
+ this._refreshOwnerEtags(target, loaded);
335
+ return { ...loaded };
336
+ }
337
+ catch (err) {
338
+ this._log.debug(`agent.md cache load skipped: aid=${target} err=${err instanceof Error ? err.message : String(err)}`);
339
+ return null;
340
+ }
341
+ }
342
+ async saveRecord(aid, fields) {
343
+ const target = String(aid ?? '').trim();
344
+ if (!target)
345
+ return {};
346
+ try {
347
+ const inputFields = { ...fields };
348
+ const hasContent = Object.prototype.hasOwnProperty.call(inputFields, 'content') && inputFields.content !== undefined && inputFields.content !== null;
349
+ if (hasContent) {
350
+ const text = String(inputFields.content ?? '');
351
+ await this.writeContent(target, text);
352
+ if (!inputFields.local_etag)
353
+ inputFields.local_etag = await AgentMdManager.contentEtag(text);
354
+ if (!inputFields.fetched_at)
355
+ inputFields.fetched_at = Date.now();
356
+ }
357
+ delete inputFields.content;
358
+ const record = await this._withLock(async () => {
359
+ const next = { ...(await this._readRecordUnlocked(target)), aid: target };
360
+ for (const [key, value] of Object.entries(inputFields)) {
361
+ if (value !== undefined && value !== null)
362
+ next[key] = value;
363
+ }
364
+ next.updated_at = Date.now();
365
+ await this._writeRecordUnlocked(target, next);
366
+ return next;
367
+ });
368
+ const loaded = { ...record };
369
+ if (hasContent)
370
+ loaded.content = String(fields.content ?? '');
371
+ this._cache.set(target, { ...loaded });
372
+ this._refreshOwnerEtags(target, loaded);
373
+ return { ...loaded };
374
+ }
375
+ catch (err) {
376
+ this._log.debug(`agent.md cache save skipped: aid=${target} err=${err instanceof Error ? err.message : String(err)}`);
377
+ return {};
378
+ }
379
+ }
380
+ static async contentEtag(content) {
381
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(String(content ?? '')));
382
+ const hex = Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');
383
+ return `"${hex}"`;
384
+ }
385
+ static checkedAtFresh(checkedAtMs, ttlDays) {
386
+ const days = Number(ttlDays || 0);
387
+ if (!Number.isFinite(days) || days <= 0)
388
+ return false;
389
+ if (!Number.isFinite(checkedAtMs) || checkedAtMs <= 0)
390
+ return false;
391
+ return Date.now() - checkedAtMs <= days * 86400000;
392
+ }
393
+ static lastModifiedFresh(lastModified, ttlDays) {
394
+ const days = Number(ttlDays || 0);
395
+ if (!Number.isFinite(days) || days <= 0)
396
+ return false;
397
+ const ts = Date.parse(String(lastModified ?? '').trim());
398
+ if (!Number.isFinite(ts))
399
+ return false;
400
+ return Date.now() <= ts + days * 86400000;
401
+ }
402
+ _defaultRoot() {
403
+ return this._joinPath(this._aunPath || '.', 'AIDs');
404
+ }
405
+ _joinPath(base, name) {
406
+ const left = String(base ?? '').trim().replace(/[\\/]+$/g, '');
407
+ return left ? `${left}/${name}` : name;
408
+ }
409
+ _logicalPath(key) {
410
+ return this._joinPath(this.root, key);
411
+ }
412
+ _safeAid(aid) {
413
+ const target = String(aid ?? '').trim();
414
+ if (!target || target.includes('/') || target.includes('\\') || target.includes('\0')) {
415
+ throw new ValidationError('agent.md aid is empty or contains path separators');
416
+ }
417
+ this._aidValidator?.(target);
418
+ return target;
419
+ }
420
+ _metaKey(aid) {
421
+ return `${this._safeAid(aid)}/agentmd.json`;
422
+ }
423
+ _contentKey(aid) {
424
+ return `${this._safeAid(aid)}/agent.md`;
425
+ }
426
+ _ownerAid() {
427
+ return String(this._ownerAidGetter?.() ?? '').trim();
428
+ }
429
+ _currentAid() {
430
+ return this._currentAidGetter?.() ?? null;
431
+ }
432
+ async _resolveGateway(aid) {
433
+ return String(await this._gatewayResolver?.(aid) ?? '');
434
+ }
435
+ async _resolvePeer(aid) {
436
+ const current = this._currentAid();
437
+ if (current?.aid === aid)
438
+ return current;
439
+ const peer = await this._peerResolver?.(aid);
440
+ if (!(peer instanceof AID)) {
441
+ throw new StateError(`agent.md peer resolver did not return AID for ${aid}`);
442
+ }
443
+ return peer;
444
+ }
445
+ async _accessToken(aid, gatewayUrl) {
446
+ const token = String(await this._accessTokenResolver?.(aid, gatewayUrl) ?? '').trim();
447
+ if (!token)
448
+ throw new StateError('authenticate did not return access_token');
449
+ return token;
450
+ }
451
+ _url(aid, gatewayUrl) {
452
+ return `${agentMdHttpScheme(gatewayUrl)}://${this._safeAid(aid)}/agent.md`;
453
+ }
454
+ async _uploadHttp(aid, content) {
455
+ const gatewayUrl = await this._resolveGateway(aid);
456
+ const token = await this._accessToken(aid, gatewayUrl);
457
+ const response = await fetchWithTimeout(this._url(aid, gatewayUrl), {
458
+ method: 'PUT',
459
+ headers: {
460
+ Authorization: `Bearer ${token}`,
461
+ 'Content-Type': 'text/markdown; charset=utf-8',
462
+ },
463
+ body: content,
464
+ });
465
+ if (response.status === 404) {
466
+ throw new NotFoundError(`agent.md endpoint not found for aid: ${aid}`);
467
+ }
468
+ if (!response.ok) {
469
+ const message = (await response.text()).trim();
470
+ throw new AUNError(`upload agent.md failed: HTTP ${response.status}${message ? ` - ${message}` : ''}`);
471
+ }
472
+ const payload = await response.json();
473
+ if (!isJsonObject(payload))
474
+ throw new AUNError('upload agent.md returned invalid JSON payload');
475
+ return payload;
476
+ }
477
+ async _downloadHttp(aid, timeoutMs) {
478
+ const target = this._safeAid(aid);
479
+ const gatewayUrl = await this._resolveGateway(target);
480
+ const url = this._url(target, gatewayUrl);
481
+ const headers = { Accept: 'text/markdown' };
482
+ const response = await fetchWithTimeout(url, {
483
+ method: 'GET',
484
+ headers,
485
+ redirect: 'follow',
486
+ }, timeoutMs);
487
+ if (response.status === 404) {
488
+ await this.saveRecord(target, { remote_status: 'missing', checked_at: Date.now(), last_error: '' });
489
+ throw new NotFoundError(`agent.md not found for aid: ${target}`);
490
+ }
491
+ if (!response.ok) {
492
+ const message = (await response.text()).trim();
493
+ throw new AUNError(`download agent.md failed: HTTP ${response.status}${message ? ` - ${message}` : ''}`);
494
+ }
495
+ const content = await response.text();
496
+ return {
497
+ content,
498
+ etag: headerValue(response.headers, 'ETag'),
499
+ last_modified: headerValue(response.headers, 'Last-Modified'),
500
+ status: response.status,
501
+ };
502
+ }
503
+ async _head(aid) {
504
+ const target = this._safeAid(aid);
505
+ const gatewayUrl = await this._resolveGateway(target);
506
+ const response = await fetchWithTimeout(this._url(target, gatewayUrl), {
507
+ method: 'HEAD',
508
+ headers: { Accept: 'text/markdown' },
509
+ }, HEAD_HTTP_TIMEOUT_MS);
510
+ const etag = headerValue(response.headers, 'ETag');
511
+ const lastModified = headerValue(response.headers, 'Last-Modified');
512
+ if (response.status === 404) {
513
+ await this.saveRecord(target, {
514
+ remote_etag: '',
515
+ last_modified: '',
516
+ checked_at: Date.now(),
517
+ remote_status: 'missing',
518
+ last_error: '',
519
+ });
520
+ return { aid: target, found: false, etag: '', last_modified: '', content_length: 0, status: 404 };
521
+ }
522
+ if (!response.ok) {
523
+ throw new AUNError(`head agent.md failed: HTTP ${response.status}`);
524
+ }
525
+ const contentLength = Number.parseInt(headerValue(response.headers, 'Content-Length') || '0', 10) || 0;
526
+ const data = { aid: target, found: true, etag, last_modified: lastModified, content_length: contentLength, status: response.status };
527
+ await this.saveRecord(target, {
528
+ remote_etag: etag,
529
+ last_modified: lastModified,
530
+ checked_at: Date.now(),
531
+ remote_status: 'found',
532
+ last_error: '',
533
+ });
534
+ return data;
535
+ }
536
+ async _readStorage(logicalKey) {
537
+ const key = String(logicalKey ?? '').trim();
538
+ if (!key)
539
+ return null;
540
+ const load = this._tokenStore?.loadAgentMdCache;
541
+ if (typeof load === 'function') {
542
+ const record = await load.call(this._tokenStore, this.root, key);
543
+ if (record && Object.prototype.hasOwnProperty.call(record, 'content')) {
544
+ return String(record.content ?? '');
545
+ }
546
+ return null;
547
+ }
548
+ return this._memoryStorage.has(key) ? String(this._memoryStorage.get(key) ?? '') : null;
549
+ }
550
+ async _writeStorage(logicalKey, content) {
551
+ const key = String(logicalKey ?? '').trim();
552
+ if (!key)
553
+ return;
554
+ const save = this._tokenStore?.upsertAgentMdCache;
555
+ const text = String(content ?? '');
556
+ if (typeof save === 'function') {
557
+ await save.call(this._tokenStore, this.root, key, {
558
+ content: text,
559
+ local_etag: await AgentMdManager.contentEtag(text),
560
+ fetched_at: Date.now(),
561
+ });
562
+ return;
563
+ }
564
+ this._memoryStorage.set(key, text);
565
+ }
566
+ async _withLock(fn) {
567
+ const previous = this._lock.catch(() => undefined);
568
+ let release;
569
+ const current = new Promise((resolve) => { release = resolve; });
570
+ this._lock = previous.then(() => current);
571
+ await previous;
572
+ try {
573
+ return await fn();
574
+ }
575
+ finally {
576
+ release();
577
+ }
578
+ }
579
+ _normalizeRecord(aid, data) {
580
+ if (!isRecord(data))
581
+ return {};
582
+ const record = {};
583
+ for (const [key, value] of Object.entries(data)) {
584
+ if (key !== 'content')
585
+ record[key] = value;
586
+ }
587
+ record.aid = this._safeAid(String(record.aid ?? aid));
588
+ for (const key of ['fetched_at', 'observed_at', 'checked_at', 'updated_at']) {
589
+ record[key] = Number(record[key] ?? 0) || 0;
590
+ }
591
+ return record;
592
+ }
593
+ async _writeRecordUnlocked(aid, record) {
594
+ const payload = {};
595
+ for (const [key, value] of Object.entries(record)) {
596
+ if (key !== 'content' && value !== undefined && value !== null)
597
+ payload[key] = value;
598
+ }
599
+ payload.aid = this._safeAid(aid);
600
+ await this._writeStorage(this._metaKey(aid), `${JSON.stringify(payload, null, 2)}\n`);
601
+ }
602
+ async _readRecordUnlocked(aid) {
603
+ const raw = await this._readStorage(this._metaKey(aid));
604
+ if (raw === null)
605
+ return {};
606
+ try {
607
+ return this._normalizeRecord(aid, JSON.parse(raw));
608
+ }
609
+ catch (err) {
610
+ this._log.warn(`agent.md metadata damaged, ignoring: aid=${aid} err=${err instanceof Error ? err.message : String(err)}`);
611
+ return {};
612
+ }
613
+ }
614
+ async _hasLocalContent(aid, record) {
615
+ if (record && typeof record.content === 'string' && record.content.length > 0)
616
+ return true;
617
+ try {
618
+ return (await this.readContent(aid)) !== null;
619
+ }
620
+ catch {
621
+ return false;
622
+ }
623
+ }
624
+ async _scheduleFetchIfMissing(aid, record, source = '') {
625
+ const target = String(aid ?? '').trim();
626
+ if (!target || await this._hasLocalContent(target, record))
627
+ return;
628
+ if (this._fetchInflight.has(target))
629
+ return;
630
+ this._fetchInflight.add(target);
631
+ try {
632
+ await this.download(target);
633
+ }
634
+ catch (err) {
635
+ await this.saveRecord(target, {
636
+ last_error: err instanceof Error ? err.message : String(err),
637
+ remote_status: 'found',
638
+ });
639
+ this._log.debug(`agent.md auto fetch failed: aid=${target} source=${source || '-'} err=${err instanceof Error ? err.message : String(err)}`);
640
+ }
641
+ finally {
642
+ this._fetchInflight.delete(target);
643
+ }
644
+ }
645
+ _refreshOwnerEtags(aid, record) {
646
+ if (aid !== this._ownerAid())
647
+ return;
648
+ const localEtag = String(record.local_etag ?? '').trim();
649
+ const remoteEtag = String(record.remote_etag ?? '').trim();
650
+ if (localEtag)
651
+ this._localAgentMdEtag = localEtag;
652
+ if (remoteEtag)
653
+ this._remoteAgentMdEtag = remoteEtag;
654
+ }
655
+ }
656
+ //# sourceMappingURL=agent-md.js.map