@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.d.ts
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 };
|
package/dist/index.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
|
}
|
|
@@ -14164,14 +14306,20 @@ async function discoverOriginSchema(options) {
|
|
|
14164
14306
|
const base = {
|
|
14165
14307
|
found: true,
|
|
14166
14308
|
origin,
|
|
14167
|
-
source:
|
|
14309
|
+
source: l2.source,
|
|
14310
|
+
...l2.title ? {
|
|
14311
|
+
info: {
|
|
14312
|
+
title: l2.title,
|
|
14313
|
+
...l2.description ? { description: l2.description } : {}
|
|
14314
|
+
}
|
|
14315
|
+
} : {},
|
|
14168
14316
|
endpoints: l2.routes
|
|
14169
14317
|
};
|
|
14170
14318
|
return withGuidance(base, l4, guidanceMode);
|
|
14171
14319
|
}
|
|
14172
14320
|
|
|
14173
14321
|
// src/core/source/probe/index.ts
|
|
14174
|
-
import { ResultAsync as
|
|
14322
|
+
import { ResultAsync as ResultAsync6 } from "neverthrow";
|
|
14175
14323
|
|
|
14176
14324
|
// src/core/protocols/x402/v1/schema.ts
|
|
14177
14325
|
function extractSchemas(accepts) {
|
|
@@ -14621,8 +14769,8 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
|
|
|
14621
14769
|
...hasBody ? { body: JSON.stringify(inputBody) } : {},
|
|
14622
14770
|
signal
|
|
14623
14771
|
}).andThen((response) => {
|
|
14624
|
-
if (!isUsableStatus(response.status)) return
|
|
14625
|
-
return
|
|
14772
|
+
if (!isUsableStatus(response.status)) return ResultAsync6.fromSafePromise(Promise.resolve(null));
|
|
14773
|
+
return ResultAsync6.fromSafePromise(
|
|
14626
14774
|
(async () => {
|
|
14627
14775
|
let authHint = response.status === 402 ? "paid" : "unprotected";
|
|
14628
14776
|
let paymentRequiredBody;
|
|
@@ -14653,7 +14801,7 @@ function probeMethod(url2, method, path, headers, signal, inputBody) {
|
|
|
14653
14801
|
}
|
|
14654
14802
|
function getProbe(url2, headers, signal, inputBody) {
|
|
14655
14803
|
const path = normalizePath(new URL(url2).pathname || "/");
|
|
14656
|
-
return
|
|
14804
|
+
return ResultAsync6.fromSafePromise(
|
|
14657
14805
|
Promise.all(
|
|
14658
14806
|
[...HTTP_METHODS].map(
|
|
14659
14807
|
(method) => probeMethod(url2, method, path, headers, signal, inputBody).match(
|
|
@@ -14666,6 +14814,20 @@ function getProbe(url2, headers, signal, inputBody) {
|
|
|
14666
14814
|
}
|
|
14667
14815
|
|
|
14668
14816
|
// src/core/protocols/mpp/index.ts
|
|
14817
|
+
import { Result } from "neverthrow";
|
|
14818
|
+
function parseBase64Json(encoded) {
|
|
14819
|
+
return Result.fromThrowable(
|
|
14820
|
+
() => {
|
|
14821
|
+
const decoded = typeof Buffer !== "undefined" ? Buffer.from(encoded, "base64").toString("utf8") : atob(encoded);
|
|
14822
|
+
const parsed = JSON.parse(decoded);
|
|
14823
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14824
|
+
throw new Error("not an object");
|
|
14825
|
+
}
|
|
14826
|
+
return parsed;
|
|
14827
|
+
},
|
|
14828
|
+
(e) => e
|
|
14829
|
+
)();
|
|
14830
|
+
}
|
|
14669
14831
|
var TEMPO_DEFAULT_CHAIN_ID = 4217;
|
|
14670
14832
|
function parseAuthParams(segment) {
|
|
14671
14833
|
const params = {};
|
|
@@ -14688,14 +14850,9 @@ function extractPaymentOptions4(wwwAuthenticate) {
|
|
|
14688
14850
|
const description = params["description"];
|
|
14689
14851
|
const requestStr = params["request"];
|
|
14690
14852
|
if (!paymentMethod || !intent || !realm || !requestStr) continue;
|
|
14691
|
-
|
|
14692
|
-
|
|
14693
|
-
|
|
14694
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
|
|
14695
|
-
request = parsed;
|
|
14696
|
-
} catch {
|
|
14697
|
-
continue;
|
|
14698
|
-
}
|
|
14853
|
+
const requestResult = parseBase64Json(requestStr);
|
|
14854
|
+
if (requestResult.isErr()) continue;
|
|
14855
|
+
const request = requestResult.value;
|
|
14699
14856
|
const asset = typeof request["currency"] === "string" ? request["currency"] : void 0;
|
|
14700
14857
|
const amountRaw = request["amount"];
|
|
14701
14858
|
const amount = typeof amountRaw === "string" ? amountRaw : typeof amountRaw === "number" ? String(amountRaw) : void 0;
|
|
@@ -14877,7 +15034,8 @@ function getL3ForProbe(probe, path, method) {
|
|
|
14877
15034
|
...inputSchema ? { inputSchema } : {},
|
|
14878
15035
|
...outputSchema ? { outputSchema } : {},
|
|
14879
15036
|
...paymentOptions.length ? { paymentOptions } : {},
|
|
14880
|
-
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {}
|
|
15037
|
+
...probeResult.paymentRequiredBody !== void 0 ? { paymentRequiredBody: probeResult.paymentRequiredBody } : {},
|
|
15038
|
+
...probeResult.wwwAuthenticate ? { wwwAuthenticate: probeResult.wwwAuthenticate } : {}
|
|
14881
15039
|
};
|
|
14882
15040
|
}
|
|
14883
15041
|
async function attachProbePayload(url2, advisories) {
|
|
@@ -15058,9 +15216,23 @@ var AUDIT_CODES = {
|
|
|
15058
15216
|
L3_INPUT_SCHEMA_MISSING: "L3_INPUT_SCHEMA_MISSING",
|
|
15059
15217
|
L3_AUTH_MODE_MISSING: "L3_AUTH_MODE_MISSING",
|
|
15060
15218
|
L3_PROTOCOLS_MISSING_ON_PAID: "L3_PROTOCOLS_MISSING_ON_PAID",
|
|
15219
|
+
L3_PAYMENT_OPTIONS_MISSING_ON_PAID: "L3_PAYMENT_OPTIONS_MISSING_ON_PAID",
|
|
15061
15220
|
// ─── L4 guidance checks ──────────────────────────────────────────────────────
|
|
15062
15221
|
L4_GUIDANCE_MISSING: "L4_GUIDANCE_MISSING",
|
|
15063
|
-
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG"
|
|
15222
|
+
L4_GUIDANCE_TOO_LONG: "L4_GUIDANCE_TOO_LONG",
|
|
15223
|
+
// ─── MPP WWW-Authenticate header checks ──────────────────────────────────────
|
|
15224
|
+
MPP_HEADER_MISSING: "MPP_HEADER_MISSING",
|
|
15225
|
+
MPP_NO_PAYMENT_CHALLENGES: "MPP_NO_PAYMENT_CHALLENGES",
|
|
15226
|
+
MPP_CHALLENGE_ID_MISSING: "MPP_CHALLENGE_ID_MISSING",
|
|
15227
|
+
MPP_CHALLENGE_METHOD_MISSING: "MPP_CHALLENGE_METHOD_MISSING",
|
|
15228
|
+
MPP_CHALLENGE_INTENT_MISSING: "MPP_CHALLENGE_INTENT_MISSING",
|
|
15229
|
+
MPP_CHALLENGE_REALM_MISSING: "MPP_CHALLENGE_REALM_MISSING",
|
|
15230
|
+
MPP_CHALLENGE_EXPIRES_MISSING: "MPP_CHALLENGE_EXPIRES_MISSING",
|
|
15231
|
+
MPP_CHALLENGE_REQUEST_MISSING: "MPP_CHALLENGE_REQUEST_MISSING",
|
|
15232
|
+
MPP_CHALLENGE_REQUEST_INVALID: "MPP_CHALLENGE_REQUEST_INVALID",
|
|
15233
|
+
MPP_CHALLENGE_ASSET_MISSING: "MPP_CHALLENGE_ASSET_MISSING",
|
|
15234
|
+
MPP_CHALLENGE_AMOUNT_MISSING: "MPP_CHALLENGE_AMOUNT_MISSING",
|
|
15235
|
+
MPP_CHALLENGE_RECIPIENT_MISSING: "MPP_CHALLENGE_RECIPIENT_MISSING"
|
|
15064
15236
|
};
|
|
15065
15237
|
|
|
15066
15238
|
// src/core/protocols/x402/v1/coinbase-schema.ts
|
|
@@ -15149,6 +15321,157 @@ function validateWithCoinbaseSchema2(body) {
|
|
|
15149
15321
|
});
|
|
15150
15322
|
}
|
|
15151
15323
|
|
|
15324
|
+
// src/audit/warnings/mpp.ts
|
|
15325
|
+
function parseAuthParams2(segment) {
|
|
15326
|
+
const params = {};
|
|
15327
|
+
const re = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
15328
|
+
let match;
|
|
15329
|
+
while ((match = re.exec(segment)) !== null) {
|
|
15330
|
+
params[match[1]] = match[2] ?? match[3] ?? "";
|
|
15331
|
+
}
|
|
15332
|
+
return params;
|
|
15333
|
+
}
|
|
15334
|
+
function getWarningsForMppHeader(wwwAuthenticate) {
|
|
15335
|
+
if (!wwwAuthenticate?.trim()) {
|
|
15336
|
+
return [
|
|
15337
|
+
{
|
|
15338
|
+
code: AUDIT_CODES.MPP_HEADER_MISSING,
|
|
15339
|
+
severity: "error",
|
|
15340
|
+
message: "WWW-Authenticate header is absent.",
|
|
15341
|
+
hint: "MPP endpoints must respond to unauthenticated requests with a 402 and a WWW-Authenticate: Payment ... header."
|
|
15342
|
+
}
|
|
15343
|
+
];
|
|
15344
|
+
}
|
|
15345
|
+
const segments = wwwAuthenticate.split(/,\s*(?=Payment\s)/i).filter((s) => /^Payment\s/i.test(s.trim()));
|
|
15346
|
+
if (segments.length === 0) {
|
|
15347
|
+
return [
|
|
15348
|
+
{
|
|
15349
|
+
code: AUDIT_CODES.MPP_NO_PAYMENT_CHALLENGES,
|
|
15350
|
+
severity: "error",
|
|
15351
|
+
message: "WWW-Authenticate header contains no Payment challenges.",
|
|
15352
|
+
hint: `Add at least one Payment challenge: WWW-Authenticate: Payment method="tempo" intent="charge" realm="..." request='...'`
|
|
15353
|
+
}
|
|
15354
|
+
];
|
|
15355
|
+
}
|
|
15356
|
+
const warnings = [];
|
|
15357
|
+
for (let i = 0; i < segments.length; i++) {
|
|
15358
|
+
const stripped = segments[i].replace(/^Payment\s+/i, "").trim();
|
|
15359
|
+
const params = parseAuthParams2(stripped);
|
|
15360
|
+
const idx = `WWW-Authenticate[${i}]`;
|
|
15361
|
+
if (!params["id"]) {
|
|
15362
|
+
warnings.push({
|
|
15363
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ID_MISSING,
|
|
15364
|
+
severity: "error",
|
|
15365
|
+
message: `Payment challenge ${i} is missing the id parameter.`,
|
|
15366
|
+
hint: "Set id to a unique challenge identifier so clients can correlate credentials to challenges.",
|
|
15367
|
+
path: `${idx}.id`
|
|
15368
|
+
});
|
|
15369
|
+
}
|
|
15370
|
+
if (!params["method"]) {
|
|
15371
|
+
warnings.push({
|
|
15372
|
+
code: AUDIT_CODES.MPP_CHALLENGE_METHOD_MISSING,
|
|
15373
|
+
severity: "error",
|
|
15374
|
+
message: `Payment challenge ${i} is missing the method parameter.`,
|
|
15375
|
+
hint: 'Set method="tempo" (or your payment method identifier) on the Payment challenge.',
|
|
15376
|
+
path: `${idx}.method`
|
|
15377
|
+
});
|
|
15378
|
+
}
|
|
15379
|
+
if (!params["intent"]) {
|
|
15380
|
+
warnings.push({
|
|
15381
|
+
code: AUDIT_CODES.MPP_CHALLENGE_INTENT_MISSING,
|
|
15382
|
+
severity: "error",
|
|
15383
|
+
message: `Payment challenge ${i} is missing the intent parameter.`,
|
|
15384
|
+
hint: 'Set intent="charge" on the Payment challenge.',
|
|
15385
|
+
path: `${idx}.intent`
|
|
15386
|
+
});
|
|
15387
|
+
}
|
|
15388
|
+
if (!params["realm"]) {
|
|
15389
|
+
warnings.push({
|
|
15390
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REALM_MISSING,
|
|
15391
|
+
severity: "error",
|
|
15392
|
+
message: `Payment challenge ${i} is missing the realm parameter.`,
|
|
15393
|
+
hint: "Set realm to a stable server identifier so clients can associate payment credentials.",
|
|
15394
|
+
path: `${idx}.realm`
|
|
15395
|
+
});
|
|
15396
|
+
}
|
|
15397
|
+
if (!params["expires"]) {
|
|
15398
|
+
warnings.push({
|
|
15399
|
+
code: AUDIT_CODES.MPP_CHALLENGE_EXPIRES_MISSING,
|
|
15400
|
+
severity: "error",
|
|
15401
|
+
message: `Payment challenge ${i} is missing the expires parameter.`,
|
|
15402
|
+
hint: "Set expires to an RFC 3339 timestamp so clients know when the challenge lapses.",
|
|
15403
|
+
path: `${idx}.expires`
|
|
15404
|
+
});
|
|
15405
|
+
}
|
|
15406
|
+
const requestStr = params["request"];
|
|
15407
|
+
if (!requestStr) {
|
|
15408
|
+
warnings.push({
|
|
15409
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_MISSING,
|
|
15410
|
+
severity: "error",
|
|
15411
|
+
message: `Payment challenge ${i} is missing the request field.`,
|
|
15412
|
+
hint: `Include a base64url-encoded JSON request field: request=base64url('{"currency":"...","amount":"...","recipient":"..."}')`,
|
|
15413
|
+
path: `${idx}.request`
|
|
15414
|
+
});
|
|
15415
|
+
continue;
|
|
15416
|
+
}
|
|
15417
|
+
let request;
|
|
15418
|
+
try {
|
|
15419
|
+
const decoded = Buffer.from(requestStr, "base64url").toString("utf-8");
|
|
15420
|
+
const parsed = JSON.parse(decoded);
|
|
15421
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15422
|
+
throw new Error("not an object");
|
|
15423
|
+
}
|
|
15424
|
+
request = parsed;
|
|
15425
|
+
} catch {
|
|
15426
|
+
warnings.push({
|
|
15427
|
+
code: AUDIT_CODES.MPP_CHALLENGE_REQUEST_INVALID,
|
|
15428
|
+
severity: "error",
|
|
15429
|
+
message: `Payment challenge ${i} request field is not valid base64url-encoded JSON.`,
|
|
15430
|
+
hint: "The request value must be a base64url-encoded JCS JSON object.",
|
|
15431
|
+
path: `${idx}.request`
|
|
15432
|
+
});
|
|
15433
|
+
continue;
|
|
15434
|
+
}
|
|
15435
|
+
if (!request["currency"]) {
|
|
15436
|
+
warnings.push({
|
|
15437
|
+
code: AUDIT_CODES.MPP_CHALLENGE_ASSET_MISSING,
|
|
15438
|
+
severity: "error",
|
|
15439
|
+
message: `Payment challenge ${i} is missing currency in the request object.`,
|
|
15440
|
+
hint: "Set currency to a TIP-20 token address (e.g. a USDC contract).",
|
|
15441
|
+
path: `${idx}.request.currency`
|
|
15442
|
+
});
|
|
15443
|
+
}
|
|
15444
|
+
const amount = request["amount"];
|
|
15445
|
+
if (amount === void 0 || amount === null) {
|
|
15446
|
+
warnings.push({
|
|
15447
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15448
|
+
severity: "error",
|
|
15449
|
+
message: `Payment challenge ${i} is missing amount in the request object.`,
|
|
15450
|
+
hint: 'Set amount to a raw token-unit string (e.g. "1000000" for 1 USDC with 6 decimals).',
|
|
15451
|
+
path: `${idx}.request.amount`
|
|
15452
|
+
});
|
|
15453
|
+
} else if (typeof amount !== "string" && typeof amount !== "number") {
|
|
15454
|
+
warnings.push({
|
|
15455
|
+
code: AUDIT_CODES.MPP_CHALLENGE_AMOUNT_MISSING,
|
|
15456
|
+
severity: "error",
|
|
15457
|
+
message: `Payment challenge ${i} has an invalid amount type (got ${typeof amount}, expected string or number).`,
|
|
15458
|
+
hint: "Set amount to a raw token-unit string.",
|
|
15459
|
+
path: `${idx}.request.amount`
|
|
15460
|
+
});
|
|
15461
|
+
}
|
|
15462
|
+
if (!request["recipient"]) {
|
|
15463
|
+
warnings.push({
|
|
15464
|
+
code: AUDIT_CODES.MPP_CHALLENGE_RECIPIENT_MISSING,
|
|
15465
|
+
severity: "error",
|
|
15466
|
+
message: `Payment challenge ${i} is missing recipient in the request object.`,
|
|
15467
|
+
hint: "Set recipient to the wallet address that should receive payment.",
|
|
15468
|
+
path: `${idx}.request.recipient`
|
|
15469
|
+
});
|
|
15470
|
+
}
|
|
15471
|
+
}
|
|
15472
|
+
return warnings;
|
|
15473
|
+
}
|
|
15474
|
+
|
|
15152
15475
|
// src/audit/warnings/l3.ts
|
|
15153
15476
|
function getWarningsFor402Body(body) {
|
|
15154
15477
|
if (!isRecord(body)) {
|
|
@@ -15278,17 +15601,28 @@ function getWarningsForL3(l3) {
|
|
|
15278
15601
|
hint: "Add a requestBody or parameters schema so agents can construct valid payloads."
|
|
15279
15602
|
});
|
|
15280
15603
|
}
|
|
15281
|
-
if (l3.authMode === "paid" && !l3.protocols?.length) {
|
|
15604
|
+
if (l3.authMode === "paid" && l3.source === "openapi" && !l3.protocols?.length) {
|
|
15282
15605
|
warnings.push({
|
|
15283
15606
|
code: AUDIT_CODES.L3_PROTOCOLS_MISSING_ON_PAID,
|
|
15284
15607
|
severity: "info",
|
|
15285
15608
|
message: "Paid endpoint does not declare supported payment protocols.",
|
|
15286
|
-
hint: "Add x-payment-info.protocols (e.g. ['x402']) to the operation."
|
|
15609
|
+
hint: "Add x-payment-info.protocols (e.g. ['x402', 'mpp']) to the operation."
|
|
15610
|
+
});
|
|
15611
|
+
}
|
|
15612
|
+
if (l3.authMode === "paid" && l3.source === "probe" && !l3.paymentOptions?.length) {
|
|
15613
|
+
warnings.push({
|
|
15614
|
+
code: AUDIT_CODES.L3_PAYMENT_OPTIONS_MISSING_ON_PAID,
|
|
15615
|
+
severity: "warn",
|
|
15616
|
+
message: "Paid endpoint did not return payment options in the 402 response.",
|
|
15617
|
+
hint: "Ensure the 402 response returns a valid payment challenge so clients know how to pay."
|
|
15287
15618
|
});
|
|
15288
15619
|
}
|
|
15289
15620
|
if (l3.paymentRequiredBody !== void 0) {
|
|
15290
15621
|
warnings.push(...getWarningsFor402Body(l3.paymentRequiredBody));
|
|
15291
15622
|
}
|
|
15623
|
+
if (l3.wwwAuthenticate !== void 0) {
|
|
15624
|
+
warnings.push(...getWarningsForMppHeader(l3.wwwAuthenticate));
|
|
15625
|
+
}
|
|
15292
15626
|
return warnings;
|
|
15293
15627
|
}
|
|
15294
15628
|
|
|
@@ -15506,14 +15840,17 @@ export {
|
|
|
15506
15840
|
getL3,
|
|
15507
15841
|
getL3ForOpenAPI,
|
|
15508
15842
|
getL3ForProbe,
|
|
15843
|
+
getMppWellKnown,
|
|
15509
15844
|
getOpenAPI,
|
|
15510
15845
|
getProbe,
|
|
15511
15846
|
getWarningsFor402Body,
|
|
15512
15847
|
getWarningsForL2,
|
|
15513
15848
|
getWarningsForL3,
|
|
15514
15849
|
getWarningsForL4,
|
|
15850
|
+
getWarningsForMppHeader,
|
|
15515
15851
|
getWarningsForOpenAPI,
|
|
15516
15852
|
getWarningsForWellKnown,
|
|
15517
15853
|
getWellKnown,
|
|
15854
|
+
getX402WellKnown,
|
|
15518
15855
|
validatePaymentRequiredDetailed
|
|
15519
15856
|
};
|
package/dist/schemas.cjs
CHANGED
|
@@ -13864,7 +13864,8 @@ var WellKnownParsedSchema = external_exports.object({
|
|
|
13864
13864
|
routes: external_exports.array(
|
|
13865
13865
|
external_exports.object({
|
|
13866
13866
|
path: external_exports.string(),
|
|
13867
|
-
method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"])
|
|
13867
|
+
method: external_exports.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"]),
|
|
13868
|
+
price: external_exports.string().optional()
|
|
13868
13869
|
})
|
|
13869
13870
|
),
|
|
13870
13871
|
instructions: external_exports.string().optional()
|
package/dist/schemas.d.cts
CHANGED
package/dist/schemas.d.ts
CHANGED