@agentcash/discovery 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -13872,7 +13872,8 @@ var WellKnownParsedSchema = external_exports.object({
13872
13872
  routes: external_exports.array(
13873
13873
  external_exports.object({
13874
13874
  path: external_exports.string(),
13875
- method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"])
13875
+ method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]),
13876
+ price: external_exports.string().optional()
13876
13877
  })
13877
13878
  ),
13878
13879
  instructions: external_exports.string().optional()
@@ -13965,9 +13966,9 @@ function toAbsoluteUrl(origin, value) {
13965
13966
 
13966
13967
  // src/core/source/fetch.ts
13967
13968
  var import_neverthrow = require("neverthrow");
13968
- function toFetchError(err) {
13969
- const cause = err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError") ? "timeout" : "network";
13970
- return { cause, message: String(err) };
13969
+ function toFetchError(err2) {
13970
+ const cause = err2 instanceof DOMException && (err2.name === "TimeoutError" || err2.name === "AbortError") ? "timeout" : "network";
13971
+ return { cause, message: String(err2) };
13971
13972
  }
13972
13973
  function fetchSafe(url2, init) {
13973
13974
  return import_neverthrow.ResultAsync.fromPromise(fetch(url2, init), toFetchError);
@@ -14014,7 +14015,13 @@ async function parseBody(response, url2) {
14014
14015
  try {
14015
14016
  const payload = await response.json();
14016
14017
  const parsed = OpenApiParsedSchema.safeParse(payload);
14017
- if (!parsed.success) return null;
14018
+ if (!parsed.success) {
14019
+ return {
14020
+ parseFailure: true,
14021
+ fetchedUrl: url2,
14022
+ issues: parsed.error.issues.map((i) => ({ path: i.path, message: i.message }))
14023
+ };
14024
+ }
14018
14025
  return { raw: payload, ...parsed.data, fetchedUrl: url2 };
14019
14026
  } catch {
14020
14027
  return null;
@@ -14033,6 +14040,9 @@ function getOpenAPI(origin, headers, signal, specificationOverrideUrl) {
14033
14040
  }
14034
14041
 
14035
14042
  // src/core/source/wellknown/index.ts
14043
+ var import_neverthrow5 = require("neverthrow");
14044
+
14045
+ // src/core/source/wellknown/x402.ts
14036
14046
  var import_neverthrow3 = require("neverthrow");
14037
14047
  function toWellKnownParsed(origin, doc) {
14038
14048
  const routes = doc.resources.flatMap((entry) => {
@@ -14059,12 +14069,17 @@ async function parseBody2(response, origin, url2) {
14059
14069
  if (!doc.success) return null;
14060
14070
  const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed(origin, doc.data));
14061
14071
  if (!parsed.success) return null;
14062
- return { raw: payload, ...parsed.data, fetchedUrl: url2 };
14072
+ return {
14073
+ raw: payload,
14074
+ ...parsed.data,
14075
+ protocol: "x402",
14076
+ fetchedUrl: url2
14077
+ };
14063
14078
  } catch {
14064
14079
  return null;
14065
14080
  }
14066
14081
  }
14067
- function getWellKnown(origin, headers, signal) {
14082
+ function getX402WellKnown(origin, headers, signal) {
14068
14083
  const url2 = `${origin}/.well-known/x402`;
14069
14084
  return fetchSafe(url2, {
14070
14085
  method: "GET",
@@ -14076,6 +14091,127 @@ function getWellKnown(origin, headers, signal) {
14076
14091
  });
14077
14092
  }
14078
14093
 
14094
+ // src/core/source/wellknown/mpp.ts
14095
+ var import_neverthrow4 = require("neverthrow");
14096
+ var MppEndpointSchema = external_exports.object({
14097
+ method: external_exports.string(),
14098
+ path: external_exports.string(),
14099
+ description: external_exports.string().optional(),
14100
+ payment: external_exports.object({
14101
+ intent: external_exports.string().optional(),
14102
+ method: external_exports.string().optional(),
14103
+ amount: external_exports.string().optional(),
14104
+ currency: external_exports.string().optional()
14105
+ }).optional()
14106
+ });
14107
+ var MppWellKnownDocSchema = external_exports.object({
14108
+ version: external_exports.number().optional(),
14109
+ name: external_exports.string().optional(),
14110
+ description: external_exports.string().optional(),
14111
+ categories: external_exports.array(external_exports.string()).optional(),
14112
+ methods: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
14113
+ endpoints: external_exports.array(MppEndpointSchema).default([]),
14114
+ docs: external_exports.object({
14115
+ homepage: external_exports.string().optional(),
14116
+ apiReference: external_exports.string().optional()
14117
+ }).optional()
14118
+ });
14119
+ var MPP_DECIMALS = 6;
14120
+ function formatMppAmount(raw) {
14121
+ if (!raw) return void 0;
14122
+ const n = Number(raw);
14123
+ if (!Number.isFinite(n)) return void 0;
14124
+ return `$${(n / 10 ** MPP_DECIMALS).toFixed(MPP_DECIMALS)}`;
14125
+ }
14126
+ function toWellKnownParsed2(doc) {
14127
+ const routes = doc.endpoints.flatMap((entry) => {
14128
+ const method = parseMethod(entry.method);
14129
+ if (!method) return [];
14130
+ const path = normalizePath(entry.path);
14131
+ if (!path) return [];
14132
+ const price = formatMppAmount(entry.payment?.amount);
14133
+ return [{ path, method, ...price ? { price } : {} }];
14134
+ });
14135
+ return {
14136
+ routes,
14137
+ ...doc.description ? { instructions: doc.description } : {}
14138
+ };
14139
+ }
14140
+ async function parseBody3(response, url2) {
14141
+ try {
14142
+ const payload = await response.json();
14143
+ const doc = MppWellKnownDocSchema.safeParse(payload);
14144
+ if (!doc.success) return null;
14145
+ const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed2(doc.data));
14146
+ if (!parsed.success) return null;
14147
+ return {
14148
+ raw: payload,
14149
+ ...parsed.data,
14150
+ ...doc.data.name ? { title: doc.data.name } : {},
14151
+ ...doc.data.description ? { description: doc.data.description } : {},
14152
+ protocol: "mpp",
14153
+ fetchedUrl: url2
14154
+ };
14155
+ } catch {
14156
+ return null;
14157
+ }
14158
+ }
14159
+ function getMppWellKnown(origin, headers, signal) {
14160
+ const url2 = `${origin}/.well-known/mpp`;
14161
+ return fetchSafe(url2, {
14162
+ method: "GET",
14163
+ headers: { Accept: "application/json", ...headers },
14164
+ signal
14165
+ }).andThen((response) => {
14166
+ if (!response.ok) return (0, import_neverthrow4.okAsync)(null);
14167
+ return import_neverthrow4.ResultAsync.fromSafePromise(parseBody3(response, url2));
14168
+ });
14169
+ }
14170
+
14171
+ // src/core/source/wellknown/index.ts
14172
+ function mergeError(a, b) {
14173
+ return {
14174
+ cause: a.cause === "network" || b.cause === "network" ? "network" : "timeout",
14175
+ message: `x402: ${a.message} | mpp: ${b.message}`
14176
+ };
14177
+ }
14178
+ function merge2(x402, mpp) {
14179
+ const seen = /* @__PURE__ */ new Set();
14180
+ const routes = [...x402.routes, ...mpp.routes].filter((r) => {
14181
+ const key = `${r.method} ${r.path}`;
14182
+ if (seen.has(key)) return false;
14183
+ seen.add(key);
14184
+ return true;
14185
+ });
14186
+ return {
14187
+ raw: { ...mpp.raw, ...x402.raw },
14188
+ routes,
14189
+ protocol: "x402+mpp",
14190
+ // Prefer x402 instructions; fall back to mpp
14191
+ ...x402.instructions || mpp.instructions ? { instructions: x402.instructions ?? mpp.instructions } : {},
14192
+ fetchedUrl: x402.fetchedUrl
14193
+ };
14194
+ }
14195
+ function getWellKnown(origin, headers, signal) {
14196
+ return new import_neverthrow5.ResultAsync(
14197
+ Promise.all([
14198
+ getX402WellKnown(origin, headers, signal),
14199
+ getMppWellKnown(origin, headers, signal)
14200
+ ]).then(([x402Result, mppResult]) => {
14201
+ const x402 = x402Result.isOk() ? x402Result.value : null;
14202
+ const mpp = mppResult.isOk() ? mppResult.value : null;
14203
+ if (x402 && mpp) return (0, import_neverthrow5.ok)(merge2(x402, mpp));
14204
+ if (x402) return (0, import_neverthrow5.ok)(x402);
14205
+ if (mpp) return (0, import_neverthrow5.ok)(mpp);
14206
+ if (x402Result.isErr() && mppResult.isErr())
14207
+ return (0, import_neverthrow5.err)(mergeError(x402Result.error, mppResult.error));
14208
+ if (x402Result.isErr()) return (0, import_neverthrow5.err)(x402Result.error);
14209
+ if (mppResult.isErr()) return (0, import_neverthrow5.err)(mppResult.error);
14210
+ return (0, import_neverthrow5.ok)(null);
14211
+ })
14212
+ );
14213
+ }
14214
+
14079
14215
  // src/core/layers/l2.ts
14080
14216
  function formatPrice(pricing) {
14081
14217
  if (pricing.pricingMode === "fixed") return `$${pricing.price}`;
@@ -14101,15 +14237,27 @@ function checkL2ForOpenAPI(openApi) {
14101
14237
  source: "openapi"
14102
14238
  };
14103
14239
  }
14240
+ var WELL_KNOWN_PROTOCOLS = {
14241
+ x402: ["x402"],
14242
+ mpp: ["mpp"],
14243
+ "x402+mpp": ["x402", "mpp"]
14244
+ };
14104
14245
  function checkL2ForWellknown(wellKnown) {
14246
+ const protocols = WELL_KNOWN_PROTOCOLS[wellKnown.protocol];
14105
14247
  const routes = wellKnown.routes.map((route) => ({
14106
14248
  path: route.path,
14107
14249
  method: route.method,
14108
14250
  summary: `${route.method} ${route.path}`,
14109
14251
  authMode: "paid",
14110
- protocols: ["x402"]
14252
+ protocols,
14253
+ ...route.price ? { price: route.price } : {}
14111
14254
  }));
14112
- return { routes, source: "well-known/x402" };
14255
+ return {
14256
+ ...wellKnown.title ? { title: wellKnown.title } : {},
14257
+ ...wellKnown.description ? { description: wellKnown.description } : {},
14258
+ routes,
14259
+ source: `well-known/${wellKnown.protocol}`
14260
+ };
14113
14261
  }
14114
14262
 
14115
14263
  // src/core/layers/l4.ts
@@ -14121,7 +14269,7 @@ function checkL4ForOpenAPI(openApi) {
14121
14269
  }
14122
14270
  function checkL4ForWellknown(wellKnown) {
14123
14271
  if (wellKnown.instructions) {
14124
- return { guidance: wellKnown.instructions, source: "well-known/x402" };
14272
+ return { guidance: wellKnown.instructions, source: `well-known/${wellKnown.protocol}` };
14125
14273
  }
14126
14274
  return null;
14127
14275
  }
@@ -14132,6 +14280,7 @@ var AUDIT_CODES = {
14132
14280
  OPENAPI_NOT_FOUND: "OPENAPI_NOT_FOUND",
14133
14281
  WELLKNOWN_NOT_FOUND: "WELLKNOWN_NOT_FOUND",
14134
14282
  // ─── OpenAPI quality ─────────────────────────────────────────────────────────
14283
+ OPENAPI_PARSE_ERROR: "OPENAPI_PARSE_ERROR",
14135
14284
  OPENAPI_NO_ROUTES: "OPENAPI_NO_ROUTES",
14136
14285
  // ─── L2 route-list checks ────────────────────────────────────────────────────
14137
14286
  L2_NO_ROUTES: "L2_NO_ROUTES",
@@ -14166,6 +14315,9 @@ var AUDIT_CODES = {
14166
14315
  };
14167
14316
 
14168
14317
  // src/audit/warnings/sources.ts
14318
+ function isOpenApiParseFailure(value) {
14319
+ return value !== null && "parseFailure" in value;
14320
+ }
14169
14321
  function getWarningsForOpenAPI(openApi) {
14170
14322
  if (openApi === null) {
14171
14323
  return [
@@ -14177,6 +14329,18 @@ function getWarningsForOpenAPI(openApi) {
14177
14329
  }
14178
14330
  ];
14179
14331
  }
14332
+ if (isOpenApiParseFailure(openApi)) {
14333
+ const details = openApi.issues.map((i) => ` ${i.path.map(String).join(".")}: ${i.message}`).join("\n");
14334
+ return [
14335
+ {
14336
+ code: AUDIT_CODES.OPENAPI_PARSE_ERROR,
14337
+ severity: "warn",
14338
+ message: `OpenAPI spec found at ${openApi.fetchedUrl} but failed schema validation:
14339
+ ${details}`,
14340
+ hint: "Fix the schema issues above so discovery can read the spec."
14341
+ }
14342
+ ];
14343
+ }
14180
14344
  const warnings = [];
14181
14345
  if (openApi.routes.length === 0) {
14182
14346
  warnings.push({
@@ -15073,7 +15237,7 @@ function getWarningsForL4(l4) {
15073
15237
  }
15074
15238
 
15075
15239
  // src/core/source/probe/index.ts
15076
- var import_neverthrow4 = require("neverthrow");
15240
+ var import_neverthrow6 = require("neverthrow");
15077
15241
 
15078
15242
  // src/core/protocols/x402/index.ts
15079
15243
  function parseVersion(payload) {
@@ -15169,8 +15333,8 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
15169
15333
  ...hasBody ? { body: JSON.stringify(inputBody) } : {},
15170
15334
  signal
15171
15335
  }).andThen((response) => {
15172
- if (!isUsableStatus(response.status)) return import_neverthrow4.ResultAsync.fromSafePromise(Promise.resolve(null));
15173
- return import_neverthrow4.ResultAsync.fromSafePromise(
15336
+ if (!isUsableStatus(response.status)) return import_neverthrow6.ResultAsync.fromSafePromise(Promise.resolve(null));
15337
+ return import_neverthrow6.ResultAsync.fromSafePromise(
15174
15338
  (async () => {
15175
15339
  let authHint = response.status === 402 ? "paid" : "unprotected";
15176
15340
  let paymentRequiredBody;
@@ -15201,7 +15365,7 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
15201
15365
  }
15202
15366
  function getProbe(url2, headers, signal, inputBody) {
15203
15367
  const path = normalizePath(new URL(url2).pathname || "/");
15204
- return import_neverthrow4.ResultAsync.fromSafePromise(
15368
+ return import_neverthrow6.ResultAsync.fromSafePromise(
15205
15369
  Promise.all(
15206
15370
  [...HTTP_METHODS].map(
15207
15371
  (method) => probeMethod(url2, method, path, headers, signal, inputBody).match(
@@ -15214,6 +15378,20 @@ function getProbe(url2, headers, signal, inputBody) {
15214
15378
  }
15215
15379
 
15216
15380
  // src/core/protocols/mpp/index.ts
15381
+ var import_neverthrow7 = require("neverthrow");
15382
+ function parseBase64Json(encoded) {
15383
+ return import_neverthrow7.Result.fromThrowable(
15384
+ () => {
15385
+ const decoded = typeof Buffer !== "undefined" ? Buffer.from(encoded, "base64").toString("utf8") : atob(encoded);
15386
+ const parsed = JSON.parse(decoded);
15387
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
15388
+ throw new Error("not an object");
15389
+ }
15390
+ return parsed;
15391
+ },
15392
+ (e) => e
15393
+ )();
15394
+ }
15217
15395
  var TEMPO_DEFAULT_CHAIN_ID = 4217;
15218
15396
  function parseAuthParams2(segment) {
15219
15397
  const params = {};
@@ -15236,14 +15414,9 @@ function extractPaymentOptions4(wwwAuthenticate) {
15236
15414
  const description = params["description"];
15237
15415
  const requestStr = params["request"];
15238
15416
  if (!paymentMethod || !intent || !realm || !requestStr) continue;
15239
- let request;
15240
- try {
15241
- const parsed = JSON.parse(requestStr);
15242
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
15243
- request = parsed;
15244
- } catch {
15245
- continue;
15246
- }
15417
+ const requestResult = parseBase64Json(requestStr);
15418
+ if (requestResult.isErr()) continue;
15419
+ const request = requestResult.value;
15247
15420
  const asset = typeof request["currency"] === "string" ? request["currency"] : void 0;
15248
15421
  const amountRaw = request["amount"];
15249
15422
  const amount = typeof amountRaw === "string" ? amountRaw : typeof amountRaw === "number" ? String(amountRaw) : void 0;
@@ -15440,6 +15613,11 @@ async function attachProbePayload(url2, advisories) {
15440
15613
  }
15441
15614
  }
15442
15615
 
15616
+ // src/core/types/sources.ts
15617
+ function isOpenApiSource(raw) {
15618
+ return raw !== null && !("parseFailure" in raw);
15619
+ }
15620
+
15443
15621
  // src/runtime/check-endpoint.ts
15444
15622
  function getAdvisoriesForOpenAPI(openApi, path) {
15445
15623
  return [...HTTP_METHODS].flatMap((method) => {
@@ -15478,7 +15656,8 @@ async function checkEndpointSchema(options) {
15478
15656
  return { found: false, origin, path, cause: "not_found" };
15479
15657
  }
15480
15658
  const openApiResult = await getOpenAPI(origin, options.headers, options.signal);
15481
- const openApi = openApiResult.isOk() ? openApiResult.value : null;
15659
+ const openApiRaw = openApiResult.isOk() ? openApiResult.value : null;
15660
+ const openApi = isOpenApiSource(openApiRaw) ? openApiRaw : null;
15482
15661
  if (openApi) {
15483
15662
  const advisories2 = getAdvisoriesForOpenAPI(openApi, path);
15484
15663
  if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
@@ -15548,10 +15727,11 @@ Discovering ${origin}...
15548
15727
  getOpenAPI(origin),
15549
15728
  getWellKnown(origin)
15550
15729
  ]);
15551
- const openApi = openApiResult.isOk() ? openApiResult.value : null;
15730
+ const openApiRaw = openApiResult.isOk() ? openApiResult.value : null;
15552
15731
  const wellKnown = wellKnownResult.isOk() ? wellKnownResult.value : null;
15732
+ const openApi = isOpenApiSource(openApiRaw) ? openApiRaw : null;
15553
15733
  const warnings = [
15554
- ...getWarningsForOpenAPI(openApi),
15734
+ ...getWarningsForOpenAPI(openApiRaw),
15555
15735
  ...getWarningsForWellKnown(wellKnown)
15556
15736
  ];
15557
15737
  if (openApi) {
@@ -15617,12 +15797,14 @@ ${l4.guidance}`);
15617
15797
  warnings.push(...l3WarningArrays.flat());
15618
15798
  if (flags.json) {
15619
15799
  const meta3 = { origin, specUrl: wellKnown.fetchedUrl };
15800
+ if (l2.title) meta3.title = l2.title;
15801
+ if (l2.description) meta3.description = l2.description;
15620
15802
  if (l4 && flags.verbose) meta3.guidance = l4.guidance;
15621
15803
  console.log(
15622
15804
  JSON.stringify(
15623
15805
  {
15624
15806
  ok: true,
15625
- selectedStage: "well-known/x402",
15807
+ selectedStage: l2.source,
15626
15808
  resources: l2.routes.map(routeToResource),
15627
15809
  warnings,
15628
15810
  trace: [],
@@ -15634,12 +15816,15 @@ ${l4.guidance}`);
15634
15816
  );
15635
15817
  return 0;
15636
15818
  }
15637
- console.log(`Source: well-known/x402`);
15819
+ console.log(`Source: ${l2.source}`);
15638
15820
  console.log(`Spec: ${wellKnown.fetchedUrl}`);
15821
+ if (l2.title) console.log(`API: ${l2.title}`);
15639
15822
  console.log(`Routes: ${l2.routes.length}
15640
15823
  `);
15641
15824
  for (const route of l2.routes) {
15642
- console.log(` ${route.method.padEnd(7)} ${route.path} paid [x402]`);
15825
+ const price = route.price ? ` ${route.price}` : "";
15826
+ const protocols = route.protocols?.length ? ` [${route.protocols.join(", ")}]` : "";
15827
+ console.log(` ${route.method.padEnd(7)} ${route.path} paid${price}${protocols}`);
15643
15828
  }
15644
15829
  } else {
15645
15830
  if (flags.json) {