@routstr/sdk 0.1.1 → 0.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.mjs CHANGED
@@ -140,6 +140,7 @@ var ModelManager = class _ModelManager {
140
140
  const lastUpdate = this.adapter.getBaseUrlsLastUpdate();
141
141
  const cacheValid = lastUpdate && Date.now() - lastUpdate <= this.cacheTTL;
142
142
  if (cacheValid) {
143
+ await this.fetchRoutstr21Models();
143
144
  return this.filterBaseUrlsForTor(cachedUrls, torMode);
144
145
  }
145
146
  }
@@ -149,6 +150,7 @@ var ModelManager = class _ModelManager {
149
150
  const filtered = this.filterBaseUrlsForTor(nostrProviders, torMode);
150
151
  this.adapter.setBaseUrlsList(filtered);
151
152
  this.adapter.setBaseUrlsLastUpdate(Date.now());
153
+ await this.fetchRoutstr21Models();
152
154
  return filtered;
153
155
  }
154
156
  } catch (e) {
@@ -280,6 +282,7 @@ var ModelManager = class _ModelManager {
280
282
  if (list.length > 0) {
281
283
  this.adapter.setBaseUrlsList(list);
282
284
  this.adapter.setBaseUrlsLastUpdate(Date.now());
285
+ await this.fetchRoutstr21Models();
283
286
  }
284
287
  return list;
285
288
  } catch (e) {
@@ -446,6 +449,69 @@ var ModelManager = class _ModelManager {
446
449
  }
447
450
  return url.endsWith("/") ? url : `${url}/`;
448
451
  }
452
+ /**
453
+ * Fetch routstr21 models from Nostr network (kind 38423)
454
+ * Uses cache if available and not expired
455
+ * @returns Array of model IDs or empty array if not found
456
+ */
457
+ async fetchRoutstr21Models(forceRefresh = false) {
458
+ const cachedModels = this.adapter.getRoutstr21Models();
459
+ if (!forceRefresh && cachedModels.length > 0) {
460
+ const lastUpdate = this.adapter.getRoutstr21ModelsLastUpdate();
461
+ const cacheValid = lastUpdate && Date.now() - lastUpdate <= this.cacheTTL;
462
+ if (cacheValid) {
463
+ return cachedModels;
464
+ }
465
+ }
466
+ const DEFAULT_RELAYS = [
467
+ "wss://relay.primal.net",
468
+ "wss://nos.lol",
469
+ "wss://relay.routstr.com"
470
+ ];
471
+ const pool = new RelayPool();
472
+ const localEventStore = new EventStore();
473
+ const timeoutMs = 5e3;
474
+ await new Promise((resolve) => {
475
+ pool.req(DEFAULT_RELAYS, {
476
+ kinds: [38423],
477
+ "#d": ["routstr-21-models"],
478
+ limit: 1,
479
+ authors: [
480
+ "4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8"
481
+ ]
482
+ }).pipe(
483
+ onlyEvents(),
484
+ tap((event2) => {
485
+ localEventStore.add(event2);
486
+ })
487
+ ).subscribe({
488
+ complete: () => {
489
+ resolve();
490
+ }
491
+ });
492
+ setTimeout(() => {
493
+ resolve();
494
+ }, timeoutMs);
495
+ });
496
+ const timeline = localEventStore.getTimeline({ kinds: [38423] });
497
+ if (timeline.length === 0) {
498
+ return cachedModels.length > 0 ? cachedModels : [];
499
+ }
500
+ const event = timeline[0];
501
+ try {
502
+ const content = JSON.parse(event.content);
503
+ const models = Array.isArray(content?.models) ? content.models : [];
504
+ this.adapter.setRoutstr21Models(models);
505
+ this.adapter.setRoutstr21ModelsLastUpdate(Date.now());
506
+ return models;
507
+ } catch {
508
+ console.warn(
509
+ "[Routstr21Models] Failed to parse Nostr event content:",
510
+ event.id
511
+ );
512
+ return cachedModels.length > 0 ? cachedModels : [];
513
+ }
514
+ }
449
515
  };
450
516
 
451
517
  // discovery/MintDiscovery.ts
@@ -675,6 +741,26 @@ var CashuSpender = class {
675
741
  this.balanceManager = balanceManager;
676
742
  }
677
743
  _isBusy = false;
744
+ debugLevel = "WARN";
745
+ async receiveToken(token) {
746
+ const result = await this.walletAdapter.receiveToken(token);
747
+ if (!result.success && result.message?.includes("Failed to fetch mint")) {
748
+ const cachedTokens = this.storageAdapter.getCachedReceiveTokens();
749
+ const existingIndex = cachedTokens.findIndex((t) => t.token === token);
750
+ if (existingIndex === -1) {
751
+ this.storageAdapter.setCachedReceiveTokens([
752
+ ...cachedTokens,
753
+ {
754
+ token,
755
+ amount: result.amount,
756
+ unit: result.unit,
757
+ createdAt: Date.now()
758
+ }
759
+ ]);
760
+ }
761
+ }
762
+ return result;
763
+ }
678
764
  async _getBalanceState() {
679
765
  const mintBalances = await this.walletAdapter.getBalances();
680
766
  const units = this.walletAdapter.getMintUnits();
@@ -717,6 +803,32 @@ var CashuSpender = class {
717
803
  get isBusy() {
718
804
  return this._isBusy;
719
805
  }
806
+ getDebugLevel() {
807
+ return this.debugLevel;
808
+ }
809
+ setDebugLevel(level) {
810
+ this.debugLevel = level;
811
+ }
812
+ _log(level, ...args) {
813
+ const levelPriority = {
814
+ DEBUG: 0,
815
+ WARN: 1,
816
+ ERROR: 2
817
+ };
818
+ if (levelPriority[level] >= levelPriority[this.debugLevel]) {
819
+ switch (level) {
820
+ case "DEBUG":
821
+ console.log(...args);
822
+ break;
823
+ case "WARN":
824
+ console.warn(...args);
825
+ break;
826
+ case "ERROR":
827
+ console.error(...args);
828
+ break;
829
+ }
830
+ }
831
+ }
720
832
  /**
721
833
  * Spend Cashu tokens with automatic mint selection and retry logic
722
834
  * Throws errors on failure instead of returning failed SpendResult
@@ -783,12 +895,16 @@ var CashuSpender = class {
783
895
  excludeMints,
784
896
  retryCount
785
897
  } = options;
786
- console.log(
898
+ this._log(
899
+ "DEBUG",
787
900
  `[CashuSpender] _spendInternal: amount=${amount}, mintUrl=${mintUrl}, baseUrl=${baseUrl}, reuseToken=${reuseToken}`
788
901
  );
789
902
  let adjustedAmount = Math.ceil(amount);
790
903
  if (!adjustedAmount || isNaN(adjustedAmount)) {
791
- console.error(`[CashuSpender] _spendInternal: Invalid amount: ${amount}`);
904
+ this._log(
905
+ "ERROR",
906
+ `[CashuSpender] _spendInternal: Invalid amount: ${amount}`
907
+ );
792
908
  return {
793
909
  token: null,
794
910
  status: "failed",
@@ -797,7 +913,8 @@ var CashuSpender = class {
797
913
  };
798
914
  }
799
915
  if (reuseToken && baseUrl) {
800
- console.log(
916
+ this._log(
917
+ "DEBUG",
801
918
  `[CashuSpender] _spendInternal: Attempting to reuse token for ${baseUrl}`
802
919
  );
803
920
  const existingResult = await this._tryReuseToken(
@@ -806,12 +923,14 @@ var CashuSpender = class {
806
923
  mintUrl
807
924
  );
808
925
  if (existingResult) {
809
- console.log(
926
+ this._log(
927
+ "DEBUG",
810
928
  `[CashuSpender] _spendInternal: Successfully reused token, balance: ${existingResult.balance}`
811
929
  );
812
930
  return existingResult;
813
931
  }
814
- console.log(
932
+ this._log(
933
+ "DEBUG",
815
934
  `[CashuSpender] _spendInternal: Could not reuse token, will create new token`
816
935
  );
817
936
  }
@@ -829,12 +948,14 @@ var CashuSpender = class {
829
948
  (sum, item) => sum + item.amount,
830
949
  0
831
950
  );
832
- console.log(
951
+ this._log(
952
+ "DEBUG",
833
953
  `[CashuSpender] _spendInternal: totalBalance=${totalBalance}, totalPending=${totalPending}, adjustedAmount=${adjustedAmount}`
834
954
  );
835
955
  const totalAvailableBalance = totalBalance + totalPending;
836
956
  if (totalAvailableBalance < adjustedAmount) {
837
- console.error(
957
+ this._log(
958
+ "ERROR",
838
959
  `[CashuSpender] _spendInternal: Insufficient balance, have=${totalAvailableBalance}, need=${adjustedAmount}`
839
960
  );
840
961
  return this._createInsufficientBalanceError(
@@ -902,7 +1023,8 @@ var CashuSpender = class {
902
1023
  baseUrl,
903
1024
  status: "success"
904
1025
  });
905
- console.log(
1026
+ this._log(
1027
+ "DEBUG",
906
1028
  `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
907
1029
  );
908
1030
  return {
@@ -920,7 +1042,7 @@ var CashuSpender = class {
920
1042
  if (!storedToken) return null;
921
1043
  const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
922
1044
  const balanceForBaseUrl = pendingDistribution.find((b) => b.baseUrl === baseUrl)?.amount || 0;
923
- console.log("RESUINGDSR GSODGNSD", balanceForBaseUrl, amount);
1045
+ this._log("DEBUG", "RESUINGDSR GSODGNSD", balanceForBaseUrl, amount);
924
1046
  if (balanceForBaseUrl > amount) {
925
1047
  const units = this.walletAdapter.getMintUnits();
926
1048
  const unit = units[mintUrl] || "sat";
@@ -938,7 +1060,7 @@ var CashuSpender = class {
938
1060
  baseUrl,
939
1061
  amount: topUpAmount
940
1062
  });
941
- console.log("TOPUP ", topUpResult);
1063
+ this._log("DEBUG", "TOPUP ", topUpResult);
942
1064
  if (topUpResult.success && topUpResult.toppedUpAmount) {
943
1065
  const newBalance = balanceForBaseUrl + topUpResult.toppedUpAmount;
944
1066
  const units = this.walletAdapter.getMintUnits();
@@ -960,7 +1082,7 @@ var CashuSpender = class {
960
1082
  baseUrl,
961
1083
  storedToken
962
1084
  );
963
- console.log(providerBalance);
1085
+ this._log("DEBUG", providerBalance);
964
1086
  if (providerBalance <= 0) {
965
1087
  this.storageAdapter.removeToken(baseUrl);
966
1088
  }
@@ -979,7 +1101,7 @@ var CashuSpender = class {
979
1101
  const refundResults = await Promise.allSettled(
980
1102
  toRefund.map(async (pending) => {
981
1103
  const token = this.storageAdapter.getToken(pending.baseUrl);
982
- console.log(token, this.balanceManager);
1104
+ this._log("DEBUG", token, this.balanceManager);
983
1105
  if (!token || !this.balanceManager) {
984
1106
  return { baseUrl: pending.baseUrl, success: false };
985
1107
  }
@@ -995,7 +1117,7 @@ var CashuSpender = class {
995
1117
  baseUrl: pending.baseUrl,
996
1118
  token
997
1119
  });
998
- console.log(result);
1120
+ this._log("DEBUG", result);
999
1121
  if (result.success) {
1000
1122
  this.storageAdapter.removeToken(pending.baseUrl);
1001
1123
  }
@@ -1093,11 +1215,22 @@ var CashuSpender = class {
1093
1215
 
1094
1216
  // wallet/BalanceManager.ts
1095
1217
  var BalanceManager = class {
1096
- constructor(walletAdapter, storageAdapter, providerRegistry) {
1218
+ constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
1097
1219
  this.walletAdapter = walletAdapter;
1098
1220
  this.storageAdapter = storageAdapter;
1099
1221
  this.providerRegistry = providerRegistry;
1222
+ if (cashuSpender) {
1223
+ this.cashuSpender = cashuSpender;
1224
+ } else {
1225
+ this.cashuSpender = new CashuSpender(
1226
+ walletAdapter,
1227
+ storageAdapter,
1228
+ providerRegistry,
1229
+ this
1230
+ );
1231
+ }
1100
1232
  }
1233
+ cashuSpender;
1101
1234
  /**
1102
1235
  * Unified refund - handles both NIP-60 and legacy wallet refunds
1103
1236
  */
@@ -1132,7 +1265,7 @@ var BalanceManager = class {
1132
1265
  this.storageAdapter.removeToken(baseUrl);
1133
1266
  return { success: true, message: "No balance to refund" };
1134
1267
  }
1135
- const receiveResult = await this.walletAdapter.receiveToken(
1268
+ const receiveResult = await this.cashuSpender.receiveToken(
1136
1269
  fetchResult.token
1137
1270
  );
1138
1271
  const totalAmountMsat = receiveResult.unit === "msat" ? receiveResult.amount : receiveResult.amount * 1e3;
@@ -1177,7 +1310,7 @@ var BalanceManager = class {
1177
1310
  if (fetchResult.error === "No balance to refund") {
1178
1311
  return { success: false, message: "No balance to refund" };
1179
1312
  }
1180
- const receiveResult = await this.walletAdapter.receiveToken(
1313
+ const receiveResult = await this.cashuSpender.receiveToken(
1181
1314
  fetchResult.token
1182
1315
  );
1183
1316
  const totalAmountMsat = receiveResult.unit === "msat" ? receiveResult.amount : receiveResult.amount * 1e3;
@@ -1618,7 +1751,7 @@ var BalanceManager = class {
1618
1751
  */
1619
1752
  async _recoverFailedTopUp(cashuToken) {
1620
1753
  try {
1621
- await this.walletAdapter.receiveToken(cashuToken);
1754
+ await this.cashuSpender.receiveToken(cashuToken);
1622
1755
  } catch (error) {
1623
1756
  console.error(
1624
1757
  "[BalanceManager._recoverFailedTopUp] Failed to recover token",
@@ -2345,12 +2478,39 @@ var RoutstrClient = class {
2345
2478
  providerManager;
2346
2479
  alertLevel;
2347
2480
  mode;
2481
+ debugLevel = "WARN";
2348
2482
  /**
2349
2483
  * Get the current client mode
2350
2484
  */
2351
2485
  getMode() {
2352
2486
  return this.mode;
2353
2487
  }
2488
+ getDebugLevel() {
2489
+ return this.debugLevel;
2490
+ }
2491
+ setDebugLevel(level) {
2492
+ this.debugLevel = level;
2493
+ }
2494
+ _log(level, ...args) {
2495
+ const levelPriority = {
2496
+ DEBUG: 0,
2497
+ WARN: 1,
2498
+ ERROR: 2
2499
+ };
2500
+ if (levelPriority[level] >= levelPriority[this.debugLevel]) {
2501
+ switch (level) {
2502
+ case "DEBUG":
2503
+ console.log(...args);
2504
+ break;
2505
+ case "WARN":
2506
+ console.warn(...args);
2507
+ break;
2508
+ case "ERROR":
2509
+ console.error(...args);
2510
+ break;
2511
+ }
2512
+ }
2513
+ }
2354
2514
  /**
2355
2515
  * Get the CashuSpender instance
2356
2516
  */
@@ -2414,7 +2574,7 @@ var RoutstrClient = class {
2414
2574
  amount: requiredSats,
2415
2575
  baseUrl
2416
2576
  });
2417
- console.log(token, baseUrl);
2577
+ this._log("DEBUG", token, baseUrl);
2418
2578
  let requestBody = body;
2419
2579
  if (body && typeof body === "object") {
2420
2580
  const bodyObj = body;
@@ -2438,8 +2598,9 @@ var RoutstrClient = class {
2438
2598
  });
2439
2599
  const tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
2440
2600
  const baseUrlUsed = response.baseUrl || baseUrl;
2601
+ const tokenUsed = response.token || token;
2441
2602
  await this._handlePostResponseBalanceUpdate({
2442
- token,
2603
+ token: tokenUsed,
2443
2604
  baseUrl: baseUrlUsed,
2444
2605
  initialTokenBalance: tokenBalanceInSats,
2445
2606
  response
@@ -2575,22 +2736,30 @@ var RoutstrClient = class {
2575
2736
  const { path, method, body, baseUrl, token, headers } = params;
2576
2737
  try {
2577
2738
  const url = `${baseUrl.replace(/\/$/, "")}${path}`;
2578
- if (this.mode === "xcashu") console.log("HEADERS,", headers);
2739
+ if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
2579
2740
  const response = await fetch(url, {
2580
2741
  method,
2581
2742
  headers,
2582
2743
  body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
2583
2744
  });
2584
- if (this.mode === "xcashu") console.log("response,", response);
2745
+ if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
2585
2746
  response.baseUrl = baseUrl;
2747
+ response.token = token;
2586
2748
  if (!response.ok) {
2587
2749
  const requestId = response.headers.get("x-routstr-request-id") || void 0;
2750
+ let bodyText;
2751
+ try {
2752
+ bodyText = await response.text();
2753
+ } catch (e) {
2754
+ bodyText = void 0;
2755
+ }
2588
2756
  return await this._handleErrorResponse(
2589
2757
  params,
2590
2758
  token,
2591
2759
  response.status,
2592
2760
  requestId,
2593
- this.mode === "xcashu" ? response.headers.get("x-cashu") ?? void 0 : void 0
2761
+ this.mode === "xcashu" ? response.headers.get("x-cashu") ?? void 0 : void 0,
2762
+ bodyText
2594
2763
  );
2595
2764
  }
2596
2765
  return response;
@@ -2609,41 +2778,47 @@ var RoutstrClient = class {
2609
2778
  /**
2610
2779
  * Handle error responses with failover
2611
2780
  */
2612
- async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken) {
2781
+ async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody) {
2613
2782
  const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
2614
2783
  let tryNextProvider = false;
2615
- console.log(
2784
+ this._log(
2785
+ "DEBUG",
2616
2786
  `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}`
2617
2787
  );
2618
- console.log(
2788
+ this._log(
2789
+ "DEBUG",
2619
2790
  `[RoutstrClient] _handleErrorResponse: Attempting to receive/restore token for ${baseUrl}`
2620
2791
  );
2621
2792
  if (params.token.startsWith("cashu")) {
2622
- const tryReceiveTokenResult = await this.walletAdapter.receiveToken(
2793
+ const tryReceiveTokenResult = await this.cashuSpender.receiveToken(
2623
2794
  params.token
2624
2795
  );
2625
2796
  if (tryReceiveTokenResult.success) {
2626
- console.log(
2797
+ this._log(
2798
+ "DEBUG",
2627
2799
  `[RoutstrClient] _handleErrorResponse: Token restored successfully, amount=${tryReceiveTokenResult.amount}`
2628
2800
  );
2629
2801
  tryNextProvider = true;
2630
2802
  if (this.mode === "lazyrefund")
2631
2803
  this.storageAdapter.removeToken(baseUrl);
2632
2804
  } else {
2633
- console.log(
2634
- `[RoutstrClient] _handleErrorResponse: Token restore failed or not needed`
2805
+ this._log(
2806
+ "DEBUG",
2807
+ `[RoutstrClient] _handleErrorResponse: Failed to receive token. `
2635
2808
  );
2636
2809
  }
2637
2810
  }
2638
2811
  if (this.mode === "xcashu") {
2639
2812
  if (xCashuRefundToken) {
2640
- console.log(
2813
+ this._log(
2814
+ "DEBUG",
2641
2815
  `[RoutstrClient] _handleErrorResponse: Attempting to receive xcashu refund token, preview=${xCashuRefundToken.substring(0, 20)}...`
2642
2816
  );
2643
2817
  try {
2644
- const receiveResult = await this.walletAdapter.receiveToken(xCashuRefundToken);
2818
+ const receiveResult = await this.cashuSpender.receiveToken(xCashuRefundToken);
2645
2819
  if (receiveResult.success) {
2646
- console.log(
2820
+ this._log(
2821
+ "DEBUG",
2647
2822
  `[RoutstrClient] _handleErrorResponse: xcashu refund received, amount=${receiveResult.amount}`
2648
2823
  );
2649
2824
  tryNextProvider = true;
@@ -2655,7 +2830,7 @@ var RoutstrClient = class {
2655
2830
  requestId
2656
2831
  );
2657
2832
  } catch (error) {
2658
- console.error("[xcashu] Failed to receive refund token:", error);
2833
+ this._log("ERROR", "[xcashu] Failed to receive refund token:", error);
2659
2834
  throw new ProviderError(
2660
2835
  baseUrl,
2661
2836
  status,
@@ -2673,14 +2848,15 @@ var RoutstrClient = class {
2673
2848
  );
2674
2849
  }
2675
2850
  }
2676
- if (status === 402 && !tryNextProvider && (this.mode === "apikeys" || this.mode === "lazyrefund")) {
2851
+ if ((status === 402 || status === 413 && responseBody?.includes("Insufficient balance")) && !tryNextProvider && (this.mode === "apikeys" || this.mode === "lazyrefund")) {
2677
2852
  const topupResult = await this.balanceManager.topUp({
2678
2853
  mintUrl,
2679
2854
  baseUrl,
2680
2855
  amount: params.requiredSats * TOPUP_MARGIN,
2681
2856
  token: params.token
2682
2857
  });
2683
- console.log(
2858
+ this._log(
2859
+ "DEBUG",
2684
2860
  `[RoutstrClient] _handleErrorResponse: Topup result for ${baseUrl}: success=${topupResult.success}, message=${topupResult.message}`
2685
2861
  );
2686
2862
  if (!topupResult.success) {
@@ -2690,18 +2866,21 @@ var RoutstrClient = class {
2690
2866
  const haveMatch = message.match(/have (\d+)/);
2691
2867
  const required = needMatch ? parseInt(needMatch[1], 10) : params.requiredSats;
2692
2868
  const available = haveMatch ? parseInt(haveMatch[1], 10) : 0;
2693
- console.log(
2869
+ this._log(
2870
+ "DEBUG",
2694
2871
  `[RoutstrClient] _handleErrorResponse: Insufficient balance, need=${required}, have=${available}`
2695
2872
  );
2696
2873
  throw new InsufficientBalanceError(required, available);
2697
2874
  } else {
2698
- console.log(
2875
+ this._log(
2876
+ "DEBUG",
2699
2877
  `[RoutstrClient] _handleErrorResponse: Topup failed with non-insufficient-balance error, will try next provider`
2700
2878
  );
2701
2879
  tryNextProvider = true;
2702
2880
  }
2703
2881
  } else {
2704
- console.log(
2882
+ this._log(
2883
+ "DEBUG",
2705
2884
  `[RoutstrClient] _handleErrorResponse: Topup successful, will retry with new token`
2706
2885
  );
2707
2886
  }
@@ -2712,8 +2891,9 @@ var RoutstrClient = class {
2712
2891
  headers: this._withAuthHeader(params.baseHeaders, params.token)
2713
2892
  });
2714
2893
  }
2715
- if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 521) && !tryNextProvider) {
2716
- console.log(
2894
+ if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
2895
+ this._log(
2896
+ "DEBUG",
2717
2897
  `[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`
2718
2898
  );
2719
2899
  if (this.mode === "lazyrefund") {
@@ -2723,7 +2903,8 @@ var RoutstrClient = class {
2723
2903
  baseUrl,
2724
2904
  token: params.token
2725
2905
  });
2726
- console.log(
2906
+ this._log(
2907
+ "DEBUG",
2727
2908
  `[RoutstrClient] _handleErrorResponse: Lazyrefund result: success=${refundResult.success}`
2728
2909
  );
2729
2910
  if (refundResult.success) this.storageAdapter.removeToken(baseUrl);
@@ -2743,14 +2924,16 @@ var RoutstrClient = class {
2743
2924
  );
2744
2925
  }
2745
2926
  } else if (this.mode === "apikeys") {
2746
- console.log(
2927
+ this._log(
2928
+ "DEBUG",
2747
2929
  `[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
2748
2930
  );
2749
2931
  const initialBalance = await this.balanceManager.getTokenBalance(
2750
2932
  token,
2751
2933
  baseUrl
2752
2934
  );
2753
- console.log(
2935
+ this._log(
2936
+ "DEBUG",
2754
2937
  `[RoutstrClient] _handleErrorResponse: Initial API key balance: ${initialBalance.amount}`
2755
2938
  );
2756
2939
  const refundResult = await this.balanceManager.refundApiKey({
@@ -2758,7 +2941,8 @@ var RoutstrClient = class {
2758
2941
  baseUrl,
2759
2942
  apiKey: token
2760
2943
  });
2761
- console.log(
2944
+ this._log(
2945
+ "DEBUG",
2762
2946
  `[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
2763
2947
  );
2764
2948
  if (!refundResult.success && initialBalance.amount > 0) {
@@ -2773,7 +2957,8 @@ var RoutstrClient = class {
2773
2957
  }
2774
2958
  }
2775
2959
  this.providerManager.markFailed(baseUrl);
2776
- console.log(
2960
+ this._log(
2961
+ "DEBUG",
2777
2962
  `[RoutstrClient] _handleErrorResponse: Marked provider ${baseUrl} as failed`
2778
2963
  );
2779
2964
  if (!selectedModel) {
@@ -2788,7 +2973,8 @@ var RoutstrClient = class {
2788
2973
  baseUrl
2789
2974
  );
2790
2975
  if (nextProvider) {
2791
- console.log(
2976
+ this._log(
2977
+ "DEBUG",
2792
2978
  `[RoutstrClient] _handleErrorResponse: Failing over to next provider: ${nextProvider}, model: ${selectedModel.id}`
2793
2979
  );
2794
2980
  const newModel = await this.providerManager.getModelForProvider(
@@ -2803,7 +2989,8 @@ var RoutstrClient = class {
2803
2989
  messagesForPricing,
2804
2990
  params.maxTokens
2805
2991
  );
2806
- console.log(
2992
+ this._log(
2993
+ "DEBUG",
2807
2994
  `[RoutstrClient] _handleErrorResponse: Creating new token for failover provider ${nextProvider}, required sats: ${newRequiredSats}`
2808
2995
  );
2809
2996
  const spendResult = await this._spendToken({
@@ -2835,10 +3022,10 @@ var RoutstrClient = class {
2835
3022
  const refundToken = response.headers.get("x-cashu") ?? void 0;
2836
3023
  if (refundToken) {
2837
3024
  try {
2838
- const receiveResult = await this.walletAdapter.receiveToken(refundToken);
3025
+ const receiveResult = await this.cashuSpender.receiveToken(refundToken);
2839
3026
  satsSpent = initialTokenBalance - receiveResult.amount * (receiveResult.unit == "sat" ? 1 : 1e3);
2840
3027
  } catch (error) {
2841
- console.error("[xcashu] Failed to receive refund token:", error);
3028
+ this._log("ERROR", "[xcashu] Failed to receive refund token:", error);
2842
3029
  }
2843
3030
  }
2844
3031
  } else if (this.mode === "lazyrefund") {
@@ -2855,7 +3042,8 @@ var RoutstrClient = class {
2855
3042
  token,
2856
3043
  baseUrl
2857
3044
  );
2858
- console.log(
3045
+ this._log(
3046
+ "DEBUG",
2859
3047
  "LATEST Balance",
2860
3048
  latestBalanceInfo.amount,
2861
3049
  latestBalanceInfo.reserved,
@@ -2867,7 +3055,7 @@ var RoutstrClient = class {
2867
3055
  this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
2868
3056
  satsSpent = initialTokenBalance - latestTokenBalance;
2869
3057
  } catch (e) {
2870
- console.warn("Could not get updated API key balance:", e);
3058
+ this._log("WARN", "Could not get updated API key balance:", e);
2871
3059
  satsSpent = fallbackSatsSpent ?? initialTokenBalance;
2872
3060
  }
2873
3061
  }
@@ -2978,11 +3166,12 @@ var RoutstrClient = class {
2978
3166
  * Handle errors and notify callbacks
2979
3167
  */
2980
3168
  _handleError(error, callbacks) {
2981
- console.error("[RoutstrClient] _handleError: Error occurred", error);
3169
+ this._log("ERROR", "[RoutstrClient] _handleError: Error occurred", error);
2982
3170
  if (error instanceof Error) {
2983
3171
  const isStreamError = error.message.includes("Error in input stream") || error.message.includes("Load failed");
2984
3172
  const modifiedErrorMsg = isStreamError ? "AI stream was cut off, turn on Keep Active or please try again" : error.message;
2985
- console.error(
3173
+ this._log(
3174
+ "ERROR",
2986
3175
  `[RoutstrClient] _handleError: Error type=${error.constructor.name}, message=${modifiedErrorMsg}, isStreamError=${isStreamError}`
2987
3176
  );
2988
3177
  callbacks.onMessageAppend({
@@ -3011,13 +3200,15 @@ var RoutstrClient = class {
3011
3200
  */
3012
3201
  async _spendToken(params) {
3013
3202
  const { mintUrl, amount, baseUrl } = params;
3014
- console.log(
3203
+ this._log(
3204
+ "DEBUG",
3015
3205
  `[RoutstrClient] _spendToken: mode=${this.mode}, amount=${amount}, baseUrl=${baseUrl}, mintUrl=${mintUrl}`
3016
3206
  );
3017
3207
  if (this.mode === "apikeys") {
3018
3208
  let parentApiKey = this.storageAdapter.getApiKey(baseUrl);
3019
3209
  if (!parentApiKey) {
3020
- console.log(
3210
+ this._log(
3211
+ "DEBUG",
3021
3212
  `[RoutstrClient] _spendToken: No existing API key for ${baseUrl}, creating new one via Cashu`
3022
3213
  );
3023
3214
  const spendResult2 = await this.cashuSpender.spend({
@@ -3027,7 +3218,8 @@ var RoutstrClient = class {
3027
3218
  reuseToken: false
3028
3219
  });
3029
3220
  if (!spendResult2.token) {
3030
- console.error(
3221
+ this._log(
3222
+ "ERROR",
3031
3223
  `[RoutstrClient] _spendToken: Failed to create Cashu token for API key creation, error:`,
3032
3224
  spendResult2.error
3033
3225
  );
@@ -3035,30 +3227,35 @@ var RoutstrClient = class {
3035
3227
  `[RoutstrClient] _spendToken: Failed to create Cashu token for API key creation, error: ${spendResult2.error}`
3036
3228
  );
3037
3229
  } else {
3038
- console.log(
3230
+ this._log(
3231
+ "DEBUG",
3039
3232
  `[RoutstrClient] _spendToken: Cashu token created, token preview: ${spendResult2.token}`
3040
3233
  );
3041
3234
  }
3042
- console.log(
3235
+ this._log(
3236
+ "DEBUG",
3043
3237
  `[RoutstrClient] _spendToken: Created API key for ${baseUrl}, key preview: ${spendResult2.token}, balance: ${spendResult2.balance}`
3044
3238
  );
3045
3239
  try {
3046
3240
  this.storageAdapter.setApiKey(baseUrl, spendResult2.token);
3047
3241
  } catch (error) {
3048
3242
  if (error instanceof Error && error.message.includes("ApiKey already exists")) {
3049
- const tryReceiveTokenResult = await this.walletAdapter.receiveToken(
3243
+ const tryReceiveTokenResult = await this.cashuSpender.receiveToken(
3050
3244
  spendResult2.token
3051
3245
  );
3052
3246
  if (tryReceiveTokenResult.success) {
3053
- console.log(
3247
+ this._log(
3248
+ "DEBUG",
3054
3249
  `[RoutstrClient] _handleErrorResponse: Token restored successfully, amount=${tryReceiveTokenResult.amount}`
3055
3250
  );
3056
3251
  } else {
3057
- console.log(
3252
+ this._log(
3253
+ "DEBUG",
3058
3254
  `[RoutstrClient] _handleErrorResponse: Token restore failed or not needed`
3059
3255
  );
3060
3256
  }
3061
- console.log(
3257
+ this._log(
3258
+ "DEBUG",
3062
3259
  `[RoutstrClient] _spendToken: API key already exists for ${baseUrl}, using existing key`
3063
3260
  );
3064
3261
  } else {
@@ -3067,7 +3264,8 @@ var RoutstrClient = class {
3067
3264
  }
3068
3265
  parentApiKey = this.storageAdapter.getApiKey(baseUrl);
3069
3266
  } else {
3070
- console.log(
3267
+ this._log(
3268
+ "DEBUG",
3071
3269
  `[RoutstrClient] _spendToken: Using existing API key for ${baseUrl}, key preview: ${parentApiKey.key}`
3072
3270
  );
3073
3271
  }
@@ -3089,10 +3287,11 @@ var RoutstrClient = class {
3089
3287
  tokenBalance = balanceInfo.amount;
3090
3288
  tokenBalanceUnit = balanceInfo.unit;
3091
3289
  } catch (e) {
3092
- console.warn("Could not get initial API key balance:", e);
3290
+ this._log("WARN", "Could not get initial API key balance:", e);
3093
3291
  }
3094
3292
  }
3095
- console.log(
3293
+ this._log(
3294
+ "DEBUG",
3096
3295
  `[RoutstrClient] _spendToken: Returning token with balance=${tokenBalance} ${tokenBalanceUnit}`
3097
3296
  );
3098
3297
  return {
@@ -3101,7 +3300,8 @@ var RoutstrClient = class {
3101
3300
  tokenBalanceUnit
3102
3301
  };
3103
3302
  }
3104
- console.log(
3303
+ this._log(
3304
+ "DEBUG",
3105
3305
  `[RoutstrClient] _spendToken: Calling CashuSpender.spend for amount=${amount}, mintUrl=${mintUrl}, mode=${this.mode}`
3106
3306
  );
3107
3307
  const spendResult = await this.cashuSpender.spend({
@@ -3111,12 +3311,14 @@ var RoutstrClient = class {
3111
3311
  reuseToken: this.mode === "lazyrefund"
3112
3312
  });
3113
3313
  if (!spendResult.token) {
3114
- console.error(
3314
+ this._log(
3315
+ "ERROR",
3115
3316
  `[RoutstrClient] _spendToken: CashuSpender.spend failed, error:`,
3116
3317
  spendResult.error
3117
3318
  );
3118
3319
  } else {
3119
- console.log(
3320
+ this._log(
3321
+ "DEBUG",
3120
3322
  `[RoutstrClient] _spendToken: Cashu token created, token preview: ${spendResult.token}, balance: ${spendResult.balance} ${spendResult.unit ?? "sat"}`
3121
3323
  );
3122
3324
  }
@@ -3428,7 +3630,10 @@ var SDK_STORAGE_KEYS = {
3428
3630
  LAST_BASE_URLS_UPDATE: "lastBaseUrlsUpdate",
3429
3631
  LOCAL_CASHU_TOKENS: "local_cashu_tokens",
3430
3632
  API_KEYS: "api_keys",
3431
- CHILD_KEYS: "child_keys"
3633
+ CHILD_KEYS: "child_keys",
3634
+ ROUTSTR21_MODELS: "routstr21Models",
3635
+ LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
3636
+ CACHED_RECEIVE_TOKENS: "cached_receive_tokens"
3432
3637
  };
3433
3638
 
3434
3639
  // storage/store.ts
@@ -3460,7 +3665,10 @@ var createSdkStore = async ({
3460
3665
  rawLastModelsUpdate,
3461
3666
  rawCachedTokens,
3462
3667
  rawApiKeys,
3463
- rawChildKeys
3668
+ rawChildKeys,
3669
+ rawRoutstr21Models,
3670
+ rawLastRoutstr21ModelsUpdate,
3671
+ rawCachedReceiveTokens
3464
3672
  ] = await Promise.all([
3465
3673
  driver.getItem(
3466
3674
  SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
@@ -3484,7 +3692,13 @@ var createSdkStore = async ({
3484
3692
  ),
3485
3693
  driver.getItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, []),
3486
3694
  driver.getItem(SDK_STORAGE_KEYS.API_KEYS, []),
3487
- driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, [])
3695
+ driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, []),
3696
+ driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
3697
+ driver.getItem(
3698
+ SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
3699
+ null
3700
+ ),
3701
+ driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, [])
3488
3702
  ]);
3489
3703
  const modelsFromAllProviders = Object.fromEntries(
3490
3704
  Object.entries(rawModels).map(([baseUrl, models]) => [
@@ -3534,6 +3748,14 @@ var createSdkStore = async ({
3534
3748
  validityDate: entry.validityDate,
3535
3749
  createdAt: entry.createdAt ?? Date.now()
3536
3750
  }));
3751
+ const routstr21Models = rawRoutstr21Models;
3752
+ const lastRoutstr21ModelsUpdate = rawLastRoutstr21ModelsUpdate;
3753
+ const cachedReceiveTokens = rawCachedReceiveTokens?.map((entry) => ({
3754
+ token: entry.token,
3755
+ amount: entry.amount,
3756
+ unit: entry.unit || "sat",
3757
+ createdAt: entry.createdAt ?? Date.now()
3758
+ }));
3537
3759
  return createStore((set, get) => ({
3538
3760
  modelsFromAllProviders,
3539
3761
  lastUsedModel,
@@ -3546,6 +3768,9 @@ var createSdkStore = async ({
3546
3768
  cachedTokens,
3547
3769
  apiKeys,
3548
3770
  childKeys,
3771
+ routstr21Models,
3772
+ lastRoutstr21ModelsUpdate,
3773
+ cachedReceiveTokens,
3549
3774
  setModelsFromAllProviders: (value) => {
3550
3775
  const normalized = {};
3551
3776
  for (const [baseUrl, models] of Object.entries(value)) {
@@ -3644,6 +3869,24 @@ var createSdkStore = async ({
3644
3869
  void driver.setItem(SDK_STORAGE_KEYS.CHILD_KEYS, normalized);
3645
3870
  return { childKeys: normalized };
3646
3871
  });
3872
+ },
3873
+ setRoutstr21Models: (value) => {
3874
+ void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, value);
3875
+ set({ routstr21Models: value });
3876
+ },
3877
+ setRoutstr21ModelsLastUpdate: (value) => {
3878
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE, value);
3879
+ set({ lastRoutstr21ModelsUpdate: value });
3880
+ },
3881
+ setCachedReceiveTokens: (value) => {
3882
+ const normalized = value.map((entry) => ({
3883
+ token: entry.token,
3884
+ amount: entry.amount,
3885
+ unit: entry.unit || "sat",
3886
+ createdAt: entry.createdAt ?? Date.now()
3887
+ }));
3888
+ void driver.setItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, normalized);
3889
+ set({ cachedReceiveTokens: normalized });
3647
3890
  }
3648
3891
  }));
3649
3892
  };
@@ -3671,7 +3914,11 @@ var createDiscoveryAdapterFromStore = (store) => ({
3671
3914
  getBaseUrlsList: () => store.getState().baseUrlsList,
3672
3915
  setBaseUrlsList: (urls) => store.getState().setBaseUrlsList(urls),
3673
3916
  getBaseUrlsLastUpdate: () => store.getState().lastBaseUrlsUpdate,
3674
- setBaseUrlsLastUpdate: (timestamp) => store.getState().setBaseUrlsLastUpdate(timestamp)
3917
+ setBaseUrlsLastUpdate: (timestamp) => store.getState().setBaseUrlsLastUpdate(timestamp),
3918
+ getRoutstr21Models: () => store.getState().routstr21Models,
3919
+ setRoutstr21Models: (models) => store.getState().setRoutstr21Models(models),
3920
+ getRoutstr21ModelsLastUpdate: () => store.getState().lastRoutstr21ModelsUpdate,
3921
+ setRoutstr21ModelsLastUpdate: (timestamp) => store.getState().setRoutstr21ModelsLastUpdate(timestamp)
3675
3922
  });
3676
3923
  var createStorageAdapterFromStore = (store) => ({
3677
3924
  getToken: (baseUrl) => {
@@ -3865,6 +4112,12 @@ var createStorageAdapterFromStore = (store) => ({
3865
4112
  validityDate: entry.validityDate,
3866
4113
  createdAt: entry.createdAt
3867
4114
  }));
4115
+ },
4116
+ getCachedReceiveTokens: () => {
4117
+ return store.getState().cachedReceiveTokens;
4118
+ },
4119
+ setCachedReceiveTokens: (tokens) => {
4120
+ store.getState().setCachedReceiveTokens(tokens);
3868
4121
  }
3869
4122
  });
3870
4123
  var createProviderRegistryFromStore = (store) => ({