@dexterai/x402 1.4.1 → 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);
@@ -411,6 +422,245 @@ function x402Middleware(config) {
411
422
  };
412
423
  }
413
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
+
414
664
  // src/server/dynamic-pricing.ts
415
665
  function simpleHash(str) {
416
666
  let hash = 2166136261;
@@ -494,7 +744,7 @@ function formatPricing(config) {
494
744
  }
495
745
 
496
746
  // src/server/token-pricing.ts
497
- var import_crypto = require("crypto");
747
+ var import_crypto2 = require("crypto");
498
748
  var import_tiktoken = require("tiktoken");
499
749
 
500
750
  // src/server/model-registry.ts
@@ -1092,7 +1342,7 @@ function countTokens(text, model = DEFAULT_MODEL) {
1092
1342
  }
1093
1343
  function generateQuoteHash(prompt, model, rate, tokens) {
1094
1344
  const configString = JSON.stringify({ model, rate, tokens });
1095
- 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);
1096
1346
  }
1097
1347
  function createTokenPricing(config = {}) {
1098
1348
  const model = config.model ?? DEFAULT_MODEL;
@@ -1211,6 +1461,7 @@ function formatTokenPricing(model = DEFAULT_MODEL) {
1211
1461
  getTextModels,
1212
1462
  isValidModel,
1213
1463
  isValidModelId,
1464
+ x402AccessPass,
1214
1465
  x402Middleware
1215
1466
  });
1216
1467
  //# sourceMappingURL=index.cjs.map