@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/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(
|
|
13939
|
-
const cause =
|
|
13940
|
-
return { cause, message: String(
|
|
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);
|
|
@@ -14003,6 +14004,9 @@ function getOpenAPI(origin, headers, signal, specificationOverrideUrl) {
|
|
|
14003
14004
|
}
|
|
14004
14005
|
|
|
14005
14006
|
// src/core/source/wellknown/index.ts
|
|
14007
|
+
import { ok, err, ResultAsync as ResultAsync5 } from "neverthrow";
|
|
14008
|
+
|
|
14009
|
+
// src/core/source/wellknown/x402.ts
|
|
14006
14010
|
import { okAsync as okAsync2, ResultAsync as ResultAsync3 } from "neverthrow";
|
|
14007
14011
|
function toWellKnownParsed(origin, doc) {
|
|
14008
14012
|
const routes = doc.resources.flatMap((entry) => {
|
|
@@ -14029,12 +14033,17 @@ async function parseBody2(response, origin, url2) {
|
|
|
14029
14033
|
if (!doc.success) return null;
|
|
14030
14034
|
const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed(origin, doc.data));
|
|
14031
14035
|
if (!parsed.success) return null;
|
|
14032
|
-
return {
|
|
14036
|
+
return {
|
|
14037
|
+
raw: payload,
|
|
14038
|
+
...parsed.data,
|
|
14039
|
+
protocol: "x402",
|
|
14040
|
+
fetchedUrl: url2
|
|
14041
|
+
};
|
|
14033
14042
|
} catch {
|
|
14034
14043
|
return null;
|
|
14035
14044
|
}
|
|
14036
14045
|
}
|
|
14037
|
-
function
|
|
14046
|
+
function getX402WellKnown(origin, headers, signal) {
|
|
14038
14047
|
const url2 = `${origin}/.well-known/x402`;
|
|
14039
14048
|
return fetchSafe(url2, {
|
|
14040
14049
|
method: "GET",
|
|
@@ -14046,6 +14055,127 @@ function getWellKnown(origin, headers, signal) {
|
|
|
14046
14055
|
});
|
|
14047
14056
|
}
|
|
14048
14057
|
|
|
14058
|
+
// src/core/source/wellknown/mpp.ts
|
|
14059
|
+
import { okAsync as okAsync3, ResultAsync as ResultAsync4 } from "neverthrow";
|
|
14060
|
+
var MppEndpointSchema = external_exports.object({
|
|
14061
|
+
method: external_exports.string(),
|
|
14062
|
+
path: external_exports.string(),
|
|
14063
|
+
description: external_exports.string().optional(),
|
|
14064
|
+
payment: external_exports.object({
|
|
14065
|
+
intent: external_exports.string().optional(),
|
|
14066
|
+
method: external_exports.string().optional(),
|
|
14067
|
+
amount: external_exports.string().optional(),
|
|
14068
|
+
currency: external_exports.string().optional()
|
|
14069
|
+
}).optional()
|
|
14070
|
+
});
|
|
14071
|
+
var MppWellKnownDocSchema = external_exports.object({
|
|
14072
|
+
version: external_exports.number().optional(),
|
|
14073
|
+
name: external_exports.string().optional(),
|
|
14074
|
+
description: external_exports.string().optional(),
|
|
14075
|
+
categories: external_exports.array(external_exports.string()).optional(),
|
|
14076
|
+
methods: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
14077
|
+
endpoints: external_exports.array(MppEndpointSchema).default([]),
|
|
14078
|
+
docs: external_exports.object({
|
|
14079
|
+
homepage: external_exports.string().optional(),
|
|
14080
|
+
apiReference: external_exports.string().optional()
|
|
14081
|
+
}).optional()
|
|
14082
|
+
});
|
|
14083
|
+
var MPP_DECIMALS = 6;
|
|
14084
|
+
function formatMppAmount(raw) {
|
|
14085
|
+
if (!raw) return void 0;
|
|
14086
|
+
const n = Number(raw);
|
|
14087
|
+
if (!Number.isFinite(n)) return void 0;
|
|
14088
|
+
return `$${(n / 10 ** MPP_DECIMALS).toFixed(MPP_DECIMALS)}`;
|
|
14089
|
+
}
|
|
14090
|
+
function toWellKnownParsed2(doc) {
|
|
14091
|
+
const routes = doc.endpoints.flatMap((entry) => {
|
|
14092
|
+
const method = parseMethod(entry.method);
|
|
14093
|
+
if (!method) return [];
|
|
14094
|
+
const path = normalizePath(entry.path);
|
|
14095
|
+
if (!path) return [];
|
|
14096
|
+
const price = formatMppAmount(entry.payment?.amount);
|
|
14097
|
+
return [{ path, method, ...price ? { price } : {} }];
|
|
14098
|
+
});
|
|
14099
|
+
return {
|
|
14100
|
+
routes,
|
|
14101
|
+
...doc.description ? { instructions: doc.description } : {}
|
|
14102
|
+
};
|
|
14103
|
+
}
|
|
14104
|
+
async function parseBody3(response, url2) {
|
|
14105
|
+
try {
|
|
14106
|
+
const payload = await response.json();
|
|
14107
|
+
const doc = MppWellKnownDocSchema.safeParse(payload);
|
|
14108
|
+
if (!doc.success) return null;
|
|
14109
|
+
const parsed = WellKnownParsedSchema.safeParse(toWellKnownParsed2(doc.data));
|
|
14110
|
+
if (!parsed.success) return null;
|
|
14111
|
+
return {
|
|
14112
|
+
raw: payload,
|
|
14113
|
+
...parsed.data,
|
|
14114
|
+
...doc.data.name ? { title: doc.data.name } : {},
|
|
14115
|
+
...doc.data.description ? { description: doc.data.description } : {},
|
|
14116
|
+
protocol: "mpp",
|
|
14117
|
+
fetchedUrl: url2
|
|
14118
|
+
};
|
|
14119
|
+
} catch {
|
|
14120
|
+
return null;
|
|
14121
|
+
}
|
|
14122
|
+
}
|
|
14123
|
+
function getMppWellKnown(origin, headers, signal) {
|
|
14124
|
+
const url2 = `${origin}/.well-known/mpp`;
|
|
14125
|
+
return fetchSafe(url2, {
|
|
14126
|
+
method: "GET",
|
|
14127
|
+
headers: { Accept: "application/json", ...headers },
|
|
14128
|
+
signal
|
|
14129
|
+
}).andThen((response) => {
|
|
14130
|
+
if (!response.ok) return okAsync3(null);
|
|
14131
|
+
return ResultAsync4.fromSafePromise(parseBody3(response, url2));
|
|
14132
|
+
});
|
|
14133
|
+
}
|
|
14134
|
+
|
|
14135
|
+
// src/core/source/wellknown/index.ts
|
|
14136
|
+
function mergeError(a, b) {
|
|
14137
|
+
return {
|
|
14138
|
+
cause: a.cause === "network" || b.cause === "network" ? "network" : "timeout",
|
|
14139
|
+
message: `x402: ${a.message} | mpp: ${b.message}`
|
|
14140
|
+
};
|
|
14141
|
+
}
|
|
14142
|
+
function merge2(x402, mpp) {
|
|
14143
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14144
|
+
const routes = [...x402.routes, ...mpp.routes].filter((r) => {
|
|
14145
|
+
const key = `${r.method} ${r.path}`;
|
|
14146
|
+
if (seen.has(key)) return false;
|
|
14147
|
+
seen.add(key);
|
|
14148
|
+
return true;
|
|
14149
|
+
});
|
|
14150
|
+
return {
|
|
14151
|
+
raw: { ...mpp.raw, ...x402.raw },
|
|
14152
|
+
routes,
|
|
14153
|
+
protocol: "x402+mpp",
|
|
14154
|
+
// Prefer x402 instructions; fall back to mpp
|
|
14155
|
+
...x402.instructions || mpp.instructions ? { instructions: x402.instructions ?? mpp.instructions } : {},
|
|
14156
|
+
fetchedUrl: x402.fetchedUrl
|
|
14157
|
+
};
|
|
14158
|
+
}
|
|
14159
|
+
function getWellKnown(origin, headers, signal) {
|
|
14160
|
+
return new ResultAsync5(
|
|
14161
|
+
Promise.all([
|
|
14162
|
+
getX402WellKnown(origin, headers, signal),
|
|
14163
|
+
getMppWellKnown(origin, headers, signal)
|
|
14164
|
+
]).then(([x402Result, mppResult]) => {
|
|
14165
|
+
const x402 = x402Result.isOk() ? x402Result.value : null;
|
|
14166
|
+
const mpp = mppResult.isOk() ? mppResult.value : null;
|
|
14167
|
+
if (x402 && mpp) return ok(merge2(x402, mpp));
|
|
14168
|
+
if (x402) return ok(x402);
|
|
14169
|
+
if (mpp) return ok(mpp);
|
|
14170
|
+
if (x402Result.isErr() && mppResult.isErr())
|
|
14171
|
+
return err(mergeError(x402Result.error, mppResult.error));
|
|
14172
|
+
if (x402Result.isErr()) return err(x402Result.error);
|
|
14173
|
+
if (mppResult.isErr()) return err(mppResult.error);
|
|
14174
|
+
return ok(null);
|
|
14175
|
+
})
|
|
14176
|
+
);
|
|
14177
|
+
}
|
|
14178
|
+
|
|
14049
14179
|
// src/core/layers/l2.ts
|
|
14050
14180
|
function formatPrice(pricing) {
|
|
14051
14181
|
if (pricing.pricingMode === "fixed") return `$${pricing.price}`;
|
|
@@ -14071,15 +14201,27 @@ function checkL2ForOpenAPI(openApi) {
|
|
|
14071
14201
|
source: "openapi"
|
|
14072
14202
|
};
|
|
14073
14203
|
}
|
|
14204
|
+
var WELL_KNOWN_PROTOCOLS = {
|
|
14205
|
+
x402: ["x402"],
|
|
14206
|
+
mpp: ["mpp"],
|
|
14207
|
+
"x402+mpp": ["x402", "mpp"]
|
|
14208
|
+
};
|
|
14074
14209
|
function checkL2ForWellknown(wellKnown) {
|
|
14210
|
+
const protocols = WELL_KNOWN_PROTOCOLS[wellKnown.protocol];
|
|
14075
14211
|
const routes = wellKnown.routes.map((route) => ({
|
|
14076
14212
|
path: route.path,
|
|
14077
14213
|
method: route.method,
|
|
14078
14214
|
summary: `${route.method} ${route.path}`,
|
|
14079
14215
|
authMode: "paid",
|
|
14080
|
-
protocols
|
|
14216
|
+
protocols,
|
|
14217
|
+
...route.price ? { price: route.price } : {}
|
|
14081
14218
|
}));
|
|
14082
|
-
return {
|
|
14219
|
+
return {
|
|
14220
|
+
...wellKnown.title ? { title: wellKnown.title } : {},
|
|
14221
|
+
...wellKnown.description ? { description: wellKnown.description } : {},
|
|
14222
|
+
routes,
|
|
14223
|
+
source: `well-known/${wellKnown.protocol}`
|
|
14224
|
+
};
|
|
14083
14225
|
}
|
|
14084
14226
|
|
|
14085
14227
|
// src/core/layers/l4.ts
|
|
@@ -14091,7 +14233,7 @@ function checkL4ForOpenAPI(openApi) {
|
|
|
14091
14233
|
}
|
|
14092
14234
|
function checkL4ForWellknown(wellKnown) {
|
|
14093
14235
|
if (wellKnown.instructions) {
|
|
14094
|
-
return { guidance: wellKnown.instructions, source:
|
|
14236
|
+
return { guidance: wellKnown.instructions, source: `well-known/${wellKnown.protocol}` };
|
|
14095
14237
|
}
|
|
14096
14238
|
return null;
|
|
14097
14239
|
}
|
|
@@ -14116,9 +14258,23 @@ var AUDIT_CODES = {
|
|
|
14116
14258
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
14117
14259
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
14118
14260
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
14261
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
14119
14262
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
14120
14263
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
14121
|
-
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
|
|
14264
|
+
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
|
|
14265
|
+
// ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
|
|
14266
|
+
MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
|
|
14267
|
+
MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
|
|
14268
|
+
MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
|
|
14269
|
+
MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
|
|
14270
|
+
MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
|
|
14271
|
+
MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
|
|
14272
|
+
MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
|
|
14273
|
+
MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
|
|
14274
|
+
MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
|
|
14275
|
+
MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
|
|
14276
|
+
MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
|
|
14277
|
+
MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
|
|
14122
14278
|
};
|
|
14123
14279
|
|
|
14124
14280
|
// src/audit/warnings/sources.ts
|
|
@@ -14697,6 +14853,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
14697
14853
|
});
|
|
14698
14854
|
}
|
|
14699
14855
|
|
|
14856
|
+
// src/audit/warnings/mpp.ts
|
|
14857
|
+
function parseAuthParams(segment) {
|
|
14858
|
+
const params = {};
|
|
14859
|
+
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
14860
|
+
let match;
|
|
14861
|
+
while ((match = re.exec(segment)) !== null) {
|
|
14862
|
+
params[match[1]] = match[2] ?? match[3] ?? "";
|
|
14863
|
+
}
|
|
14864
|
+
return params;
|
|
14865
|
+
}
|
|
14866
|
+
function getWarningsForMppHeader(wwwAuthenticate) {
|
|
14867
|
+
if (!wwwAuthenticate?.trim()) {
|
|
14868
|
+
return [
|
|
14869
|
+
{
|
|
14870
|
+
code: AUDIT_CODES.MPP_HEADER_MISSING,
|
|
14871
|
+
severity: "error",
|
|
14872
|
+
message: "WWW-Authenticate header is absent.",
|
|
14873
|
+
hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
|
|
14874
|
+
}
|
|
14875
|
+
];
|
|
14876
|
+
}
|
|
14877
|
+
const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
|
|
14878
|
+
if (segments.length === 0) {
|
|
14879
|
+
return [
|
|
14880
|
+
{
|
|
14881
|
+
code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
|
|
14882
|
+
severity: "error",
|
|
14883
|
+
message: "WWW-Authenticate header contains no Payment challenges.",
|
|
14884
|
+
hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
|
|
14885
|
+
}
|
|
14886
|
+
];
|
|
14887
|
+
}
|
|
14888
|
+
const warnings = [];
|
|
14889
|
+
for (let i = 0; i < segments.length; i++) {
|
|
14890
|
+
const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
|
|
14891
|
+
const params = parseAuthParams(stripped);
|
|
14892
|
+
const idx = `WWW-Authenticate[${i}]`;
|
|
14893
|
+
if (!params["id"]) {
|
|
14894
|
+
warnings.push({
|
|
14895
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
|
|
14896
|
+
severity: "error",
|
|
14897
|
+
message: `Payment challenge ${i} is missing the id parameter.`,
|
|
14898
|
+
hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
|
|
14899
|
+
path: `${idx}.id`
|
|
14900
|
+
});
|
|
14901
|
+
}
|
|
14902
|
+
if (!params["method"]) {
|
|
14903
|
+
warnings.push({
|
|
14904
|
+
code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
|
|
14905
|
+
severity: "error",
|
|
14906
|
+
message: `Payment challenge ${i} is missing the method parameter.`,
|
|
14907
|
+
hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
|
|
14908
|
+
path: `${idx}.method`
|
|
14909
|
+
});
|
|
14910
|
+
}
|
|
14911
|
+
if (!params["intent"]) {
|
|
14912
|
+
warnings.push({
|
|
14913
|
+
code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
|
|
14914
|
+
severity: "error",
|
|
14915
|
+
message: `Payment challenge ${i} is missing the intent parameter.`,
|
|
14916
|
+
hint: 'Set intent="charge" on the Payment challenge.',
|
|
14917
|
+
path: `${idx}.intent`
|
|
14918
|
+
});
|
|
14919
|
+
}
|
|
14920
|
+
if (!params["realm"]) {
|
|
14921
|
+
warnings.push({
|
|
14922
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
|
|
14923
|
+
severity: "error",
|
|
14924
|
+
message: `Payment challenge ${i} is missing the realm parameter.`,
|
|
14925
|
+
hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
|
|
14926
|
+
path: `${idx}.realm`
|
|
14927
|
+
});
|
|
14928
|
+
}
|
|
14929
|
+
if (!params["expires"]) {
|
|
14930
|
+
warnings.push({
|
|
14931
|
+
code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
|
|
14932
|
+
severity: "error",
|
|
14933
|
+
message: `Payment challenge ${i} is missing the expires parameter.`,
|
|
14934
|
+
hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
|
|
14935
|
+
path: `${idx}.expires`
|
|
14936
|
+
});
|
|
14937
|
+
}
|
|
14938
|
+
const requestStr = params["request"];
|
|
14939
|
+
if (!requestStr) {
|
|
14940
|
+
warnings.push({
|
|
14941
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
|
|
14942
|
+
severity: "error",
|
|
14943
|
+
message: `Payment challenge ${i} is missing the request field.`,
|
|
14944
|
+
hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
|
|
14945
|
+
path: `${idx}.request`
|
|
14946
|
+
});
|
|
14947
|
+
continue;
|
|
14948
|
+
}
|
|
14949
|
+
let request;
|
|
14950
|
+
try {
|
|
14951
|
+
const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
|
|
14952
|
+
const parsed = JSON.parse(decoded);
|
|
14953
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14954
|
+
throw new Error("not an object");
|
|
14955
|
+
}
|
|
14956
|
+
request = parsed;
|
|
14957
|
+
} catch {
|
|
14958
|
+
warnings.push({
|
|
14959
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
|
|
14960
|
+
severity: "error",
|
|
14961
|
+
message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
|
|
14962
|
+
hint: "The request value must be a base64url-encoded JCS JSON object.",
|
|
14963
|
+
path: `${idx}.request`
|
|
14964
|
+
});
|
|
14965
|
+
continue;
|
|
14966
|
+
}
|
|
14967
|
+
if (!request["currency"]) {
|
|
14968
|
+
warnings.push({
|
|
14969
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
|
|
14970
|
+
severity: "error",
|
|
14971
|
+
message: `Payment challenge ${i} is missing currency in the request object.`,
|
|
14972
|
+
hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
|
|
14973
|
+
path: `${idx}.request.currency`
|
|
14974
|
+
});
|
|
14975
|
+
}
|
|
14976
|
+
const amount = request["amount"];
|
|
14977
|
+
if (amount === void 0 || amount === null) {
|
|
14978
|
+
warnings.push({
|
|
14979
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
14980
|
+
severity: "error",
|
|
14981
|
+
message: `Payment challenge ${i} is missing amount in the request object.`,
|
|
14982
|
+
hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
|
|
14983
|
+
path: `${idx}.request.amount`
|
|
14984
|
+
});
|
|
14985
|
+
} else if (typeof amount !== "string" && typeof amount !== "number") {
|
|
14986
|
+
warnings.push({
|
|
14987
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
14988
|
+
severity: "error",
|
|
14989
|
+
message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
|
|
14990
|
+
hint: "Set amount to a raw token-unit string.",
|
|
14991
|
+
path: `${idx}.request.amount`
|
|
14992
|
+
});
|
|
14993
|
+
}
|
|
14994
|
+
if (!request["recipient"]) {
|
|
14995
|
+
warnings.push({
|
|
14996
|
+
code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
|
|
14997
|
+
severity: "error",
|
|
14998
|
+
message: `Payment challenge ${i} is missing recipient in the request object.`,
|
|
14999
|
+
hint: "Set recipient to the wallet address that should receive payment.",
|
|
15000
|
+
path: `${idx}.request.recipient`
|
|
15001
|
+
});
|
|
15002
|
+
}
|
|
15003
|
+
}
|
|
15004
|
+
return warnings;
|
|
15005
|
+
}
|
|
15006
|
+
|
|
14700
15007
|
// src/audit/warnings/l3.ts
|
|
14701
15008
|
function getWarningsFor402Body(body) {
|
|
14702
15009
|
if (!isRecord(body)) {
|
|
@@ -14826,17 +15133,28 @@ function getWarningsForL3(l3) {
|
|
|
14826
15133
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
14827
15134
|
});
|
|
14828
15135
|
}
|
|
14829
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
15136
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
14830
15137
|
warnings.push({
|
|
14831
15138
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
14832
15139
|
severity: "info",
|
|
14833
15140
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
14834
|
-
hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
|
|
15141
|
+
hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
|
|
15142
|
+
});
|
|
15143
|
+
}
|
|
15144
|
+
if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
|
|
15145
|
+
warnings.push({
|
|
15146
|
+
code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
|
|
15147
|
+
severity: "warn",
|
|
15148
|
+
message: "Paid endpoint did not return payment options in the 402 response.",
|
|
15149
|
+
hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
|
|
14835
15150
|
});
|
|
14836
15151
|
}
|
|
14837
15152
|
if (l3.paymentRequiredBody !== void 0) {
|
|
14838
15153
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
14839
15154
|
}
|
|
15155
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15156
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15157
|
+
}
|
|
14840
15158
|
return warnings;
|
|
14841
15159
|
}
|
|
14842
15160
|
|
|
@@ -14867,7 +15185,7 @@ function getWarningsForL4(l4) {
|
|
|
14867
15185
|
}
|
|
14868
15186
|
|
|
14869
15187
|
// src/core/source/probe/index.ts
|
|
14870
|
-
import { ResultAsync as
|
|
15188
|
+
import { ResultAsync as ResultAsync6 } from "neverthrow";
|
|
14871
15189
|
|
|
14872
15190
|
// src/core/protocols/x402/index.ts
|
|
14873
15191
|
function parseVersion(payload) {
|
|
@@ -14963,8 +15281,8 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
|
|
|
14963
15281
|
...hasBody ? { body: JSON.stringify(inputBody) } : {},
|
|
14964
15282
|
signal
|
|
14965
15283
|
}).andThen((response) => {
|
|
14966
|
-
if (!isUsableStatus(response.status)) return
|
|
14967
|
-
return
|
|
15284
|
+
if (!isUsableStatus(response.status)) return ResultAsync6.fromSafePromise(Promise.resolve(null));
|
|
15285
|
+
return ResultAsync6.fromSafePromise(
|
|
14968
15286
|
(async () => {
|
|
14969
15287
|
let authHint = response.status === 402 ? "paid" : "unprotected";
|
|
14970
15288
|
let paymentRequiredBody;
|
|
@@ -14995,7 +15313,7 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
|
|
|
14995
15313
|
}
|
|
14996
15314
|
function getProbe(url2, headers, signal, inputBody) {
|
|
14997
15315
|
const path = normalizePath(new URL(url2).pathname || "/");
|
|
14998
|
-
return
|
|
15316
|
+
return ResultAsync6.fromSafePromise(
|
|
14999
15317
|
Promise.all(
|
|
15000
15318
|
[...HTTP_METHODS].map(
|
|
15001
15319
|
(method) => probeMethod(url2, method, path, headers, signal, inputBody).match(
|
|
@@ -15008,8 +15326,22 @@ function getProbe(url2, headers, signal, inputBody) {
|
|
|
15008
15326
|
}
|
|
15009
15327
|
|
|
15010
15328
|
// src/core/protocols/mpp/index.ts
|
|
15329
|
+
import { Result } from "neverthrow";
|
|
15330
|
+
function parseBase64Json(encoded) {
|
|
15331
|
+
return Result.fromThrowable(
|
|
15332
|
+
() => {
|
|
15333
|
+
const decoded = typeof Buffer !== "undefined" ? Buffer.from(encoded, "base64").toString("utf8") : atob(encoded);
|
|
15334
|
+
const parsed = JSON.parse(decoded);
|
|
15335
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15336
|
+
throw new Error("not an object");
|
|
15337
|
+
}
|
|
15338
|
+
return parsed;
|
|
15339
|
+
},
|
|
15340
|
+
(e) => e
|
|
15341
|
+
)();
|
|
15342
|
+
}
|
|
15011
15343
|
var TEMPO_DEFAULT_CHAIN_ID = 4217;
|
|
15012
|
-
function
|
|
15344
|
+
function parseAuthParams2(segment) {
|
|
15013
15345
|
const params = {};
|
|
15014
15346
|
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15015
15347
|
let match;
|
|
@@ -15023,21 +15355,16 @@ function extractPaymentOptions4(wwwAuthenticate) {
|
|
|
15023
15355
|
const options = [];
|
|
15024
15356
|
for (const segment of wwwAuthenticate.split(/,\s*(?=Payment\s)/i)) {
|
|
15025
15357
|
const stripped = segment.replace(/^Payment\s+/i, "").trim();
|
|
15026
|
-
const params =
|
|
15358
|
+
const params = parseAuthParams2(stripped);
|
|
15027
15359
|
const paymentMethod = params["method"];
|
|
15028
15360
|
const intent = params["intent"];
|
|
15029
15361
|
const realm = params["realm"];
|
|
15030
15362
|
const description = params["description"];
|
|
15031
15363
|
const requestStr = params["request"];
|
|
15032
15364
|
if (!paymentMethod || !intent || !realm || !requestStr) continue;
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
|
|
15037
|
-
request = parsed;
|
|
15038
|
-
} catch {
|
|
15039
|
-
continue;
|
|
15040
|
-
}
|
|
15365
|
+
const requestResult = parseBase64Json(requestStr);
|
|
15366
|
+
if (requestResult.isErr()) continue;
|
|
15367
|
+
const request = requestResult.value;
|
|
15041
15368
|
const asset = typeof request["currency"] === "string" ? request["currency"] : void 0;
|
|
15042
15369
|
const amountRaw = request["amount"];
|
|
15043
15370
|
const amount = typeof amountRaw === "string" ? amountRaw : typeof amountRaw === "number" ? String(amountRaw) : void 0;
|
|
@@ -15219,7 +15546,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
15219
15546
|
...inputSchema ? { inputSchema } : {},
|
|
15220
15547
|
...outputSchema ? { outputSchema } : {},
|
|
15221
15548
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
15222
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
15549
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
15550
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
15223
15551
|
};
|
|
15224
15552
|
}
|
|
15225
15553
|
async function attachProbePayload(url2, advisories) {
|
|
@@ -15410,12 +15738,14 @@ ${l4.guidance}`);
|
|
|
15410
15738
|
warnings.push(...l3WarningArrays.flat());
|
|
15411
15739
|
if (flags.json) {
|
|
15412
15740
|
const meta3 = { origin, specUrl: wellKnown.fetchedUrl };
|
|
15741
|
+
if (l2.title) meta3.title = l2.title;
|
|
15742
|
+
if (l2.description) meta3.description = l2.description;
|
|
15413
15743
|
if (l4 && flags.verbose) meta3.guidance = l4.guidance;
|
|
15414
15744
|
console.log(
|
|
15415
15745
|
JSON.stringify(
|
|
15416
15746
|
{
|
|
15417
15747
|
ok: true,
|
|
15418
|
-
selectedStage:
|
|
15748
|
+
selectedStage: l2.source,
|
|
15419
15749
|
resources: l2.routes.map(routeToResource),
|
|
15420
15750
|
warnings,
|
|
15421
15751
|
trace: [],
|
|
@@ -15427,12 +15757,15 @@ ${l4.guidance}`);
|
|
|
15427
15757
|
);
|
|
15428
15758
|
return 0;
|
|
15429
15759
|
}
|
|
15430
|
-
console.log(`Source:
|
|
15760
|
+
console.log(`Source: ${l2.source}`);
|
|
15431
15761
|
console.log(`Spec: ${wellKnown.fetchedUrl}`);
|
|
15762
|
+
if (l2.title) console.log(`API: ${l2.title}`);
|
|
15432
15763
|
console.log(`Routes: ${l2.routes.length}
|
|
15433
15764
|
`);
|
|
15434
15765
|
for (const route of l2.routes) {
|
|
15435
|
-
|
|
15766
|
+
const price = route.price ? ` ${route.price}` : "";
|
|
15767
|
+
const protocols = route.protocols?.length ? ` [${route.protocols.join(", ")}]` : "";
|
|
15768
|
+
console.log(` ${route.method.padEnd(7)} ${route.path} paid${price}${protocols}`);
|
|
15436
15769
|
}
|
|
15437
15770
|
} else {
|
|
15438
15771
|
if (flags.json) {
|