@agentcash/router 1.1.1 → 1.1.3

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/index.cjs CHANGED
@@ -267,12 +267,12 @@ async function createX402Server(config) {
267
267
  facilitatorsByNetwork
268
268
  };
269
269
  }
270
- function cachedClient(inner, networks) {
270
+ function cachedClient(inner, kinds) {
271
271
  return {
272
272
  verify: inner.verify.bind(inner),
273
273
  settle: inner.settle.bind(inner),
274
274
  getSupported: async () => ({
275
- kinds: networks.map((network) => ({ x402Version: 2, scheme: "exact", network })),
275
+ kinds,
276
276
  extensions: [],
277
277
  signers: {}
278
278
  })
@@ -282,7 +282,19 @@ function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient)
282
282
  const groups = getResolvedX402FacilitatorGroups(facilitatorsByNetwork);
283
283
  return groups.map((group) => {
284
284
  const inner = new HTTPFacilitatorClient(group.config);
285
- return group.family === "evm" ? cachedClient(inner, group.networks) : inner;
285
+ const kinds = group.networks.map((network) => ({
286
+ x402Version: 2,
287
+ scheme: "exact",
288
+ network,
289
+ ...group.family === "solana" ? {
290
+ extra: {
291
+ features: {
292
+ xSettlementAccountSupported: true
293
+ }
294
+ }
295
+ } : {}
296
+ }));
297
+ return cachedClient(inner, kinds);
286
298
  });
287
299
  }
288
300
  var init_server = __esm({
@@ -366,6 +378,11 @@ var RouteRegistry = class {
366
378
  // src/orchestrate.ts
367
379
  var import_server2 = require("next/server");
368
380
 
381
+ // src/auth/normalize-wallet.ts
382
+ function normalizeWalletAddress(address) {
383
+ return address.startsWith("0x") ? address.toLowerCase() : address;
384
+ }
385
+
369
386
  // src/plugin.ts
370
387
  function createDefaultContext(meta) {
371
388
  const ctx = {
@@ -676,12 +693,33 @@ async function buildExpectedRequirements(server, request, price, accepts) {
676
693
  return [...exactRequirements, ...customRequirements];
677
694
  }
678
695
  async function buildExactRequirements(server, request, price, accepts) {
679
- const exactOptions = [
680
- ...buildEvmExactOptions(accepts, price),
681
- ...buildSolanaExactOptions(accepts, price)
682
- ];
683
- if (exactOptions.length === 0) return [];
684
- return server.buildPaymentRequirementsFromOptions(exactOptions, { request });
696
+ const exactGroups = [
697
+ buildEvmExactOptions(accepts, price),
698
+ buildSolanaExactOptions(accepts, price)
699
+ ].filter((options) => options.length > 0);
700
+ if (exactGroups.length === 0) return [];
701
+ const requirements = [];
702
+ const failures = [];
703
+ for (const options of exactGroups) {
704
+ try {
705
+ requirements.push(
706
+ ...await server.buildPaymentRequirementsFromOptions(options, { request })
707
+ );
708
+ } catch (error) {
709
+ const err = error instanceof Error ? error : new Error(String(error));
710
+ failures.push(err);
711
+ if (exactGroups.length === 1) {
712
+ throw err;
713
+ }
714
+ console.warn(
715
+ `[router] Failed to build x402 exact requirements for ${options[0]?.network}: ${err.message}`
716
+ );
717
+ }
718
+ }
719
+ if (requirements.length > 0) {
720
+ return requirements;
721
+ }
722
+ throw failures[0] ?? new Error("Failed to build x402 exact requirements");
685
723
  }
686
724
  function buildCustomRequirements(price, accepts) {
687
725
  return accepts.filter((accept) => accept.scheme !== "exact").map((accept) => buildCustomRequirement(price, accept));
@@ -877,6 +915,23 @@ function getRequirementNetwork(requirements, fallback) {
877
915
  const network = requirements?.network;
878
916
  return typeof network === "string" ? network : fallback;
879
917
  }
918
+ function siwxSignatureType(network) {
919
+ return network.startsWith("solana:") ? "ed25519" : "eip191";
920
+ }
921
+ function getSupportedChains(x402Accepts, fallbackNetwork) {
922
+ const seen = /* @__PURE__ */ new Set();
923
+ const chains = [];
924
+ for (const accept of x402Accepts) {
925
+ if (accept.network && !seen.has(accept.network)) {
926
+ seen.add(accept.network);
927
+ chains.push({ chainId: accept.network, type: siwxSignatureType(accept.network) });
928
+ }
929
+ }
930
+ if (chains.length === 0) {
931
+ chains.push({ chainId: fallbackNetwork, type: siwxSignatureType(fallbackNetwork) });
932
+ }
933
+ return chains;
934
+ }
880
935
  function createRequestHandler(routeEntry, handler, deps) {
881
936
  async function invoke(request, meta, pluginCtx, wallet, account, parsedBody) {
882
937
  const ctx = {
@@ -1004,12 +1059,14 @@ function createRequestHandler(routeEntry, handler, deps) {
1004
1059
  if (!siwxHeader && routeEntry.authMode === "siwx") {
1005
1060
  const url = new URL(request.url);
1006
1061
  const nonce = crypto.randomUUID().replace(/-/g, "");
1062
+ const supportedChains = getSupportedChains(deps.x402Accepts, deps.network);
1063
+ const primaryChain = supportedChains[0];
1007
1064
  const siwxInfo = {
1008
1065
  domain: url.hostname,
1009
1066
  uri: request.url,
1010
1067
  version: "1",
1011
- chainId: deps.network,
1012
- type: "eip191",
1068
+ chainId: primaryChain.chainId,
1069
+ type: primaryChain.type,
1013
1070
  nonce,
1014
1071
  issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1015
1072
  expirationTime: new Date(Date.now() + SIWX_CHALLENGE_EXPIRY_MS).toISOString(),
@@ -1033,7 +1090,7 @@ function createRequestHandler(routeEntry, handler, deps) {
1033
1090
  "sign-in-with-x": {
1034
1091
  info: siwxInfo,
1035
1092
  // supportedChains at top level required by MCP tools for chain detection
1036
- supportedChains: [{ chainId: deps.network, type: "eip191" }],
1093
+ supportedChains,
1037
1094
  ...siwxSchema ? { schema: siwxSchema } : {}
1038
1095
  }
1039
1096
  }
@@ -1069,7 +1126,7 @@ function createRequestHandler(routeEntry, handler, deps) {
1069
1126
  return response;
1070
1127
  }
1071
1128
  } else {
1072
- const wallet = siwx.wallet.toLowerCase();
1129
+ const wallet = normalizeWalletAddress(siwx.wallet);
1073
1130
  pluginCtx.setVerifiedWallet(wallet);
1074
1131
  if (routeEntry.authMode === "siwx") {
1075
1132
  firePluginHook(deps.plugin, "onAuthVerified", pluginCtx, {
@@ -1171,7 +1228,7 @@ function createRequestHandler(routeEntry, handler, deps) {
1171
1228
  if (!verify?.valid) return await build402(request, routeEntry, deps, meta, pluginCtx);
1172
1229
  const { payload: verifyPayload, requirements: verifyRequirements } = verify;
1173
1230
  const matchedNetwork = getRequirementNetwork(verifyRequirements, deps.network);
1174
- const wallet = verify.payer.toLowerCase();
1231
+ const wallet = normalizeWalletAddress(verify.payer);
1175
1232
  pluginCtx.setVerifiedWallet(wallet);
1176
1233
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1177
1234
  protocol: "x402",
@@ -1273,7 +1330,7 @@ function createRequestHandler(routeEntry, handler, deps) {
1273
1330
  const rawSource = credential?.source ?? "";
1274
1331
  const didParts = rawSource.split(":");
1275
1332
  const lastPart = didParts[didParts.length - 1];
1276
- const wallet = ((0, import_viem.isAddress)(lastPart) ? (0, import_viem.getAddress)(lastPart) : rawSource).toLowerCase();
1333
+ const wallet = normalizeWalletAddress((0, import_viem.isAddress)(lastPart) ? (0, import_viem.getAddress)(lastPart) : rawSource);
1277
1334
  pluginCtx.setVerifiedWallet(wallet);
1278
1335
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1279
1336
  protocol: "mpp",
@@ -1769,16 +1826,16 @@ var MemoryEntitlementStore = class {
1769
1826
  async has(route, wallet) {
1770
1827
  const wallets = this.routeToWallets.get(route);
1771
1828
  if (!wallets) return false;
1772
- return wallets.has(wallet.toLowerCase());
1829
+ return wallets.has(normalizeWalletAddress(wallet));
1773
1830
  }
1774
1831
  async grant(route, wallet) {
1775
- const normalizedWallet = wallet.toLowerCase();
1832
+ const normalized = normalizeWalletAddress(wallet);
1776
1833
  let wallets = this.routeToWallets.get(route);
1777
1834
  if (!wallets) {
1778
1835
  wallets = /* @__PURE__ */ new Set();
1779
1836
  this.routeToWallets.set(route, wallets);
1780
1837
  }
1781
- wallets.add(normalizedWallet);
1838
+ wallets.add(normalized);
1782
1839
  }
1783
1840
  };
1784
1841
  function detectRedisClientType2(client) {
@@ -1801,26 +1858,26 @@ function createRedisEntitlementStore(client, options) {
1801
1858
  return {
1802
1859
  async has(route, wallet) {
1803
1860
  const key = `${prefix}${route}`;
1804
- const normalizedWallet = wallet.toLowerCase();
1861
+ const normalized = normalizeWalletAddress(wallet);
1805
1862
  if (clientType === "upstash") {
1806
1863
  const redis2 = client;
1807
- const result2 = await redis2.sismember(key, normalizedWallet);
1864
+ const result2 = await redis2.sismember(key, normalized);
1808
1865
  return result2 === 1 || result2 === true;
1809
1866
  }
1810
1867
  const redis = client;
1811
- const result = await redis.sismember(key, normalizedWallet);
1868
+ const result = await redis.sismember(key, normalized);
1812
1869
  return result === 1;
1813
1870
  },
1814
1871
  async grant(route, wallet) {
1815
1872
  const key = `${prefix}${route}`;
1816
- const normalizedWallet = wallet.toLowerCase();
1873
+ const normalized = normalizeWalletAddress(wallet);
1817
1874
  if (clientType === "upstash") {
1818
1875
  const redis2 = client;
1819
- await redis2.sadd(key, normalizedWallet);
1876
+ await redis2.sadd(key, normalized);
1820
1877
  return;
1821
1878
  }
1822
1879
  const redis = client;
1823
- await redis.sadd(key, normalizedWallet);
1880
+ await redis.sadd(key, normalized);
1824
1881
  }
1825
1882
  };
1826
1883
  }
package/dist/index.js CHANGED
@@ -245,12 +245,12 @@ async function createX402Server(config) {
245
245
  facilitatorsByNetwork
246
246
  };
247
247
  }
248
- function cachedClient(inner, networks) {
248
+ function cachedClient(inner, kinds) {
249
249
  return {
250
250
  verify: inner.verify.bind(inner),
251
251
  settle: inner.settle.bind(inner),
252
252
  getSupported: async () => ({
253
- kinds: networks.map((network) => ({ x402Version: 2, scheme: "exact", network })),
253
+ kinds,
254
254
  extensions: [],
255
255
  signers: {}
256
256
  })
@@ -260,7 +260,19 @@ function createFacilitatorClients(facilitatorsByNetwork, HTTPFacilitatorClient)
260
260
  const groups = getResolvedX402FacilitatorGroups(facilitatorsByNetwork);
261
261
  return groups.map((group) => {
262
262
  const inner = new HTTPFacilitatorClient(group.config);
263
- return group.family === "evm" ? cachedClient(inner, group.networks) : inner;
263
+ const kinds = group.networks.map((network) => ({
264
+ x402Version: 2,
265
+ scheme: "exact",
266
+ network,
267
+ ...group.family === "solana" ? {
268
+ extra: {
269
+ features: {
270
+ xSettlementAccountSupported: true
271
+ }
272
+ }
273
+ } : {}
274
+ }));
275
+ return cachedClient(inner, kinds);
264
276
  });
265
277
  }
266
278
  var init_server = __esm({
@@ -327,6 +339,11 @@ var RouteRegistry = class {
327
339
  // src/orchestrate.ts
328
340
  import { NextResponse as NextResponse2 } from "next/server";
329
341
 
342
+ // src/auth/normalize-wallet.ts
343
+ function normalizeWalletAddress(address) {
344
+ return address.startsWith("0x") ? address.toLowerCase() : address;
345
+ }
346
+
330
347
  // src/plugin.ts
331
348
  function createDefaultContext(meta) {
332
349
  const ctx = {
@@ -637,12 +654,33 @@ async function buildExpectedRequirements(server, request, price, accepts) {
637
654
  return [...exactRequirements, ...customRequirements];
638
655
  }
639
656
  async function buildExactRequirements(server, request, price, accepts) {
640
- const exactOptions = [
641
- ...buildEvmExactOptions(accepts, price),
642
- ...buildSolanaExactOptions(accepts, price)
643
- ];
644
- if (exactOptions.length === 0) return [];
645
- return server.buildPaymentRequirementsFromOptions(exactOptions, { request });
657
+ const exactGroups = [
658
+ buildEvmExactOptions(accepts, price),
659
+ buildSolanaExactOptions(accepts, price)
660
+ ].filter((options) => options.length > 0);
661
+ if (exactGroups.length === 0) return [];
662
+ const requirements = [];
663
+ const failures = [];
664
+ for (const options of exactGroups) {
665
+ try {
666
+ requirements.push(
667
+ ...await server.buildPaymentRequirementsFromOptions(options, { request })
668
+ );
669
+ } catch (error) {
670
+ const err = error instanceof Error ? error : new Error(String(error));
671
+ failures.push(err);
672
+ if (exactGroups.length === 1) {
673
+ throw err;
674
+ }
675
+ console.warn(
676
+ `[router] Failed to build x402 exact requirements for ${options[0]?.network}: ${err.message}`
677
+ );
678
+ }
679
+ }
680
+ if (requirements.length > 0) {
681
+ return requirements;
682
+ }
683
+ throw failures[0] ?? new Error("Failed to build x402 exact requirements");
646
684
  }
647
685
  function buildCustomRequirements(price, accepts) {
648
686
  return accepts.filter((accept) => accept.scheme !== "exact").map((accept) => buildCustomRequirement(price, accept));
@@ -838,6 +876,23 @@ function getRequirementNetwork(requirements, fallback) {
838
876
  const network = requirements?.network;
839
877
  return typeof network === "string" ? network : fallback;
840
878
  }
879
+ function siwxSignatureType(network) {
880
+ return network.startsWith("solana:") ? "ed25519" : "eip191";
881
+ }
882
+ function getSupportedChains(x402Accepts, fallbackNetwork) {
883
+ const seen = /* @__PURE__ */ new Set();
884
+ const chains = [];
885
+ for (const accept of x402Accepts) {
886
+ if (accept.network && !seen.has(accept.network)) {
887
+ seen.add(accept.network);
888
+ chains.push({ chainId: accept.network, type: siwxSignatureType(accept.network) });
889
+ }
890
+ }
891
+ if (chains.length === 0) {
892
+ chains.push({ chainId: fallbackNetwork, type: siwxSignatureType(fallbackNetwork) });
893
+ }
894
+ return chains;
895
+ }
841
896
  function createRequestHandler(routeEntry, handler, deps) {
842
897
  async function invoke(request, meta, pluginCtx, wallet, account, parsedBody) {
843
898
  const ctx = {
@@ -965,12 +1020,14 @@ function createRequestHandler(routeEntry, handler, deps) {
965
1020
  if (!siwxHeader && routeEntry.authMode === "siwx") {
966
1021
  const url = new URL(request.url);
967
1022
  const nonce = crypto.randomUUID().replace(/-/g, "");
1023
+ const supportedChains = getSupportedChains(deps.x402Accepts, deps.network);
1024
+ const primaryChain = supportedChains[0];
968
1025
  const siwxInfo = {
969
1026
  domain: url.hostname,
970
1027
  uri: request.url,
971
1028
  version: "1",
972
- chainId: deps.network,
973
- type: "eip191",
1029
+ chainId: primaryChain.chainId,
1030
+ type: primaryChain.type,
974
1031
  nonce,
975
1032
  issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
976
1033
  expirationTime: new Date(Date.now() + SIWX_CHALLENGE_EXPIRY_MS).toISOString(),
@@ -994,7 +1051,7 @@ function createRequestHandler(routeEntry, handler, deps) {
994
1051
  "sign-in-with-x": {
995
1052
  info: siwxInfo,
996
1053
  // supportedChains at top level required by MCP tools for chain detection
997
- supportedChains: [{ chainId: deps.network, type: "eip191" }],
1054
+ supportedChains,
998
1055
  ...siwxSchema ? { schema: siwxSchema } : {}
999
1056
  }
1000
1057
  }
@@ -1030,7 +1087,7 @@ function createRequestHandler(routeEntry, handler, deps) {
1030
1087
  return response;
1031
1088
  }
1032
1089
  } else {
1033
- const wallet = siwx.wallet.toLowerCase();
1090
+ const wallet = normalizeWalletAddress(siwx.wallet);
1034
1091
  pluginCtx.setVerifiedWallet(wallet);
1035
1092
  if (routeEntry.authMode === "siwx") {
1036
1093
  firePluginHook(deps.plugin, "onAuthVerified", pluginCtx, {
@@ -1132,7 +1189,7 @@ function createRequestHandler(routeEntry, handler, deps) {
1132
1189
  if (!verify?.valid) return await build402(request, routeEntry, deps, meta, pluginCtx);
1133
1190
  const { payload: verifyPayload, requirements: verifyRequirements } = verify;
1134
1191
  const matchedNetwork = getRequirementNetwork(verifyRequirements, deps.network);
1135
- const wallet = verify.payer.toLowerCase();
1192
+ const wallet = normalizeWalletAddress(verify.payer);
1136
1193
  pluginCtx.setVerifiedWallet(wallet);
1137
1194
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1138
1195
  protocol: "x402",
@@ -1234,7 +1291,7 @@ function createRequestHandler(routeEntry, handler, deps) {
1234
1291
  const rawSource = credential?.source ?? "";
1235
1292
  const didParts = rawSource.split(":");
1236
1293
  const lastPart = didParts[didParts.length - 1];
1237
- const wallet = (isAddress(lastPart) ? getAddress(lastPart) : rawSource).toLowerCase();
1294
+ const wallet = normalizeWalletAddress(isAddress(lastPart) ? getAddress(lastPart) : rawSource);
1238
1295
  pluginCtx.setVerifiedWallet(wallet);
1239
1296
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1240
1297
  protocol: "mpp",
@@ -1730,16 +1787,16 @@ var MemoryEntitlementStore = class {
1730
1787
  async has(route, wallet) {
1731
1788
  const wallets = this.routeToWallets.get(route);
1732
1789
  if (!wallets) return false;
1733
- return wallets.has(wallet.toLowerCase());
1790
+ return wallets.has(normalizeWalletAddress(wallet));
1734
1791
  }
1735
1792
  async grant(route, wallet) {
1736
- const normalizedWallet = wallet.toLowerCase();
1793
+ const normalized = normalizeWalletAddress(wallet);
1737
1794
  let wallets = this.routeToWallets.get(route);
1738
1795
  if (!wallets) {
1739
1796
  wallets = /* @__PURE__ */ new Set();
1740
1797
  this.routeToWallets.set(route, wallets);
1741
1798
  }
1742
- wallets.add(normalizedWallet);
1799
+ wallets.add(normalized);
1743
1800
  }
1744
1801
  };
1745
1802
  function detectRedisClientType2(client) {
@@ -1762,26 +1819,26 @@ function createRedisEntitlementStore(client, options) {
1762
1819
  return {
1763
1820
  async has(route, wallet) {
1764
1821
  const key = `${prefix}${route}`;
1765
- const normalizedWallet = wallet.toLowerCase();
1822
+ const normalized = normalizeWalletAddress(wallet);
1766
1823
  if (clientType === "upstash") {
1767
1824
  const redis2 = client;
1768
- const result2 = await redis2.sismember(key, normalizedWallet);
1825
+ const result2 = await redis2.sismember(key, normalized);
1769
1826
  return result2 === 1 || result2 === true;
1770
1827
  }
1771
1828
  const redis = client;
1772
- const result = await redis.sismember(key, normalizedWallet);
1829
+ const result = await redis.sismember(key, normalized);
1773
1830
  return result === 1;
1774
1831
  },
1775
1832
  async grant(route, wallet) {
1776
1833
  const key = `${prefix}${route}`;
1777
- const normalizedWallet = wallet.toLowerCase();
1834
+ const normalized = normalizeWalletAddress(wallet);
1778
1835
  if (clientType === "upstash") {
1779
1836
  const redis2 = client;
1780
- await redis2.sadd(key, normalizedWallet);
1837
+ await redis2.sadd(key, normalized);
1781
1838
  return;
1782
1839
  }
1783
1840
  const redis = client;
1784
- await redis.sadd(key, normalizedWallet);
1841
+ await redis.sadd(key, normalized);
1785
1842
  }
1786
1843
  };
1787
1844
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
5
5
  "type": "module",
6
6
  "exports": {