@agentcash/discovery 0.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1448 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULT_COMPAT_MODE: () => DEFAULT_COMPAT_MODE,
24
+ LEGACY_SUNSET_DATE: () => LEGACY_SUNSET_DATE,
25
+ STRICT_ESCALATION_CODES: () => STRICT_ESCALATION_CODES,
26
+ UPGRADE_WARNING_CODES: () => UPGRADE_WARNING_CODES,
27
+ computeUpgradeSignal: () => computeUpgradeSignal,
28
+ discover: () => discover,
29
+ discoverDetailed: () => discoverDetailed
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/flags.ts
34
+ var LEGACY_SUNSET_DATE = "2026-03-24";
35
+ var DEFAULT_COMPAT_MODE = "on";
36
+ var STRICT_ESCALATION_CODES = [
37
+ "LEGACY_WELL_KNOWN_USED",
38
+ "LEGACY_DNS_USED",
39
+ "LEGACY_DNS_PLAIN_URL",
40
+ "LEGACY_MISSING_METHOD",
41
+ "LEGACY_INSTRUCTIONS_USED",
42
+ "LEGACY_OWNERSHIP_PROOFS_USED",
43
+ "INTEROP_MPP_USED"
44
+ ];
45
+
46
+ // src/core/constants.ts
47
+ var OPENAPI_PATH_CANDIDATES = ["/openapi.json", "/.well-known/openapi.json"];
48
+ var WELL_KNOWN_MPP_PATH = "/.well-known/mpp";
49
+ var LLMS_TOKEN_WARNING_THRESHOLD = 2500;
50
+ var HTTP_METHODS = /* @__PURE__ */ new Set([
51
+ "GET",
52
+ "POST",
53
+ "PUT",
54
+ "DELETE",
55
+ "PATCH",
56
+ "HEAD",
57
+ "OPTIONS",
58
+ "TRACE"
59
+ ]);
60
+ var DEFAULT_PROBE_METHODS = ["GET", "POST"];
61
+ var DEFAULT_MISSING_METHOD = "POST";
62
+
63
+ // src/core/url.ts
64
+ function normalizeOrigin(target) {
65
+ const trimmed = target.trim();
66
+ const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
67
+ const url = new URL(withProtocol);
68
+ url.pathname = "";
69
+ url.search = "";
70
+ url.hash = "";
71
+ return url.toString().replace(/\/$/, "");
72
+ }
73
+ function normalizePath(pathname) {
74
+ const parsed = pathname.trim();
75
+ if (parsed.length === 0 || parsed === "/") return "/";
76
+ const pathOnly = parsed.split("?")[0]?.split("#")[0] ?? "/";
77
+ const prefixed = pathOnly.startsWith("/") ? pathOnly : `/${pathOnly}`;
78
+ const normalized = prefixed.replace(/\/+/g, "/");
79
+ return normalized !== "/" ? normalized.replace(/\/$/, "") : "/";
80
+ }
81
+ function toResourceKey(origin, method, path) {
82
+ return `${origin} ${method} ${normalizePath(path)}`;
83
+ }
84
+ function parseMethod(value) {
85
+ if (!value) return void 0;
86
+ const upper = value.toUpperCase();
87
+ return HTTP_METHODS.has(upper) ? upper : void 0;
88
+ }
89
+ function toAbsoluteUrl(origin, value) {
90
+ try {
91
+ if (/^https?:\/\//i.test(value)) return new URL(value);
92
+ return new URL(normalizePath(value), `${origin}/`);
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+
98
+ // src/core/warnings.ts
99
+ function warning(code, severity, message, options) {
100
+ return {
101
+ code,
102
+ severity,
103
+ message,
104
+ ...options?.hint ? { hint: options.hint } : {},
105
+ ...options?.stage ? { stage: options.stage } : {},
106
+ ...options?.resourceKey ? { resourceKey: options.resourceKey } : {}
107
+ };
108
+ }
109
+ function applyStrictEscalation(warnings, strict) {
110
+ if (!strict) return warnings;
111
+ return warnings.map((entry) => {
112
+ if (!STRICT_ESCALATION_CODES.includes(entry.code)) {
113
+ return entry;
114
+ }
115
+ if (entry.severity === "error") return entry;
116
+ return {
117
+ ...entry,
118
+ severity: "error",
119
+ message: `${entry.message} (strict mode escalated)`
120
+ };
121
+ });
122
+ }
123
+ function dedupeWarnings(warnings) {
124
+ const seen = /* @__PURE__ */ new Set();
125
+ const output = [];
126
+ for (const item of warnings) {
127
+ const key = `${item.code}|${item.severity}|${item.stage ?? ""}|${item.resourceKey ?? ""}|${item.message}`;
128
+ if (seen.has(key)) continue;
129
+ seen.add(key);
130
+ output.push(item);
131
+ }
132
+ return output;
133
+ }
134
+
135
+ // src/core/normalize.ts
136
+ function createResource(input, confidence, trustTier) {
137
+ const normalizedOrigin = normalizeOrigin(input.origin);
138
+ const normalizedPath = normalizePath(input.path);
139
+ return {
140
+ resourceKey: toResourceKey(normalizedOrigin, input.method, normalizedPath),
141
+ origin: normalizedOrigin,
142
+ method: input.method,
143
+ path: normalizedPath,
144
+ source: input.source,
145
+ verified: false,
146
+ ...input.protocolHints?.length ? { protocolHints: [...new Set(input.protocolHints)] } : {},
147
+ ...input.priceHint ? { priceHint: input.priceHint } : {},
148
+ ...input.pricing ? { pricing: input.pricing } : {},
149
+ ...input.authHint ? { authHint: input.authHint } : {},
150
+ ...input.summary ? { summary: input.summary } : {},
151
+ confidence,
152
+ trustTier,
153
+ ...input.links ? { links: input.links } : {}
154
+ };
155
+ }
156
+
157
+ // src/compat/legacy-x402scan/wellKnown.ts
158
+ function isRecord(value) {
159
+ return value !== null && typeof value === "object" && !Array.isArray(value);
160
+ }
161
+ function parseLegacyResourceEntry(entry) {
162
+ const trimmed = entry.trim();
163
+ if (!trimmed) return null;
164
+ const parts = trimmed.split(/\s+/);
165
+ if (parts.length >= 2) {
166
+ const maybeMethod = parseMethod(parts[0]);
167
+ if (maybeMethod) {
168
+ const target = parts.slice(1).join(" ");
169
+ return { method: maybeMethod, target };
170
+ }
171
+ }
172
+ if (trimmed.startsWith("/") || /^https?:\/\//i.test(trimmed)) {
173
+ return { target: trimmed };
174
+ }
175
+ return null;
176
+ }
177
+ function parseWellKnownPayload(payload, origin, sourceUrl) {
178
+ const warnings = [
179
+ warning(
180
+ "LEGACY_WELL_KNOWN_USED",
181
+ "warn",
182
+ "Using legacy /.well-known/x402 compatibility path. Migrate to OpenAPI-first.",
183
+ {
184
+ stage: "well-known/x402"
185
+ }
186
+ )
187
+ ];
188
+ if (!isRecord(payload)) {
189
+ warnings.push(
190
+ warning("PARSE_FAILED", "error", "Legacy well-known payload is not an object", {
191
+ stage: "well-known/x402"
192
+ })
193
+ );
194
+ return { resources: [], warnings, raw: payload };
195
+ }
196
+ const resourcesRaw = Array.isArray(payload.resources) ? payload.resources.filter((entry) => typeof entry === "string") : [];
197
+ if (resourcesRaw.length === 0) {
198
+ warnings.push(
199
+ warning("STAGE_EMPTY", "warn", "Legacy well-known has no valid resources array", {
200
+ stage: "well-known/x402"
201
+ })
202
+ );
203
+ }
204
+ const instructions = typeof payload.instructions === "string" ? payload.instructions : void 0;
205
+ const ownershipProofs = Array.isArray(payload.ownershipProofs) ? payload.ownershipProofs.filter((entry) => typeof entry === "string") : [];
206
+ if (instructions) {
207
+ warnings.push(
208
+ warning(
209
+ "LEGACY_INSTRUCTIONS_USED",
210
+ "warn",
211
+ "Using /.well-known/x402.instructions as compatibility guidance fallback. Prefer llms.txt.",
212
+ {
213
+ stage: "well-known/x402",
214
+ hint: "Move guidance to llms.txt and reference via x-agentcash-guidance.llmsTxtUrl."
215
+ }
216
+ )
217
+ );
218
+ }
219
+ if (ownershipProofs.length > 0) {
220
+ warnings.push(
221
+ warning(
222
+ "LEGACY_OWNERSHIP_PROOFS_USED",
223
+ "warn",
224
+ "Using /.well-known/x402.ownershipProofs compatibility field. Prefer OpenAPI provenance extension.",
225
+ {
226
+ stage: "well-known/x402",
227
+ hint: "Move ownership proofs to x-agentcash-provenance.ownershipProofs in OpenAPI."
228
+ }
229
+ )
230
+ );
231
+ }
232
+ const resources = [];
233
+ for (const rawEntry of resourcesRaw) {
234
+ const parsed = parseLegacyResourceEntry(rawEntry);
235
+ if (!parsed) {
236
+ warnings.push(
237
+ warning("PARSE_FAILED", "warn", `Invalid legacy resource entry: ${rawEntry}`, {
238
+ stage: "well-known/x402"
239
+ })
240
+ );
241
+ continue;
242
+ }
243
+ const absolute = toAbsoluteUrl(origin, parsed.target);
244
+ if (!absolute) {
245
+ warnings.push(
246
+ warning("PARSE_FAILED", "warn", `Invalid legacy resource URL: ${rawEntry}`, {
247
+ stage: "well-known/x402"
248
+ })
249
+ );
250
+ continue;
251
+ }
252
+ const method = parsed.method ?? DEFAULT_MISSING_METHOD;
253
+ if (!parsed.method) {
254
+ warnings.push(
255
+ warning(
256
+ "LEGACY_MISSING_METHOD",
257
+ "warn",
258
+ `Legacy resource '${rawEntry}' missing method. Defaulting to ${DEFAULT_MISSING_METHOD}.`,
259
+ {
260
+ stage: "well-known/x402"
261
+ }
262
+ )
263
+ );
264
+ }
265
+ const path = normalizePath(absolute.pathname);
266
+ resources.push(
267
+ createResource(
268
+ {
269
+ origin: absolute.origin,
270
+ method,
271
+ path,
272
+ source: "well-known/x402",
273
+ summary: `${method} ${path}`,
274
+ authHint: "paid",
275
+ protocolHints: ["x402"],
276
+ links: {
277
+ wellKnownUrl: sourceUrl,
278
+ discoveryUrl: sourceUrl
279
+ }
280
+ },
281
+ 0.65,
282
+ ownershipProofs.length > 0 ? "ownership_verified" : "origin_hosted"
283
+ )
284
+ );
285
+ }
286
+ return {
287
+ resources,
288
+ warnings,
289
+ raw: payload
290
+ };
291
+ }
292
+ async function runWellKnownX402Stage(options) {
293
+ const stageUrl = options.url ?? `${options.origin}/.well-known/x402`;
294
+ try {
295
+ const response = await options.fetcher(stageUrl, {
296
+ method: "GET",
297
+ headers: { Accept: "application/json", ...options.headers },
298
+ signal: options.signal
299
+ });
300
+ if (!response.ok) {
301
+ if (response.status === 404) {
302
+ return {
303
+ stage: "well-known/x402",
304
+ valid: false,
305
+ resources: [],
306
+ warnings: [],
307
+ links: { wellKnownUrl: stageUrl }
308
+ };
309
+ }
310
+ return {
311
+ stage: "well-known/x402",
312
+ valid: false,
313
+ resources: [],
314
+ warnings: [
315
+ warning("FETCH_FAILED", "warn", `Legacy well-known fetch failed (${response.status})`, {
316
+ stage: "well-known/x402"
317
+ })
318
+ ],
319
+ links: { wellKnownUrl: stageUrl }
320
+ };
321
+ }
322
+ const payload = await response.json();
323
+ const parsed = parseWellKnownPayload(payload, options.origin, stageUrl);
324
+ return {
325
+ stage: "well-known/x402",
326
+ valid: parsed.resources.length > 0,
327
+ resources: parsed.resources,
328
+ warnings: parsed.warnings,
329
+ links: { wellKnownUrl: stageUrl },
330
+ ...options.includeRaw ? { raw: parsed.raw } : {}
331
+ };
332
+ } catch (error) {
333
+ return {
334
+ stage: "well-known/x402",
335
+ valid: false,
336
+ resources: [],
337
+ warnings: [
338
+ warning(
339
+ "FETCH_FAILED",
340
+ "warn",
341
+ `Legacy well-known fetch exception: ${error instanceof Error ? error.message : String(error)}`,
342
+ {
343
+ stage: "well-known/x402"
344
+ }
345
+ )
346
+ ],
347
+ links: { wellKnownUrl: stageUrl }
348
+ };
349
+ }
350
+ }
351
+
352
+ // src/compat/legacy-x402scan/dns.ts
353
+ function parseDnsRecord(record) {
354
+ const trimmed = record.trim();
355
+ if (!trimmed) return null;
356
+ if (/^https?:\/\//i.test(trimmed)) {
357
+ return { url: trimmed, legacyPlainUrl: true };
358
+ }
359
+ const parts = trimmed.split(";").map((entry) => entry.trim()).filter(Boolean);
360
+ const keyValues = /* @__PURE__ */ new Map();
361
+ for (const part of parts) {
362
+ const separator = part.indexOf("=");
363
+ if (separator <= 0) continue;
364
+ const key = part.slice(0, separator).trim().toLowerCase();
365
+ const value = part.slice(separator + 1).trim();
366
+ if (key && value) keyValues.set(key, value);
367
+ }
368
+ if (keyValues.get("v") !== "x4021") return null;
369
+ const url = keyValues.get("url");
370
+ if (!url || !/^https?:\/\//i.test(url)) return null;
371
+ return { url, legacyPlainUrl: false };
372
+ }
373
+ async function runDnsStage(options) {
374
+ if (!options.txtResolver) {
375
+ return {
376
+ stage: "dns/_x402",
377
+ valid: false,
378
+ resources: [],
379
+ warnings: []
380
+ };
381
+ }
382
+ const origin = normalizeOrigin(options.origin);
383
+ const hostname = new URL(origin).hostname;
384
+ const fqdn = `_x402.${hostname}`;
385
+ let records;
386
+ try {
387
+ records = await options.txtResolver(fqdn);
388
+ } catch (error) {
389
+ return {
390
+ stage: "dns/_x402",
391
+ valid: false,
392
+ resources: [],
393
+ warnings: [
394
+ warning(
395
+ "FETCH_FAILED",
396
+ "warn",
397
+ `DNS TXT lookup failed for ${fqdn}: ${error instanceof Error ? error.message : String(error)}`,
398
+ { stage: "dns/_x402" }
399
+ )
400
+ ]
401
+ };
402
+ }
403
+ if (records.length === 0) {
404
+ return {
405
+ stage: "dns/_x402",
406
+ valid: false,
407
+ resources: [],
408
+ warnings: []
409
+ };
410
+ }
411
+ const warnings = [
412
+ warning(
413
+ "LEGACY_DNS_USED",
414
+ "warn",
415
+ "Using DNS _x402 compatibility path. Migrate to OpenAPI-first discovery.",
416
+ {
417
+ stage: "dns/_x402"
418
+ }
419
+ )
420
+ ];
421
+ const urls = [];
422
+ for (const record of records) {
423
+ const parsed = parseDnsRecord(record);
424
+ if (!parsed) {
425
+ warnings.push(
426
+ warning("PARSE_FAILED", "warn", `Invalid DNS _x402 TXT record: ${record}`, {
427
+ stage: "dns/_x402"
428
+ })
429
+ );
430
+ continue;
431
+ }
432
+ urls.push(parsed.url);
433
+ if (parsed.legacyPlainUrl) {
434
+ warnings.push(
435
+ warning("LEGACY_DNS_PLAIN_URL", "warn", `Legacy plain URL TXT format used: ${record}`, {
436
+ stage: "dns/_x402",
437
+ hint: "Use v=x4021;url=<https-url> format."
438
+ })
439
+ );
440
+ }
441
+ }
442
+ if (urls.length === 0) {
443
+ return {
444
+ stage: "dns/_x402",
445
+ valid: false,
446
+ resources: [],
447
+ warnings,
448
+ ...options.includeRaw ? { raw: { dnsRecords: records } } : {}
449
+ };
450
+ }
451
+ const mergedWarnings = [...warnings];
452
+ const mergedResources = [];
453
+ const rawDocuments = [];
454
+ for (const url of urls) {
455
+ const stageResult = await runWellKnownX402Stage({
456
+ origin,
457
+ url,
458
+ fetcher: options.fetcher,
459
+ headers: options.headers,
460
+ signal: options.signal,
461
+ includeRaw: options.includeRaw
462
+ });
463
+ mergedResources.push(...stageResult.resources);
464
+ mergedWarnings.push(...stageResult.warnings);
465
+ if (options.includeRaw && stageResult.raw !== void 0) {
466
+ rawDocuments.push({ url, document: stageResult.raw });
467
+ }
468
+ }
469
+ const deduped = /* @__PURE__ */ new Map();
470
+ for (const resource of mergedResources) {
471
+ if (!deduped.has(resource.resourceKey)) {
472
+ deduped.set(resource.resourceKey, {
473
+ ...resource,
474
+ source: "dns/_x402",
475
+ links: {
476
+ ...resource.links,
477
+ discoveryUrl: resource.links?.discoveryUrl
478
+ }
479
+ });
480
+ }
481
+ }
482
+ return {
483
+ stage: "dns/_x402",
484
+ valid: deduped.size > 0,
485
+ resources: [...deduped.values()],
486
+ warnings: mergedWarnings,
487
+ ...options.includeRaw ? { raw: { dnsRecords: records, documents: rawDocuments } } : {}
488
+ };
489
+ }
490
+
491
+ // src/compat/interop-mpp/wellKnownMpp.ts
492
+ function isRecord2(value) {
493
+ return value !== null && typeof value === "object" && !Array.isArray(value);
494
+ }
495
+ async function runInteropMppStage(options) {
496
+ const url = `${options.origin}${WELL_KNOWN_MPP_PATH}`;
497
+ try {
498
+ const response = await options.fetcher(url, {
499
+ method: "GET",
500
+ headers: { Accept: "application/json", ...options.headers },
501
+ signal: options.signal
502
+ });
503
+ if (!response.ok) {
504
+ return {
505
+ stage: "interop/mpp",
506
+ valid: false,
507
+ resources: [],
508
+ warnings: []
509
+ };
510
+ }
511
+ const payload = await response.json();
512
+ if (!isRecord2(payload)) {
513
+ return {
514
+ stage: "interop/mpp",
515
+ valid: false,
516
+ resources: [],
517
+ warnings: [
518
+ warning("PARSE_FAILED", "warn", "Interop /.well-known/mpp payload is not an object", {
519
+ stage: "interop/mpp"
520
+ })
521
+ ],
522
+ ...options.includeRaw ? { raw: payload } : {}
523
+ };
524
+ }
525
+ const warnings = [
526
+ warning(
527
+ "INTEROP_MPP_USED",
528
+ "info",
529
+ "Using /.well-known/mpp interop adapter. This is additive and non-canonical.",
530
+ {
531
+ stage: "interop/mpp",
532
+ hint: "Use for registry indexing only, not canonical runtime routing."
533
+ }
534
+ )
535
+ ];
536
+ const resources = [];
537
+ const fromStringResources = Array.isArray(payload.resources) ? payload.resources.filter((entry) => typeof entry === "string") : [];
538
+ for (const entry of fromStringResources) {
539
+ const parts = entry.trim().split(/\s+/);
540
+ let method = parseMethod(parts[0]);
541
+ let path = parts.join(" ");
542
+ if (method) {
543
+ path = parts.slice(1).join(" ");
544
+ } else {
545
+ method = "POST";
546
+ }
547
+ const normalizedPath = normalizePath(path);
548
+ resources.push(
549
+ createResource(
550
+ {
551
+ origin: options.origin,
552
+ method,
553
+ path: normalizedPath,
554
+ source: "interop/mpp",
555
+ summary: `${method} ${normalizedPath}`,
556
+ authHint: "paid",
557
+ protocolHints: ["mpp"],
558
+ links: { discoveryUrl: url }
559
+ },
560
+ 0.45,
561
+ "unverified"
562
+ )
563
+ );
564
+ }
565
+ const fromServiceObjects = Array.isArray(payload.services) ? payload.services.filter((entry) => isRecord2(entry)) : [];
566
+ for (const service of fromServiceObjects) {
567
+ const path = typeof service.path === "string" ? service.path : void 0;
568
+ const method = parseMethod(typeof service.method === "string" ? service.method : void 0) ?? "POST";
569
+ if (!path) continue;
570
+ const normalizedPath = normalizePath(path);
571
+ resources.push(
572
+ createResource(
573
+ {
574
+ origin: options.origin,
575
+ method,
576
+ path: normalizedPath,
577
+ source: "interop/mpp",
578
+ summary: typeof service.summary === "string" ? service.summary : `${method} ${normalizedPath}`,
579
+ authHint: "paid",
580
+ protocolHints: ["mpp"],
581
+ links: { discoveryUrl: url }
582
+ },
583
+ 0.45,
584
+ "unverified"
585
+ )
586
+ );
587
+ }
588
+ const deduped = /* @__PURE__ */ new Map();
589
+ for (const resource of resources) {
590
+ if (!deduped.has(resource.resourceKey)) deduped.set(resource.resourceKey, resource);
591
+ }
592
+ return {
593
+ stage: "interop/mpp",
594
+ valid: deduped.size > 0,
595
+ resources: [...deduped.values()],
596
+ warnings,
597
+ ...options.includeRaw ? { raw: payload } : {}
598
+ };
599
+ } catch (error) {
600
+ return {
601
+ stage: "interop/mpp",
602
+ valid: false,
603
+ resources: [],
604
+ warnings: [
605
+ warning(
606
+ "FETCH_FAILED",
607
+ "warn",
608
+ `Interop /.well-known/mpp fetch failed: ${error instanceof Error ? error.message : String(error)}`,
609
+ {
610
+ stage: "interop/mpp"
611
+ }
612
+ )
613
+ ]
614
+ };
615
+ }
616
+ }
617
+
618
+ // src/core/token.ts
619
+ function estimateTokenCount(text) {
620
+ return Math.ceil(text.length / 4);
621
+ }
622
+
623
+ // src/core/openapi.ts
624
+ function isRecord3(value) {
625
+ return value !== null && typeof value === "object" && !Array.isArray(value);
626
+ }
627
+ function asString(value) {
628
+ return typeof value === "string" && value.length > 0 ? value : void 0;
629
+ }
630
+ function parsePriceValue(value) {
631
+ if (typeof value === "string" && value.length > 0) return value;
632
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
633
+ return void 0;
634
+ }
635
+ function require402Response(operation) {
636
+ const responses = operation.responses;
637
+ if (!isRecord3(responses)) return false;
638
+ return Boolean(responses["402"]);
639
+ }
640
+ function parseProtocols(paymentInfo) {
641
+ const protocols = paymentInfo.protocols;
642
+ if (!Array.isArray(protocols)) return [];
643
+ return protocols.filter(
644
+ (entry) => typeof entry === "string" && entry.length > 0
645
+ );
646
+ }
647
+ function parseAuthMode(operation) {
648
+ const auth = operation["x-agentcash-auth"];
649
+ if (!isRecord3(auth)) return void 0;
650
+ const mode = auth.mode;
651
+ if (mode === "paid" || mode === "siwx" || mode === "apiKey" || mode === "unprotected") {
652
+ return mode;
653
+ }
654
+ return void 0;
655
+ }
656
+ async function runOpenApiStage(options) {
657
+ const warnings = [];
658
+ const resources = [];
659
+ let fetchedUrl;
660
+ let document;
661
+ for (const path of OPENAPI_PATH_CANDIDATES) {
662
+ const url = `${options.origin}${path}`;
663
+ try {
664
+ const response = await options.fetcher(url, {
665
+ method: "GET",
666
+ headers: { Accept: "application/json", ...options.headers },
667
+ signal: options.signal
668
+ });
669
+ if (!response.ok) {
670
+ if (response.status !== 404) {
671
+ warnings.push(
672
+ warning("FETCH_FAILED", "warn", `OpenAPI fetch failed (${response.status}) at ${url}`, {
673
+ stage: "openapi"
674
+ })
675
+ );
676
+ }
677
+ continue;
678
+ }
679
+ const payload = await response.json();
680
+ if (!isRecord3(payload)) {
681
+ warnings.push(
682
+ warning("PARSE_FAILED", "error", `OpenAPI payload at ${url} is not a JSON object`, {
683
+ stage: "openapi"
684
+ })
685
+ );
686
+ continue;
687
+ }
688
+ document = payload;
689
+ fetchedUrl = url;
690
+ break;
691
+ } catch (error) {
692
+ warnings.push(
693
+ warning(
694
+ "FETCH_FAILED",
695
+ "warn",
696
+ `OpenAPI fetch exception at ${url}: ${error instanceof Error ? error.message : String(error)}`,
697
+ { stage: "openapi" }
698
+ )
699
+ );
700
+ }
701
+ }
702
+ if (!document || !fetchedUrl) {
703
+ return {
704
+ stage: "openapi",
705
+ valid: false,
706
+ resources: [],
707
+ warnings
708
+ };
709
+ }
710
+ const hasTopLevel = typeof document.openapi === "string" && isRecord3(document.info) && typeof document.info.title === "string" && typeof document.info.version === "string" && isRecord3(document.paths);
711
+ if (!hasTopLevel) {
712
+ warnings.push(
713
+ warning("OPENAPI_TOP_LEVEL_INVALID", "error", "OpenAPI required fields are missing", {
714
+ stage: "openapi"
715
+ })
716
+ );
717
+ return {
718
+ stage: "openapi",
719
+ valid: false,
720
+ resources: [],
721
+ warnings,
722
+ links: { openapiUrl: fetchedUrl },
723
+ ...options.includeRaw ? { raw: document } : {}
724
+ };
725
+ }
726
+ const paths = document.paths;
727
+ const provenance = isRecord3(document["x-agentcash-provenance"]) ? document["x-agentcash-provenance"] : void 0;
728
+ const ownershipProofs = Array.isArray(provenance?.ownershipProofs) ? provenance.ownershipProofs.filter((entry) => typeof entry === "string") : [];
729
+ const guidance = isRecord3(document["x-agentcash-guidance"]) ? document["x-agentcash-guidance"] : void 0;
730
+ const llmsTxtUrl = asString(guidance?.llmsTxtUrl);
731
+ if (ownershipProofs.length > 0) {
732
+ warnings.push(
733
+ warning("OPENAPI_OWNERSHIP_PROOFS_PRESENT", "info", "OpenAPI ownership proofs detected", {
734
+ stage: "openapi"
735
+ })
736
+ );
737
+ }
738
+ for (const [rawPath, rawPathItem] of Object.entries(paths)) {
739
+ if (!isRecord3(rawPathItem)) {
740
+ warnings.push(
741
+ warning("OPENAPI_OPERATION_INVALID", "warn", `Path item ${rawPath} is not an object`, {
742
+ stage: "openapi"
743
+ })
744
+ );
745
+ continue;
746
+ }
747
+ for (const [rawMethod, rawOperation] of Object.entries(rawPathItem)) {
748
+ const method = parseMethod(rawMethod);
749
+ if (!method) continue;
750
+ if (!isRecord3(rawOperation)) {
751
+ warnings.push(
752
+ warning(
753
+ "OPENAPI_OPERATION_INVALID",
754
+ "warn",
755
+ `${rawMethod.toUpperCase()} ${rawPath} is not an object`,
756
+ {
757
+ stage: "openapi"
758
+ }
759
+ )
760
+ );
761
+ continue;
762
+ }
763
+ const operation = rawOperation;
764
+ const summary = asString(operation.summary) ?? asString(operation.description);
765
+ if (!summary) {
766
+ warnings.push(
767
+ warning(
768
+ "OPENAPI_SUMMARY_MISSING",
769
+ "warn",
770
+ `${method} ${rawPath} missing summary/description, using fallback summary`,
771
+ { stage: "openapi" }
772
+ )
773
+ );
774
+ }
775
+ const authMode = parseAuthMode(operation);
776
+ if (!authMode) {
777
+ warnings.push(
778
+ warning(
779
+ "OPENAPI_AUTH_MODE_MISSING",
780
+ "error",
781
+ `${method} ${rawPath} missing x-agentcash-auth.mode`,
782
+ {
783
+ stage: "openapi"
784
+ }
785
+ )
786
+ );
787
+ continue;
788
+ }
789
+ if ((authMode === "paid" || authMode === "siwx") && !require402Response(operation)) {
790
+ warnings.push(
791
+ warning(
792
+ "OPENAPI_402_MISSING",
793
+ "error",
794
+ `${method} ${rawPath} requires 402 response for authMode=${authMode}`,
795
+ { stage: "openapi" }
796
+ )
797
+ );
798
+ continue;
799
+ }
800
+ const paymentInfo = isRecord3(operation["x-payment-info"]) ? operation["x-payment-info"] : void 0;
801
+ const protocols = paymentInfo ? parseProtocols(paymentInfo) : [];
802
+ if (authMode === "paid" && protocols.length === 0) {
803
+ warnings.push(
804
+ warning(
805
+ "OPENAPI_PAID_PROTOCOLS_MISSING",
806
+ "error",
807
+ `${method} ${rawPath} must define x-payment-info.protocols when authMode=paid`,
808
+ { stage: "openapi" }
809
+ )
810
+ );
811
+ continue;
812
+ }
813
+ let pricing;
814
+ let priceHint;
815
+ if (paymentInfo && authMode === "paid") {
816
+ const pricingModeRaw = asString(paymentInfo.pricingMode);
817
+ const price = parsePriceValue(paymentInfo.price);
818
+ const minPrice = parsePriceValue(paymentInfo.minPrice);
819
+ const maxPrice = parsePriceValue(paymentInfo.maxPrice);
820
+ const inferredPricingMode = pricingModeRaw ?? (price ? "fixed" : minPrice && maxPrice ? "range" : "quote");
821
+ if (inferredPricingMode !== "fixed" && inferredPricingMode !== "range" && inferredPricingMode !== "quote") {
822
+ warnings.push(
823
+ warning(
824
+ "OPENAPI_PRICING_INVALID",
825
+ "error",
826
+ `${method} ${rawPath} has invalid pricingMode`,
827
+ {
828
+ stage: "openapi"
829
+ }
830
+ )
831
+ );
832
+ continue;
833
+ }
834
+ if (inferredPricingMode === "fixed" && !price) {
835
+ warnings.push(
836
+ warning(
837
+ "OPENAPI_PRICING_INVALID",
838
+ "error",
839
+ `${method} ${rawPath} fixed pricing requires price`,
840
+ {
841
+ stage: "openapi"
842
+ }
843
+ )
844
+ );
845
+ continue;
846
+ }
847
+ if (inferredPricingMode === "range") {
848
+ if (!minPrice || !maxPrice) {
849
+ warnings.push(
850
+ warning(
851
+ "OPENAPI_PRICING_INVALID",
852
+ "error",
853
+ `${method} ${rawPath} range pricing requires minPrice and maxPrice`,
854
+ { stage: "openapi" }
855
+ )
856
+ );
857
+ continue;
858
+ }
859
+ const min = Number(minPrice);
860
+ const max = Number(maxPrice);
861
+ if (!Number.isFinite(min) || !Number.isFinite(max) || min > max) {
862
+ warnings.push(
863
+ warning(
864
+ "OPENAPI_PRICING_INVALID",
865
+ "error",
866
+ `${method} ${rawPath} range pricing requires numeric minPrice <= maxPrice`,
867
+ { stage: "openapi" }
868
+ )
869
+ );
870
+ continue;
871
+ }
872
+ }
873
+ pricing = {
874
+ pricingMode: inferredPricingMode,
875
+ ...price ? { price } : {},
876
+ ...minPrice ? { minPrice } : {},
877
+ ...maxPrice ? { maxPrice } : {}
878
+ };
879
+ priceHint = inferredPricingMode === "fixed" ? price : inferredPricingMode === "range" ? `${minPrice}-${maxPrice}` : maxPrice;
880
+ }
881
+ const resource = createResource(
882
+ {
883
+ origin: options.origin,
884
+ method,
885
+ path: normalizePath(rawPath),
886
+ source: "openapi",
887
+ summary: summary ?? `${method} ${normalizePath(rawPath)}`,
888
+ authHint: authMode,
889
+ protocolHints: protocols,
890
+ ...priceHint ? { priceHint } : {},
891
+ ...pricing ? { pricing } : {},
892
+ links: {
893
+ openapiUrl: fetchedUrl,
894
+ ...llmsTxtUrl ? { llmsTxtUrl } : {}
895
+ }
896
+ },
897
+ 0.95,
898
+ ownershipProofs.length > 0 ? "ownership_verified" : "origin_hosted"
899
+ );
900
+ resources.push(resource);
901
+ }
902
+ }
903
+ if (llmsTxtUrl) {
904
+ try {
905
+ const llmsResponse = await options.fetcher(llmsTxtUrl, {
906
+ method: "GET",
907
+ headers: { Accept: "text/plain", ...options.headers },
908
+ signal: options.signal
909
+ });
910
+ if (llmsResponse.ok) {
911
+ const llmsText = await llmsResponse.text();
912
+ const tokenCount = estimateTokenCount(llmsText);
913
+ if (tokenCount > LLMS_TOKEN_WARNING_THRESHOLD) {
914
+ warnings.push(
915
+ warning(
916
+ "LLMSTXT_TOO_LARGE",
917
+ "warn",
918
+ `llms.txt estimated ${tokenCount} tokens (threshold ${LLMS_TOKEN_WARNING_THRESHOLD})`,
919
+ {
920
+ stage: "openapi",
921
+ hint: "Keep llms.txt concise and domain-level only."
922
+ }
923
+ )
924
+ );
925
+ }
926
+ if (options.includeRaw) {
927
+ return {
928
+ stage: "openapi",
929
+ valid: resources.length > 0,
930
+ resources,
931
+ warnings,
932
+ links: { openapiUrl: fetchedUrl, llmsTxtUrl },
933
+ raw: {
934
+ openapi: document,
935
+ llmsTxt: llmsText
936
+ }
937
+ };
938
+ }
939
+ } else {
940
+ warnings.push(
941
+ warning(
942
+ "LLMSTXT_FETCH_FAILED",
943
+ "warn",
944
+ `llms.txt fetch failed (${llmsResponse.status})`,
945
+ {
946
+ stage: "openapi"
947
+ }
948
+ )
949
+ );
950
+ }
951
+ } catch (error) {
952
+ warnings.push(
953
+ warning(
954
+ "LLMSTXT_FETCH_FAILED",
955
+ "warn",
956
+ `llms.txt fetch failed: ${error instanceof Error ? error.message : String(error)}`,
957
+ { stage: "openapi" }
958
+ )
959
+ );
960
+ }
961
+ }
962
+ return {
963
+ stage: "openapi",
964
+ valid: resources.length > 0,
965
+ resources,
966
+ warnings,
967
+ links: {
968
+ openapiUrl: fetchedUrl,
969
+ ...llmsTxtUrl ? { llmsTxtUrl } : {}
970
+ },
971
+ ...options.includeRaw ? { raw: { openapi: document } } : {}
972
+ };
973
+ }
974
+
975
+ // src/core/probe.ts
976
+ function detectAuthHintFrom402Payload(payload) {
977
+ if (payload && typeof payload === "object") {
978
+ const asRecord = payload;
979
+ const extensions = asRecord.extensions;
980
+ if (extensions && typeof extensions === "object") {
981
+ const extRecord = extensions;
982
+ if (extRecord["sign-in-with-x"]) return "siwx";
983
+ }
984
+ }
985
+ return "paid";
986
+ }
987
+ function detectProtocols(response) {
988
+ const protocols = /* @__PURE__ */ new Set();
989
+ const directHeader = response.headers.get("x-payment-protocol");
990
+ if (directHeader) {
991
+ for (const part of directHeader.split(",")) {
992
+ const protocol = part.trim().toLowerCase();
993
+ if (protocol) protocols.add(protocol);
994
+ }
995
+ }
996
+ const authHeader = response.headers.get("www-authenticate")?.toLowerCase() ?? "";
997
+ if (authHeader.includes("x402")) protocols.add("x402");
998
+ if (authHeader.includes("mpp")) protocols.add("mpp");
999
+ return [...protocols];
1000
+ }
1001
+ async function runProbeStage(options) {
1002
+ const candidates = options.probeCandidates ?? [];
1003
+ if (candidates.length === 0) {
1004
+ return {
1005
+ stage: "probe",
1006
+ valid: false,
1007
+ resources: [],
1008
+ warnings: [
1009
+ warning(
1010
+ "PROBE_STAGE_SKIPPED",
1011
+ "info",
1012
+ "Probe stage skipped because no probe candidates were provided.",
1013
+ { stage: "probe" }
1014
+ )
1015
+ ]
1016
+ };
1017
+ }
1018
+ const resources = [];
1019
+ const warnings = [];
1020
+ for (const candidate of candidates) {
1021
+ const path = normalizePath(candidate.path);
1022
+ const methods = candidate.methods?.length ? candidate.methods : DEFAULT_PROBE_METHODS;
1023
+ for (const method of methods) {
1024
+ const url = `${options.origin}${path}`;
1025
+ try {
1026
+ const response = await options.fetcher(url, {
1027
+ method,
1028
+ headers: {
1029
+ Accept: "application/json, text/plain;q=0.9, */*;q=0.8",
1030
+ ...options.headers
1031
+ },
1032
+ signal: options.signal
1033
+ });
1034
+ if (response.status !== 402 && (response.status < 200 || response.status >= 300)) {
1035
+ continue;
1036
+ }
1037
+ let authHint = response.status === 402 ? "paid" : "unprotected";
1038
+ if (response.status === 402) {
1039
+ try {
1040
+ const payload = await response.clone().json();
1041
+ authHint = detectAuthHintFrom402Payload(payload);
1042
+ } catch {
1043
+ }
1044
+ }
1045
+ const protocolHints = detectProtocols(response);
1046
+ resources.push(
1047
+ createResource(
1048
+ {
1049
+ origin: options.origin,
1050
+ method,
1051
+ path,
1052
+ source: "probe",
1053
+ summary: `${method} ${path}`,
1054
+ authHint,
1055
+ ...protocolHints.length ? { protocolHints } : {}
1056
+ },
1057
+ 0.6,
1058
+ "runtime_verified"
1059
+ )
1060
+ );
1061
+ } catch (error) {
1062
+ warnings.push(
1063
+ warning(
1064
+ "FETCH_FAILED",
1065
+ "info",
1066
+ `Probe ${method} ${path} failed: ${error instanceof Error ? error.message : String(error)}`,
1067
+ { stage: "probe" }
1068
+ )
1069
+ );
1070
+ }
1071
+ }
1072
+ }
1073
+ return {
1074
+ stage: "probe",
1075
+ valid: resources.length > 0,
1076
+ resources,
1077
+ warnings
1078
+ };
1079
+ }
1080
+
1081
+ // src/core/upgrade.ts
1082
+ var UPGRADE_WARNING_CODES = [
1083
+ "LEGACY_WELL_KNOWN_USED",
1084
+ "LEGACY_DNS_USED",
1085
+ "LEGACY_DNS_PLAIN_URL",
1086
+ "LEGACY_INSTRUCTIONS_USED",
1087
+ "LEGACY_OWNERSHIP_PROOFS_USED",
1088
+ "OPENAPI_AUTH_MODE_MISSING",
1089
+ "OPENAPI_TOP_LEVEL_INVALID"
1090
+ ];
1091
+ var UPGRADE_WARNING_CODE_SET = new Set(UPGRADE_WARNING_CODES);
1092
+ function computeUpgradeSignal(warnings) {
1093
+ const reasons = warnings.map((entry) => entry.code).filter((code, index, list) => list.indexOf(code) === index).filter((code) => UPGRADE_WARNING_CODE_SET.has(code));
1094
+ return {
1095
+ upgradeSuggested: reasons.length > 0,
1096
+ upgradeReasons: reasons
1097
+ };
1098
+ }
1099
+
1100
+ // src/core/discovery.ts
1101
+ function mergeResources(target, incoming, resourceWarnings) {
1102
+ const warnings = [];
1103
+ for (const resource of incoming) {
1104
+ const existing = target.get(resource.resourceKey);
1105
+ if (!existing) {
1106
+ target.set(resource.resourceKey, resource);
1107
+ continue;
1108
+ }
1109
+ const conflict = existing.authHint !== resource.authHint || existing.priceHint !== resource.priceHint || JSON.stringify(existing.protocolHints ?? []) !== JSON.stringify(resource.protocolHints ?? []);
1110
+ if (conflict) {
1111
+ const conflictWarning = warning(
1112
+ "CROSS_SOURCE_CONFLICT",
1113
+ "warn",
1114
+ `Resource conflict for ${resource.resourceKey}; keeping higher-precedence source ${existing.source} over ${resource.source}.`,
1115
+ {
1116
+ resourceKey: resource.resourceKey
1117
+ }
1118
+ );
1119
+ warnings.push(conflictWarning);
1120
+ resourceWarnings[resource.resourceKey] = [
1121
+ ...resourceWarnings[resource.resourceKey] ?? [],
1122
+ conflictWarning
1123
+ ];
1124
+ continue;
1125
+ }
1126
+ target.set(resource.resourceKey, {
1127
+ ...existing,
1128
+ summary: existing.summary ?? resource.summary,
1129
+ links: { ...resource.links, ...existing.links },
1130
+ confidence: Math.max(existing.confidence ?? 0, resource.confidence ?? 0)
1131
+ });
1132
+ }
1133
+ return warnings;
1134
+ }
1135
+ function toTrace(stageResult, startedAt) {
1136
+ return {
1137
+ stage: stageResult.stage,
1138
+ attempted: true,
1139
+ valid: stageResult.valid,
1140
+ resourceCount: stageResult.resources.length,
1141
+ durationMs: Date.now() - startedAt,
1142
+ warnings: stageResult.warnings,
1143
+ ...stageResult.links ? { links: stageResult.links } : {}
1144
+ };
1145
+ }
1146
+ async function runOverrideStage(options) {
1147
+ const warnings = [];
1148
+ for (const overrideUrl of options.overrideUrls) {
1149
+ const normalized = overrideUrl.trim();
1150
+ if (!normalized) continue;
1151
+ try {
1152
+ const response = await options.fetcher(normalized, {
1153
+ method: "GET",
1154
+ headers: { Accept: "application/json, text/plain;q=0.9", ...options.headers },
1155
+ signal: options.signal
1156
+ });
1157
+ if (!response.ok) {
1158
+ warnings.push(
1159
+ warning(
1160
+ "FETCH_FAILED",
1161
+ "warn",
1162
+ `Override fetch failed (${response.status}) at ${normalized}`,
1163
+ {
1164
+ stage: "override"
1165
+ }
1166
+ )
1167
+ );
1168
+ continue;
1169
+ }
1170
+ const bodyText = await response.text();
1171
+ let parsedJson;
1172
+ try {
1173
+ parsedJson = JSON.parse(bodyText);
1174
+ } catch {
1175
+ parsedJson = void 0;
1176
+ }
1177
+ if (parsedJson && typeof parsedJson === "object" && !Array.isArray(parsedJson) && "openapi" in parsedJson && "paths" in parsedJson) {
1178
+ const openapiResult = await runOpenApiStage({
1179
+ origin: options.origin,
1180
+ fetcher: async (input, init) => {
1181
+ const inputString = String(input);
1182
+ if (inputString === `${options.origin}/openapi.json` || inputString === `${options.origin}/.well-known/openapi.json`) {
1183
+ return new Response(bodyText, {
1184
+ status: 200,
1185
+ headers: { "content-type": "application/json" }
1186
+ });
1187
+ }
1188
+ return options.fetcher(input, init);
1189
+ },
1190
+ headers: options.headers,
1191
+ signal: options.signal,
1192
+ includeRaw: options.includeRaw
1193
+ });
1194
+ return {
1195
+ ...openapiResult,
1196
+ stage: "override",
1197
+ warnings: [
1198
+ warning("OVERRIDE_USED", "info", `Using explicit override source ${normalized}`, {
1199
+ stage: "override"
1200
+ }),
1201
+ ...openapiResult.warnings
1202
+ ]
1203
+ };
1204
+ }
1205
+ if (parsedJson && typeof parsedJson === "object" && !Array.isArray(parsedJson)) {
1206
+ const parsedWellKnown = parseWellKnownPayload(parsedJson, options.origin, normalized);
1207
+ if (parsedWellKnown.resources.length > 0) {
1208
+ return {
1209
+ stage: "override",
1210
+ valid: true,
1211
+ resources: parsedWellKnown.resources,
1212
+ warnings: [
1213
+ warning("OVERRIDE_USED", "info", `Using explicit override source ${normalized}`, {
1214
+ stage: "override"
1215
+ }),
1216
+ ...parsedWellKnown.warnings
1217
+ ],
1218
+ links: { discoveryUrl: normalized },
1219
+ ...options.includeRaw ? { raw: parsedWellKnown.raw } : {}
1220
+ };
1221
+ }
1222
+ }
1223
+ } catch (error) {
1224
+ warnings.push(
1225
+ warning(
1226
+ "FETCH_FAILED",
1227
+ "warn",
1228
+ `Override fetch exception at ${normalized}: ${error instanceof Error ? error.message : String(error)}`,
1229
+ { stage: "override" }
1230
+ )
1231
+ );
1232
+ continue;
1233
+ }
1234
+ const fallbackWellKnownResult = await runWellKnownX402Stage({
1235
+ origin: options.origin,
1236
+ url: normalized,
1237
+ fetcher: options.fetcher,
1238
+ headers: options.headers,
1239
+ signal: options.signal,
1240
+ includeRaw: options.includeRaw
1241
+ });
1242
+ if (fallbackWellKnownResult.valid) {
1243
+ return {
1244
+ ...fallbackWellKnownResult,
1245
+ stage: "override",
1246
+ warnings: [
1247
+ warning("OVERRIDE_USED", "info", `Using explicit override source ${normalized}`, {
1248
+ stage: "override"
1249
+ }),
1250
+ ...fallbackWellKnownResult.warnings
1251
+ ]
1252
+ };
1253
+ }
1254
+ warnings.push(...fallbackWellKnownResult.warnings);
1255
+ }
1256
+ return {
1257
+ stage: "override",
1258
+ valid: false,
1259
+ resources: [],
1260
+ warnings
1261
+ };
1262
+ }
1263
+ async function runDiscovery({
1264
+ detailed,
1265
+ options
1266
+ }) {
1267
+ const fetcher = options.fetcher ?? fetch;
1268
+ const compatMode = options.compatMode ?? DEFAULT_COMPAT_MODE;
1269
+ const strictCompat = compatMode === "strict";
1270
+ const rawView = detailed ? options.rawView ?? "none" : "none";
1271
+ const includeRaw = rawView === "full";
1272
+ const includeInteropMpp = detailed && Boolean(options.includeInteropMpp);
1273
+ const origin = normalizeOrigin(options.target);
1274
+ const warnings = [];
1275
+ const trace = [];
1276
+ const resourceWarnings = {};
1277
+ const rawSources = {};
1278
+ const merged = /* @__PURE__ */ new Map();
1279
+ if (compatMode !== "off") {
1280
+ warnings.push(
1281
+ warning(
1282
+ "COMPAT_MODE_ENABLED",
1283
+ compatMode === "strict" ? "warn" : "info",
1284
+ `Compatibility mode is '${compatMode}'. Legacy adapters are active.`
1285
+ )
1286
+ );
1287
+ }
1288
+ const stages = [];
1289
+ if (options.overrideUrls && options.overrideUrls.length > 0) {
1290
+ stages.push(
1291
+ () => runOverrideStage({
1292
+ origin,
1293
+ overrideUrls: options.overrideUrls ?? [],
1294
+ fetcher,
1295
+ headers: options.headers,
1296
+ signal: options.signal,
1297
+ includeRaw
1298
+ })
1299
+ );
1300
+ }
1301
+ stages.push(
1302
+ () => runOpenApiStage({
1303
+ origin,
1304
+ fetcher,
1305
+ headers: options.headers,
1306
+ signal: options.signal,
1307
+ includeRaw
1308
+ })
1309
+ );
1310
+ if (compatMode !== "off") {
1311
+ stages.push(
1312
+ () => runWellKnownX402Stage({
1313
+ origin,
1314
+ fetcher,
1315
+ headers: options.headers,
1316
+ signal: options.signal,
1317
+ includeRaw
1318
+ })
1319
+ );
1320
+ stages.push(
1321
+ () => runDnsStage({
1322
+ origin,
1323
+ fetcher,
1324
+ txtResolver: options.txtResolver,
1325
+ headers: options.headers,
1326
+ signal: options.signal,
1327
+ includeRaw
1328
+ })
1329
+ );
1330
+ if (includeInteropMpp) {
1331
+ stages.push(
1332
+ () => runInteropMppStage({
1333
+ origin,
1334
+ fetcher,
1335
+ headers: options.headers,
1336
+ signal: options.signal,
1337
+ includeRaw
1338
+ })
1339
+ );
1340
+ }
1341
+ }
1342
+ stages.push(
1343
+ () => runProbeStage({
1344
+ origin,
1345
+ fetcher,
1346
+ headers: options.headers,
1347
+ signal: options.signal,
1348
+ probeCandidates: options.probeCandidates
1349
+ })
1350
+ );
1351
+ let selectedStage;
1352
+ for (const runStage of stages) {
1353
+ const startedAt = Date.now();
1354
+ const stageResult = await runStage();
1355
+ stageResult.warnings = applyStrictEscalation(stageResult.warnings, strictCompat);
1356
+ trace.push(toTrace(stageResult, startedAt));
1357
+ warnings.push(...stageResult.warnings);
1358
+ if (stageResult.resources.length > 0) {
1359
+ for (const stageWarning of stageResult.warnings) {
1360
+ if (stageWarning.resourceKey) {
1361
+ resourceWarnings[stageWarning.resourceKey] = [
1362
+ ...resourceWarnings[stageWarning.resourceKey] ?? [],
1363
+ stageWarning
1364
+ ];
1365
+ }
1366
+ }
1367
+ }
1368
+ if (!stageResult.valid) {
1369
+ continue;
1370
+ }
1371
+ const mergeWarnings = mergeResources(merged, stageResult.resources, resourceWarnings);
1372
+ warnings.push(...mergeWarnings);
1373
+ if (includeRaw && stageResult.raw !== void 0) {
1374
+ if (stageResult.stage === "openapi" || stageResult.stage === "override") {
1375
+ const rawObject = stageResult.raw;
1376
+ if (rawObject.openapi !== void 0) rawSources.openapi = rawObject.openapi;
1377
+ if (typeof rawObject.llmsTxt === "string") rawSources.llmsTxt = rawObject.llmsTxt;
1378
+ }
1379
+ if (stageResult.stage === "well-known/x402" || stageResult.stage === "override") {
1380
+ rawSources.wellKnownX402 = [...rawSources.wellKnownX402 ?? [], stageResult.raw];
1381
+ }
1382
+ if (stageResult.stage === "dns/_x402") {
1383
+ const rawObject = stageResult.raw;
1384
+ if (Array.isArray(rawObject.dnsRecords)) {
1385
+ rawSources.dnsRecords = rawObject.dnsRecords;
1386
+ }
1387
+ }
1388
+ if (stageResult.stage === "interop/mpp") {
1389
+ rawSources.interopMpp = stageResult.raw;
1390
+ }
1391
+ }
1392
+ if (!detailed) {
1393
+ selectedStage = selectedStage ?? stageResult.stage;
1394
+ break;
1395
+ }
1396
+ }
1397
+ if (merged.size === 0) {
1398
+ warnings.push(
1399
+ warning(
1400
+ "NO_DISCOVERY_SOURCES",
1401
+ "error",
1402
+ "No discovery stage returned first-valid-non-empty results."
1403
+ )
1404
+ );
1405
+ }
1406
+ const dedupedWarnings = dedupeWarnings(warnings);
1407
+ const upgradeSignal = computeUpgradeSignal(dedupedWarnings);
1408
+ const resources = [...merged.values()];
1409
+ if (!detailed) {
1410
+ return {
1411
+ origin,
1412
+ resources,
1413
+ warnings: dedupedWarnings,
1414
+ compatMode,
1415
+ ...upgradeSignal,
1416
+ ...selectedStage ? { selectedStage } : {}
1417
+ };
1418
+ }
1419
+ return {
1420
+ origin,
1421
+ resources,
1422
+ warnings: dedupedWarnings,
1423
+ compatMode,
1424
+ ...upgradeSignal,
1425
+ selectedStage: trace.find((entry) => entry.valid)?.stage,
1426
+ trace,
1427
+ resourceWarnings,
1428
+ ...includeRaw ? { rawSources } : {}
1429
+ };
1430
+ }
1431
+
1432
+ // src/index.ts
1433
+ async function discover(options) {
1434
+ return await runDiscovery({ detailed: false, options });
1435
+ }
1436
+ async function discoverDetailed(options) {
1437
+ return await runDiscovery({ detailed: true, options });
1438
+ }
1439
+ // Annotate the CommonJS export names for ESM import in node:
1440
+ 0 && (module.exports = {
1441
+ DEFAULT_COMPAT_MODE,
1442
+ LEGACY_SUNSET_DATE,
1443
+ STRICT_ESCALATION_CODES,
1444
+ UPGRADE_WARNING_CODES,
1445
+ computeUpgradeSignal,
1446
+ discover,
1447
+ discoverDetailed
1448
+ });