@devlab.group/agent-commerce 2.2.1 → 2.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.
Files changed (61) hide show
  1. package/README.md +20 -16
  2. package/dist/ap2.d.ts +6 -5
  3. package/dist/ap2.js +3 -3
  4. package/dist/{authorization-CMkIuAkO.d.ts → authorization-DjAecwuW.d.ts} +1 -1
  5. package/dist/{chunk-REBY2IEP.js → chunk-4OEXK3YG.js} +4 -4
  6. package/dist/{chunk-REBY2IEP.js.map → chunk-4OEXK3YG.js.map} +1 -1
  7. package/dist/{chunk-2DAJAJI7.js → chunk-6WACRJVT.js} +3 -3
  8. package/dist/{chunk-2DAJAJI7.js.map → chunk-6WACRJVT.js.map} +1 -1
  9. package/dist/{chunk-XYHDQ22D.js → chunk-AITMDXKW.js} +3 -3
  10. package/dist/{chunk-XYHDQ22D.js.map → chunk-AITMDXKW.js.map} +1 -1
  11. package/dist/{chunk-BNUD4RBN.js → chunk-WUIAUAA7.js} +200 -8
  12. package/dist/chunk-WUIAUAA7.js.map +1 -0
  13. package/dist/cli/{ap2-HLBIUSTK.js → ap2-QDHPRSTF.js} +3 -3
  14. package/dist/cli/{ap2-HLBIUSTK.js.map → ap2-QDHPRSTF.js.map} +1 -1
  15. package/dist/cli/{chunk-QZ6TOMX6.js → chunk-AZDWFVDW.js} +200 -8
  16. package/dist/cli/chunk-AZDWFVDW.js.map +1 -0
  17. package/dist/cli/{chunk-SNM3OVCY.js → chunk-DCMW67JM.js} +19 -5
  18. package/dist/cli/chunk-DCMW67JM.js.map +1 -0
  19. package/dist/cli/{chunk-ILUYEQSV.js → chunk-G2JEY7MB.js} +26 -9
  20. package/dist/cli/chunk-G2JEY7MB.js.map +1 -0
  21. package/dist/cli/{chunk-2LN5PO5I.js → chunk-PIMFPCNZ.js} +3 -2
  22. package/dist/cli/chunk-PIMFPCNZ.js.map +1 -0
  23. package/dist/cli/chunk-UXB65XCS.js +8 -0
  24. package/dist/cli/{chunk-QY3K6PEQ.js.map → chunk-UXB65XCS.js.map} +1 -1
  25. package/dist/cli/{chunk-KTV52MCU.js → chunk-WYOGIANL.js} +3 -3
  26. package/dist/cli/{chunk-KTV52MCU.js.map → chunk-WYOGIANL.js.map} +1 -1
  27. package/dist/cli/{config-VYAJGZXE.js → config-UI4EDG4L.js} +5 -5
  28. package/dist/cli/{config-VYAJGZXE.js.map → config-UI4EDG4L.js.map} +1 -1
  29. package/dist/cli/index.js +98 -12
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/{mcp-HFD7ZNCQ.js → mcp-26LJMN4N.js} +3 -3
  32. package/dist/cli/{mcp-HFD7ZNCQ.js.map → mcp-26LJMN4N.js.map} +1 -1
  33. package/dist/cli/{provider-JV5HCQGR.js → provider-Z4OJNAYI.js} +5 -5
  34. package/dist/cli/{provider-JV5HCQGR.js.map → provider-Z4OJNAYI.js.map} +1 -1
  35. package/dist/cli/{run-WV44B62Y.js → run-TEPYJUHG.js} +33 -12
  36. package/dist/cli/run-TEPYJUHG.js.map +1 -0
  37. package/dist/cli/{x402-NADFV5CD.js → x402-BMVONZKA.js} +6 -6
  38. package/dist/cli/{x402-NADFV5CD.js.map → x402-BMVONZKA.js.map} +1 -1
  39. package/dist/{constants-nackLItD.d.ts → constants-BG22tE8S.d.ts} +2 -1
  40. package/dist/index.d.ts +35 -12
  41. package/dist/index.js +61 -11
  42. package/dist/index.js.map +1 -1
  43. package/dist/mcp.d.ts +5 -4
  44. package/dist/mcp.js +1 -1
  45. package/dist/mpp.d.ts +5 -4
  46. package/dist/mpp.js +3 -3
  47. package/dist/{networks-DTWRlj-K.d.ts → networks-DDHYJLcg.d.ts} +2 -1
  48. package/dist/{protocol-adapter-CfxU8Z2T.d.ts → protocol-adapter-C9odYl6l.d.ts} +4 -3
  49. package/dist/{runtime-7vLNYvmU.d.ts → runtime-Cl_BLVZV.d.ts} +8 -1
  50. package/dist/{types-CVw6Sf46.d.ts → types-j7MnUXYN.d.ts} +2 -1
  51. package/dist/x402.d.ts +27 -18
  52. package/dist/x402.js +4 -4
  53. package/package.json +6 -6
  54. package/dist/chunk-BNUD4RBN.js.map +0 -1
  55. package/dist/cli/chunk-2LN5PO5I.js.map +0 -1
  56. package/dist/cli/chunk-ILUYEQSV.js.map +0 -1
  57. package/dist/cli/chunk-QY3K6PEQ.js +0 -8
  58. package/dist/cli/chunk-QZ6TOMX6.js.map +0 -1
  59. package/dist/cli/chunk-SNM3OVCY.js.map +0 -1
  60. package/dist/cli/index.d.ts +0 -1
  61. package/dist/cli/run-WV44B62Y.js.map +0 -1
@@ -1,16 +1,17 @@
1
- import { assertPayToIsNotDevAddress, resolveX402Deployment, assertDevKeyIsLocalOnly, describeRpc, x402OfferName } from './chunk-KTV52MCU.js';
1
+ import { isWellKnownDevKey, assertPayToIsNotDevAddress, resolveX402Deployment, assertDevKeyIsLocalOnly, describeRpc, x402OfferName } from './chunk-WYOGIANL.js';
2
2
  import { findX402Network, normalizeAddress, X402_DEFAULT_PAYMENT_FLOW, REUSE_REJECTED_AUTHORIZATION_METADATA_KEY, SETTLE_AFTER_BACKEND_METADATA_KEY, sameAddress, LOCAL_CHAIN_ID, isTransactionReference, isEvmAddress, describeDeploymentMode } from './chunk-OZ4LAWBK.js';
3
3
  import { computeReplayKey, computePermit2ReplayKey } from './chunk-OSTCU6ZK.js';
4
- import { PACKAGE_VERSION } from './chunk-QY3K6PEQ.js';
4
+ import { PACKAGE_VERSION } from './chunk-UXB65XCS.js';
5
5
  import { CommerceError, systemClock, NOOP_LOGGER, redactedErrorText } from './chunk-QQ3WY5L3.js';
6
6
  import { isRecord } from './chunk-3M6U7ILB.js';
7
7
  import { x402ExactPermit2ProxyAddress, toFacilitatorEvmSigner, PERMIT2_ADDRESS } from '@x402/evm';
8
- import { isAddress, getAddress, formatUnits, createWalletClient, http, publicActions, createPublicClient, HttpRequestError, TimeoutError, WaitForTransactionReceiptTimeoutError, BaseError, ContractFunctionRevertedError, erc20Abi } from 'viem';
8
+ import { isAddress, keccak256, stringToBytes, getAddress, formatUnits, createWalletClient, http, publicActions, createPublicClient, HttpRequestError, TimeoutError, WaitForTransactionReceiptTimeoutError, BaseError, ContractFunctionRevertedError, erc20Abi } from 'viem';
9
9
  import { privateKeyToAccount } from 'viem/accounts';
10
10
  import { HTTPFacilitatorClient } from '@x402/core/http';
11
11
  import { SettleError, VerifyError } from '@x402/core/types';
12
12
  import { x402Facilitator } from '@x402/core/facilitator';
13
13
  import { registerExactEvmScheme } from '@x402/evm/exact/facilitator';
14
+ import { randomBytes } from 'crypto';
14
15
  import { PaymentPayloadV2Schema } from '@x402/core/schemas';
15
16
 
16
17
  // src/payments/x402/provider/constants.ts
@@ -799,6 +800,172 @@ function offerCurrency(offer, quoted) {
799
800
  const canonical = offer.profile.canonicalAsset;
800
801
  return canonical !== void 0 && sameAddress(offer.profile, offer.asset, canonical.address) ? canonical.symbol : quoted;
801
802
  }
803
+
804
+ // src/core/canonical-json.ts
805
+ var LONE_SURROGATE = new RegExp("\\p{Cs}", "u");
806
+ function canonicalJson(value) {
807
+ if (typeof value === "string")
808
+ return LONE_SURROGATE.test(value) ? void 0 : JSON.stringify(value);
809
+ if (typeof value === "number") return Number.isFinite(value) ? JSON.stringify(value) : void 0;
810
+ if (value === null || typeof value === "boolean") return JSON.stringify(value);
811
+ if (Array.isArray(value)) {
812
+ const items = value.map(canonicalJson);
813
+ return items.includes(void 0) ? void 0 : `[${items.join(",")}]`;
814
+ }
815
+ if (typeof value !== "object") return void 0;
816
+ const members = [];
817
+ for (const key of Object.keys(value).sort()) {
818
+ const member = canonicalJson(value[key]);
819
+ if (member === void 0 || LONE_SURROGATE.test(key)) return void 0;
820
+ members.push(`${JSON.stringify(key)}:${member}`);
821
+ }
822
+ return `{${members.join(",")}}`;
823
+ }
824
+
825
+ // src/payments/x402/provider/quote.ts
826
+ var QUOTE_EXTENSION = "erc-8409";
827
+ var QUOTE_REQUEST_SCHEME = "agent-commerce/request/v1";
828
+ var QUOTE_DOMAIN = { name: "Signed Service Payment Quotes", version: "1" };
829
+ var QUOTE_TYPES = {
830
+ ServicePaymentQuote: [
831
+ { name: "issuer", type: "address" },
832
+ { name: "payer", type: "address" },
833
+ { name: "recipient", type: "address" },
834
+ { name: "asset", type: "address" },
835
+ { name: "amount", type: "uint256" },
836
+ { name: "requestScheme", type: "string" },
837
+ { name: "requestHash", type: "bytes32" },
838
+ { name: "quoteId", type: "bytes32" },
839
+ { name: "validAfter", type: "uint64" },
840
+ { name: "validUntil", type: "uint64" }
841
+ ]
842
+ };
843
+ var BEARER = "0x0000000000000000000000000000000000000000";
844
+ var QUOTE_INFO_SCHEMA = {
845
+ type: "object",
846
+ required: ["quotes"],
847
+ properties: {
848
+ quotes: {
849
+ type: "array",
850
+ items: {
851
+ type: "object",
852
+ required: ["chainId", "quote", "signature"],
853
+ properties: {
854
+ chainId: { type: "string" },
855
+ // Every field is a string in the ERC's JSON encoding
856
+ quote: {
857
+ type: "object",
858
+ required: QUOTE_TYPES.ServicePaymentQuote.map((field) => field.name),
859
+ properties: Object.fromEntries(
860
+ QUOTE_TYPES.ServicePaymentQuote.map((field) => [field.name, { type: "string" }])
861
+ )
862
+ },
863
+ signature: { type: "string" }
864
+ }
865
+ }
866
+ }
867
+ }
868
+ };
869
+ var QUOTE_CACHE_LIMIT = 1024;
870
+ function createQuoteIssuer(options) {
871
+ const { signerPrivateKey, offers, deploymentMode, clock, maxTimeoutSeconds } = options;
872
+ let account;
873
+ try {
874
+ account = privateKeyToAccount(signerPrivateKey);
875
+ } catch (cause) {
876
+ throw new CommerceError(
877
+ "CONFIG_INVALID",
878
+ 'x402 provider: quote.signerPrivateKey is not a valid private key (expected "0x" followed by 64 hex characters)',
879
+ { cause }
880
+ );
881
+ }
882
+ if (deploymentMode !== "local" && isWellKnownDevKey(signerPrivateKey)) {
883
+ throw new CommerceError(
884
+ "CONFIG_INVALID",
885
+ "x402 provider: quote.signerPrivateKey is a well-known Anvil development key, so anyone could sign quotes as this gateway. Use a dedicated key outside the local chain."
886
+ );
887
+ }
888
+ const evmOffers = offers.filter((offer) => offer.family === "evm");
889
+ if (evmOffers.length === 0) {
890
+ throw new CommerceError(
891
+ "CONFIG_INVALID",
892
+ "x402 provider: quote needs an EVM offer; ERC-8409 quotes cover EVM networks only"
893
+ );
894
+ }
895
+ const issuer = account.address;
896
+ if (evmOffers.some((offer) => offer.payTo.toLowerCase() === issuer.toLowerCase())) {
897
+ throw new CommerceError(
898
+ "CONFIG_INVALID",
899
+ "x402 provider: quote.signerPrivateKey controls a payTo address. Use a dedicated key, not the merchant wallet."
900
+ );
901
+ }
902
+ const cache = /* @__PURE__ */ new Map();
903
+ async function quoteFor(offer, amount, requestHash) {
904
+ const now = Math.floor(clock.now().getTime() / 1e3);
905
+ const { chainId } = offer.profile;
906
+ const key = [
907
+ chainId,
908
+ offer.payTo.toLowerCase(),
909
+ offer.asset.toLowerCase(),
910
+ amount,
911
+ requestHash
912
+ ].join("|");
913
+ const cached = cache.get(key);
914
+ if (cached !== void 0 && Number(cached.quote.validUntil) - now >= maxTimeoutSeconds) {
915
+ return cached;
916
+ }
917
+ const validUntil = now + 2 * maxTimeoutSeconds;
918
+ const quote = {
919
+ issuer,
920
+ payer: BEARER,
921
+ recipient: offer.payTo,
922
+ asset: offer.asset,
923
+ amount,
924
+ requestScheme: QUOTE_REQUEST_SCHEME,
925
+ requestHash,
926
+ // Use a new ID for each signed body, as the ERC requires
927
+ quoteId: `0x${randomBytes(32).toString("hex")}`,
928
+ validAfter: String(now),
929
+ validUntil: String(validUntil)
930
+ };
931
+ const signature = await account.signTypedData({
932
+ domain: { ...QUOTE_DOMAIN, chainId },
933
+ types: QUOTE_TYPES,
934
+ primaryType: "ServicePaymentQuote",
935
+ message: {
936
+ ...quote,
937
+ amount: BigInt(amount),
938
+ validAfter: BigInt(now),
939
+ validUntil: BigInt(validUntil)
940
+ }
941
+ });
942
+ const signed = { chainId: String(chainId), quote, signature };
943
+ cache.delete(key);
944
+ if (cache.size >= QUOTE_CACHE_LIMIT) {
945
+ const oldest = cache.keys().next().value;
946
+ if (oldest !== void 0) cache.delete(oldest);
947
+ }
948
+ cache.set(key, signed);
949
+ return signed;
950
+ }
951
+ return {
952
+ issuer,
953
+ async extensionsFor(resourceId, input, quoted) {
954
+ if (quoted.length === 0) return void 0;
955
+ const requestHash = quoteRequestHash(resourceId, input);
956
+ if (requestHash === void 0) return void 0;
957
+ const quotes = [];
958
+ for (const { offer, amount } of quoted) {
959
+ quotes.push(await quoteFor(offer, amount, requestHash));
960
+ }
961
+ return { [QUOTE_EXTENSION]: { info: { quotes }, schema: QUOTE_INFO_SCHEMA } };
962
+ }
963
+ };
964
+ }
965
+ function quoteRequestHash(resourceId, input) {
966
+ const canonical = canonicalJson({ input, resource: resourceId });
967
+ return canonical === void 0 ? void 0 : keccak256(stringToBytes(canonical));
968
+ }
802
969
  function decodePaymentSubmission(payloadBase64) {
803
970
  const json = decodeJson(payloadBase64);
804
971
  if (json === void 0) return void 0;
@@ -885,6 +1052,14 @@ function createRequirementBuilders(ctx, svmFeePayer) {
885
1052
  }))
886
1053
  );
887
1054
  const single = offers.length === 1 ? offers[0] : void 0;
1055
+ const extensions = await ctx.quotes?.extensionsFor(
1056
+ context.resource.id,
1057
+ context.input,
1058
+ offers.flatMap((offer, index) => {
1059
+ const accepted = accepts[index];
1060
+ return offer.family === "evm" && accepted ? [{ offer, amount: accepted.amount }] : [];
1061
+ })
1062
+ );
888
1063
  const resourceId = encodeURIComponent(context.resource.id);
889
1064
  const envelope = {
890
1065
  x402Version: X402_VERSION,
@@ -895,7 +1070,8 @@ function createRequirementBuilders(ctx, svmFeePayer) {
895
1070
  // delivery carries the backend's own type when it is text and fits the body
896
1071
  mimeType: options.resourceMimeTypes?.[context.resource.id] ?? DEFAULT_MIME_TYPE
897
1072
  },
898
- accepts
1073
+ accepts,
1074
+ ...extensions !== void 0 ? { extensions } : {}
899
1075
  };
900
1076
  const expiresAt = new Date(clock.now().getTime() + maxTimeoutSeconds * 1e3).toISOString();
901
1077
  return {
@@ -1321,6 +1497,13 @@ function createX402PaymentProvider(options) {
1321
1497
  const ids = options.ids ?? DEFAULT_IDS;
1322
1498
  const logger = options.logger ?? NOOP_LOGGER;
1323
1499
  const maxTimeoutSeconds = options.maxTimeoutSeconds ?? DEFAULT_MAX_TIMEOUT_SECONDS;
1500
+ const quotes = options.quote ? createQuoteIssuer({
1501
+ signerPrivateKey: options.quote.signerPrivateKey,
1502
+ offers,
1503
+ deploymentMode,
1504
+ clock,
1505
+ maxTimeoutSeconds
1506
+ }) : void 0;
1324
1507
  const evmOffers = offers.filter((offer) => offer.family === "evm");
1325
1508
  const hasSvm = offers.some((offer) => offer.family === "svm");
1326
1509
  const hasNear = offers.some((offer) => offer.family === "near");
@@ -1344,7 +1527,8 @@ function createX402PaymentProvider(options) {
1344
1527
  `mode=${deploymentMode}`,
1345
1528
  "authorization-flow",
1346
1529
  "upfront-flow",
1347
- ...severalOffers ? ["multiple-offers"] : []
1530
+ ...severalOffers ? ["multiple-offers"] : [],
1531
+ ...quotes ? ["erc-8409-quotes"] : []
1348
1532
  ],
1349
1533
  status: "stable",
1350
1534
  unsupported: [
@@ -1376,6 +1560,7 @@ function createX402PaymentProvider(options) {
1376
1560
  ids,
1377
1561
  logger,
1378
1562
  maxTimeoutSeconds,
1563
+ quotes,
1379
1564
  svm,
1380
1565
  near
1381
1566
  };
@@ -1394,7 +1579,14 @@ function createX402PaymentProvider(options) {
1394
1579
  ...severalOffers ? { selectRequirement } : {},
1395
1580
  verify,
1396
1581
  settle,
1397
- health
1582
+ health,
1583
+ ...quotes ? {
1584
+ quote: {
1585
+ issuer: quotes.issuer,
1586
+ requestScheme: QUOTE_REQUEST_SCHEME,
1587
+ extension: QUOTE_EXTENSION
1588
+ }
1589
+ } : {}
1398
1590
  };
1399
1591
  }
1400
1592
  function loadOptionalModule(load, missing) {
@@ -1409,5 +1601,5 @@ function loadOptionalModule(load, missing) {
1409
1601
  }
1410
1602
 
1411
1603
  export { DEFAULT_IDS, chainIdFromCaip2, createX402PaymentProvider, parseCanonicalAmount };
1412
- //# sourceMappingURL=chunk-QZ6TOMX6.js.map
1413
- //# sourceMappingURL=chunk-QZ6TOMX6.js.map
1604
+ //# sourceMappingURL=chunk-AZDWFVDW.js.map
1605
+ //# sourceMappingURL=chunk-AZDWFVDW.js.map