@dexterai/x402 1.4.0 → 1.5.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.
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/server/index.ts
@@ -48,6 +58,7 @@ __export(server_exports, {
48
58
  getTextModels: () => getTextModels,
49
59
  isValidModel: () => isValidModel,
50
60
  isValidModelId: () => isValidModelId,
61
+ x402AccessPass: () => x402AccessPass,
51
62
  x402Middleware: () => x402Middleware
52
63
  });
53
64
  module.exports = __toCommonJS(server_exports);
@@ -137,6 +148,7 @@ var FacilitatorClient = class {
137
148
  try {
138
149
  const paymentPayload = decodeBase64Json(paymentSignatureHeader);
139
150
  const verifyPayload = {
151
+ x402Version: 2,
140
152
  paymentPayload,
141
153
  paymentRequirements: requirements
142
154
  };
@@ -175,6 +187,7 @@ var FacilitatorClient = class {
175
187
  try {
176
188
  const paymentPayload = decodeBase64Json(paymentSignatureHeader);
177
189
  const settlePayload = {
190
+ x402Version: 2,
178
191
  paymentPayload,
179
192
  paymentRequirements: requirements
180
193
  };
@@ -245,6 +258,7 @@ function createX402Server(config) {
245
258
  return {
246
259
  scheme: "exact",
247
260
  network,
261
+ amount: amountAtomic,
248
262
  maxAmountRequired: amountAtomic,
249
263
  asset: asset.address,
250
264
  payTo,
@@ -390,6 +404,13 @@ function x402Middleware(config) {
390
404
  payer: verifyResult.payer ?? "",
391
405
  network
392
406
  };
407
+ const paymentResponseData = {
408
+ success: true,
409
+ transaction: settleResult.transaction,
410
+ network,
411
+ payer: verifyResult.payer ?? ""
412
+ };
413
+ res.setHeader("PAYMENT-RESPONSE", btoa(JSON.stringify(paymentResponseData)));
393
414
  next();
394
415
  } catch (error) {
395
416
  log("Middleware error:", error);
@@ -401,6 +422,245 @@ function x402Middleware(config) {
401
422
  };
402
423
  }
403
424
 
425
+ // src/server/access-pass.ts
426
+ var import_crypto = __toESM(require("crypto"), 1);
427
+ var DURATION_REGEX = /^(\d+)(m|h|d|w)$/;
428
+ function parseTierDuration(tierId) {
429
+ const match = tierId.match(DURATION_REGEX);
430
+ if (!match) return null;
431
+ const value = parseInt(match[1], 10);
432
+ const unit = match[2];
433
+ switch (unit) {
434
+ case "m":
435
+ return value * 60;
436
+ case "h":
437
+ return value * 3600;
438
+ case "d":
439
+ return value * 86400;
440
+ case "w":
441
+ return value * 604800;
442
+ default:
443
+ return null;
444
+ }
445
+ }
446
+ function formatDuration(seconds) {
447
+ if (seconds >= 604800 && seconds % 604800 === 0) return `${seconds / 604800} week${seconds / 604800 > 1 ? "s" : ""}`;
448
+ if (seconds >= 86400 && seconds % 86400 === 0) return `${seconds / 86400} day${seconds / 86400 > 1 ? "s" : ""}`;
449
+ if (seconds >= 3600 && seconds % 3600 === 0) return `${seconds / 3600} hour${seconds / 3600 > 1 ? "s" : ""}`;
450
+ if (seconds >= 60 && seconds % 60 === 0) return `${seconds / 60} minute${seconds / 60 > 1 ? "s" : ""}`;
451
+ return `${seconds} second${seconds > 1 ? "s" : ""}`;
452
+ }
453
+ function signJwt(payload, secret) {
454
+ const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
455
+ const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
456
+ const sig = import_crypto.default.createHmac("sha256", secret).update(`${header}.${body}`).digest("base64url");
457
+ return `${header}.${body}.${sig}`;
458
+ }
459
+ function verifyJwt(token, secret) {
460
+ try {
461
+ const parts = token.split(".");
462
+ if (parts.length !== 3) return null;
463
+ const [header, body, sig] = parts;
464
+ const expected = import_crypto.default.createHmac("sha256", secret).update(`${header}.${body}`).digest("base64url");
465
+ if (sig !== expected) return null;
466
+ const payload = JSON.parse(Buffer.from(body, "base64url").toString());
467
+ if (payload.exp && Date.now() / 1e3 > payload.exp) return null;
468
+ if (payload.sub !== "x402-access-pass") return null;
469
+ return payload;
470
+ } catch {
471
+ return null;
472
+ }
473
+ }
474
+ var DEFAULT_NETWORK2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
475
+ var USDC_DECIMALS2 = 6;
476
+ function x402AccessPass(config) {
477
+ const {
478
+ payTo,
479
+ network = DEFAULT_NETWORK2,
480
+ asset,
481
+ facilitatorUrl,
482
+ tiers: tierPrices,
483
+ ratePerHour,
484
+ secret = import_crypto.default.randomBytes(32),
485
+ issuer = "x402-access-pass",
486
+ verbose = false,
487
+ description
488
+ } = config;
489
+ if (!tierPrices && !ratePerHour) {
490
+ throw new Error("x402AccessPass: at least one of `tiers` or `ratePerHour` is required");
491
+ }
492
+ const log = verbose ? console.log.bind(console, "[x402:access-pass]") : () => {
493
+ };
494
+ const decimals = asset?.decimals ?? USDC_DECIMALS2;
495
+ const builtTiers = [];
496
+ if (tierPrices) {
497
+ for (const [id, price] of Object.entries(tierPrices)) {
498
+ const seconds = parseTierDuration(id);
499
+ if (!seconds) {
500
+ console.warn(`x402AccessPass: skipping tier "${id}" \u2014 unrecognized duration format (use 5m, 1h, 24h, 7d)`);
501
+ continue;
502
+ }
503
+ builtTiers.push({
504
+ id,
505
+ label: formatDuration(seconds),
506
+ seconds,
507
+ price,
508
+ priceAtomic: toAtomicUnits(parseFloat(price), decimals)
509
+ });
510
+ }
511
+ builtTiers.sort((a, b) => a.seconds - b.seconds);
512
+ }
513
+ const server = createX402Server({
514
+ payTo,
515
+ network,
516
+ asset,
517
+ facilitatorUrl
518
+ });
519
+ const passInfo = {
520
+ tiers: builtTiers.length > 0 ? builtTiers : void 0,
521
+ ratePerHour: ratePerHour || void 0,
522
+ issuer
523
+ };
524
+ const passInfoEncoded = btoa(JSON.stringify(passInfo));
525
+ function calculateCustomPrice(durationSeconds) {
526
+ if (!ratePerHour) {
527
+ throw new Error("Custom durations not supported \u2014 no ratePerHour configured");
528
+ }
529
+ const hours = durationSeconds / 3600;
530
+ const price = (parseFloat(ratePerHour) * hours).toFixed(decimals > 4 ? 4 : 2);
531
+ return { price, priceAtomic: toAtomicUnits(parseFloat(price), decimals) };
532
+ }
533
+ function resolvePricing(req) {
534
+ const tierParam = req.query.tier;
535
+ const durationParam = req.query.duration;
536
+ if (tierParam) {
537
+ const found = builtTiers.find((t) => t.id === tierParam);
538
+ if (found) {
539
+ return { tier: found.id, seconds: found.seconds, price: found.price, priceAtomic: found.priceAtomic, label: found.label };
540
+ }
541
+ }
542
+ if (durationParam) {
543
+ const seconds = parseInt(durationParam, 10);
544
+ if (seconds > 0 && ratePerHour) {
545
+ const pricing2 = calculateCustomPrice(seconds);
546
+ return { tier: "custom", seconds, ...pricing2, label: formatDuration(seconds) };
547
+ }
548
+ }
549
+ if (builtTiers.length > 0) {
550
+ const t = builtTiers[0];
551
+ return { tier: t.id, seconds: t.seconds, price: t.price, priceAtomic: t.priceAtomic, label: t.label };
552
+ }
553
+ const pricing = calculateCustomPrice(3600);
554
+ return { tier: "custom", seconds: 3600, ...pricing, label: "1 hour" };
555
+ }
556
+ return async (req, res, next) => {
557
+ try {
558
+ const auth = req.headers.authorization;
559
+ if (auth?.startsWith("Bearer ")) {
560
+ const claims = verifyJwt(auth.slice(7), secret);
561
+ if (claims) {
562
+ log("Valid access pass:", claims.tier, "| expires:", new Date(claims.exp * 1e3).toISOString());
563
+ req.accessPass = {
564
+ tier: claims.tier,
565
+ duration: claims.duration,
566
+ expiresAt: new Date(claims.exp * 1e3).toISOString(),
567
+ payer: claims.payer,
568
+ network: claims.network
569
+ };
570
+ return next();
571
+ }
572
+ log("Invalid or expired access pass token");
573
+ }
574
+ const paymentSignature = req.headers["payment-signature"];
575
+ if (paymentSignature) {
576
+ log("Payment signature received, verifying for pass purchase...");
577
+ const verifyResult = await server.verifyPayment(paymentSignature);
578
+ if (!verifyResult.isValid) {
579
+ log("Payment verification failed:", verifyResult.invalidReason);
580
+ res.status(402).json({ error: "Payment verification failed", reason: verifyResult.invalidReason });
581
+ return;
582
+ }
583
+ const settleResult = await server.settlePayment(paymentSignature);
584
+ if (!settleResult.success) {
585
+ log("Payment settlement failed:", settleResult.errorReason);
586
+ res.status(402).json({ error: "Payment settlement failed", reason: settleResult.errorReason });
587
+ return;
588
+ }
589
+ log("Payment settled:", settleResult.transaction);
590
+ const pricing2 = resolvePricing(req);
591
+ const now = Math.floor(Date.now() / 1e3);
592
+ const claims = {
593
+ sub: "x402-access-pass",
594
+ tier: pricing2.tier,
595
+ duration: pricing2.seconds,
596
+ iat: now,
597
+ exp: now + pricing2.seconds,
598
+ payer: verifyResult.payer ?? "",
599
+ network,
600
+ iss: issuer
601
+ };
602
+ const jwt = signJwt(claims, secret);
603
+ req.x402 = {
604
+ transaction: settleResult.transaction,
605
+ payer: verifyResult.payer ?? "",
606
+ network
607
+ };
608
+ const paymentResponseData = {
609
+ success: true,
610
+ transaction: settleResult.transaction,
611
+ network,
612
+ payer: verifyResult.payer ?? ""
613
+ };
614
+ res.setHeader("PAYMENT-RESPONSE", btoa(JSON.stringify(paymentResponseData)));
615
+ res.setHeader("ACCESS-PASS", jwt);
616
+ res.json({
617
+ accessPass: {
618
+ token: jwt,
619
+ tier: pricing2.tier,
620
+ duration: pricing2.label,
621
+ durationSeconds: pricing2.seconds,
622
+ expiresAt: new Date((now + pricing2.seconds) * 1e3).toISOString(),
623
+ usage: "Include on subsequent requests as: Authorization: Bearer <token>"
624
+ },
625
+ transaction: settleResult.transaction,
626
+ payer: verifyResult.payer
627
+ });
628
+ return;
629
+ }
630
+ log("No access pass or payment, returning 402");
631
+ const pricing = resolvePricing(req);
632
+ const amountAtomic = pricing.priceAtomic;
633
+ const resourceUrl = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
634
+ const requirements = await server.buildRequirements({
635
+ amountAtomic,
636
+ resourceUrl,
637
+ description: description || `Access pass: ${pricing.label}`,
638
+ mimeType: "application/json"
639
+ });
640
+ const encoded = server.encodeRequirements(requirements);
641
+ res.setHeader("PAYMENT-REQUIRED", encoded);
642
+ res.setHeader("X-ACCESS-PASS-TIERS", passInfoEncoded);
643
+ res.status(402).json({
644
+ error: "Access pass required",
645
+ message: "Purchase an access pass to unlock unlimited API access for a time window.",
646
+ accepts: requirements.accepts,
647
+ resource: requirements.resource,
648
+ accessPass: {
649
+ tiers: builtTiers.length > 0 ? builtTiers : void 0,
650
+ ratePerHour: ratePerHour || void 0,
651
+ usage: "Add ?tier=<id> or ?duration=<seconds> to your payment request to choose a pass duration."
652
+ }
653
+ });
654
+ } catch (error) {
655
+ log("Access pass middleware error:", error);
656
+ res.status(500).json({
657
+ error: "Payment processing error",
658
+ message: error instanceof Error ? error.message : "Unknown error"
659
+ });
660
+ }
661
+ };
662
+ }
663
+
404
664
  // src/server/dynamic-pricing.ts
405
665
  function simpleHash(str) {
406
666
  let hash = 2166136261;
@@ -484,7 +744,7 @@ function formatPricing(config) {
484
744
  }
485
745
 
486
746
  // src/server/token-pricing.ts
487
- var import_crypto = require("crypto");
747
+ var import_crypto2 = require("crypto");
488
748
  var import_tiktoken = require("tiktoken");
489
749
 
490
750
  // src/server/model-registry.ts
@@ -1082,7 +1342,7 @@ function countTokens(text, model = DEFAULT_MODEL) {
1082
1342
  }
1083
1343
  function generateQuoteHash(prompt, model, rate, tokens) {
1084
1344
  const configString = JSON.stringify({ model, rate, tokens });
1085
- return (0, import_crypto.createHash)("sha256").update(prompt + configString).digest("hex").slice(0, 16);
1345
+ return (0, import_crypto2.createHash)("sha256").update(prompt + configString).digest("hex").slice(0, 16);
1086
1346
  }
1087
1347
  function createTokenPricing(config = {}) {
1088
1348
  const model = config.model ?? DEFAULT_MODEL;
@@ -1201,6 +1461,7 @@ function formatTokenPricing(model = DEFAULT_MODEL) {
1201
1461
  getTextModels,
1202
1462
  isValidModel,
1203
1463
  isValidModelId,
1464
+ x402AccessPass,
1204
1465
  x402Middleware
1205
1466
  });
1206
1467
  //# sourceMappingURL=index.cjs.map