@devlab.group/agent-commerce 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -66
- package/dist/ap2.d.ts +164 -0
- package/dist/ap2.js +669 -0
- package/dist/ap2.js.map +1 -0
- package/dist/authorization-CPDcpJVa.d.ts +123 -0
- package/dist/chunk-7NKWFGBY.js +68 -0
- package/dist/chunk-7NKWFGBY.js.map +1 -0
- package/dist/{chunk-TBZA6Z5B.js → chunk-IUR3Q7DE.js} +3 -3
- package/dist/{chunk-TBZA6Z5B.js.map → chunk-IUR3Q7DE.js.map} +1 -1
- package/dist/{chunk-N3DNLXGC.js → chunk-ZDVJ7EVL.js} +149 -66
- package/dist/chunk-ZDVJ7EVL.js.map +1 -0
- package/dist/cli/index.js +419 -53
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +108 -6
- package/dist/index.js +454 -112
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +3 -2
- package/dist/mcp.js +10 -12
- package/dist/mcp.js.map +1 -1
- package/dist/{protocol-adapter-DTFGWGXq.d.ts → protocol-adapter-CaMZj6Wm.d.ts} +19 -2
- package/dist/{runtime-Plk5Peqi.d.ts → runtime-CKq3d_3K.d.ts} +22 -2
- package/dist/types-CJtwLu1c.d.ts +99 -0
- package/dist/x402.d.ts +1 -1
- package/dist/x402.js +3 -3
- package/package.json +26 -4
- package/dist/chunk-N3DNLXGC.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { ZodError, z } from 'zod';
|
|
3
|
-
import { existsSync, mkdirSync, accessSync, constants,
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, accessSync, constants, closeSync, openSync, chmodSync, promises } from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { resolve, dirname, basename as basename$1, extname, join } from 'path';
|
|
6
6
|
import { stringify, parse, Document, YAMLError } from 'yaml';
|
|
@@ -51,14 +51,6 @@ var init_resource = __esm({
|
|
|
51
51
|
}
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
// src/core/domain/wire.ts
|
|
55
|
-
var PAYMENT_INPUT_FIELD;
|
|
56
|
-
var init_wire = __esm({
|
|
57
|
-
"src/core/domain/wire.ts"() {
|
|
58
|
-
PAYMENT_INPUT_FIELD = "_payment";
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
54
|
// src/core/errors/codes.ts
|
|
63
55
|
var COMMERCE_ERROR_HTTP_STATUS, RETRYABLE_ERROR_CODES;
|
|
64
56
|
var init_codes = __esm({
|
|
@@ -72,6 +64,14 @@ var init_codes = __esm({
|
|
|
72
64
|
PAYMENT_REPLAYED: 409,
|
|
73
65
|
PAYMENT_PROVIDER_UNAVAILABLE: 503,
|
|
74
66
|
PAYMENT_SETTLEMENT_FAILED: 502,
|
|
67
|
+
// 403, not 402: the buyer's money is not the problem. A 402 tells a client
|
|
68
|
+
// "pay and retry", which cannot fix a missing or rejected mandate, and a
|
|
69
|
+
// client that auto-pays on 402 would be charged for a request that was
|
|
70
|
+
// never going to be delivered.
|
|
71
|
+
AUTHORIZATION_REQUIRED: 403,
|
|
72
|
+
AUTHORIZATION_INVALID: 403,
|
|
73
|
+
AUTHORIZATION_REPLAYED: 409,
|
|
74
|
+
AUTHORIZATION_PROVIDER_UNAVAILABLE: 503,
|
|
75
75
|
BACKEND_TIMEOUT: 504,
|
|
76
76
|
BACKEND_ERROR: 502,
|
|
77
77
|
PROTOCOL_UNSUPPORTED: 501,
|
|
@@ -81,6 +81,10 @@ var init_codes = __esm({
|
|
|
81
81
|
};
|
|
82
82
|
RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
83
83
|
"PAYMENT_PROVIDER_UNAVAILABLE",
|
|
84
|
+
// Our verifier or our replay store was unreachable, so no verdict on the
|
|
85
|
+
// mandate was ever reached. That is an outage on our side, and the same
|
|
86
|
+
// proof will verify once it clears.
|
|
87
|
+
"AUTHORIZATION_PROVIDER_UNAVAILABLE",
|
|
84
88
|
"BACKEND_TIMEOUT",
|
|
85
89
|
// Load shedding is transient by definition: the caller should back off and
|
|
86
90
|
// try again. Before this code existed the MCP adapter's queue-full path threw
|
|
@@ -147,6 +151,20 @@ var init_errors = __esm({
|
|
|
147
151
|
}
|
|
148
152
|
});
|
|
149
153
|
|
|
154
|
+
// src/core/domain/wire.ts
|
|
155
|
+
var PAYMENT_INPUT_FIELD, AUTHORIZATION_INPUT_FIELD, RESERVED_INPUT_FIELDS;
|
|
156
|
+
var init_wire = __esm({
|
|
157
|
+
"src/core/domain/wire.ts"() {
|
|
158
|
+
init_errors();
|
|
159
|
+
PAYMENT_INPUT_FIELD = "_payment";
|
|
160
|
+
AUTHORIZATION_INPUT_FIELD = "_authorization";
|
|
161
|
+
RESERVED_INPUT_FIELDS = [
|
|
162
|
+
PAYMENT_INPUT_FIELD,
|
|
163
|
+
AUTHORIZATION_INPUT_FIELD
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
150
168
|
// src/core/interfaces/logger.ts
|
|
151
169
|
var NOOP_LOGGER;
|
|
152
170
|
var init_logger = __esm({
|
|
@@ -204,6 +222,24 @@ var init_core = __esm({
|
|
|
204
222
|
}
|
|
205
223
|
});
|
|
206
224
|
|
|
225
|
+
// src/authorization/ap2/constants.ts
|
|
226
|
+
var AP2_SPEC_VERSION, AP2_MODES, AP2_SIGNING_ALGORITHM, AP2_KEY_TYPE, AP2_JWK_CURVE, AP2_JWK_COORDINATE_BYTES, AP2_JWK_MEMBERS, AP2_CHECKOUT_MANDATE_VCT, AP2_CHECKOUT_PROFILE, AP2_DEFAULT_CLOCK_SKEW_SECONDS, AP2_MAX_CLOCK_SKEW_SECONDS;
|
|
227
|
+
var init_constants = __esm({
|
|
228
|
+
"src/authorization/ap2/constants.ts"() {
|
|
229
|
+
AP2_SPEC_VERSION = "0.2.0";
|
|
230
|
+
AP2_MODES = ["direct"];
|
|
231
|
+
AP2_SIGNING_ALGORITHM = "ES256";
|
|
232
|
+
AP2_KEY_TYPE = "EC";
|
|
233
|
+
AP2_JWK_CURVE = "P-256";
|
|
234
|
+
AP2_JWK_COORDINATE_BYTES = 32;
|
|
235
|
+
AP2_JWK_MEMBERS = ["kty", "crv", "x", "y", "kid", "alg", "use"];
|
|
236
|
+
AP2_CHECKOUT_MANDATE_VCT = "mandate.checkout.1";
|
|
237
|
+
AP2_CHECKOUT_PROFILE = "agent-commerce/ap2/checkout/v1";
|
|
238
|
+
AP2_DEFAULT_CLOCK_SKEW_SECONDS = 60;
|
|
239
|
+
AP2_MAX_CLOCK_SKEW_SECONDS = 300;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
207
243
|
// src/core/execution/backend-http.ts
|
|
208
244
|
function extractPathParameterNames(url) {
|
|
209
245
|
return [...url.matchAll(PATH_PARAM_PATTERN)].map((match) => match[1]);
|
|
@@ -248,14 +284,13 @@ var init_validation = __esm({
|
|
|
248
284
|
"src/core/execution/validation.ts"() {
|
|
249
285
|
}
|
|
250
286
|
});
|
|
251
|
-
|
|
252
|
-
// src/core/execution/pipeline.ts
|
|
253
287
|
var init_pipeline = __esm({
|
|
254
288
|
"src/core/execution/pipeline.ts"() {
|
|
255
289
|
init_wire();
|
|
256
290
|
init_errors();
|
|
257
291
|
init_backend_http();
|
|
258
292
|
init_validation();
|
|
293
|
+
/* @__PURE__ */ new Set([...RESERVED_INPUT_FIELDS, "__proto__"]);
|
|
259
294
|
}
|
|
260
295
|
});
|
|
261
296
|
|
|
@@ -337,7 +372,7 @@ var init_networks = __esm({
|
|
|
337
372
|
|
|
338
373
|
// src/protocols/acp/constants.ts
|
|
339
374
|
var ACP_SPEC_VERSION, ACP_API_VERSION, ACP_WELL_KNOWN_PATH, ACP_CHECKOUT_OPERATIONS, ACP_OPERATION_INPUT_KEYS;
|
|
340
|
-
var
|
|
375
|
+
var init_constants2 = __esm({
|
|
341
376
|
"src/protocols/acp/constants.ts"() {
|
|
342
377
|
ACP_SPEC_VERSION = "2026-04-17";
|
|
343
378
|
ACP_API_VERSION = "2026-04-17";
|
|
@@ -767,8 +802,12 @@ function normalise(raw) {
|
|
|
767
802
|
...x402.allowUnauthenticatedFacilitator !== void 0 ? { allowUnauthenticatedFacilitator: x402.allowUnauthenticatedFacilitator } : {}
|
|
768
803
|
});
|
|
769
804
|
}
|
|
805
|
+
const ap2 = normaliseAp2(raw.authorization?.ap2);
|
|
806
|
+
if (ap2?.enabled) {
|
|
807
|
+
validateReplayStoreIsolated(ap2.replay.path, raw.storage.receipts.path, protocols.acp);
|
|
808
|
+
}
|
|
770
809
|
const resources = Object.entries(raw.resources).map(
|
|
771
|
-
([id, entry]) => normaliseResource(id, entry, protocols, x402)
|
|
810
|
+
([id, entry]) => normaliseResource(id, entry, protocols, x402, ap2)
|
|
772
811
|
);
|
|
773
812
|
if (protocols.acp.enabled) validateAcpCheckoutMapping(protocols.acp, resources);
|
|
774
813
|
return {
|
|
@@ -791,7 +830,8 @@ function normalise(raw) {
|
|
|
791
830
|
resources,
|
|
792
831
|
payments: {
|
|
793
832
|
...x402 !== void 0 ? { x402 } : {}
|
|
794
|
-
}
|
|
833
|
+
},
|
|
834
|
+
...ap2 !== void 0 ? { authorization: { ap2 } } : {}
|
|
795
835
|
};
|
|
796
836
|
}
|
|
797
837
|
function validateMountPaths(protocols) {
|
|
@@ -814,6 +854,194 @@ function validateMountPaths(protocols) {
|
|
|
814
854
|
}
|
|
815
855
|
}
|
|
816
856
|
}
|
|
857
|
+
function ap2Invalid(path2, message, extra = {}) {
|
|
858
|
+
return new CommerceError("CONFIG_INVALID", message, { details: { path: path2, ...extra } });
|
|
859
|
+
}
|
|
860
|
+
function normaliseAp2(raw) {
|
|
861
|
+
if (raw === void 0) return void 0;
|
|
862
|
+
if (!toBoolean(raw.enabled, "authorization.ap2.enabled")) return { enabled: false };
|
|
863
|
+
const specVersion = raw.specVersion ?? AP2_SPEC_VERSION;
|
|
864
|
+
if (specVersion !== AP2_SPEC_VERSION) {
|
|
865
|
+
throw ap2Invalid(
|
|
866
|
+
"authorization.ap2.specVersion",
|
|
867
|
+
`authorization.ap2.specVersion "${specVersion}" is not supported - this gateway verifies against the tagged AP2 release ${AP2_SPEC_VERSION} only`
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
const mode = raw.mode ?? AP2_MODES[0];
|
|
871
|
+
if (!AP2_MODES.includes(mode)) {
|
|
872
|
+
throw ap2Invalid(
|
|
873
|
+
"authorization.ap2.mode",
|
|
874
|
+
`authorization.ap2.mode "${mode}" is not supported. Supported: ${AP2_MODES.join(", ")}. Autonomous mode needs open mandates, agent key binding and constraint evaluation, none of which this release implements.`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
if (raw.replay === void 0) {
|
|
878
|
+
throw ap2Invalid(
|
|
879
|
+
"authorization.ap2.replay",
|
|
880
|
+
"authorization.ap2.replay is required when AP2 is enabled - it names the SQLite file recording which mandates have been spent, and without it a verified mandate could authorise a second settlement"
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
const mandateIssuers = normaliseIssuers(
|
|
884
|
+
raw.trust?.mandateIssuers,
|
|
885
|
+
"authorization.ap2.trust.mandateIssuers"
|
|
886
|
+
);
|
|
887
|
+
const checkoutIssuers = normaliseIssuers(
|
|
888
|
+
raw.trust?.checkoutIssuers,
|
|
889
|
+
"authorization.ap2.trust.checkoutIssuers"
|
|
890
|
+
);
|
|
891
|
+
return {
|
|
892
|
+
enabled: true,
|
|
893
|
+
specVersion: AP2_SPEC_VERSION,
|
|
894
|
+
mode,
|
|
895
|
+
trust: { mandateIssuers, checkoutIssuers },
|
|
896
|
+
clockSkewSeconds: toNumber(
|
|
897
|
+
raw.clockSkewSeconds ?? AP2_DEFAULT_CLOCK_SKEW_SECONDS,
|
|
898
|
+
"authorization.ap2.clockSkewSeconds",
|
|
899
|
+
{ min: 0, max: AP2_MAX_CLOCK_SKEW_SECONDS }
|
|
900
|
+
),
|
|
901
|
+
replay: { path: raw.replay.path }
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function normaliseIssuers(raw, path2) {
|
|
905
|
+
if (raw === void 0 || raw.length === 0) {
|
|
906
|
+
throw ap2Invalid(
|
|
907
|
+
path2,
|
|
908
|
+
`${path2} is required when AP2 is enabled and must name at least one issuer - trust is operator-configured and static, so an empty list means no mandate can ever verify`
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
const seen = /* @__PURE__ */ new Set();
|
|
912
|
+
return raw.map((entry, index) => {
|
|
913
|
+
const entryPath = `${path2}.${index}`;
|
|
914
|
+
if (seen.has(entry.issuer)) {
|
|
915
|
+
throw ap2Invalid(
|
|
916
|
+
`${entryPath}.issuer`,
|
|
917
|
+
`${entryPath}.issuer "${entry.issuer}" is listed twice - key lookup resolves by issuer, so a second entry for the same one is silently unreachable. Merge the keys into one entry instead (which is also how a rotation overlaps an old and a new key).`,
|
|
918
|
+
{ issuer: entry.issuer }
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
seen.add(entry.issuer);
|
|
922
|
+
const kids = /* @__PURE__ */ new Set();
|
|
923
|
+
const keys = entry.keys.map((key, keyIndex) => {
|
|
924
|
+
const keyPath = `${entryPath}.keys.${keyIndex}`;
|
|
925
|
+
if (kids.has(key.kid)) {
|
|
926
|
+
throw ap2Invalid(
|
|
927
|
+
`${keyPath}.kid`,
|
|
928
|
+
`${keyPath}.kid "${key.kid}" is listed twice for issuer "${entry.issuer}" - a kid selects exactly one key, so which of the two verifies a signature would be undefined`,
|
|
929
|
+
{ issuer: entry.issuer, kid: key.kid }
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
kids.add(key.kid);
|
|
933
|
+
return { kid: key.kid, jwk: validateJwk(key.jwk, key.kid, keyPath) };
|
|
934
|
+
});
|
|
935
|
+
return { issuer: entry.issuer, audience: entry.audience, keys };
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
function validateJwk(jwk, kid, path2) {
|
|
939
|
+
const fail = (detail) => {
|
|
940
|
+
throw ap2Invalid(`${path2}.jwk`, `${path2}.jwk (kid "${kid}") ${detail}`, { kid });
|
|
941
|
+
};
|
|
942
|
+
for (const member of Object.keys(jwk)) {
|
|
943
|
+
if (AP2_JWK_MEMBERS.includes(member)) continue;
|
|
944
|
+
if (member === "d" || member === "k") {
|
|
945
|
+
fail(
|
|
946
|
+
`carries private key material ("${member}"). The gateway verifies signatures and never produces them; publish only the public half. Treat the pasted key as compromised and rotate it.`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
fail(
|
|
950
|
+
`has unsupported member "${member}". Allowed: ${AP2_JWK_MEMBERS.join(", ")}. Members naming a URL are refused on purpose - keys are configured inline and never fetched.`
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
const value = (member) => {
|
|
954
|
+
const raw = jwk[member];
|
|
955
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
956
|
+
fail(`must give "${member}" as a non-empty string`);
|
|
957
|
+
}
|
|
958
|
+
return raw;
|
|
959
|
+
};
|
|
960
|
+
if (value("kty") !== AP2_KEY_TYPE) {
|
|
961
|
+
fail(
|
|
962
|
+
`must have kty "${AP2_KEY_TYPE}" (got "${value("kty")}") - ${AP2_SIGNING_ALGORITHM} is the only accepted algorithm`
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
if (value("crv") !== AP2_JWK_CURVE) {
|
|
966
|
+
fail(
|
|
967
|
+
`must be on curve "${AP2_JWK_CURVE}" (got "${value("crv")}") - ${AP2_SIGNING_ALGORITHM} is the only accepted algorithm`
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
for (const coordinate of ["x", "y"]) {
|
|
971
|
+
const encoded = value(coordinate);
|
|
972
|
+
if (!/^[A-Za-z0-9_-]+$/.test(encoded)) {
|
|
973
|
+
fail(`coordinate "${coordinate}" is not base64url (no padding, no "+" or "/")`);
|
|
974
|
+
}
|
|
975
|
+
if (Buffer.from(encoded, "base64url").length !== AP2_JWK_COORDINATE_BYTES) {
|
|
976
|
+
fail(
|
|
977
|
+
`coordinate "${coordinate}" decodes to ${Buffer.from(encoded, "base64url").length} bytes; a ${AP2_JWK_CURVE} coordinate is ${AP2_JWK_COORDINATE_BYTES}`
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (jwk["alg"] !== void 0 && value("alg") !== AP2_SIGNING_ALGORITHM) {
|
|
982
|
+
fail(`declares alg "${value("alg")}"; only ${AP2_SIGNING_ALGORITHM} is accepted`);
|
|
983
|
+
}
|
|
984
|
+
if (jwk["use"] !== void 0 && value("use") !== "sig") {
|
|
985
|
+
fail(`declares use "${value("use")}"; a verification key must be "sig"`);
|
|
986
|
+
}
|
|
987
|
+
if (jwk["kid"] !== void 0 && value("kid") !== kid) {
|
|
988
|
+
fail(`declares kid "${value("kid")}", which disagrees with the configured kid "${kid}"`);
|
|
989
|
+
}
|
|
990
|
+
const normalised = {};
|
|
991
|
+
for (const member of AP2_JWK_MEMBERS) {
|
|
992
|
+
if (jwk[member] !== void 0) normalised[member] = value(member);
|
|
993
|
+
}
|
|
994
|
+
return normalised;
|
|
995
|
+
}
|
|
996
|
+
function validateReplayStoreIsolated(replayPath, receiptsPath, acp) {
|
|
997
|
+
if (replayPath === ":memory:") return;
|
|
998
|
+
const others = [["storage.receipts.path", receiptsPath]];
|
|
999
|
+
if (acp.enabled) others.push(["protocols.acp.idempotency.path", acp.idempotency.path]);
|
|
1000
|
+
for (const [otherPath, other] of others) {
|
|
1001
|
+
if (replayPath !== other) continue;
|
|
1002
|
+
throw ap2Invalid(
|
|
1003
|
+
"authorization.ap2.replay.path",
|
|
1004
|
+
`authorization.ap2.replay.path is the same file as ${otherPath} ("${replayPath}") - the AP2 replay store keeps its own schema and must not share a database with another store`
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function normaliseResourceAuthorization(id, entry, pricing, ap2) {
|
|
1009
|
+
const required = entry.authorization?.required;
|
|
1010
|
+
if (required === void 0) return void 0;
|
|
1011
|
+
const path2 = `resources.${id}.authorization.required`;
|
|
1012
|
+
const methods = [];
|
|
1013
|
+
for (const method of required) {
|
|
1014
|
+
if (!SUPPORTED_AUTHORIZATION_METHODS.has(method)) {
|
|
1015
|
+
throw ap2Invalid(
|
|
1016
|
+
path2,
|
|
1017
|
+
`Resource "${id}" requires unsupported authorization method "${method}". Supported: ${[...SUPPORTED_AUTHORIZATION_METHODS].join(", ")}.`,
|
|
1018
|
+
{ resourceId: id, method }
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
if (methods.includes(method)) {
|
|
1022
|
+
throw ap2Invalid(path2, `Resource "${id}" lists authorization method "${method}" twice`, {
|
|
1023
|
+
resourceId: id,
|
|
1024
|
+
method
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
if (method === "ap2" && (ap2 === void 0 || !ap2.enabled)) {
|
|
1028
|
+
throw ap2Invalid(
|
|
1029
|
+
path2,
|
|
1030
|
+
`Resource "${id}" requires authorization method "ap2", which is not configured or not enabled under authorization.ap2`,
|
|
1031
|
+
{ resourceId: id, method }
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
methods.push(method);
|
|
1035
|
+
}
|
|
1036
|
+
if (pricing.type !== "fixed") {
|
|
1037
|
+
throw ap2Invalid(
|
|
1038
|
+
path2,
|
|
1039
|
+
`Resource "${id}" requires authorization but its pricing is "${pricing.type}" - authorization proves a purchase was approved and never replaces payment, so it applies to fixed-price paid resources only in this release`,
|
|
1040
|
+
{ resourceId: id }
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
return { required: methods };
|
|
1044
|
+
}
|
|
817
1045
|
function acpInvalid(path2, message, extra = {}) {
|
|
818
1046
|
return new CommerceError("CONFIG_INVALID", message, { details: { path: path2, ...extra } });
|
|
819
1047
|
}
|
|
@@ -953,20 +1181,23 @@ function validateAcpOperationInput(path2, operation, resource) {
|
|
|
953
1181
|
}
|
|
954
1182
|
}
|
|
955
1183
|
}
|
|
956
|
-
function normaliseResource(id, entry, protocols, x402) {
|
|
1184
|
+
function normaliseResource(id, entry, protocols, x402, ap2) {
|
|
957
1185
|
if (entry.input !== void 0) validateResourceSchemaKeywords(id, "input", entry.input);
|
|
958
1186
|
const inputProperties = entry.input?.["properties"];
|
|
959
|
-
if (inputProperties && typeof inputProperties === "object"
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1187
|
+
if (inputProperties && typeof inputProperties === "object") {
|
|
1188
|
+
for (const reserved of RESERVED_INPUT_FIELDS) {
|
|
1189
|
+
if (!(reserved in inputProperties)) continue;
|
|
1190
|
+
throw new CommerceError(
|
|
1191
|
+
"CONFIG_INVALID",
|
|
1192
|
+
`Resource "${id}" declares an input property "${reserved}", which is reserved by the gateway`,
|
|
1193
|
+
{
|
|
1194
|
+
details: {
|
|
1195
|
+
path: `resources.${id}.input.properties.${reserved}`,
|
|
1196
|
+
resourceId: id
|
|
1197
|
+
}
|
|
967
1198
|
}
|
|
968
|
-
|
|
969
|
-
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
970
1201
|
}
|
|
971
1202
|
for (const protocol of entry.expose) {
|
|
972
1203
|
if (!SUPPORTED_PROTOCOLS.has(protocol)) {
|
|
@@ -1055,6 +1286,7 @@ function normaliseResource(id, entry, protocols, x402) {
|
|
|
1055
1286
|
}
|
|
1056
1287
|
}
|
|
1057
1288
|
const pricing = entry.pricing.type === "free" ? { type: "free" } : { type: "fixed", amount: entry.pricing.amount, currency: entry.pricing.currency };
|
|
1289
|
+
const authorization = normaliseResourceAuthorization(id, entry, pricing, ap2);
|
|
1058
1290
|
return {
|
|
1059
1291
|
id,
|
|
1060
1292
|
name: entry.name,
|
|
@@ -1075,7 +1307,8 @@ function normaliseResource(id, entry, protocols, x402) {
|
|
|
1075
1307
|
},
|
|
1076
1308
|
pricing,
|
|
1077
1309
|
exposedVia: entry.expose,
|
|
1078
|
-
paymentMethods
|
|
1310
|
+
paymentMethods,
|
|
1311
|
+
...authorization !== void 0 ? { authorization } : {}
|
|
1079
1312
|
};
|
|
1080
1313
|
}
|
|
1081
1314
|
function defaultClosedObjectSchema(schema) {
|
|
@@ -1225,11 +1458,8 @@ function validateInputBindings(id, backend, input) {
|
|
|
1225
1458
|
Array.isArray(input?.["required"]) ? input["required"].filter((value) => typeof value === "string") : []
|
|
1226
1459
|
);
|
|
1227
1460
|
for (const [location, property] of entries) {
|
|
1228
|
-
if (property
|
|
1229
|
-
fail(
|
|
1230
|
-
`binds "${location}" to "${PAYMENT_INPUT_FIELD}", which is reserved for payment proofs`,
|
|
1231
|
-
{ location }
|
|
1232
|
-
);
|
|
1461
|
+
if (RESERVED_INPUT_FIELDS.includes(property)) {
|
|
1462
|
+
fail(`binds "${location}" to "${property}", which is reserved by the gateway`, { location });
|
|
1233
1463
|
}
|
|
1234
1464
|
const other = seen.get(property);
|
|
1235
1465
|
if (other !== void 0) {
|
|
@@ -1358,13 +1588,14 @@ function validateAddress(path2, value) {
|
|
|
1358
1588
|
);
|
|
1359
1589
|
}
|
|
1360
1590
|
}
|
|
1361
|
-
var SUPPORTED_CONFIG_VERSION, ADDRESS_PATTERN, ZERO_ADDRESS_PATTERN, MCP_TOOL_NAME_PATTERN, NumberOrString, BooleanOrString, MerchantSchema, ServerSchema, StorageSchema, RESERVED_GATEWAY_PATHS, MountPathSchema, AcpAuthSchema, AcpIdempotencySchema, AcpCheckoutSchema, AcpDiscoverySchema, ProtocolsSchema, DEFAULT_A2A_MOUNT_PATH, DEFAULT_ACP_MOUNT_PATH, ACP_RETENTION_HOURS, BackendMethodSchema, BackendInputBindingsSchema, BackendHandlerSchema, PricingSchema, JsonSchemaValueSchema, ResourceEntrySchema, ResourcesMapSchema, FacilitatorAuthSchema, FacilitatorSchema, X402Schema, PaymentsSchema, RawConfigSchema, SUPPORTED_PROTOCOLS, SUPPORTED_PAYMENT_METHODS, EMPTY_CLOSED_OBJECT_SCHEMA, UNSUPPORTED_SCHEMA_KEYWORDS, PRICING_AMOUNT_PATTERN, ZERO_AMOUNT_PATTERN;
|
|
1591
|
+
var SUPPORTED_CONFIG_VERSION, ADDRESS_PATTERN, ZERO_ADDRESS_PATTERN, MCP_TOOL_NAME_PATTERN, NumberOrString, BooleanOrString, MerchantSchema, ServerSchema, StorageSchema, RESERVED_GATEWAY_PATHS, MountPathSchema, AcpAuthSchema, AcpIdempotencySchema, AcpCheckoutSchema, AcpDiscoverySchema, ProtocolsSchema, DEFAULT_A2A_MOUNT_PATH, DEFAULT_ACP_MOUNT_PATH, ACP_RETENTION_HOURS, BackendMethodSchema, BackendInputBindingsSchema, BackendHandlerSchema, PricingSchema, JsonSchemaValueSchema, ResourceEntrySchema, ResourcesMapSchema, FacilitatorAuthSchema, FacilitatorSchema, X402Schema, PaymentsSchema, Ap2KeySchema, Ap2IssuerSchema, Ap2Schema, AuthorizationSchema, RawConfigSchema, SUPPORTED_PROTOCOLS, SUPPORTED_PAYMENT_METHODS, SUPPORTED_AUTHORIZATION_METHODS, EMPTY_CLOSED_OBJECT_SCHEMA, UNSUPPORTED_SCHEMA_KEYWORDS, PRICING_AMOUNT_PATTERN, ZERO_AMOUNT_PATTERN;
|
|
1362
1592
|
var init_schema = __esm({
|
|
1363
1593
|
"src/config/schema.ts"() {
|
|
1594
|
+
init_constants();
|
|
1364
1595
|
init_execution();
|
|
1365
1596
|
init_core();
|
|
1366
1597
|
init_guardrails();
|
|
1367
|
-
|
|
1598
|
+
init_constants2();
|
|
1368
1599
|
init_env();
|
|
1369
1600
|
SUPPORTED_CONFIG_VERSION = 1;
|
|
1370
1601
|
ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
|
|
@@ -1504,7 +1735,8 @@ var init_schema = __esm({
|
|
|
1504
1735
|
backend: BackendHandlerSchema,
|
|
1505
1736
|
pricing: PricingSchema,
|
|
1506
1737
|
expose: z.array(z.string().min(1)).min(1),
|
|
1507
|
-
payments: z.array(z.string().min(1)).optional()
|
|
1738
|
+
payments: z.array(z.string().min(1)).optional(),
|
|
1739
|
+
authorization: z.object({ required: z.array(z.string().min(1)).min(1) }).strict().optional()
|
|
1508
1740
|
}).strict();
|
|
1509
1741
|
ResourcesMapSchema = z.record(z.string().min(1), ResourceEntrySchema);
|
|
1510
1742
|
FacilitatorAuthSchema = z.discriminatedUnion("type", [
|
|
@@ -1552,6 +1784,32 @@ var init_schema = __esm({
|
|
|
1552
1784
|
PaymentsSchema = z.object({
|
|
1553
1785
|
x402: X402Schema.optional()
|
|
1554
1786
|
}).strict();
|
|
1787
|
+
Ap2KeySchema = z.object({
|
|
1788
|
+
kid: z.string().min(1),
|
|
1789
|
+
jwk: z.record(z.string(), z.unknown())
|
|
1790
|
+
}).strict();
|
|
1791
|
+
Ap2IssuerSchema = z.object({
|
|
1792
|
+
issuer: z.string().min(1),
|
|
1793
|
+
/**
|
|
1794
|
+
* Required, not defaulted. Without it, a mandate minted for another
|
|
1795
|
+
* merchant would verify here, and there is no value worth guessing for
|
|
1796
|
+
* something that decides that.
|
|
1797
|
+
*/
|
|
1798
|
+
audience: z.string().min(1),
|
|
1799
|
+
keys: z.array(Ap2KeySchema).min(1)
|
|
1800
|
+
}).strict();
|
|
1801
|
+
Ap2Schema = z.object({
|
|
1802
|
+
enabled: BooleanOrString,
|
|
1803
|
+
specVersion: z.string().min(1).optional(),
|
|
1804
|
+
mode: z.string().min(1).optional(),
|
|
1805
|
+
trust: z.object({
|
|
1806
|
+
mandateIssuers: z.array(Ap2IssuerSchema).optional(),
|
|
1807
|
+
checkoutIssuers: z.array(Ap2IssuerSchema).optional()
|
|
1808
|
+
}).strict().optional(),
|
|
1809
|
+
clockSkewSeconds: NumberOrString.optional(),
|
|
1810
|
+
replay: z.object({ path: z.string().min(1) }).strict().optional()
|
|
1811
|
+
}).strict();
|
|
1812
|
+
AuthorizationSchema = z.object({ ap2: Ap2Schema.optional() }).strict();
|
|
1555
1813
|
RawConfigSchema = z.object({
|
|
1556
1814
|
version: z.literal(SUPPORTED_CONFIG_VERSION),
|
|
1557
1815
|
merchant: MerchantSchema,
|
|
@@ -1559,10 +1817,12 @@ var init_schema = __esm({
|
|
|
1559
1817
|
storage: StorageSchema,
|
|
1560
1818
|
protocols: ProtocolsSchema,
|
|
1561
1819
|
resources: ResourcesMapSchema,
|
|
1562
|
-
payments: PaymentsSchema
|
|
1820
|
+
payments: PaymentsSchema,
|
|
1821
|
+
authorization: AuthorizationSchema.optional()
|
|
1563
1822
|
}).strict();
|
|
1564
1823
|
SUPPORTED_PROTOCOLS = new Set(PROTOCOL_NAMES);
|
|
1565
1824
|
SUPPORTED_PAYMENT_METHODS = /* @__PURE__ */ new Set(["x402"]);
|
|
1825
|
+
SUPPORTED_AUTHORIZATION_METHODS = /* @__PURE__ */ new Set(["ap2"]);
|
|
1566
1826
|
EMPTY_CLOSED_OBJECT_SCHEMA = {
|
|
1567
1827
|
type: "object",
|
|
1568
1828
|
properties: {},
|
|
@@ -1746,6 +2006,33 @@ async function execCommand(command, args) {
|
|
|
1746
2006
|
});
|
|
1747
2007
|
}
|
|
1748
2008
|
|
|
2009
|
+
// src/cli/commands/doctor.ts
|
|
2010
|
+
init_constants();
|
|
2011
|
+
|
|
2012
|
+
// src/authorization/ap2/descriptor.ts
|
|
2013
|
+
init_constants();
|
|
2014
|
+
var AP2_UNSUPPORTED = [
|
|
2015
|
+
// Mandate kinds. Open mandates carry spending constraints nothing here
|
|
2016
|
+
// evaluates, so accepting one would tell a buyer their limits were checked.
|
|
2017
|
+
"autonomous mode",
|
|
2018
|
+
"open checkout mandates (mandate.checkout.open.1)",
|
|
2019
|
+
"intent mandates",
|
|
2020
|
+
"cart mandates",
|
|
2021
|
+
"spending constraint evaluation",
|
|
2022
|
+
"cnf-bound agent keys",
|
|
2023
|
+
// Key handling. Every key is written into config by an operator
|
|
2024
|
+
"JWKS and any key discovery by URL (jku, x5u)",
|
|
2025
|
+
"issuer metadata fetching",
|
|
2026
|
+
"key rotation without a config change",
|
|
2027
|
+
// Algorithms
|
|
2028
|
+
"signature algorithms other than ES256",
|
|
2029
|
+
"digest algorithms other than sha-256",
|
|
2030
|
+
// Roles this gateway does not play
|
|
2031
|
+
"mandate issuance",
|
|
2032
|
+
"merchant checkout JWT issuance",
|
|
2033
|
+
"AP2 over the ACP checkout adapter"
|
|
2034
|
+
];
|
|
2035
|
+
|
|
1749
2036
|
// src/cli/commands/doctor.ts
|
|
1750
2037
|
init_execution();
|
|
1751
2038
|
init_core();
|
|
@@ -1789,10 +2076,10 @@ var A2A_UNSUPPORTED = [
|
|
|
1789
2076
|
];
|
|
1790
2077
|
|
|
1791
2078
|
// src/cli/commands/doctor.ts
|
|
1792
|
-
|
|
2079
|
+
init_constants2();
|
|
1793
2080
|
|
|
1794
2081
|
// src/protocols/acp/descriptor.ts
|
|
1795
|
-
|
|
2082
|
+
init_constants2();
|
|
1796
2083
|
[
|
|
1797
2084
|
"rest transport",
|
|
1798
2085
|
"well-known discovery",
|
|
@@ -1826,7 +2113,7 @@ var ACP_UNSUPPORTED = [
|
|
|
1826
2113
|
|
|
1827
2114
|
// src/storage/receipts/store.ts
|
|
1828
2115
|
init_core();
|
|
1829
|
-
var PACKAGE_VERSION = "1.
|
|
2116
|
+
var PACKAGE_VERSION = "1.4.0" ;
|
|
1830
2117
|
|
|
1831
2118
|
// src/storage/sqlite.ts
|
|
1832
2119
|
init_core();
|
|
@@ -1911,12 +2198,14 @@ function receiptToRow(receipt) {
|
|
|
1911
2198
|
backend_status: receipt.backendStatus,
|
|
1912
2199
|
duration_ms: receipt.durationMs !== void 0 ? receipt.durationMs : null,
|
|
1913
2200
|
protocol: receipt.protocol !== void 0 ? receipt.protocol : null,
|
|
1914
|
-
metadata_json: receipt.metadata !== void 0 ? JSON.stringify(redact(receipt.metadata)) : null
|
|
2201
|
+
metadata_json: receipt.metadata !== void 0 ? JSON.stringify(redact(receipt.metadata)) : null,
|
|
2202
|
+
authorization_json: receipt.authorization !== void 0 ? JSON.stringify(redact(receipt.authorization)) : null
|
|
1915
2203
|
};
|
|
1916
2204
|
}
|
|
1917
2205
|
function rowToReceipt(row) {
|
|
1918
2206
|
const payment = row.payment_json !== null ? JSON.parse(row.payment_json) : void 0;
|
|
1919
2207
|
const metadata = row.metadata_json !== null ? JSON.parse(row.metadata_json) : void 0;
|
|
2208
|
+
const authorization = row.authorization_json !== null ? JSON.parse(row.authorization_json) : void 0;
|
|
1920
2209
|
return {
|
|
1921
2210
|
id: row.id,
|
|
1922
2211
|
requestId: row.request_id,
|
|
@@ -1926,7 +2215,8 @@ function rowToReceipt(row) {
|
|
|
1926
2215
|
...payment !== void 0 ? { payment } : {},
|
|
1927
2216
|
...row.duration_ms !== null ? { durationMs: row.duration_ms } : {},
|
|
1928
2217
|
...row.protocol !== null ? { protocol: row.protocol } : {},
|
|
1929
|
-
...metadata !== void 0 ? { metadata } : {}
|
|
2218
|
+
...metadata !== void 0 ? { metadata } : {},
|
|
2219
|
+
...authorization !== void 0 ? { authorization } : {}
|
|
1930
2220
|
};
|
|
1931
2221
|
}
|
|
1932
2222
|
function eventToRow(event) {
|
|
@@ -2035,6 +2325,12 @@ var MIGRATIONS = [
|
|
|
2035
2325
|
CREATE INDEX IF NOT EXISTS idx_payment_attempts_created_at ON payment_attempts(created_at);
|
|
2036
2326
|
`);
|
|
2037
2327
|
}
|
|
2328
|
+
},
|
|
2329
|
+
{
|
|
2330
|
+
version: 2,
|
|
2331
|
+
up(db) {
|
|
2332
|
+
db.exec(`ALTER TABLE receipts ADD COLUMN authorization_json TEXT;`);
|
|
2333
|
+
}
|
|
2038
2334
|
}
|
|
2039
2335
|
];
|
|
2040
2336
|
var SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;
|
|
@@ -2072,8 +2368,8 @@ function createSqliteReceiptStore(options) {
|
|
|
2072
2368
|
const db = openSqliteDatabase({ path: path2, label: "Receipt database", logger });
|
|
2073
2369
|
migrate(db);
|
|
2074
2370
|
const insertReceiptStmt = db.prepare(
|
|
2075
|
-
`INSERT INTO receipts (id, request_id, resource_id, payment_json, delivered_at, backend_status, duration_ms, protocol, metadata_json)
|
|
2076
|
-
VALUES (@id, @request_id, @resource_id, @payment_json, @delivered_at, @backend_status, @duration_ms, @protocol, @metadata_json)`
|
|
2371
|
+
`INSERT INTO receipts (id, request_id, resource_id, payment_json, delivered_at, backend_status, duration_ms, protocol, metadata_json, authorization_json)
|
|
2372
|
+
VALUES (@id, @request_id, @resource_id, @payment_json, @delivered_at, @backend_status, @duration_ms, @protocol, @metadata_json, @authorization_json)`
|
|
2077
2373
|
);
|
|
2078
2374
|
const getReceiptStmt = db.prepare("SELECT * FROM receipts WHERE id = ?");
|
|
2079
2375
|
const listReceiptsStmt = db.prepare(
|
|
@@ -2588,7 +2884,7 @@ function maskMiddle(value, keep = 6) {
|
|
|
2588
2884
|
}
|
|
2589
2885
|
createRequire(import.meta.url);
|
|
2590
2886
|
function readVersionReport() {
|
|
2591
|
-
const injected = '{"cliVersion":"1.
|
|
2887
|
+
const injected = '{"cliVersion":"1.4.0","pinned":[{"name":"@coinbase/x402","version":"2.1.0","via":"@devlab.group/agent-commerce"},{"name":"@modelcontextprotocol/sdk","version":"1.30.0","via":"@devlab.group/agent-commerce"},{"name":"@x402/core","version":"2.23.0","via":"@devlab.group/agent-commerce"},{"name":"@x402/evm","version":"2.23.0","via":"@devlab.group/agent-commerce"},{"name":"viem","version":"2.55.18","via":"@devlab.group/agent-commerce"},{"name":"better-sqlite3","version":"13.0.3","via":"@devlab.group/agent-commerce"},{"name":"fastify","version":"5.12.0","via":"@devlab.group/agent-commerce"},{"name":"pino","version":"10.3.1","via":"@devlab.group/agent-commerce"},{"name":"zod","version":"3.25.76","via":"@devlab.group/agent-commerce"}]}' ;
|
|
2592
2888
|
{
|
|
2593
2889
|
return JSON.parse(injected);
|
|
2594
2890
|
}
|
|
@@ -2650,6 +2946,50 @@ function findX402Mismatch(configured, live) {
|
|
|
2650
2946
|
if (diffs.length === 0) return void 0;
|
|
2651
2947
|
return `${diffs.join("; ")} \u2014 the gateway may be running against an older deployment; restart it or re-run chain:deploy`;
|
|
2652
2948
|
}
|
|
2949
|
+
function describeIssuers(label, issuers) {
|
|
2950
|
+
const described = issuers.map(
|
|
2951
|
+
(entry) => `${entry.issuer} (${entry.keys.length} key${entry.keys.length === 1 ? "" : "s"})`
|
|
2952
|
+
).join(", ");
|
|
2953
|
+
return `${label} issuers: ${described}`;
|
|
2954
|
+
}
|
|
2955
|
+
function isWritableFile(path2) {
|
|
2956
|
+
try {
|
|
2957
|
+
accessSync(path2, constants.W_OK);
|
|
2958
|
+
return true;
|
|
2959
|
+
} catch {
|
|
2960
|
+
return false;
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
function ap2ReplayCheck(path2) {
|
|
2964
|
+
const name = "AP2 replay store";
|
|
2965
|
+
if (path2 === ":memory:") {
|
|
2966
|
+
return {
|
|
2967
|
+
name,
|
|
2968
|
+
status: "WARN",
|
|
2969
|
+
detail: "in-memory store - every spent mandate is forgotten on restart, so one could authorize a second purchase; use a file path in production"
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
if (existsSync(path2)) {
|
|
2973
|
+
return isWritableFile(path2) ? { name, status: "PASS", detail: `writable at "${path2}"` } : {
|
|
2974
|
+
name,
|
|
2975
|
+
status: "FAIL",
|
|
2976
|
+
detail: `"${path2}" exists but is not writable by this user - no mandate could be recorded as spent`
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
const directory = dirname(path2);
|
|
2980
|
+
if (directory !== "" && directory !== "." && !existsSync(directory)) {
|
|
2981
|
+
return {
|
|
2982
|
+
name,
|
|
2983
|
+
status: "WARN",
|
|
2984
|
+
detail: `no store yet at "${path2}" and its directory does not exist - it is created when the gateway starts, provided that path is writable`
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
return {
|
|
2988
|
+
name,
|
|
2989
|
+
status: "WARN",
|
|
2990
|
+
detail: `no store yet at "${path2}" - it is created the first time the gateway starts`
|
|
2991
|
+
};
|
|
2992
|
+
}
|
|
2653
2993
|
function acpIdempotencyCheck(idempotency) {
|
|
2654
2994
|
const retention = `retention ${idempotency.retentionHours}h`;
|
|
2655
2995
|
if (idempotency.retentionHours < 24) {
|
|
@@ -2668,16 +3008,11 @@ function acpIdempotencyCheck(idempotency) {
|
|
|
2668
3008
|
}
|
|
2669
3009
|
const directory = dirname(idempotency.path);
|
|
2670
3010
|
if (existsSync(idempotency.path)) {
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
name: "ACP idempotency",
|
|
2677
|
-
status: "FAIL",
|
|
2678
|
-
detail: `"${idempotency.path}" exists but is not writable by this user \u2014 the adapter cannot claim idempotency keys`
|
|
2679
|
-
};
|
|
2680
|
-
}
|
|
3011
|
+
return isWritableFile(idempotency.path) ? { name: "ACP idempotency", status: "PASS", detail: `writable, ${retention}` } : {
|
|
3012
|
+
name: "ACP idempotency",
|
|
3013
|
+
status: "FAIL",
|
|
3014
|
+
detail: `"${idempotency.path}" exists but is not writable by this user \u2014 the adapter cannot claim idempotency keys`
|
|
3015
|
+
};
|
|
2681
3016
|
}
|
|
2682
3017
|
if (directory !== "" && directory !== "." && !existsSync(directory)) {
|
|
2683
3018
|
return {
|
|
@@ -2841,6 +3176,37 @@ async function runDoctor(options, deps = {}) {
|
|
|
2841
3176
|
detail: ACP_UNSUPPORTED.join(", ")
|
|
2842
3177
|
});
|
|
2843
3178
|
}
|
|
3179
|
+
const ap2 = config?.authorization?.ap2;
|
|
3180
|
+
if (config === void 0) {
|
|
3181
|
+
checks.push({ name: "AP2", status: "WARN", detail: "skipped \u2014 config invalid" });
|
|
3182
|
+
} else if (ap2 === void 0 || !ap2.enabled) {
|
|
3183
|
+
checks.push({ name: "AP2", status: "INFO", detail: "disabled" });
|
|
3184
|
+
} else {
|
|
3185
|
+
checks.push({
|
|
3186
|
+
name: "AP2",
|
|
3187
|
+
status: "PASS",
|
|
3188
|
+
detail: `experimental \xB7 spec ${AP2_SPEC_VERSION} \xB7 mode ${ap2.mode} \xB7 ${AP2_CHECKOUT_MANDATE_VCT} \xB7 profile ${AP2_CHECKOUT_PROFILE} \xB7 clock skew ${ap2.clockSkewSeconds}s`
|
|
3189
|
+
});
|
|
3190
|
+
checks.push({
|
|
3191
|
+
name: "AP2 trust",
|
|
3192
|
+
status: "PASS",
|
|
3193
|
+
detail: `${describeIssuers("mandate", ap2.trust.mandateIssuers)} \xB7 ${describeIssuers("checkout", ap2.trust.checkoutIssuers)}`
|
|
3194
|
+
});
|
|
3195
|
+
checks.push(ap2ReplayCheck(ap2.replay.path));
|
|
3196
|
+
const gated = config.resources.filter(
|
|
3197
|
+
(resource) => resource.authorization?.required.includes("ap2")
|
|
3198
|
+
);
|
|
3199
|
+
checks.push({
|
|
3200
|
+
name: "AP2 resources",
|
|
3201
|
+
status: gated.length > 0 ? "PASS" : "WARN",
|
|
3202
|
+
detail: gated.length > 0 ? gated.map((resource) => resource.id).join(", ") : "AP2 is enabled but no resource requires it \u2014 every purchase settles without a mandate"
|
|
3203
|
+
});
|
|
3204
|
+
checks.push({
|
|
3205
|
+
name: "AP2 unsupported",
|
|
3206
|
+
status: "INFO",
|
|
3207
|
+
detail: AP2_UNSUPPORTED.join(", ")
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
2844
3210
|
const x402 = config?.payments.x402;
|
|
2845
3211
|
if (x402 === void 0 || !x402.enabled) {
|
|
2846
3212
|
checks.push({ name: "Payments", status: "INFO", detail: "x402 not configured" });
|