@agentcash/discovery 1.1.3 → 1.3.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 +363 -30
- package/dist/cli.js +363 -30
- package/dist/index.cjs +366 -26
- package/dist/index.d.cts +54 -3
- package/dist/index.d.ts +54 -3
- package/dist/index.js +363 -26
- package/dist/schemas.cjs +2 -1
- package/dist/schemas.d.cts +1 -0
- package/dist/schemas.d.ts +1 -0
- package/dist/schemas.js +2 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -44,15 +44,18 @@ __export(index_exports, {
|
|
|
44
44
|
getL3: () => getL3,
|
|
45
45
|
getL3ForOpenAPI: () => getL3ForOpenAPI,
|
|
46
46
|
getL3ForProbe: () => getL3ForProbe,
|
|
47
|
+
getMppWellKnown: () => getMppWellKnown,
|
|
47
48
|
getOpenAPI: () => getOpenAPI,
|
|
48
49
|
getProbe: () => getProbe,
|
|
49
50
|
getWarningsFor402Body: () => getWarningsFor402Body,
|
|
50
51
|
getWarningsForL2: () => getWarningsForL2,
|
|
51
52
|
getWarningsForL3: () => getWarningsForL3,
|
|
52
53
|
getWarningsForL4: () => getWarningsForL4,
|
|
54
|
+
getWarningsForMppHeader: () => getWarningsForMppHeader,
|
|
53
55
|
getWarningsForOpenAPI: () => getWarningsForOpenAPI,
|
|
54
56
|
getWarningsForWellKnown: () => getWarningsForWellKnown,
|
|
55
57
|
getWellKnown: () => getWellKnown,
|
|
58
|
+
getX402WellKnown: () => getX402WellKnown,
|
|
56
59
|
validatePaymentRequiredDetailed: () => validatePaymentRequiredDetailed
|
|
57
60
|
});
|
|
58
61
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -13895,7 +13898,8 @@ var WellKnownParsedSchema = external_exports.object({
|
|
|
13895
13898
|
routes: external_exports.array(
|
|
13896
13899
|
external_exports.object({
|
|
13897
13900
|
path: external_exports.string(),
|
|
13898
|
-
method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"])
|
|
13901
|
+
method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]),
|
|
13902
|
+
price: external_exports.string().optional()
|
|
13899
13903
|
})
|
|
13900
13904
|
),
|
|
13901
13905
|
instructions: external_exports.string().optional()
|
|
@@ -13988,9 +13992,9 @@ function toAbsoluteUrl(origin, value) {
|
|
|
13988
13992
|
|
|
13989
13993
|
// src/core/source/fetch.ts
|
|
13990
13994
|
var import_neverthrow = require("neverthrow");
|
|
13991
|
-
function toFetchError(
|
|
13992
|
-
const cause =
|
|
13993
|
-
return { cause, message: String(
|
|
13995
|
+
function toFetchError(err2) {
|
|
13996
|
+
const cause = err2 instanceof DOMException && (err2.name === "TimeoutError" || err2.name === "AbortError") ? "timeout" : "network";
|
|
13997
|
+
return { cause, message: String(err2) };
|
|
13994
13998
|
}
|
|
13995
13999
|
function fetchSafe(url2, init) {
|
|
13996
14000
|
return import_neverthrow.ResultAsync.fromPromise(fetch(url2, init), toFetchError);
|
|
@@ -14056,6 +14060,9 @@ function getOpenAPI(origin, headers, signal, specificationOverrideUrl) {
|
|
|
14056
14060
|
}
|
|
14057
14061
|
|
|
14058
14062
|
// src/core/source/wellknown/index.ts
|
|
14063
|
+
var import_neverthrow5 = require("neverthrow");
|
|
14064
|
+
|
|
14065
|
+
// src/core/source/wellknown/x402.ts
|
|
14059
14066
|
var import_neverthrow3 = require("neverthrow");
|
|
14060
14067
|
function toWellKnownParsed(origin, doc) {
|
|
14061
14068
|
const routes = doc.resources.flatMap((entry) => {
|
|
@@ -14082,12 +14089,17 @@ async function parseBody2(response, origin, url2) {
|
|
|
14082
14089
|
if (!doc.success) return null;
|
|
14083
14090
|
const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed(origin, doc.data));
|
|
14084
14091
|
if (!parsed.success) return null;
|
|
14085
|
-
return {
|
|
14092
|
+
return {
|
|
14093
|
+
raw: payload,
|
|
14094
|
+
...parsed.data,
|
|
14095
|
+
protocol: "x402",
|
|
14096
|
+
fetchedUrl: url2
|
|
14097
|
+
};
|
|
14086
14098
|
} catch {
|
|
14087
14099
|
return null;
|
|
14088
14100
|
}
|
|
14089
14101
|
}
|
|
14090
|
-
function
|
|
14102
|
+
function getX402WellKnown(origin, headers, signal) {
|
|
14091
14103
|
const url2 = `${origin}/.well-known/x402`;
|
|
14092
14104
|
return fetchSafe(url2, {
|
|
14093
14105
|
method: "GET",
|
|
@@ -14099,6 +14111,127 @@ function getWellKnown(origin, headers, signal) {
|
|
|
14099
14111
|
});
|
|
14100
14112
|
}
|
|
14101
14113
|
|
|
14114
|
+
// src/core/source/wellknown/mpp.ts
|
|
14115
|
+
var import_neverthrow4 = require("neverthrow");
|
|
14116
|
+
var MppEndpointSchema = external_exports.object({
|
|
14117
|
+
method: external_exports.string(),
|
|
14118
|
+
path: external_exports.string(),
|
|
14119
|
+
description: external_exports.string().optional(),
|
|
14120
|
+
payment: external_exports.object({
|
|
14121
|
+
intent: external_exports.string().optional(),
|
|
14122
|
+
method: external_exports.string().optional(),
|
|
14123
|
+
amount: external_exports.string().optional(),
|
|
14124
|
+
currency: external_exports.string().optional()
|
|
14125
|
+
}).optional()
|
|
14126
|
+
});
|
|
14127
|
+
var MppWellKnownDocSchema = external_exports.object({
|
|
14128
|
+
version: external_exports.number().optional(),
|
|
14129
|
+
name: external_exports.string().optional(),
|
|
14130
|
+
description: external_exports.string().optional(),
|
|
14131
|
+
categories: external_exports.array(external_exports.string()).optional(),
|
|
14132
|
+
methods: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
14133
|
+
endpoints: external_exports.array(MppEndpointSchema).default([]),
|
|
14134
|
+
docs: external_exports.object({
|
|
14135
|
+
homepage: external_exports.string().optional(),
|
|
14136
|
+
apiReference: external_exports.string().optional()
|
|
14137
|
+
}).optional()
|
|
14138
|
+
});
|
|
14139
|
+
var MPP_DECIMALS = 6;
|
|
14140
|
+
function formatMppAmount(raw) {
|
|
14141
|
+
if (!raw) return void 0;
|
|
14142
|
+
const n = Number(raw);
|
|
14143
|
+
if (!Number.isFinite(n)) return void 0;
|
|
14144
|
+
return `$${(n / 10 ** MPP_DECIMALS).toFixed(MPP_DECIMALS)}`;
|
|
14145
|
+
}
|
|
14146
|
+
function toWellKnownParsed2(doc) {
|
|
14147
|
+
const routes = doc.endpoints.flatMap((entry) => {
|
|
14148
|
+
const method = parseMethod(entry.method);
|
|
14149
|
+
if (!method) return [];
|
|
14150
|
+
const path = normalizePath(entry.path);
|
|
14151
|
+
if (!path) return [];
|
|
14152
|
+
const price = formatMppAmount(entry.payment?.amount);
|
|
14153
|
+
return [{ path, method, ...price ? { price } : {} }];
|
|
14154
|
+
});
|
|
14155
|
+
return {
|
|
14156
|
+
routes,
|
|
14157
|
+
...doc.description ? { instructions: doc.description } : {}
|
|
14158
|
+
};
|
|
14159
|
+
}
|
|
14160
|
+
async function parseBody3(response, url2) {
|
|
14161
|
+
try {
|
|
14162
|
+
const payload = await response.json();
|
|
14163
|
+
const doc = MppWellKnownDocSchema.safeParse(payload);
|
|
14164
|
+
if (!doc.success) return null;
|
|
14165
|
+
const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed2(doc.data));
|
|
14166
|
+
if (!parsed.success) return null;
|
|
14167
|
+
return {
|
|
14168
|
+
raw: payload,
|
|
14169
|
+
...parsed.data,
|
|
14170
|
+
...doc.data.name ? { title: doc.data.name } : {},
|
|
14171
|
+
...doc.data.description ? { description: doc.data.description } : {},
|
|
14172
|
+
protocol: "mpp",
|
|
14173
|
+
fetchedUrl: url2
|
|
14174
|
+
};
|
|
14175
|
+
} catch {
|
|
14176
|
+
return null;
|
|
14177
|
+
}
|
|
14178
|
+
}
|
|
14179
|
+
function getMppWellKnown(origin, headers, signal) {
|
|
14180
|
+
const url2 = `${origin}/.well-known/mpp`;
|
|
14181
|
+
return fetchSafe(url2, {
|
|
14182
|
+
method: "GET",
|
|
14183
|
+
headers: { Accept: "application/json", ...headers },
|
|
14184
|
+
signal
|
|
14185
|
+
}).andThen((response) => {
|
|
14186
|
+
if (!response.ok) return (0, import_neverthrow4.okAsync)(null);
|
|
14187
|
+
return import_neverthrow4.ResultAsync.fromSafePromise(parseBody3(response, url2));
|
|
14188
|
+
});
|
|
14189
|
+
}
|
|
14190
|
+
|
|
14191
|
+
// src/core/source/wellknown/index.ts
|
|
14192
|
+
function mergeError(a, b) {
|
|
14193
|
+
return {
|
|
14194
|
+
cause: a.cause === "network" || b.cause === "network" ? "network" : "timeout",
|
|
14195
|
+
message: `x402: ${a.message} | mpp: ${b.message}`
|
|
14196
|
+
};
|
|
14197
|
+
}
|
|
14198
|
+
function merge2(x402, mpp) {
|
|
14199
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14200
|
+
const routes = [...x402.routes, ...mpp.routes].filter((r) => {
|
|
14201
|
+
const key = `${r.method} ${r.path}`;
|
|
14202
|
+
if (seen.has(key)) return false;
|
|
14203
|
+
seen.add(key);
|
|
14204
|
+
return true;
|
|
14205
|
+
});
|
|
14206
|
+
return {
|
|
14207
|
+
raw: { ...mpp.raw, ...x402.raw },
|
|
14208
|
+
routes,
|
|
14209
|
+
protocol: "x402+mpp",
|
|
14210
|
+
// Prefer x402 instructions; fall back to mpp
|
|
14211
|
+
...x402.instructions || mpp.instructions ? { instructions: x402.instructions ?? mpp.instructions } : {},
|
|
14212
|
+
fetchedUrl: x402.fetchedUrl
|
|
14213
|
+
};
|
|
14214
|
+
}
|
|
14215
|
+
function getWellKnown(origin, headers, signal) {
|
|
14216
|
+
return new import_neverthrow5.ResultAsync(
|
|
14217
|
+
Promise.all([
|
|
14218
|
+
getX402WellKnown(origin, headers, signal),
|
|
14219
|
+
getMppWellKnown(origin, headers, signal)
|
|
14220
|
+
]).then(([x402Result, mppResult]) => {
|
|
14221
|
+
const x402 = x402Result.isOk() ? x402Result.value : null;
|
|
14222
|
+
const mpp = mppResult.isOk() ? mppResult.value : null;
|
|
14223
|
+
if (x402 && mpp) return (0, import_neverthrow5.ok)(merge2(x402, mpp));
|
|
14224
|
+
if (x402) return (0, import_neverthrow5.ok)(x402);
|
|
14225
|
+
if (mpp) return (0, import_neverthrow5.ok)(mpp);
|
|
14226
|
+
if (x402Result.isErr() && mppResult.isErr())
|
|
14227
|
+
return (0, import_neverthrow5.err)(mergeError(x402Result.error, mppResult.error));
|
|
14228
|
+
if (x402Result.isErr()) return (0, import_neverthrow5.err)(x402Result.error);
|
|
14229
|
+
if (mppResult.isErr()) return (0, import_neverthrow5.err)(mppResult.error);
|
|
14230
|
+
return (0, import_neverthrow5.ok)(null);
|
|
14231
|
+
})
|
|
14232
|
+
);
|
|
14233
|
+
}
|
|
14234
|
+
|
|
14102
14235
|
// src/core/layers/l2.ts
|
|
14103
14236
|
function formatPrice(pricing) {
|
|
14104
14237
|
if (pricing.pricingMode === "fixed") return `$${pricing.price}`;
|
|
@@ -14124,15 +14257,27 @@ function checkL2ForOpenAPI(openApi) {
|
|
|
14124
14257
|
source: "openapi"
|
|
14125
14258
|
};
|
|
14126
14259
|
}
|
|
14260
|
+
var WELL_KNOWN_PROTOCOLS = {
|
|
14261
|
+
x402: ["x402"],
|
|
14262
|
+
mpp: ["mpp"],
|
|
14263
|
+
"x402+mpp": ["x402", "mpp"]
|
|
14264
|
+
};
|
|
14127
14265
|
function checkL2ForWellknown(wellKnown) {
|
|
14266
|
+
const protocols = WELL_KNOWN_PROTOCOLS[wellKnown.protocol];
|
|
14128
14267
|
const routes = wellKnown.routes.map((route) => ({
|
|
14129
14268
|
path: route.path,
|
|
14130
14269
|
method: route.method,
|
|
14131
14270
|
summary: `${route.method} ${route.path}`,
|
|
14132
14271
|
authMode: "paid",
|
|
14133
|
-
protocols
|
|
14272
|
+
protocols,
|
|
14273
|
+
...route.price ? { price: route.price } : {}
|
|
14134
14274
|
}));
|
|
14135
|
-
return {
|
|
14275
|
+
return {
|
|
14276
|
+
...wellKnown.title ? { title: wellKnown.title } : {},
|
|
14277
|
+
...wellKnown.description ? { description: wellKnown.description } : {},
|
|
14278
|
+
routes,
|
|
14279
|
+
source: `well-known/${wellKnown.protocol}`
|
|
14280
|
+
};
|
|
14136
14281
|
}
|
|
14137
14282
|
|
|
14138
14283
|
// src/core/layers/l4.ts
|
|
@@ -14144,7 +14289,7 @@ function checkL4ForOpenAPI(openApi) {
|
|
|
14144
14289
|
}
|
|
14145
14290
|
function checkL4ForWellknown(wellKnown) {
|
|
14146
14291
|
if (wellKnown.instructions) {
|
|
14147
|
-
return { guidance: wellKnown.instructions, source:
|
|
14292
|
+
return { guidance: wellKnown.instructions, source: `well-known/${wellKnown.protocol}` };
|
|
14148
14293
|
}
|
|
14149
14294
|
return null;
|
|
14150
14295
|
}
|
|
@@ -14217,14 +14362,20 @@ async function discoverOriginSchema(options) {
|
|
|
14217
14362
|
const base = {
|
|
14218
14363
|
found: true,
|
|
14219
14364
|
origin,
|
|
14220
|
-
source:
|
|
14365
|
+
source: l2.source,
|
|
14366
|
+
...l2.title ? {
|
|
14367
|
+
info: {
|
|
14368
|
+
title: l2.title,
|
|
14369
|
+
...l2.description ? { description: l2.description } : {}
|
|
14370
|
+
}
|
|
14371
|
+
} : {},
|
|
14221
14372
|
endpoints: l2.routes
|
|
14222
14373
|
};
|
|
14223
14374
|
return withGuidance(base, l4, guidanceMode);
|
|
14224
14375
|
}
|
|
14225
14376
|
|
|
14226
14377
|
// src/core/source/probe/index.ts
|
|
14227
|
-
var
|
|
14378
|
+
var import_neverthrow6 = require("neverthrow");
|
|
14228
14379
|
|
|
14229
14380
|
// src/core/protocols/x402/v1/schema.ts
|
|
14230
14381
|
function extractSchemas(accepts) {
|
|
@@ -14674,8 +14825,8 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
|
|
|
14674
14825
|
...hasBody ? { body: JSON.stringify(inputBody) } : {},
|
|
14675
14826
|
signal
|
|
14676
14827
|
}).andThen((response) => {
|
|
14677
|
-
if (!isUsableStatus(response.status)) return
|
|
14678
|
-
return
|
|
14828
|
+
if (!isUsableStatus(response.status)) return import_neverthrow6.ResultAsync.fromSafePromise(Promise.resolve(null));
|
|
14829
|
+
return import_neverthrow6.ResultAsync.fromSafePromise(
|
|
14679
14830
|
(async () => {
|
|
14680
14831
|
let authHint = response.status === 402 ? "paid" : "unprotected";
|
|
14681
14832
|
let paymentRequiredBody;
|
|
@@ -14706,7 +14857,7 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
|
|
|
14706
14857
|
}
|
|
14707
14858
|
function getProbe(url2, headers, signal, inputBody) {
|
|
14708
14859
|
const path = normalizePath(new URL(url2).pathname || "/");
|
|
14709
|
-
return
|
|
14860
|
+
return import_neverthrow6.ResultAsync.fromSafePromise(
|
|
14710
14861
|
Promise.all(
|
|
14711
14862
|
[...HTTP_METHODS].map(
|
|
14712
14863
|
(method) => probeMethod(url2, method, path, headers, signal, inputBody).match(
|
|
@@ -14719,6 +14870,20 @@ function getProbe(url2, headers, signal, inputBody) {
|
|
|
14719
14870
|
}
|
|
14720
14871
|
|
|
14721
14872
|
// src/core/protocols/mpp/index.ts
|
|
14873
|
+
var import_neverthrow7 = require("neverthrow");
|
|
14874
|
+
function parseBase64Json(encoded) {
|
|
14875
|
+
return import_neverthrow7.Result.fromThrowable(
|
|
14876
|
+
() => {
|
|
14877
|
+
const decoded = typeof Buffer !== "undefined" ? Buffer.from(encoded, "base64").toString("utf8") : atob(encoded);
|
|
14878
|
+
const parsed = JSON.parse(decoded);
|
|
14879
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14880
|
+
throw new Error("not an object");
|
|
14881
|
+
}
|
|
14882
|
+
return parsed;
|
|
14883
|
+
},
|
|
14884
|
+
(e) => e
|
|
14885
|
+
)();
|
|
14886
|
+
}
|
|
14722
14887
|
var TEMPO_DEFAULT_CHAIN_ID = 4217;
|
|
14723
14888
|
function parseAuthParams(segment) {
|
|
14724
14889
|
const params = {};
|
|
@@ -14741,14 +14906,9 @@ function extractPaymentOptions4(wwwAuthenticate) {
|
|
|
14741
14906
|
const description = params["description"];
|
|
14742
14907
|
const requestStr = params["request"];
|
|
14743
14908
|
if (!paymentMethod || !intent || !realm || !requestStr) continue;
|
|
14744
|
-
|
|
14745
|
-
|
|
14746
|
-
|
|
14747
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
|
|
14748
|
-
request = parsed;
|
|
14749
|
-
} catch {
|
|
14750
|
-
continue;
|
|
14751
|
-
}
|
|
14909
|
+
const requestResult = parseBase64Json(requestStr);
|
|
14910
|
+
if (requestResult.isErr()) continue;
|
|
14911
|
+
const request = requestResult.value;
|
|
14752
14912
|
const asset = typeof request["currency"] === "string" ? request["currency"] : void 0;
|
|
14753
14913
|
const amountRaw = request["amount"];
|
|
14754
14914
|
const amount = typeof amountRaw === "string" ? amountRaw : typeof amountRaw === "number" ? String(amountRaw) : void 0;
|
|
@@ -14930,7 +15090,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
14930
15090
|
...inputSchema ? { inputSchema } : {},
|
|
14931
15091
|
...outputSchema ? { outputSchema } : {},
|
|
14932
15092
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
14933
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
15093
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
15094
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
14934
15095
|
};
|
|
14935
15096
|
}
|
|
14936
15097
|
async function attachProbePayload(url2, advisories) {
|
|
@@ -15111,9 +15272,23 @@ var AUDIT_CODES = {
|
|
|
15111
15272
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
15112
15273
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
15113
15274
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
15275
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
15114
15276
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
15115
15277
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
15116
|
-
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
|
|
15278
|
+
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
|
|
15279
|
+
// ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
|
|
15280
|
+
MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
|
|
15281
|
+
MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
|
|
15282
|
+
MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
|
|
15283
|
+
MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
|
|
15284
|
+
MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
|
|
15285
|
+
MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
|
|
15286
|
+
MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
|
|
15287
|
+
MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
|
|
15288
|
+
MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
|
|
15289
|
+
MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
|
|
15290
|
+
MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
|
|
15291
|
+
MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
|
|
15117
15292
|
};
|
|
15118
15293
|
|
|
15119
15294
|
// src/core/protocols/x402/v1/coinbase-schema.ts
|
|
@@ -15202,6 +15377,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
15202
15377
|
});
|
|
15203
15378
|
}
|
|
15204
15379
|
|
|
15380
|
+
// src/audit/warnings/mpp.ts
|
|
15381
|
+
function parseAuthParams2(segment) {
|
|
15382
|
+
const params = {};
|
|
15383
|
+
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15384
|
+
let match;
|
|
15385
|
+
while ((match = re.exec(segment)) !== null) {
|
|
15386
|
+
params[match[1]] = match[2] ?? match[3] ?? "";
|
|
15387
|
+
}
|
|
15388
|
+
return params;
|
|
15389
|
+
}
|
|
15390
|
+
function getWarningsForMppHeader(wwwAuthenticate) {
|
|
15391
|
+
if (!wwwAuthenticate?.trim()) {
|
|
15392
|
+
return [
|
|
15393
|
+
{
|
|
15394
|
+
code: AUDIT_CODES.MPP_HEADER_MISSING,
|
|
15395
|
+
severity: "error",
|
|
15396
|
+
message: "WWW-Authenticate header is absent.",
|
|
15397
|
+
hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
|
|
15398
|
+
}
|
|
15399
|
+
];
|
|
15400
|
+
}
|
|
15401
|
+
const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
|
|
15402
|
+
if (segments.length === 0) {
|
|
15403
|
+
return [
|
|
15404
|
+
{
|
|
15405
|
+
code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
|
|
15406
|
+
severity: "error",
|
|
15407
|
+
message: "WWW-Authenticate header contains no Payment challenges.",
|
|
15408
|
+
hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
|
|
15409
|
+
}
|
|
15410
|
+
];
|
|
15411
|
+
}
|
|
15412
|
+
const warnings = [];
|
|
15413
|
+
for (let i = 0; i < segments.length; i++) {
|
|
15414
|
+
const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
|
|
15415
|
+
const params = parseAuthParams2(stripped);
|
|
15416
|
+
const idx = `WWW-Authenticate[${i}]`;
|
|
15417
|
+
if (!params["id"]) {
|
|
15418
|
+
warnings.push({
|
|
15419
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
|
|
15420
|
+
severity: "error",
|
|
15421
|
+
message: `Payment challenge ${i} is missing the id parameter.`,
|
|
15422
|
+
hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
|
|
15423
|
+
path: `${idx}.id`
|
|
15424
|
+
});
|
|
15425
|
+
}
|
|
15426
|
+
if (!params["method"]) {
|
|
15427
|
+
warnings.push({
|
|
15428
|
+
code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
|
|
15429
|
+
severity: "error",
|
|
15430
|
+
message: `Payment challenge ${i} is missing the method parameter.`,
|
|
15431
|
+
hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
|
|
15432
|
+
path: `${idx}.method`
|
|
15433
|
+
});
|
|
15434
|
+
}
|
|
15435
|
+
if (!params["intent"]) {
|
|
15436
|
+
warnings.push({
|
|
15437
|
+
code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
|
|
15438
|
+
severity: "error",
|
|
15439
|
+
message: `Payment challenge ${i} is missing the intent parameter.`,
|
|
15440
|
+
hint: 'Set intent="charge" on the Payment challenge.',
|
|
15441
|
+
path: `${idx}.intent`
|
|
15442
|
+
});
|
|
15443
|
+
}
|
|
15444
|
+
if (!params["realm"]) {
|
|
15445
|
+
warnings.push({
|
|
15446
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
|
|
15447
|
+
severity: "error",
|
|
15448
|
+
message: `Payment challenge ${i} is missing the realm parameter.`,
|
|
15449
|
+
hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
|
|
15450
|
+
path: `${idx}.realm`
|
|
15451
|
+
});
|
|
15452
|
+
}
|
|
15453
|
+
if (!params["expires"]) {
|
|
15454
|
+
warnings.push({
|
|
15455
|
+
code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
|
|
15456
|
+
severity: "error",
|
|
15457
|
+
message: `Payment challenge ${i} is missing the expires parameter.`,
|
|
15458
|
+
hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
|
|
15459
|
+
path: `${idx}.expires`
|
|
15460
|
+
});
|
|
15461
|
+
}
|
|
15462
|
+
const requestStr = params["request"];
|
|
15463
|
+
if (!requestStr) {
|
|
15464
|
+
warnings.push({
|
|
15465
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
|
|
15466
|
+
severity: "error",
|
|
15467
|
+
message: `Payment challenge ${i} is missing the request field.`,
|
|
15468
|
+
hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
|
|
15469
|
+
path: `${idx}.request`
|
|
15470
|
+
});
|
|
15471
|
+
continue;
|
|
15472
|
+
}
|
|
15473
|
+
let request;
|
|
15474
|
+
try {
|
|
15475
|
+
const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
|
|
15476
|
+
const parsed = JSON.parse(decoded);
|
|
15477
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15478
|
+
throw new Error("not an object");
|
|
15479
|
+
}
|
|
15480
|
+
request = parsed;
|
|
15481
|
+
} catch {
|
|
15482
|
+
warnings.push({
|
|
15483
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
|
|
15484
|
+
severity: "error",
|
|
15485
|
+
message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
|
|
15486
|
+
hint: "The request value must be a base64url-encoded JCS JSON object.",
|
|
15487
|
+
path: `${idx}.request`
|
|
15488
|
+
});
|
|
15489
|
+
continue;
|
|
15490
|
+
}
|
|
15491
|
+
if (!request["currency"]) {
|
|
15492
|
+
warnings.push({
|
|
15493
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
|
|
15494
|
+
severity: "error",
|
|
15495
|
+
message: `Payment challenge ${i} is missing currency in the request object.`,
|
|
15496
|
+
hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
|
|
15497
|
+
path: `${idx}.request.currency`
|
|
15498
|
+
});
|
|
15499
|
+
}
|
|
15500
|
+
const amount = request["amount"];
|
|
15501
|
+
if (amount === void 0 || amount === null) {
|
|
15502
|
+
warnings.push({
|
|
15503
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15504
|
+
severity: "error",
|
|
15505
|
+
message: `Payment challenge ${i} is missing amount in the request object.`,
|
|
15506
|
+
hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
|
|
15507
|
+
path: `${idx}.request.amount`
|
|
15508
|
+
});
|
|
15509
|
+
} else if (typeof amount !== "string" && typeof amount !== "number") {
|
|
15510
|
+
warnings.push({
|
|
15511
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15512
|
+
severity: "error",
|
|
15513
|
+
message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
|
|
15514
|
+
hint: "Set amount to a raw token-unit string.",
|
|
15515
|
+
path: `${idx}.request.amount`
|
|
15516
|
+
});
|
|
15517
|
+
}
|
|
15518
|
+
if (!request["recipient"]) {
|
|
15519
|
+
warnings.push({
|
|
15520
|
+
code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
|
|
15521
|
+
severity: "error",
|
|
15522
|
+
message: `Payment challenge ${i} is missing recipient in the request object.`,
|
|
15523
|
+
hint: "Set recipient to the wallet address that should receive payment.",
|
|
15524
|
+
path: `${idx}.request.recipient`
|
|
15525
|
+
});
|
|
15526
|
+
}
|
|
15527
|
+
}
|
|
15528
|
+
return warnings;
|
|
15529
|
+
}
|
|
15530
|
+
|
|
15205
15531
|
// src/audit/warnings/l3.ts
|
|
15206
15532
|
function getWarningsFor402Body(body) {
|
|
15207
15533
|
if (!isRecord(body)) {
|
|
@@ -15331,17 +15657,28 @@ function getWarningsForL3(l3) {
|
|
|
15331
15657
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
15332
15658
|
});
|
|
15333
15659
|
}
|
|
15334
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
15660
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
15335
15661
|
warnings.push({
|
|
15336
15662
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
15337
15663
|
severity: "info",
|
|
15338
15664
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
15339
|
-
hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
|
|
15665
|
+
hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
|
|
15666
|
+
});
|
|
15667
|
+
}
|
|
15668
|
+
if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
|
|
15669
|
+
warnings.push({
|
|
15670
|
+
code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
|
|
15671
|
+
severity: "warn",
|
|
15672
|
+
message: "Paid endpoint did not return payment options in the 402 response.",
|
|
15673
|
+
hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
|
|
15340
15674
|
});
|
|
15341
15675
|
}
|
|
15342
15676
|
if (l3.paymentRequiredBody !== void 0) {
|
|
15343
15677
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
15344
15678
|
}
|
|
15679
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15680
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15681
|
+
}
|
|
15345
15682
|
return warnings;
|
|
15346
15683
|
}
|
|
15347
15684
|
|
|
@@ -15560,14 +15897,17 @@ function getWarningsForL4(l4) {
|
|
|
15560
15897
|
getL3,
|
|
15561
15898
|
getL3ForOpenAPI,
|
|
15562
15899
|
getL3ForProbe,
|
|
15900
|
+
getMppWellKnown,
|
|
15563
15901
|
getOpenAPI,
|
|
15564
15902
|
getProbe,
|
|
15565
15903
|
getWarningsFor402Body,
|
|
15566
15904
|
getWarningsForL2,
|
|
15567
15905
|
getWarningsForL3,
|
|
15568
15906
|
getWarningsForL4,
|
|
15907
|
+
getWarningsForMppHeader,
|
|
15569
15908
|
getWarningsForOpenAPI,
|
|
15570
15909
|
getWarningsForWellKnown,
|
|
15571
15910
|
getWellKnown,
|
|
15911
|
+
getX402WellKnown,
|
|
15572
15912
|
validatePaymentRequiredDetailed
|
|
15573
15913
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -89,12 +89,18 @@ interface OpenApiRoute {
|
|
|
89
89
|
interface WellKnownSource {
|
|
90
90
|
raw: Record<string, unknown>;
|
|
91
91
|
routes: WellKnownRoute[];
|
|
92
|
+
title?: string;
|
|
93
|
+
description?: string;
|
|
92
94
|
instructions?: string;
|
|
93
95
|
fetchedUrl: string;
|
|
96
|
+
/** Which well-known document(s) this source was built from. */
|
|
97
|
+
protocol: 'x402' | 'mpp' | 'x402+mpp';
|
|
94
98
|
}
|
|
95
99
|
interface WellKnownRoute {
|
|
96
100
|
path: string;
|
|
97
101
|
method: HttpMethod;
|
|
102
|
+
/** Raw price hint from the well-known document (e.g. MPP `payment.amount`). */
|
|
103
|
+
price?: string;
|
|
98
104
|
}
|
|
99
105
|
interface ProbeResult {
|
|
100
106
|
path: string;
|
|
@@ -111,7 +117,7 @@ interface L2Result {
|
|
|
111
117
|
description?: string;
|
|
112
118
|
version?: string;
|
|
113
119
|
routes: L2Route[];
|
|
114
|
-
source: 'openapi' | 'well-known/x402' | null;
|
|
120
|
+
source: 'openapi' | 'well-known/x402' | 'well-known/mpp' | 'well-known/x402+mpp' | null;
|
|
115
121
|
}
|
|
116
122
|
interface L2Route {
|
|
117
123
|
path: string;
|
|
@@ -137,10 +143,15 @@ interface L3Result {
|
|
|
137
143
|
* and returned a 402. Used by getWarningsForL3 to run full payment-required validation.
|
|
138
144
|
*/
|
|
139
145
|
paymentRequiredBody?: unknown;
|
|
146
|
+
/**
|
|
147
|
+
* Raw WWW-Authenticate header value from the 402 response. Present when the endpoint
|
|
148
|
+
* was probed and returned a 402 with an MPP challenge. Used by getWarningsForMppHeader.
|
|
149
|
+
*/
|
|
150
|
+
wwwAuthenticate?: string;
|
|
140
151
|
}
|
|
141
152
|
interface L4Result {
|
|
142
153
|
guidance: string;
|
|
143
|
-
source: 'openapi' | 'well-known/x402';
|
|
154
|
+
source: 'openapi' | 'well-known/x402' | 'well-known/mpp' | 'well-known/x402+mpp';
|
|
144
155
|
}
|
|
145
156
|
|
|
146
157
|
declare enum GuidanceMode {
|
|
@@ -236,8 +247,21 @@ interface FetchError {
|
|
|
236
247
|
|
|
237
248
|
declare function getOpenAPI(origin: string, headers?: Record<string, string>, signal?: AbortSignal, specificationOverrideUrl?: string): ResultAsync<OpenApiSource | null, FetchError>;
|
|
238
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Fetches both `/.well-known/x402` and `/.well-known/mpp` in parallel and merges results.
|
|
252
|
+
*
|
|
253
|
+
* In practice these are mutually exclusive, but if both exist their routes are combined
|
|
254
|
+
* (deduplicated by method+path). x402 wins on instruction/fetchedUrl conflicts.
|
|
255
|
+
*
|
|
256
|
+
* Individual leg failures are treated as "not found" for that leg so valid data from
|
|
257
|
+
* the other is never suppressed. Returns Err(FetchError) only when both legs hard-fail.
|
|
258
|
+
*/
|
|
239
259
|
declare function getWellKnown(origin: string, headers?: Record<string, string>, signal?: AbortSignal): ResultAsync<WellKnownSource | null, FetchError>;
|
|
240
260
|
|
|
261
|
+
declare function getX402WellKnown(origin: string, headers?: Record<string, string>, signal?: AbortSignal): ResultAsync<WellKnownSource | null, FetchError>;
|
|
262
|
+
|
|
263
|
+
declare function getMppWellKnown(origin: string, headers?: Record<string, string>, signal?: AbortSignal): ResultAsync<WellKnownSource | null, FetchError>;
|
|
264
|
+
|
|
241
265
|
declare function getProbe(url: string, headers?: Record<string, string>, signal?: AbortSignal, inputBody?: Record<string, unknown>): ResultAsync<ProbeResult[], FetchError>;
|
|
242
266
|
|
|
243
267
|
declare function checkL2ForOpenAPI(openApi: OpenApiSource): L2Result;
|
|
@@ -361,8 +385,21 @@ declare const AUDIT_CODES: {
|
|
|
361
385
|
readonly L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING";
|
|
362
386
|
readonly L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING";
|
|
363
387
|
readonly L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID";
|
|
388
|
+
readonly L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID";
|
|
364
389
|
readonly L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING";
|
|
365
390
|
readonly L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG";
|
|
391
|
+
readonly MPP_HEADER_MISSING: "MPP_HEADER_MISSING";
|
|
392
|
+
readonly MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES";
|
|
393
|
+
readonly MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING";
|
|
394
|
+
readonly MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING";
|
|
395
|
+
readonly MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING";
|
|
396
|
+
readonly MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING";
|
|
397
|
+
readonly MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING";
|
|
398
|
+
readonly MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING";
|
|
399
|
+
readonly MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID";
|
|
400
|
+
readonly MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING";
|
|
401
|
+
readonly MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING";
|
|
402
|
+
readonly MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING";
|
|
366
403
|
};
|
|
367
404
|
type AuditCode = (typeof AUDIT_CODES)[keyof typeof AUDIT_CODES];
|
|
368
405
|
|
|
@@ -392,4 +429,18 @@ declare function getWarningsForL3(l3: L3Result | null): AuditWarning[];
|
|
|
392
429
|
|
|
393
430
|
declare function getWarningsForL4(l4: L4Result | null): AuditWarning[];
|
|
394
431
|
|
|
395
|
-
|
|
432
|
+
/**
|
|
433
|
+
* Validates a raw WWW-Authenticate header value from an MPP 402 response and
|
|
434
|
+
* returns issues as AuditWarnings.
|
|
435
|
+
*
|
|
436
|
+
* Checks for:
|
|
437
|
+
* - Header presence
|
|
438
|
+
* - At least one Payment challenge
|
|
439
|
+
* - Required challenge parameters: id, method, intent, realm, expires, request
|
|
440
|
+
* - Valid base64url-encoded JSON in the request field
|
|
441
|
+
* - Required request fields: currency (asset), amount
|
|
442
|
+
* - Recommended request field: recipient (payTo)
|
|
443
|
+
*/
|
|
444
|
+
declare function getWarningsForMppHeader(wwwAuthenticate: string | null | undefined): AuditWarning[];
|
|
445
|
+
|
|
446
|
+
export { AUDIT_CODES, type AuditCode, type AuditSeverity, type AuditWarning, type AuthMode, type CheckEndpointNotFound, type CheckEndpointOptions, type CheckEndpointResult, type CheckEndpointSuccess, type DiscoverOriginSchemaNotFound, type DiscoverOriginSchemaOptions, type DiscoverOriginSchemaResult, type DiscoverOriginSchemaSuccess, type EndpointMethodAdvisory, GuidanceMode, type HttpMethod, type L2Result, type L2Route, type L3Result, type L4Result, type MetadataPreview, type MppPaymentOption, type NormalizedAccept, type NormalizedPaymentRequired, type OpenApiRoute, type OpenApiSource, type PaymentOption, type PricingMode, type ProbeResult, type TrustTier, VALIDATION_CODES, type ValidatePaymentRequiredDetailedResult, type ValidatePaymentRequiredOptions, type ValidationIssue, type ValidationSeverity, type ValidationStage, type ValidationSummary, type WellKnownRoute, type WellKnownSource, type X402PaymentOption, type X402V1PaymentOption, type X402V2PaymentOption, attachProbePayload, checkEndpointSchema, checkL2ForOpenAPI, checkL2ForWellknown, checkL4ForOpenAPI, checkL4ForWellknown, discoverOriginSchema, evaluateMetadataCompleteness, getL3, getL3ForOpenAPI, getL3ForProbe, getMppWellKnown, getOpenAPI, getProbe, getWarningsFor402Body, getWarningsForL2, getWarningsForL3, getWarningsForL4, getWarningsForMppHeader, getWarningsForOpenAPI, getWarningsForWellKnown, getWellKnown, getX402WellKnown, validatePaymentRequiredDetailed };
|