@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.js CHANGED
@@ -13842,7 +13842,8 @@ var WellKnownParsedSchema = external_exports.object({
13842
13842
  routes: external_exports.array(
13843
13843
  external_exports.object({
13844
13844
  path: external_exports.string(),
13845
- method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"])
13845
+ method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]),
13846
+ price: external_exports.string().optional()
13846
13847
  })
13847
13848
  ),
13848
13849
  instructions: external_exports.string().optional()
@@ -13935,9 +13936,9 @@ function toAbsoluteUrl(origin, value) {
13935
13936
 
13936
13937
  // src/core/source/fetch.ts
13937
13938
  import { ResultAsync } from "neverthrow";
13938
- function toFetchError(err) {
13939
- const cause = err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError") ? "timeout" : "network";
13940
- return { cause, message: String(err) };
13939
+ function toFetchError(err2) {
13940
+ const cause = err2 instanceof DOMException && (err2.name === "TimeoutError" || err2.name === "AbortError") ? "timeout" : "network";
13941
+ return { cause, message: String(err2) };
13941
13942
  }
13942
13943
  function fetchSafe(url2, init) {
13943
13944
  return ResultAsync.fromPromise(fetch(url2, init), toFetchError);
@@ -13984,7 +13985,13 @@ async function parseBody(response, url2) {
13984
13985
  try {
13985
13986
  const payload = await response.json();
13986
13987
  const parsed = OpenApiParsedSchema.safeParse(payload);
13987
- if (!parsed.success) return null;
13988
+ if (!parsed.success) {
13989
+ return {
13990
+ parseFailure: true,
13991
+ fetchedUrl: url2,
13992
+ issues: parsed.error.issues.map((i) => ({ path: i.path, message: i.message }))
13993
+ };
13994
+ }
13988
13995
  return { raw: payload, ...parsed.data, fetchedUrl: url2 };
13989
13996
  } catch {
13990
13997
  return null;
@@ -14003,6 +14010,9 @@ function getOpenAPI(origin, headers, signal, specificationOverrideUrl) {
14003
14010
  }
14004
14011
 
14005
14012
  // src/core/source/wellknown/index.ts
14013
+ import { ok, err, ResultAsync as ResultAsync5 } from "neverthrow";
14014
+
14015
+ // src/core/source/wellknown/x402.ts
14006
14016
  import { okAsync as okAsync2, ResultAsync as ResultAsync3 } from "neverthrow";
14007
14017
  function toWellKnownParsed(origin, doc) {
14008
14018
  const routes = doc.resources.flatMap((entry) => {
@@ -14029,12 +14039,17 @@ async function parseBody2(response, origin, url2) {
14029
14039
  if (!doc.success) return null;
14030
14040
  const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed(origin, doc.data));
14031
14041
  if (!parsed.success) return null;
14032
- return { raw: payload, ...parsed.data, fetchedUrl: url2 };
14042
+ return {
14043
+ raw: payload,
14044
+ ...parsed.data,
14045
+ protocol: "x402",
14046
+ fetchedUrl: url2
14047
+ };
14033
14048
  } catch {
14034
14049
  return null;
14035
14050
  }
14036
14051
  }
14037
- function getWellKnown(origin, headers, signal) {
14052
+ function getX402WellKnown(origin, headers, signal) {
14038
14053
  const url2 = `${origin}/.well-known/x402`;
14039
14054
  return fetchSafe(url2, {
14040
14055
  method: "GET",
@@ -14046,6 +14061,127 @@ function getWellKnown(origin, headers, signal) {
14046
14061
  });
14047
14062
  }
14048
14063
 
14064
+ // src/core/source/wellknown/mpp.ts
14065
+ import { okAsync as okAsync3, ResultAsync as ResultAsync4 } from "neverthrow";
14066
+ var MppEndpointSchema = external_exports.object({
14067
+ method: external_exports.string(),
14068
+ path: external_exports.string(),
14069
+ description: external_exports.string().optional(),
14070
+ payment: external_exports.object({
14071
+ intent: external_exports.string().optional(),
14072
+ method: external_exports.string().optional(),
14073
+ amount: external_exports.string().optional(),
14074
+ currency: external_exports.string().optional()
14075
+ }).optional()
14076
+ });
14077
+ var MppWellKnownDocSchema = external_exports.object({
14078
+ version: external_exports.number().optional(),
14079
+ name: external_exports.string().optional(),
14080
+ description: external_exports.string().optional(),
14081
+ categories: external_exports.array(external_exports.string()).optional(),
14082
+ methods: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
14083
+ endpoints: external_exports.array(MppEndpointSchema).default([]),
14084
+ docs: external_exports.object({
14085
+ homepage: external_exports.string().optional(),
14086
+ apiReference: external_exports.string().optional()
14087
+ }).optional()
14088
+ });
14089
+ var MPP_DECIMALS = 6;
14090
+ function formatMppAmount(raw) {
14091
+ if (!raw) return void 0;
14092
+ const n = Number(raw);
14093
+ if (!Number.isFinite(n)) return void 0;
14094
+ return `$${(n / 10 ** MPP_DECIMALS).toFixed(MPP_DECIMALS)}`;
14095
+ }
14096
+ function toWellKnownParsed2(doc) {
14097
+ const routes = doc.endpoints.flatMap((entry) => {
14098
+ const method = parseMethod(entry.method);
14099
+ if (!method) return [];
14100
+ const path = normalizePath(entry.path);
14101
+ if (!path) return [];
14102
+ const price = formatMppAmount(entry.payment?.amount);
14103
+ return [{ path, method, ...price ? { price } : {} }];
14104
+ });
14105
+ return {
14106
+ routes,
14107
+ ...doc.description ? { instructions: doc.description } : {}
14108
+ };
14109
+ }
14110
+ async function parseBody3(response, url2) {
14111
+ try {
14112
+ const payload = await response.json();
14113
+ const doc = MppWellKnownDocSchema.safeParse(payload);
14114
+ if (!doc.success) return null;
14115
+ const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed2(doc.data));
14116
+ if (!parsed.success) return null;
14117
+ return {
14118
+ raw: payload,
14119
+ ...parsed.data,
14120
+ ...doc.data.name ? { title: doc.data.name } : {},
14121
+ ...doc.data.description ? { description: doc.data.description } : {},
14122
+ protocol: "mpp",
14123
+ fetchedUrl: url2
14124
+ };
14125
+ } catch {
14126
+ return null;
14127
+ }
14128
+ }
14129
+ function getMppWellKnown(origin, headers, signal) {
14130
+ const url2 = `${origin}/.well-known/mpp`;
14131
+ return fetchSafe(url2, {
14132
+ method: "GET",
14133
+ headers: { Accept: "application/json", ...headers },
14134
+ signal
14135
+ }).andThen((response) => {
14136
+ if (!response.ok) return okAsync3(null);
14137
+ return ResultAsync4.fromSafePromise(parseBody3(response, url2));
14138
+ });
14139
+ }
14140
+
14141
+ // src/core/source/wellknown/index.ts
14142
+ function mergeError(a, b) {
14143
+ return {
14144
+ cause: a.cause === "network" || b.cause === "network" ? "network" : "timeout",
14145
+ message: `x402: ${a.message} | mpp: ${b.message}`
14146
+ };
14147
+ }
14148
+ function merge2(x402, mpp) {
14149
+ const seen = /* @__PURE__ */ new Set();
14150
+ const routes = [...x402.routes, ...mpp.routes].filter((r) => {
14151
+ const key = `${r.method} ${r.path}`;
14152
+ if (seen.has(key)) return false;
14153
+ seen.add(key);
14154
+ return true;
14155
+ });
14156
+ return {
14157
+ raw: { ...mpp.raw, ...x402.raw },
14158
+ routes,
14159
+ protocol: "x402+mpp",
14160
+ // Prefer x402 instructions; fall back to mpp
14161
+ ...x402.instructions || mpp.instructions ? { instructions: x402.instructions ?? mpp.instructions } : {},
14162
+ fetchedUrl: x402.fetchedUrl
14163
+ };
14164
+ }
14165
+ function getWellKnown(origin, headers, signal) {
14166
+ return new ResultAsync5(
14167
+ Promise.all([
14168
+ getX402WellKnown(origin, headers, signal),
14169
+ getMppWellKnown(origin, headers, signal)
14170
+ ]).then(([x402Result, mppResult]) => {
14171
+ const x402 = x402Result.isOk() ? x402Result.value : null;
14172
+ const mpp = mppResult.isOk() ? mppResult.value : null;
14173
+ if (x402 && mpp) return ok(merge2(x402, mpp));
14174
+ if (x402) return ok(x402);
14175
+ if (mpp) return ok(mpp);
14176
+ if (x402Result.isErr() && mppResult.isErr())
14177
+ return err(mergeError(x402Result.error, mppResult.error));
14178
+ if (x402Result.isErr()) return err(x402Result.error);
14179
+ if (mppResult.isErr()) return err(mppResult.error);
14180
+ return ok(null);
14181
+ })
14182
+ );
14183
+ }
14184
+
14049
14185
  // src/core/layers/l2.ts
14050
14186
  function formatPrice(pricing) {
14051
14187
  if (pricing.pricingMode === "fixed") return `$${pricing.price}`;
@@ -14071,15 +14207,27 @@ function checkL2ForOpenAPI(openApi) {
14071
14207
  source: "openapi"
14072
14208
  };
14073
14209
  }
14210
+ var WELL_KNOWN_PROTOCOLS = {
14211
+ x402: ["x402"],
14212
+ mpp: ["mpp"],
14213
+ "x402+mpp": ["x402", "mpp"]
14214
+ };
14074
14215
  function checkL2ForWellknown(wellKnown) {
14216
+ const protocols = WELL_KNOWN_PROTOCOLS[wellKnown.protocol];
14075
14217
  const routes = wellKnown.routes.map((route) => ({
14076
14218
  path: route.path,
14077
14219
  method: route.method,
14078
14220
  summary: `${route.method} ${route.path}`,
14079
14221
  authMode: "paid",
14080
- protocols: ["x402"]
14222
+ protocols,
14223
+ ...route.price ? { price: route.price } : {}
14081
14224
  }));
14082
- return { routes, source: "well-known/x402" };
14225
+ return {
14226
+ ...wellKnown.title ? { title: wellKnown.title } : {},
14227
+ ...wellKnown.description ? { description: wellKnown.description } : {},
14228
+ routes,
14229
+ source: `well-known/${wellKnown.protocol}`
14230
+ };
14083
14231
  }
14084
14232
 
14085
14233
  // src/core/layers/l4.ts
@@ -14091,7 +14239,7 @@ function checkL4ForOpenAPI(openApi) {
14091
14239
  }
14092
14240
  function checkL4ForWellknown(wellKnown) {
14093
14241
  if (wellKnown.instructions) {
14094
- return { guidance: wellKnown.instructions, source: "well-known/x402" };
14242
+ return { guidance: wellKnown.instructions, source: `well-known/${wellKnown.protocol}` };
14095
14243
  }
14096
14244
  return null;
14097
14245
  }
@@ -14102,6 +14250,7 @@ var AUDIT_CODES = {
14102
14250
  OPENAPI_NOT_FOUND: "OPENAPI_NOT_FOUND",
14103
14251
  WELLKNOWN_NOT_FOUND: "WELLKNOWN_NOT_FOUND",
14104
14252
  // ─── OpenAPI quality ─────────────────────────────────────────────────────────
14253
+ OPENAPI_PARSE_ERROR: "OPENAPI_PARSE_ERROR",
14105
14254
  OPENAPI_NO_ROUTES: "OPENAPI_NO_ROUTES",
14106
14255
  // ─── L2 route-list checks ────────────────────────────────────────────────────
14107
14256
  L2_NO_ROUTES: "L2_NO_ROUTES",
@@ -14136,6 +14285,9 @@ var AUDIT_CODES = {
14136
14285
  };
14137
14286
 
14138
14287
  // src/audit/warnings/sources.ts
14288
+ function isOpenApiParseFailure(value) {
14289
+ return value !== null && "parseFailure" in value;
14290
+ }
14139
14291
  function getWarningsForOpenAPI(openApi) {
14140
14292
  if (openApi === null) {
14141
14293
  return [
@@ -14147,6 +14299,18 @@ function getWarningsForOpenAPI(openApi) {
14147
14299
  }
14148
14300
  ];
14149
14301
  }
14302
+ if (isOpenApiParseFailure(openApi)) {
14303
+ const details = openApi.issues.map((i) => ` ${i.path.map(String).join(".")}: ${i.message}`).join("\n");
14304
+ return [
14305
+ {
14306
+ code: AUDIT_CODES.OPENAPI_PARSE_ERROR,
14307
+ severity: "warn",
14308
+ message: `OpenAPI spec found at ${openApi.fetchedUrl} but failed schema validation:
14309
+ ${details}`,
14310
+ hint: "Fix the schema issues above so discovery can read the spec."
14311
+ }
14312
+ ];
14313
+ }
14150
14314
  const warnings = [];
14151
14315
  if (openApi.routes.length === 0) {
14152
14316
  warnings.push({
@@ -15043,7 +15207,7 @@ function getWarningsForL4(l4) {
15043
15207
  }
15044
15208
 
15045
15209
  // src/core/source/probe/index.ts
15046
- import { ResultAsync as ResultAsync4 } from "neverthrow";
15210
+ import { ResultAsync as ResultAsync6 } from "neverthrow";
15047
15211
 
15048
15212
  // src/core/protocols/x402/index.ts
15049
15213
  function parseVersion(payload) {
@@ -15139,8 +15303,8 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
15139
15303
  ...hasBody ? { body: JSON.stringify(inputBody) } : {},
15140
15304
  signal
15141
15305
  }).andThen((response) => {
15142
- if (!isUsableStatus(response.status)) return ResultAsync4.fromSafePromise(Promise.resolve(null));
15143
- return ResultAsync4.fromSafePromise(
15306
+ if (!isUsableStatus(response.status)) return ResultAsync6.fromSafePromise(Promise.resolve(null));
15307
+ return ResultAsync6.fromSafePromise(
15144
15308
  (async () => {
15145
15309
  let authHint = response.status === 402 ? "paid" : "unprotected";
15146
15310
  let paymentRequiredBody;
@@ -15171,7 +15335,7 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
15171
15335
  }
15172
15336
  function getProbe(url2, headers, signal, inputBody) {
15173
15337
  const path = normalizePath(new URL(url2).pathname || "/");
15174
- return ResultAsync4.fromSafePromise(
15338
+ return ResultAsync6.fromSafePromise(
15175
15339
  Promise.all(
15176
15340
  [...HTTP_METHODS].map(
15177
15341
  (method) => probeMethod(url2, method, path, headers, signal, inputBody).match(
@@ -15184,6 +15348,20 @@ function getProbe(url2, headers, signal, inputBody) {
15184
15348
  }
15185
15349
 
15186
15350
  // src/core/protocols/mpp/index.ts
15351
+ import { Result } from "neverthrow";
15352
+ function parseBase64Json(encoded) {
15353
+ return Result.fromThrowable(
15354
+ () => {
15355
+ const decoded = typeof Buffer !== "undefined" ? Buffer.from(encoded, "base64").toString("utf8") : atob(encoded);
15356
+ const parsed = JSON.parse(decoded);
15357
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
15358
+ throw new Error("not an object");
15359
+ }
15360
+ return parsed;
15361
+ },
15362
+ (e) => e
15363
+ )();
15364
+ }
15187
15365
  var TEMPO_DEFAULT_CHAIN_ID = 4217;
15188
15366
  function parseAuthParams2(segment) {
15189
15367
  const params = {};
@@ -15206,14 +15384,9 @@ function extractPaymentOptions4(wwwAuthenticate) {
15206
15384
  const description = params["description"];
15207
15385
  const requestStr = params["request"];
15208
15386
  if (!paymentMethod || !intent || !realm || !requestStr) continue;
15209
- let request;
15210
- try {
15211
- const parsed = JSON.parse(requestStr);
15212
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
15213
- request = parsed;
15214
- } catch {
15215
- continue;
15216
- }
15387
+ const requestResult = parseBase64Json(requestStr);
15388
+ if (requestResult.isErr()) continue;
15389
+ const request = requestResult.value;
15217
15390
  const asset = typeof request["currency"] === "string" ? request["currency"] : void 0;
15218
15391
  const amountRaw = request["amount"];
15219
15392
  const amount = typeof amountRaw === "string" ? amountRaw : typeof amountRaw === "number" ? String(amountRaw) : void 0;
@@ -15410,6 +15583,11 @@ async function attachProbePayload(url2, advisories) {
15410
15583
  }
15411
15584
  }
15412
15585
 
15586
+ // src/core/types/sources.ts
15587
+ function isOpenApiSource(raw) {
15588
+ return raw !== null && !("parseFailure" in raw);
15589
+ }
15590
+
15413
15591
  // src/runtime/check-endpoint.ts
15414
15592
  function getAdvisoriesForOpenAPI(openApi, path) {
15415
15593
  return [...HTTP_METHODS].flatMap((method) => {
@@ -15448,7 +15626,8 @@ async function checkEndpointSchema(options) {
15448
15626
  return { found: false, origin, path, cause: "not_found" };
15449
15627
  }
15450
15628
  const openApiResult = await getOpenAPI(origin, options.headers, options.signal);
15451
- const openApi = openApiResult.isOk() ? openApiResult.value : null;
15629
+ const openApiRaw = openApiResult.isOk() ? openApiResult.value : null;
15630
+ const openApi = isOpenApiSource(openApiRaw) ? openApiRaw : null;
15452
15631
  if (openApi) {
15453
15632
  const advisories2 = getAdvisoriesForOpenAPI(openApi, path);
15454
15633
  if (advisories2.length > 0) return { found: true, origin, path, advisories: advisories2 };
@@ -15518,10 +15697,11 @@ Discovering ${origin}...
15518
15697
  getOpenAPI(origin),
15519
15698
  getWellKnown(origin)
15520
15699
  ]);
15521
- const openApi = openApiResult.isOk() ? openApiResult.value : null;
15700
+ const openApiRaw = openApiResult.isOk() ? openApiResult.value : null;
15522
15701
  const wellKnown = wellKnownResult.isOk() ? wellKnownResult.value : null;
15702
+ const openApi = isOpenApiSource(openApiRaw) ? openApiRaw : null;
15523
15703
  const warnings = [
15524
- ...getWarningsForOpenAPI(openApi),
15704
+ ...getWarningsForOpenAPI(openApiRaw),
15525
15705
  ...getWarningsForWellKnown(wellKnown)
15526
15706
  ];
15527
15707
  if (openApi) {
@@ -15587,12 +15767,14 @@ ${l4.guidance}`);
15587
15767
  warnings.push(...l3WarningArrays.flat());
15588
15768
  if (flags.json) {
15589
15769
  const meta3 = { origin, specUrl: wellKnown.fetchedUrl };
15770
+ if (l2.title) meta3.title = l2.title;
15771
+ if (l2.description) meta3.description = l2.description;
15590
15772
  if (l4 && flags.verbose) meta3.guidance = l4.guidance;
15591
15773
  console.log(
15592
15774
  JSON.stringify(
15593
15775
  {
15594
15776
  ok: true,
15595
- selectedStage: "well-known/x402",
15777
+ selectedStage: l2.source,
15596
15778
  resources: l2.routes.map(routeToResource),
15597
15779
  warnings,
15598
15780
  trace: [],
@@ -15604,12 +15786,15 @@ ${l4.guidance}`);
15604
15786
  );
15605
15787
  return 0;
15606
15788
  }
15607
- console.log(`Source: well-known/x402`);
15789
+ console.log(`Source: ${l2.source}`);
15608
15790
  console.log(`Spec: ${wellKnown.fetchedUrl}`);
15791
+ if (l2.title) console.log(`API: ${l2.title}`);
15609
15792
  console.log(`Routes: ${l2.routes.length}
15610
15793
  `);
15611
15794
  for (const route of l2.routes) {
15612
- console.log(` ${route.method.padEnd(7)} ${route.path} paid [x402]`);
15795
+ const price = route.price ? ` ${route.price}` : "";
15796
+ const protocols = route.protocols?.length ? ` [${route.protocols.join(", ")}]` : "";
15797
+ console.log(` ${route.method.padEnd(7)} ${route.path} paid${price}${protocols}`);
15613
15798
  }
15614
15799
  } else {
15615
15800
  if (flags.json) {