@agentcash/discovery 1.0.1 → 1.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/cli.js CHANGED
@@ -50,6 +50,7 @@ var OpenApiDocSchema = z.object({
50
50
  description: z.string().optional(),
51
51
  guidance: z.string().optional()
52
52
  }),
53
+ security: z.array(z.record(z.string(), z.array(z.string()))).optional(),
53
54
  servers: z.array(z.object({ url: z.string() })).optional(),
54
55
  tags: z.array(z.object({ name: z.string() })).optional(),
55
56
  components: z.object({ securitySchemes: z.record(z.string(), z.unknown()).optional() }).optional(),
@@ -79,20 +80,31 @@ var WellKnownParsedSchema = z.object({
79
80
  function isRecord(value) {
80
81
  return value !== null && typeof value === "object" && !Array.isArray(value);
81
82
  }
82
- function hasSecurity(operation, scheme) {
83
- return operation.security?.some((s) => scheme in s) ?? false;
84
- }
85
- function has402Response(operation) {
86
- return Boolean(operation.responses?.["402"]);
83
+ function resolveSecurityFlags(requirements, securitySchemes) {
84
+ let hasApiKey = false;
85
+ let hasSiwx = false;
86
+ for (const requirement of requirements) {
87
+ for (const schemeName of Object.keys(requirement)) {
88
+ if (schemeName === "siwx") {
89
+ hasSiwx = true;
90
+ continue;
91
+ }
92
+ if (schemeName === "apiKey") {
93
+ hasApiKey = true;
94
+ continue;
95
+ }
96
+ const def = securitySchemes[schemeName];
97
+ if (isRecord(def) && def["type"] === "apiKey") hasApiKey = true;
98
+ }
99
+ }
100
+ return { hasApiKey, hasSiwx };
87
101
  }
88
- function inferAuthMode(operation) {
102
+ function inferAuthMode(operation, globalSecurity, securitySchemes) {
89
103
  const hasXPaymentInfo = Boolean(operation["x-payment-info"]);
90
- const hasPayment = hasXPaymentInfo || has402Response(operation);
91
- const hasApiKey = hasSecurity(operation, "apiKey");
92
- const hasSiwx = hasSecurity(operation, "siwx");
93
- if (hasPayment && hasApiKey) return "apiKey+paid";
104
+ const effectiveSecurity = operation.security !== void 0 && operation.security.length > 0 ? operation.security : globalSecurity ?? [];
105
+ const { hasApiKey, hasSiwx } = resolveSecurityFlags(effectiveSecurity, securitySchemes ?? {});
106
+ if (hasXPaymentInfo && hasApiKey) return "apiKey+paid";
94
107
  if (hasXPaymentInfo) return "paid";
95
- if (hasPayment) return hasSiwx ? "siwx" : "paid";
96
108
  if (hasApiKey) return "apiKey";
97
109
  if (hasSiwx) return "siwx";
98
110
  return void 0;
@@ -112,10 +124,12 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
112
124
  var DEFAULT_MISSING_METHOD = "POST";
113
125
 
114
126
  // src/core/lib/url.ts
115
- function normalizeOrigin(target) {
127
+ function ensureProtocol(target) {
116
128
  const trimmed = target.trim();
117
- const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
118
- const url = new URL(withProtocol);
129
+ return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
130
+ }
131
+ function normalizeOrigin(target) {
132
+ const url = new URL(ensureProtocol(target));
119
133
  url.pathname = "";
120
134
  url.search = "";
121
135
  url.hash = "";
@@ -154,7 +168,7 @@ function fetchSafe(url, init) {
154
168
  }
155
169
 
156
170
  // src/mmm-enabled.ts
157
- var isMmmEnabled = () => "1.0.1".includes("-mmm");
171
+ var isMmmEnabled = () => "1.1.0".includes("-mmm");
158
172
 
159
173
  // src/core/source/openapi/index.ts
160
174
  var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
@@ -163,15 +177,13 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
163
177
  for (const httpMethod of [...HTTP_METHODS]) {
164
178
  const operation = pathItem[httpMethod.toLowerCase()];
165
179
  if (!operation) continue;
166
- const authMode = inferAuthMode(operation) ?? void 0;
180
+ const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
167
181
  if (!authMode) continue;
168
182
  const p = operation["x-payment-info"];
169
183
  const protocols = (p?.protocols ?? []).filter(
170
184
  (proto) => proto !== "mpp" || isMmmEnabled()
171
185
  );
172
- if ((authMode === "paid" || authMode === "siwx") && !has402Response(operation)) continue;
173
- if (authMode === "paid" && protocols.length === 0) continue;
174
- const pricing = authMode === "paid" && p ? {
186
+ const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
175
187
  pricingMode: p.pricingMode,
176
188
  ...p.price ? { price: p.price } : {},
177
189
  ...p.minPrice ? { minPrice: p.minPrice } : {},
@@ -322,6 +334,7 @@ var AUDIT_CODES = {
322
334
  L2_NO_ROUTES: "L2_NO_ROUTES",
323
335
  L2_ROUTE_COUNT_HIGH: "L2_ROUTE_COUNT_HIGH",
324
336
  L2_AUTH_MODE_MISSING: "L2_AUTH_MODE_MISSING",
337
+ L2_NO_PAID_ROUTES: "L2_NO_PAID_ROUTES",
325
338
  L2_PRICE_MISSING_ON_PAID: "L2_PRICE_MISSING_ON_PAID",
326
339
  L2_PROTOCOLS_MISSING_ON_PAID: "L2_PROTOCOLS_MISSING_ON_PAID",
327
340
  // ─── L3 endpoint advisory checks ─────────────────────────────────────────────
@@ -383,6 +396,15 @@ function getWarningsForL2(l2) {
383
396
  });
384
397
  return warnings;
385
398
  }
399
+ const hasPaidRoute = l2.routes.some((r) => r.authMode === "paid" || r.authMode === "apiKey+paid");
400
+ if (!hasPaidRoute) {
401
+ warnings.push({
402
+ code: AUDIT_CODES.L2_NO_PAID_ROUTES,
403
+ severity: "info",
404
+ message: "No endpoints are marked as paid or apiKey+paid.",
405
+ hint: "Add x-payment-info to operations that require payment so agents know which endpoints are monetized."
406
+ });
407
+ }
386
408
  if (l2.routes.length > ROUTE_COUNT_HIGH) {
387
409
  warnings.push({
388
410
  code: AUDIT_CODES.L2_ROUTE_COUNT_HIGH,
@@ -469,8 +491,8 @@ function getWarningsForL4(l4) {
469
491
  {
470
492
  code: AUDIT_CODES.L4_GUIDANCE_MISSING,
471
493
  severity: "info",
472
- message: "No guidance text found (llms.txt or OpenAPI info.guidance).",
473
- hint: "Add an info.guidance field to your OpenAPI spec or expose /llms.txt for agent-readable instructions."
494
+ message: "No guidance text found (OpenAPI info.guidance).",
495
+ hint: "Add an info.guidance field to your OpenAPI spec for agent-readable instructions."
474
496
  }
475
497
  ];
476
498
  }
@@ -779,54 +801,31 @@ function extractPaymentOptions4(wwwAuthenticate) {
779
801
  return options;
780
802
  }
781
803
 
782
- // src/core/layers/l3.ts
783
- function findMatchingOpenApiPath(paths, targetPath) {
784
- const exact = paths[targetPath];
785
- if (isRecord(exact)) return { matchedPath: targetPath, pathItem: exact };
786
- for (const [specPath, entry] of Object.entries(paths)) {
787
- if (!isRecord(entry)) continue;
788
- const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
789
- const regex = new RegExp(`^${pattern}$`);
790
- if (regex.test(targetPath)) {
791
- return { matchedPath: specPath, pathItem: entry };
792
- }
793
- }
794
- return null;
795
- }
796
- function resolveRef(document, ref, seen) {
797
- if (!ref.startsWith("#/")) return void 0;
798
- if (seen.has(ref)) return { $circular: ref };
799
- seen.add(ref);
800
- const parts = ref.slice(2).split("/");
801
- let current = document;
802
- for (const part of parts) {
803
- if (!isRecord(current)) return void 0;
804
- current = current[part];
805
- if (current === void 0) return void 0;
806
- }
807
- if (isRecord(current)) return resolveRefs(document, current, seen);
808
- return current;
804
+ // src/core/lib/resolve-ref.ts
805
+ import pkg from "dereference-json-schema";
806
+ var { resolveRefSync } = pkg;
807
+ function isRecord2(value) {
808
+ return value !== null && typeof value === "object" && !Array.isArray(value);
809
809
  }
810
- function resolveRefs(document, obj, seen, depth = 0) {
811
- if (depth > 4) return obj;
810
+ function deepResolveRefs(document, obj) {
812
811
  const resolved = {};
813
812
  for (const [key, value] of Object.entries(obj)) {
814
813
  if (key === "$ref" && typeof value === "string") {
815
- const deref = resolveRef(document, value, seen);
816
- if (isRecord(deref)) {
817
- Object.assign(resolved, deref);
814
+ const deref = resolveRefSync(document, value);
815
+ if (isRecord2(deref)) {
816
+ Object.assign(resolved, deepResolveRefs(document, deref));
818
817
  } else {
819
818
  resolved[key] = value;
820
819
  }
821
820
  continue;
822
821
  }
823
- if (isRecord(value)) {
824
- resolved[key] = resolveRefs(document, value, seen, depth + 1);
822
+ if (isRecord2(value)) {
823
+ resolved[key] = deepResolveRefs(document, value);
825
824
  continue;
826
825
  }
827
826
  if (Array.isArray(value)) {
828
827
  resolved[key] = value.map(
829
- (item) => isRecord(item) ? resolveRefs(document, item, seen, depth + 1) : item
828
+ (item) => isRecord2(item) ? deepResolveRefs(document, item) : item
830
829
  );
831
830
  continue;
832
831
  }
@@ -834,6 +833,24 @@ function resolveRefs(document, obj, seen, depth = 0) {
834
833
  }
835
834
  return resolved;
836
835
  }
836
+ function resolveRefs(obj, document) {
837
+ return deepResolveRefs(document, obj);
838
+ }
839
+
840
+ // src/core/layers/l3.ts
841
+ function findMatchingOpenApiPath(paths, targetPath) {
842
+ const exact = paths[targetPath];
843
+ if (isRecord(exact)) return { matchedPath: targetPath, pathItem: exact };
844
+ for (const [specPath, entry] of Object.entries(paths)) {
845
+ if (!isRecord(entry)) continue;
846
+ const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
847
+ const regex = new RegExp(`^${pattern}$`);
848
+ if (regex.test(targetPath)) {
849
+ return { matchedPath: specPath, pathItem: entry };
850
+ }
851
+ }
852
+ return null;
853
+ }
837
854
  function extractRequestBodySchema(operationSchema) {
838
855
  const requestBody = operationSchema.requestBody;
839
856
  if (!isRecord(requestBody)) return void 0;
@@ -913,7 +930,7 @@ function getL3ForOpenAPI(openApi, path, method) {
913
930
  if (!matched) return null;
914
931
  const operation = matched.pathItem[method.toLowerCase()];
915
932
  if (!isRecord(operation)) return null;
916
- const resolvedOperation = resolveRefs(document, operation, /* @__PURE__ */ new Set());
933
+ const resolvedOperation = resolveRefs(operation, document);
917
934
  const summary = typeof resolvedOperation.summary === "string" ? resolvedOperation.summary : typeof resolvedOperation.description === "string" ? resolvedOperation.description : void 0;
918
935
  return {
919
936
  source: "openapi",
@@ -959,12 +976,12 @@ function getAdvisoriesForProbe(probe, path) {
959
976
  });
960
977
  }
961
978
  async function checkEndpointSchema(options) {
962
- const endpoint = new URL(options.url);
979
+ const endpoint = new URL(ensureProtocol(options.url));
963
980
  const origin = normalizeOrigin(endpoint.origin);
964
981
  const path = normalizePath(endpoint.pathname || "/");
965
982
  if (options.sampleInputBody !== void 0) {
966
983
  const probeResult2 = await getProbe(
967
- options.url,
984
+ endpoint.href,
968
985
  options.headers,
969
986
  options.signal,
970
987
  options.sampleInputBody
@@ -989,7 +1006,7 @@ async function checkEndpointSchema(options) {
989
1006
  if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
990
1007
  return { found: false, origin, path, cause: "not_found" };
991
1008
  }
992
- const probeResult = await getProbe(options.url, options.headers, options.signal);
1009
+ const probeResult = await getProbe(endpoint.href, options.headers, options.signal);
993
1010
  if (probeResult.isErr()) {
994
1011
  return {
995
1012
  found: false,
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -97,6 +107,7 @@ var OpenApiDocSchema = import_zod.z.object({
97
107
  description: import_zod.z.string().optional(),
98
108
  guidance: import_zod.z.string().optional()
99
109
  }),
110
+ security: import_zod.z.array(import_zod.z.record(import_zod.z.string(), import_zod.z.array(import_zod.z.string()))).optional(),
100
111
  servers: import_zod.z.array(import_zod.z.object({ url: import_zod.z.string() })).optional(),
101
112
  tags: import_zod.z.array(import_zod.z.object({ name: import_zod.z.string() })).optional(),
102
113
  components: import_zod.z.object({ securitySchemes: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional() }).optional(),
@@ -126,20 +137,31 @@ var WellKnownParsedSchema = import_zod.z.object({
126
137
  function isRecord(value) {
127
138
  return value !== null && typeof value === "object" && !Array.isArray(value);
128
139
  }
129
- function hasSecurity(operation, scheme) {
130
- return operation.security?.some((s) => scheme in s) ?? false;
131
- }
132
- function has402Response(operation) {
133
- return Boolean(operation.responses?.["402"]);
140
+ function resolveSecurityFlags(requirements, securitySchemes) {
141
+ let hasApiKey = false;
142
+ let hasSiwx = false;
143
+ for (const requirement of requirements) {
144
+ for (const schemeName of Object.keys(requirement)) {
145
+ if (schemeName === "siwx") {
146
+ hasSiwx = true;
147
+ continue;
148
+ }
149
+ if (schemeName === "apiKey") {
150
+ hasApiKey = true;
151
+ continue;
152
+ }
153
+ const def = securitySchemes[schemeName];
154
+ if (isRecord(def) && def["type"] === "apiKey") hasApiKey = true;
155
+ }
156
+ }
157
+ return { hasApiKey, hasSiwx };
134
158
  }
135
- function inferAuthMode(operation) {
159
+ function inferAuthMode(operation, globalSecurity, securitySchemes) {
136
160
  const hasXPaymentInfo = Boolean(operation["x-payment-info"]);
137
- const hasPayment = hasXPaymentInfo || has402Response(operation);
138
- const hasApiKey = hasSecurity(operation, "apiKey");
139
- const hasSiwx = hasSecurity(operation, "siwx");
140
- if (hasPayment && hasApiKey) return "apiKey+paid";
161
+ const effectiveSecurity = operation.security !== void 0 && operation.security.length > 0 ? operation.security : globalSecurity ?? [];
162
+ const { hasApiKey, hasSiwx } = resolveSecurityFlags(effectiveSecurity, securitySchemes ?? {});
163
+ if (hasXPaymentInfo && hasApiKey) return "apiKey+paid";
141
164
  if (hasXPaymentInfo) return "paid";
142
- if (hasPayment) return hasSiwx ? "siwx" : "paid";
143
165
  if (hasApiKey) return "apiKey";
144
166
  if (hasSiwx) return "siwx";
145
167
  return void 0;
@@ -159,10 +181,12 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
159
181
  var DEFAULT_MISSING_METHOD = "POST";
160
182
 
161
183
  // src/core/lib/url.ts
162
- function normalizeOrigin(target) {
184
+ function ensureProtocol(target) {
163
185
  const trimmed = target.trim();
164
- const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
165
- const url = new URL(withProtocol);
186
+ return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
187
+ }
188
+ function normalizeOrigin(target) {
189
+ const url = new URL(ensureProtocol(target));
166
190
  url.pathname = "";
167
191
  url.search = "";
168
192
  url.hash = "";
@@ -201,7 +225,7 @@ function fetchSafe(url, init) {
201
225
  }
202
226
 
203
227
  // src/mmm-enabled.ts
204
- var isMmmEnabled = () => "1.0.1".includes("-mmm");
228
+ var isMmmEnabled = () => "1.1.0".includes("-mmm");
205
229
 
206
230
  // src/core/source/openapi/index.ts
207
231
  var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
@@ -210,15 +234,13 @@ var OpenApiParsedSchema = OpenApiDocSchema.transform((doc) => {
210
234
  for (const httpMethod of [...HTTP_METHODS]) {
211
235
  const operation = pathItem[httpMethod.toLowerCase()];
212
236
  if (!operation) continue;
213
- const authMode = inferAuthMode(operation) ?? void 0;
237
+ const authMode = inferAuthMode(operation, doc.security, doc.components?.securitySchemes) ?? void 0;
214
238
  if (!authMode) continue;
215
239
  const p = operation["x-payment-info"];
216
240
  const protocols = (p?.protocols ?? []).filter(
217
241
  (proto) => proto !== "mpp" || isMmmEnabled()
218
242
  );
219
- if ((authMode === "paid" || authMode === "siwx") && !has402Response(operation)) continue;
220
- if (authMode === "paid" && protocols.length === 0) continue;
221
- const pricing = authMode === "paid" && p ? {
243
+ const pricing = (authMode === "paid" || authMode === "apiKey+paid") && p ? {
222
244
  pricingMode: p.pricingMode,
223
245
  ...p.price ? { price: p.price } : {},
224
246
  ...p.minPrice ? { minPrice: p.minPrice } : {},
@@ -978,54 +1000,31 @@ function extractPaymentOptions4(wwwAuthenticate) {
978
1000
  return options;
979
1001
  }
980
1002
 
981
- // src/core/layers/l3.ts
982
- function findMatchingOpenApiPath(paths, targetPath) {
983
- const exact = paths[targetPath];
984
- if (isRecord(exact)) return { matchedPath: targetPath, pathItem: exact };
985
- for (const [specPath, entry] of Object.entries(paths)) {
986
- if (!isRecord(entry)) continue;
987
- const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
988
- const regex = new RegExp(`^${pattern}$`);
989
- if (regex.test(targetPath)) {
990
- return { matchedPath: specPath, pathItem: entry };
991
- }
992
- }
993
- return null;
994
- }
995
- function resolveRef(document, ref, seen) {
996
- if (!ref.startsWith("#/")) return void 0;
997
- if (seen.has(ref)) return { $circular: ref };
998
- seen.add(ref);
999
- const parts = ref.slice(2).split("/");
1000
- let current = document;
1001
- for (const part of parts) {
1002
- if (!isRecord(current)) return void 0;
1003
- current = current[part];
1004
- if (current === void 0) return void 0;
1005
- }
1006
- if (isRecord(current)) return resolveRefs(document, current, seen);
1007
- return current;
1003
+ // src/core/lib/resolve-ref.ts
1004
+ var import_dereference_json_schema = __toESM(require("dereference-json-schema"), 1);
1005
+ var { resolveRefSync } = import_dereference_json_schema.default;
1006
+ function isRecord2(value) {
1007
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1008
1008
  }
1009
- function resolveRefs(document, obj, seen, depth = 0) {
1010
- if (depth > 4) return obj;
1009
+ function deepResolveRefs(document, obj) {
1011
1010
  const resolved = {};
1012
1011
  for (const [key, value] of Object.entries(obj)) {
1013
1012
  if (key === "$ref" && typeof value === "string") {
1014
- const deref = resolveRef(document, value, seen);
1015
- if (isRecord(deref)) {
1016
- Object.assign(resolved, deref);
1013
+ const deref = resolveRefSync(document, value);
1014
+ if (isRecord2(deref)) {
1015
+ Object.assign(resolved, deepResolveRefs(document, deref));
1017
1016
  } else {
1018
1017
  resolved[key] = value;
1019
1018
  }
1020
1019
  continue;
1021
1020
  }
1022
- if (isRecord(value)) {
1023
- resolved[key] = resolveRefs(document, value, seen, depth + 1);
1021
+ if (isRecord2(value)) {
1022
+ resolved[key] = deepResolveRefs(document, value);
1024
1023
  continue;
1025
1024
  }
1026
1025
  if (Array.isArray(value)) {
1027
1026
  resolved[key] = value.map(
1028
- (item) => isRecord(item) ? resolveRefs(document, item, seen, depth + 1) : item
1027
+ (item) => isRecord2(item) ? deepResolveRefs(document, item) : item
1029
1028
  );
1030
1029
  continue;
1031
1030
  }
@@ -1033,6 +1032,24 @@ function resolveRefs(document, obj, seen, depth = 0) {
1033
1032
  }
1034
1033
  return resolved;
1035
1034
  }
1035
+ function resolveRefs(obj, document) {
1036
+ return deepResolveRefs(document, obj);
1037
+ }
1038
+
1039
+ // src/core/layers/l3.ts
1040
+ function findMatchingOpenApiPath(paths, targetPath) {
1041
+ const exact = paths[targetPath];
1042
+ if (isRecord(exact)) return { matchedPath: targetPath, pathItem: exact };
1043
+ for (const [specPath, entry] of Object.entries(paths)) {
1044
+ if (!isRecord(entry)) continue;
1045
+ const pattern = specPath.replace(/\{[^}]+\}/g, "[^/]+");
1046
+ const regex = new RegExp(`^${pattern}$`);
1047
+ if (regex.test(targetPath)) {
1048
+ return { matchedPath: specPath, pathItem: entry };
1049
+ }
1050
+ }
1051
+ return null;
1052
+ }
1036
1053
  function extractRequestBodySchema(operationSchema) {
1037
1054
  const requestBody = operationSchema.requestBody;
1038
1055
  if (!isRecord(requestBody)) return void 0;
@@ -1112,7 +1129,7 @@ function getL3ForOpenAPI(openApi, path, method) {
1112
1129
  if (!matched) return null;
1113
1130
  const operation = matched.pathItem[method.toLowerCase()];
1114
1131
  if (!isRecord(operation)) return null;
1115
- const resolvedOperation = resolveRefs(document, operation, /* @__PURE__ */ new Set());
1132
+ const resolvedOperation = resolveRefs(operation, document);
1116
1133
  const summary = typeof resolvedOperation.summary === "string" ? resolvedOperation.summary : typeof resolvedOperation.description === "string" ? resolvedOperation.description : void 0;
1117
1134
  return {
1118
1135
  source: "openapi",
@@ -1162,12 +1179,12 @@ function getAdvisoriesForProbe(probe, path) {
1162
1179
  });
1163
1180
  }
1164
1181
  async function checkEndpointSchema(options) {
1165
- const endpoint = new URL(options.url);
1182
+ const endpoint = new URL(ensureProtocol(options.url));
1166
1183
  const origin = normalizeOrigin(endpoint.origin);
1167
1184
  const path = normalizePath(endpoint.pathname || "/");
1168
1185
  if (options.sampleInputBody !== void 0) {
1169
1186
  const probeResult2 = await getProbe(
1170
- options.url,
1187
+ endpoint.href,
1171
1188
  options.headers,
1172
1189
  options.signal,
1173
1190
  options.sampleInputBody
@@ -1192,7 +1209,7 @@ async function checkEndpointSchema(options) {
1192
1209
  if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
1193
1210
  return { found: false, origin, path, cause: "not_found" };
1194
1211
  }
1195
- const probeResult = await getProbe(options.url, options.headers, options.signal);
1212
+ const probeResult = await getProbe(endpoint.href, options.headers, options.signal);
1196
1213
  if (probeResult.isErr()) {
1197
1214
  return {
1198
1215
  found: false,
@@ -1297,7 +1314,7 @@ var import_schemas3 = require("@x402/core/schemas");
1297
1314
  var DEFAULT_COMPAT_MODE = "on";
1298
1315
 
1299
1316
  // src/x402scan-validation/payment-required.ts
1300
- function isRecord2(value) {
1317
+ function isRecord3(value) {
1301
1318
  return typeof value === "object" && value !== null && !Array.isArray(value);
1302
1319
  }
1303
1320
  function pushIssue2(issues, issue) {
@@ -1351,7 +1368,7 @@ function validatePaymentRequiredDetailed(payload, options = {}) {
1351
1368
  const compatMode = options.compatMode ?? DEFAULT_COMPAT_MODE;
1352
1369
  const requireInputSchema = options.requireInputSchema ?? true;
1353
1370
  const requireOutputSchema = options.requireOutputSchema ?? true;
1354
- if (!isRecord2(payload)) {
1371
+ if (!isRecord3(payload)) {
1355
1372
  pushIssue2(issues, {
1356
1373
  code: VALIDATION_CODES.X402_NOT_OBJECT,
1357
1374
  severity: "error",
@@ -1470,6 +1487,7 @@ var AUDIT_CODES = {
1470
1487
  L2_NO_ROUTES: "L2_NO_ROUTES",
1471
1488
  L2_ROUTE_COUNT_HIGH: "L2_ROUTE_COUNT_HIGH",
1472
1489
  L2_AUTH_MODE_MISSING: "L2_AUTH_MODE_MISSING",
1490
+ L2_NO_PAID_ROUTES: "L2_NO_PAID_ROUTES",
1473
1491
  L2_PRICE_MISSING_ON_PAID: "L2_PRICE_MISSING_ON_PAID",
1474
1492
  L2_PROTOCOLS_MISSING_ON_PAID: "L2_PROTOCOLS_MISSING_ON_PAID",
1475
1493
  // ─── L3 endpoint advisory checks ─────────────────────────────────────────────
@@ -1531,6 +1549,15 @@ function getWarningsForL2(l2) {
1531
1549
  });
1532
1550
  return warnings;
1533
1551
  }
1552
+ const hasPaidRoute = l2.routes.some((r) => r.authMode === "paid" || r.authMode === "apiKey+paid");
1553
+ if (!hasPaidRoute) {
1554
+ warnings.push({
1555
+ code: AUDIT_CODES.L2_NO_PAID_ROUTES,
1556
+ severity: "info",
1557
+ message: "No endpoints are marked as paid or apiKey+paid.",
1558
+ hint: "Add x-payment-info to operations that require payment so agents know which endpoints are monetized."
1559
+ });
1560
+ }
1534
1561
  if (l2.routes.length > ROUTE_COUNT_HIGH) {
1535
1562
  warnings.push({
1536
1563
  code: AUDIT_CODES.L2_ROUTE_COUNT_HIGH,
@@ -1617,8 +1644,8 @@ function getWarningsForL4(l4) {
1617
1644
  {
1618
1645
  code: AUDIT_CODES.L4_GUIDANCE_MISSING,
1619
1646
  severity: "info",
1620
- message: "No guidance text found (llms.txt or OpenAPI info.guidance).",
1621
- hint: "Add an info.guidance field to your OpenAPI spec or expose /llms.txt for agent-readable instructions."
1647
+ message: "No guidance text found (OpenAPI info.guidance).",
1648
+ hint: "Add an info.guidance field to your OpenAPI spec for agent-readable instructions."
1622
1649
  }
1623
1650
  ];
1624
1651
  }
package/dist/index.d.cts CHANGED
@@ -325,6 +325,7 @@ declare const AUDIT_CODES: {
325
325
  readonly L2_NO_ROUTES: "L2_NO_ROUTES";
326
326
  readonly L2_ROUTE_COUNT_HIGH: "L2_ROUTE_COUNT_HIGH";
327
327
  readonly L2_AUTH_MODE_MISSING: "L2_AUTH_MODE_MISSING";
328
+ readonly L2_NO_PAID_ROUTES: "L2_NO_PAID_ROUTES";
328
329
  readonly L2_PRICE_MISSING_ON_PAID: "L2_PRICE_MISSING_ON_PAID";
329
330
  readonly L2_PROTOCOLS_MISSING_ON_PAID: "L2_PROTOCOLS_MISSING_ON_PAID";
330
331
  readonly L3_NOT_FOUND: "L3_NOT_FOUND";
package/dist/index.d.ts CHANGED
@@ -325,6 +325,7 @@ declare const AUDIT_CODES: {
325
325
  readonly L2_NO_ROUTES: "L2_NO_ROUTES";
326
326
  readonly L2_ROUTE_COUNT_HIGH: "L2_ROUTE_COUNT_HIGH";
327
327
  readonly L2_AUTH_MODE_MISSING: "L2_AUTH_MODE_MISSING";
328
+ readonly L2_NO_PAID_ROUTES: "L2_NO_PAID_ROUTES";
328
329
  readonly L2_PRICE_MISSING_ON_PAID: "L2_PRICE_MISSING_ON_PAID";
329
330
  readonly L2_PROTOCOLS_MISSING_ON_PAID: "L2_PROTOCOLS_MISSING_ON_PAID";
330
331
  readonly L3_NOT_FOUND: "L3_NOT_FOUND";