@agentcash/router 0.2.0 → 0.2.1

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
@@ -41,8 +41,9 @@ async function createX402Server(config) {
41
41
  const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
42
42
  const { siwxResourceServerExtension } = await import("@x402/extensions/sign-in-with-x");
43
43
  const { facilitator: defaultFacilitator } = await import("@coinbase/x402");
44
- const facilitatorUrl = config.facilitatorUrl ?? defaultFacilitator;
45
- const client = new HTTPFacilitatorClient(facilitatorUrl);
44
+ const raw = config.facilitatorUrl ?? defaultFacilitator;
45
+ const facilitatorConfig = typeof raw === "string" ? { url: raw } : raw;
46
+ const client = new HTTPFacilitatorClient(facilitatorConfig);
46
47
  const server = new x402ResourceServer(client);
47
48
  registerExactEvmScheme(server);
48
49
  server.registerExtension(bazaarResourceServerExtension);
@@ -53,7 +54,7 @@ async function createX402Server(config) {
53
54
  async function retryInit(server, maxAttempts = 3, backoff = [1e3, 2e3, 4e3]) {
54
55
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
55
56
  try {
56
- await server.init();
57
+ await server.initialize();
57
58
  return;
58
59
  } catch (err) {
59
60
  const is429 = err instanceof Error && (err.message.includes("429") || err.message.includes("rate limit"));
@@ -301,10 +302,10 @@ async function buildX402Challenge(server, routeEntry, request, price, payeeAddre
301
302
  method: routeEntry.method,
302
303
  description: routeEntry.description
303
304
  };
304
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
305
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
305
306
  request
306
307
  });
307
- const paymentRequired = server.createPaymentRequiredResponse(
308
+ const paymentRequired = await server.createPaymentRequiredResponse(
308
309
  requirements,
309
310
  resource,
310
311
  null,
@@ -324,7 +325,7 @@ async function verifyX402Payment(server, request, routeEntry, price, payeeAddres
324
325
  price,
325
326
  payTo: payeeAddress
326
327
  };
327
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
328
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
328
329
  request
329
330
  });
330
331
  const matching = server.findMatchingRequirements(requirements, payload);
@@ -368,12 +369,12 @@ async function ensureMpay() {
368
369
  }
369
370
  async function buildMPPChallenge(routeEntry, request, mppConfig, price) {
370
371
  await ensureMpay();
371
- const intent = {
372
+ const methodIntent = tempo.charge({
372
373
  amount: price,
373
374
  currency: mppConfig.currency,
374
375
  recipient: mppConfig.recipient ?? ""
375
- };
376
- const challenge = Challenge.fromIntent(intent, {
376
+ });
377
+ const challenge = Challenge.fromIntent(methodIntent, {
377
378
  secretKey: mppConfig.secretKey,
378
379
  realm: new URL(request.url).origin,
379
380
  request
@@ -384,7 +385,7 @@ async function verifyMPPCredential(request, _routeEntry, mppConfig, price) {
384
385
  await ensureMpay();
385
386
  const credential = Credential.fromRequest(request);
386
387
  if (!credential) return null;
387
- const isValid = Challenge.verify(credential, { secretKey: mppConfig.secretKey });
388
+ const isValid = Challenge.verify(credential.challenge, { secretKey: mppConfig.secretKey });
388
389
  if (!isValid) {
389
390
  return { valid: false, payer: null };
390
391
  }
@@ -408,7 +409,7 @@ async function buildMPPReceipt(reference) {
408
409
  method: "tempo",
409
410
  status: "success",
410
411
  reference,
411
- timestamp: Date.now()
412
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
412
413
  });
413
414
  return Receipt.serialize(receipt);
414
415
  }
@@ -571,7 +572,12 @@ function createRequestHandler(routeEntry, handler, deps) {
571
572
  try {
572
573
  const { encodePaymentRequiredHeader } = await import("@x402/core/http");
573
574
  encoded = encodePaymentRequiredHeader(paymentRequired);
574
- } catch {
575
+ } catch (err) {
576
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
577
+ level: "warn",
578
+ message: `SIWX challenge header encoding failed: ${err instanceof Error ? err.message : String(err)}`,
579
+ route: routeEntry.key
580
+ });
575
581
  }
576
582
  const response = new import_server2.NextResponse(JSON.stringify(paymentRequired), {
577
583
  status: 402,
@@ -774,7 +780,12 @@ async function build402(request, routeEntry, deps, meta, pluginCtx) {
774
780
  extensions
775
781
  );
776
782
  response.headers.set("PAYMENT-REQUIRED", encoded);
777
- } catch {
783
+ } catch (err) {
784
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
785
+ level: "critical",
786
+ message: `x402 challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
787
+ route: routeEntry.key
788
+ });
778
789
  }
779
790
  }
780
791
  if (routeEntry.protocols.includes("mpp") && deps.mppConfig) {
@@ -783,7 +794,12 @@ async function build402(request, routeEntry, deps, meta, pluginCtx) {
783
794
  "WWW-Authenticate",
784
795
  await buildMPPChallenge(routeEntry, request, deps.mppConfig, challengePrice)
785
796
  );
786
- } catch {
797
+ } catch (err) {
798
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
799
+ level: "critical",
800
+ message: `MPP challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
801
+ route: routeEntry.key
802
+ });
787
803
  }
788
804
  }
789
805
  firePluginResponse(deps, pluginCtx, meta, response);
@@ -1158,7 +1174,11 @@ function createRouter(config) {
1158
1174
  const baseUrl = typeof globalThis.process !== "undefined" ? process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000" : "http://localhost:3000";
1159
1175
  if (config.plugin?.init) {
1160
1176
  try {
1161
- config.plugin.init({ origin: baseUrl });
1177
+ const result = config.plugin.init({ origin: baseUrl });
1178
+ if (result && typeof result.catch === "function") {
1179
+ result.catch(() => {
1180
+ });
1181
+ }
1162
1182
  } catch {
1163
1183
  }
1164
1184
  }
package/dist/index.d.cts CHANGED
@@ -82,6 +82,28 @@ interface AlertEvent {
82
82
  meta?: Record<string, unknown>;
83
83
  }
84
84
  type AlertFn = (level: AlertLevel, message: string, meta?: Record<string, unknown>) => void;
85
+ interface X402Server {
86
+ initialize(): Promise<void>;
87
+ buildPaymentRequirementsFromOptions(options: Array<{
88
+ scheme: string;
89
+ network: string;
90
+ price: string;
91
+ payTo: string;
92
+ }>, context: {
93
+ request: Request;
94
+ }): Promise<unknown[]>;
95
+ createPaymentRequiredResponse(requirements: unknown[], resource: {
96
+ url: string;
97
+ method: string;
98
+ description?: string;
99
+ }, error: string | null, extensions?: Record<string, unknown>): Promise<unknown>;
100
+ findMatchingRequirements(requirements: unknown[], payload: unknown): unknown;
101
+ verifyPayment(payload: unknown, requirements: unknown): Promise<{
102
+ isValid: boolean;
103
+ payer?: string;
104
+ }>;
105
+ settlePayment(payload: unknown, requirements: unknown): Promise<unknown>;
106
+ }
85
107
  type ProtocolType = 'x402' | 'mpp';
86
108
  type AuthMode = 'paid' | 'siwx' | 'apiKey' | 'unprotected';
87
109
  interface TierConfig {
@@ -192,7 +214,7 @@ interface OpenAPIOptions {
192
214
  }
193
215
 
194
216
  interface OrchestrateDeps {
195
- x402Server: Record<string, Function> | null;
217
+ x402Server: X402Server | null;
196
218
  initPromise: Promise<void>;
197
219
  x402InitError?: string;
198
220
  plugin?: RouterPlugin;
@@ -272,4 +294,4 @@ interface ServiceRouter {
272
294
  }
273
295
  declare function createRouter(config: RouterConfig): ServiceRouter;
274
296
 
275
- export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, consolePlugin, createRouter };
297
+ export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRouter };
package/dist/index.d.ts CHANGED
@@ -82,6 +82,28 @@ interface AlertEvent {
82
82
  meta?: Record<string, unknown>;
83
83
  }
84
84
  type AlertFn = (level: AlertLevel, message: string, meta?: Record<string, unknown>) => void;
85
+ interface X402Server {
86
+ initialize(): Promise<void>;
87
+ buildPaymentRequirementsFromOptions(options: Array<{
88
+ scheme: string;
89
+ network: string;
90
+ price: string;
91
+ payTo: string;
92
+ }>, context: {
93
+ request: Request;
94
+ }): Promise<unknown[]>;
95
+ createPaymentRequiredResponse(requirements: unknown[], resource: {
96
+ url: string;
97
+ method: string;
98
+ description?: string;
99
+ }, error: string | null, extensions?: Record<string, unknown>): Promise<unknown>;
100
+ findMatchingRequirements(requirements: unknown[], payload: unknown): unknown;
101
+ verifyPayment(payload: unknown, requirements: unknown): Promise<{
102
+ isValid: boolean;
103
+ payer?: string;
104
+ }>;
105
+ settlePayment(payload: unknown, requirements: unknown): Promise<unknown>;
106
+ }
85
107
  type ProtocolType = 'x402' | 'mpp';
86
108
  type AuthMode = 'paid' | 'siwx' | 'apiKey' | 'unprotected';
87
109
  interface TierConfig {
@@ -192,7 +214,7 @@ interface OpenAPIOptions {
192
214
  }
193
215
 
194
216
  interface OrchestrateDeps {
195
- x402Server: Record<string, Function> | null;
217
+ x402Server: X402Server | null;
196
218
  initPromise: Promise<void>;
197
219
  x402InitError?: string;
198
220
  plugin?: RouterPlugin;
@@ -272,4 +294,4 @@ interface ServiceRouter {
272
294
  }
273
295
  declare function createRouter(config: RouterConfig): ServiceRouter;
274
296
 
275
- export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, consolePlugin, createRouter };
297
+ export { type AlertEvent, type AlertFn, type AlertLevel, type AuthMode, type ErrorEvent, type HandlerContext, HttpError, MemoryNonceStore, type MonitorEntry, type NonceStore, type OveragePolicy, type PaidOptions, type PaymentEvent, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, type RouterPlugin, type ServiceRouter, type SettlementEvent, type TierConfig, type X402Server, consolePlugin, createRouter };
package/dist/index.js CHANGED
@@ -19,8 +19,9 @@ async function createX402Server(config) {
19
19
  const { bazaarResourceServerExtension } = await import("@x402/extensions/bazaar");
20
20
  const { siwxResourceServerExtension } = await import("@x402/extensions/sign-in-with-x");
21
21
  const { facilitator: defaultFacilitator } = await import("@coinbase/x402");
22
- const facilitatorUrl = config.facilitatorUrl ?? defaultFacilitator;
23
- const client = new HTTPFacilitatorClient(facilitatorUrl);
22
+ const raw = config.facilitatorUrl ?? defaultFacilitator;
23
+ const facilitatorConfig = typeof raw === "string" ? { url: raw } : raw;
24
+ const client = new HTTPFacilitatorClient(facilitatorConfig);
24
25
  const server = new x402ResourceServer(client);
25
26
  registerExactEvmScheme(server);
26
27
  server.registerExtension(bazaarResourceServerExtension);
@@ -31,7 +32,7 @@ async function createX402Server(config) {
31
32
  async function retryInit(server, maxAttempts = 3, backoff = [1e3, 2e3, 4e3]) {
32
33
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
33
34
  try {
34
- await server.init();
35
+ await server.initialize();
35
36
  return;
36
37
  } catch (err) {
37
38
  const is429 = err instanceof Error && (err.message.includes("429") || err.message.includes("rate limit"));
@@ -267,10 +268,10 @@ async function buildX402Challenge(server, routeEntry, request, price, payeeAddre
267
268
  method: routeEntry.method,
268
269
  description: routeEntry.description
269
270
  };
270
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
271
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
271
272
  request
272
273
  });
273
- const paymentRequired = server.createPaymentRequiredResponse(
274
+ const paymentRequired = await server.createPaymentRequiredResponse(
274
275
  requirements,
275
276
  resource,
276
277
  null,
@@ -290,7 +291,7 @@ async function verifyX402Payment(server, request, routeEntry, price, payeeAddres
290
291
  price,
291
292
  payTo: payeeAddress
292
293
  };
293
- const requirements = server.buildPaymentRequirementsFromOptions(options, {
294
+ const requirements = await server.buildPaymentRequirementsFromOptions([options], {
294
295
  request
295
296
  });
296
297
  const matching = server.findMatchingRequirements(requirements, payload);
@@ -334,12 +335,12 @@ async function ensureMpay() {
334
335
  }
335
336
  async function buildMPPChallenge(routeEntry, request, mppConfig, price) {
336
337
  await ensureMpay();
337
- const intent = {
338
+ const methodIntent = tempo.charge({
338
339
  amount: price,
339
340
  currency: mppConfig.currency,
340
341
  recipient: mppConfig.recipient ?? ""
341
- };
342
- const challenge = Challenge.fromIntent(intent, {
342
+ });
343
+ const challenge = Challenge.fromIntent(methodIntent, {
343
344
  secretKey: mppConfig.secretKey,
344
345
  realm: new URL(request.url).origin,
345
346
  request
@@ -350,7 +351,7 @@ async function verifyMPPCredential(request, _routeEntry, mppConfig, price) {
350
351
  await ensureMpay();
351
352
  const credential = Credential.fromRequest(request);
352
353
  if (!credential) return null;
353
- const isValid = Challenge.verify(credential, { secretKey: mppConfig.secretKey });
354
+ const isValid = Challenge.verify(credential.challenge, { secretKey: mppConfig.secretKey });
354
355
  if (!isValid) {
355
356
  return { valid: false, payer: null };
356
357
  }
@@ -374,7 +375,7 @@ async function buildMPPReceipt(reference) {
374
375
  method: "tempo",
375
376
  status: "success",
376
377
  reference,
377
- timestamp: Date.now()
378
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
378
379
  });
379
380
  return Receipt.serialize(receipt);
380
381
  }
@@ -537,7 +538,12 @@ function createRequestHandler(routeEntry, handler, deps) {
537
538
  try {
538
539
  const { encodePaymentRequiredHeader } = await import("@x402/core/http");
539
540
  encoded = encodePaymentRequiredHeader(paymentRequired);
540
- } catch {
541
+ } catch (err) {
542
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
543
+ level: "warn",
544
+ message: `SIWX challenge header encoding failed: ${err instanceof Error ? err.message : String(err)}`,
545
+ route: routeEntry.key
546
+ });
541
547
  }
542
548
  const response = new NextResponse2(JSON.stringify(paymentRequired), {
543
549
  status: 402,
@@ -740,7 +746,12 @@ async function build402(request, routeEntry, deps, meta, pluginCtx) {
740
746
  extensions
741
747
  );
742
748
  response.headers.set("PAYMENT-REQUIRED", encoded);
743
- } catch {
749
+ } catch (err) {
750
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
751
+ level: "critical",
752
+ message: `x402 challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
753
+ route: routeEntry.key
754
+ });
744
755
  }
745
756
  }
746
757
  if (routeEntry.protocols.includes("mpp") && deps.mppConfig) {
@@ -749,7 +760,12 @@ async function build402(request, routeEntry, deps, meta, pluginCtx) {
749
760
  "WWW-Authenticate",
750
761
  await buildMPPChallenge(routeEntry, request, deps.mppConfig, challengePrice)
751
762
  );
752
- } catch {
763
+ } catch (err) {
764
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
765
+ level: "critical",
766
+ message: `MPP challenge build failed: ${err instanceof Error ? err.message : String(err)}`,
767
+ route: routeEntry.key
768
+ });
753
769
  }
754
770
  }
755
771
  firePluginResponse(deps, pluginCtx, meta, response);
@@ -1124,7 +1140,11 @@ function createRouter(config) {
1124
1140
  const baseUrl = typeof globalThis.process !== "undefined" ? process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000" : "http://localhost:3000";
1125
1141
  if (config.plugin?.init) {
1126
1142
  try {
1127
- config.plugin.init({ origin: baseUrl });
1143
+ const result = config.plugin.init({ origin: baseUrl });
1144
+ if (result && typeof result.catch === "function") {
1145
+ result.catch(() => {
1146
+ });
1147
+ }
1128
1148
  } catch {
1129
1149
  }
1130
1150
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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": {
@@ -70,6 +70,6 @@
70
70
  "license": "MIT",
71
71
  "repository": {
72
72
  "type": "git",
73
- "url": "https://github.com/merit-systems/agentcash-router"
73
+ "url": "git+https://github.com/merit-systems/agentcash-router.git"
74
74
  }
75
75
  }