@routstr/sdk 0.1.1 → 0.1.2

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
@@ -149,6 +149,7 @@ var ModelManager = class _ModelManager {
149
149
  const filtered = this.filterBaseUrlsForTor(nostrProviders, torMode);
150
150
  this.adapter.setBaseUrlsList(filtered);
151
151
  this.adapter.setBaseUrlsLastUpdate(Date.now());
152
+ await this.fetchRoutstr21Models();
152
153
  return filtered;
153
154
  }
154
155
  } catch (e) {
@@ -280,6 +281,7 @@ var ModelManager = class _ModelManager {
280
281
  if (list.length > 0) {
281
282
  this.adapter.setBaseUrlsList(list);
282
283
  this.adapter.setBaseUrlsLastUpdate(Date.now());
284
+ await this.fetchRoutstr21Models();
283
285
  }
284
286
  return list;
285
287
  } catch (e) {
@@ -446,6 +448,57 @@ var ModelManager = class _ModelManager {
446
448
  }
447
449
  return url.endsWith("/") ? url : `${url}/`;
448
450
  }
451
+ /**
452
+ * Fetch routstr21 models from Nostr network (kind 38423)
453
+ * @returns Array of model IDs or empty array if not found
454
+ */
455
+ async fetchRoutstr21Models() {
456
+ const DEFAULT_RELAYS = [
457
+ "wss://relay.primal.net",
458
+ "wss://nos.lol",
459
+ "wss://relay.routstr.com"
460
+ ];
461
+ const pool = new RelayPool();
462
+ const localEventStore = new EventStore();
463
+ const timeoutMs = 5e3;
464
+ await new Promise((resolve) => {
465
+ pool.req(DEFAULT_RELAYS, {
466
+ kinds: [38423],
467
+ "#d": ["routstr-21-models"],
468
+ limit: 1,
469
+ authors: ["4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8"]
470
+ }).pipe(
471
+ onlyEvents(),
472
+ tap((event2) => {
473
+ localEventStore.add(event2);
474
+ })
475
+ ).subscribe({
476
+ complete: () => {
477
+ resolve();
478
+ }
479
+ });
480
+ setTimeout(() => {
481
+ resolve();
482
+ }, timeoutMs);
483
+ });
484
+ const timeline = localEventStore.getTimeline({ kinds: [38423] });
485
+ if (timeline.length === 0) {
486
+ return [];
487
+ }
488
+ const event = timeline[0];
489
+ try {
490
+ const content = JSON.parse(event.content);
491
+ const models = Array.isArray(content?.models) ? content.models : [];
492
+ this.adapter.setRoutstr21Models(models);
493
+ return models;
494
+ } catch {
495
+ console.warn(
496
+ "[Routstr21Models] Failed to parse Nostr event content:",
497
+ event.id
498
+ );
499
+ return [];
500
+ }
501
+ }
449
502
  };
450
503
 
451
504
  // discovery/MintDiscovery.ts
@@ -675,6 +728,26 @@ var CashuSpender = class {
675
728
  this.balanceManager = balanceManager;
676
729
  }
677
730
  _isBusy = false;
731
+ debugLevel = "WARN";
732
+ async receiveToken(token) {
733
+ const result = await this.walletAdapter.receiveToken(token);
734
+ if (!result.success && result.message?.includes("Failed to fetch mint")) {
735
+ const cachedTokens = this.storageAdapter.getCachedReceiveTokens();
736
+ const existingIndex = cachedTokens.findIndex((t) => t.token === token);
737
+ if (existingIndex === -1) {
738
+ this.storageAdapter.setCachedReceiveTokens([
739
+ ...cachedTokens,
740
+ {
741
+ token,
742
+ amount: result.amount,
743
+ unit: result.unit,
744
+ createdAt: Date.now()
745
+ }
746
+ ]);
747
+ }
748
+ }
749
+ return result;
750
+ }
678
751
  async _getBalanceState() {
679
752
  const mintBalances = await this.walletAdapter.getBalances();
680
753
  const units = this.walletAdapter.getMintUnits();
@@ -717,6 +790,32 @@ var CashuSpender = class {
717
790
  get isBusy() {
718
791
  return this._isBusy;
719
792
  }
793
+ getDebugLevel() {
794
+ return this.debugLevel;
795
+ }
796
+ setDebugLevel(level) {
797
+ this.debugLevel = level;
798
+ }
799
+ _log(level, ...args) {
800
+ const levelPriority = {
801
+ DEBUG: 0,
802
+ WARN: 1,
803
+ ERROR: 2
804
+ };
805
+ if (levelPriority[level] >= levelPriority[this.debugLevel]) {
806
+ switch (level) {
807
+ case "DEBUG":
808
+ console.log(...args);
809
+ break;
810
+ case "WARN":
811
+ console.warn(...args);
812
+ break;
813
+ case "ERROR":
814
+ console.error(...args);
815
+ break;
816
+ }
817
+ }
818
+ }
720
819
  /**
721
820
  * Spend Cashu tokens with automatic mint selection and retry logic
722
821
  * Throws errors on failure instead of returning failed SpendResult
@@ -783,12 +882,16 @@ var CashuSpender = class {
783
882
  excludeMints,
784
883
  retryCount
785
884
  } = options;
786
- console.log(
885
+ this._log(
886
+ "DEBUG",
787
887
  `[CashuSpender] _spendInternal: amount=${amount}, mintUrl=${mintUrl}, baseUrl=${baseUrl}, reuseToken=${reuseToken}`
788
888
  );
789
889
  let adjustedAmount = Math.ceil(amount);
790
890
  if (!adjustedAmount || isNaN(adjustedAmount)) {
791
- console.error(`[CashuSpender] _spendInternal: Invalid amount: ${amount}`);
891
+ this._log(
892
+ "ERROR",
893
+ `[CashuSpender] _spendInternal: Invalid amount: ${amount}`
894
+ );
792
895
  return {
793
896
  token: null,
794
897
  status: "failed",
@@ -797,7 +900,8 @@ var CashuSpender = class {
797
900
  };
798
901
  }
799
902
  if (reuseToken && baseUrl) {
800
- console.log(
903
+ this._log(
904
+ "DEBUG",
801
905
  `[CashuSpender] _spendInternal: Attempting to reuse token for ${baseUrl}`
802
906
  );
803
907
  const existingResult = await this._tryReuseToken(
@@ -806,12 +910,14 @@ var CashuSpender = class {
806
910
  mintUrl
807
911
  );
808
912
  if (existingResult) {
809
- console.log(
913
+ this._log(
914
+ "DEBUG",
810
915
  `[CashuSpender] _spendInternal: Successfully reused token, balance: ${existingResult.balance}`
811
916
  );
812
917
  return existingResult;
813
918
  }
814
- console.log(
919
+ this._log(
920
+ "DEBUG",
815
921
  `[CashuSpender] _spendInternal: Could not reuse token, will create new token`
816
922
  );
817
923
  }
@@ -829,12 +935,14 @@ var CashuSpender = class {
829
935
  (sum, item) => sum + item.amount,
830
936
  0
831
937
  );
832
- console.log(
938
+ this._log(
939
+ "DEBUG",
833
940
  `[CashuSpender] _spendInternal: totalBalance=${totalBalance}, totalPending=${totalPending}, adjustedAmount=${adjustedAmount}`
834
941
  );
835
942
  const totalAvailableBalance = totalBalance + totalPending;
836
943
  if (totalAvailableBalance < adjustedAmount) {
837
- console.error(
944
+ this._log(
945
+ "ERROR",
838
946
  `[CashuSpender] _spendInternal: Insufficient balance, have=${totalAvailableBalance}, need=${adjustedAmount}`
839
947
  );
840
948
  return this._createInsufficientBalanceError(
@@ -902,7 +1010,8 @@ var CashuSpender = class {
902
1010
  baseUrl,
903
1011
  status: "success"
904
1012
  });
905
- console.log(
1013
+ this._log(
1014
+ "DEBUG",
906
1015
  `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
907
1016
  );
908
1017
  return {
@@ -920,7 +1029,7 @@ var CashuSpender = class {
920
1029
  if (!storedToken) return null;
921
1030
  const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
922
1031
  const balanceForBaseUrl = pendingDistribution.find((b) => b.baseUrl === baseUrl)?.amount || 0;
923
- console.log("RESUINGDSR GSODGNSD", balanceForBaseUrl, amount);
1032
+ this._log("DEBUG", "RESUINGDSR GSODGNSD", balanceForBaseUrl, amount);
924
1033
  if (balanceForBaseUrl > amount) {
925
1034
  const units = this.walletAdapter.getMintUnits();
926
1035
  const unit = units[mintUrl] || "sat";
@@ -938,7 +1047,7 @@ var CashuSpender = class {
938
1047
  baseUrl,
939
1048
  amount: topUpAmount
940
1049
  });
941
- console.log("TOPUP ", topUpResult);
1050
+ this._log("DEBUG", "TOPUP ", topUpResult);
942
1051
  if (topUpResult.success && topUpResult.toppedUpAmount) {
943
1052
  const newBalance = balanceForBaseUrl + topUpResult.toppedUpAmount;
944
1053
  const units = this.walletAdapter.getMintUnits();
@@ -960,7 +1069,7 @@ var CashuSpender = class {
960
1069
  baseUrl,
961
1070
  storedToken
962
1071
  );
963
- console.log(providerBalance);
1072
+ this._log("DEBUG", providerBalance);
964
1073
  if (providerBalance <= 0) {
965
1074
  this.storageAdapter.removeToken(baseUrl);
966
1075
  }
@@ -979,7 +1088,7 @@ var CashuSpender = class {
979
1088
  const refundResults = await Promise.allSettled(
980
1089
  toRefund.map(async (pending) => {
981
1090
  const token = this.storageAdapter.getToken(pending.baseUrl);
982
- console.log(token, this.balanceManager);
1091
+ this._log("DEBUG", token, this.balanceManager);
983
1092
  if (!token || !this.balanceManager) {
984
1093
  return { baseUrl: pending.baseUrl, success: false };
985
1094
  }
@@ -995,7 +1104,7 @@ var CashuSpender = class {
995
1104
  baseUrl: pending.baseUrl,
996
1105
  token
997
1106
  });
998
- console.log(result);
1107
+ this._log("DEBUG", result);
999
1108
  if (result.success) {
1000
1109
  this.storageAdapter.removeToken(pending.baseUrl);
1001
1110
  }
@@ -1093,11 +1202,22 @@ var CashuSpender = class {
1093
1202
 
1094
1203
  // wallet/BalanceManager.ts
1095
1204
  var BalanceManager = class {
1096
- constructor(walletAdapter, storageAdapter, providerRegistry) {
1205
+ constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
1097
1206
  this.walletAdapter = walletAdapter;
1098
1207
  this.storageAdapter = storageAdapter;
1099
1208
  this.providerRegistry = providerRegistry;
1209
+ if (cashuSpender) {
1210
+ this.cashuSpender = cashuSpender;
1211
+ } else {
1212
+ this.cashuSpender = new CashuSpender(
1213
+ walletAdapter,
1214
+ storageAdapter,
1215
+ providerRegistry,
1216
+ this
1217
+ );
1218
+ }
1100
1219
  }
1220
+ cashuSpender;
1101
1221
  /**
1102
1222
  * Unified refund - handles both NIP-60 and legacy wallet refunds
1103
1223
  */
@@ -1132,7 +1252,7 @@ var BalanceManager = class {
1132
1252
  this.storageAdapter.removeToken(baseUrl);
1133
1253
  return { success: true, message: "No balance to refund" };
1134
1254
  }
1135
- const receiveResult = await this.walletAdapter.receiveToken(
1255
+ const receiveResult = await this.cashuSpender.receiveToken(
1136
1256
  fetchResult.token
1137
1257
  );
1138
1258
  const totalAmountMsat = receiveResult.unit === "msat" ? receiveResult.amount : receiveResult.amount * 1e3;
@@ -1177,7 +1297,7 @@ var BalanceManager = class {
1177
1297
  if (fetchResult.error === "No balance to refund") {
1178
1298
  return { success: false, message: "No balance to refund" };
1179
1299
  }
1180
- const receiveResult = await this.walletAdapter.receiveToken(
1300
+ const receiveResult = await this.cashuSpender.receiveToken(
1181
1301
  fetchResult.token
1182
1302
  );
1183
1303
  const totalAmountMsat = receiveResult.unit === "msat" ? receiveResult.amount : receiveResult.amount * 1e3;
@@ -1618,7 +1738,7 @@ var BalanceManager = class {
1618
1738
  */
1619
1739
  async _recoverFailedTopUp(cashuToken) {
1620
1740
  try {
1621
- await this.walletAdapter.receiveToken(cashuToken);
1741
+ await this.cashuSpender.receiveToken(cashuToken);
1622
1742
  } catch (error) {
1623
1743
  console.error(
1624
1744
  "[BalanceManager._recoverFailedTopUp] Failed to recover token",
@@ -2345,12 +2465,39 @@ var RoutstrClient = class {
2345
2465
  providerManager;
2346
2466
  alertLevel;
2347
2467
  mode;
2468
+ debugLevel = "WARN";
2348
2469
  /**
2349
2470
  * Get the current client mode
2350
2471
  */
2351
2472
  getMode() {
2352
2473
  return this.mode;
2353
2474
  }
2475
+ getDebugLevel() {
2476
+ return this.debugLevel;
2477
+ }
2478
+ setDebugLevel(level) {
2479
+ this.debugLevel = level;
2480
+ }
2481
+ _log(level, ...args) {
2482
+ const levelPriority = {
2483
+ DEBUG: 0,
2484
+ WARN: 1,
2485
+ ERROR: 2
2486
+ };
2487
+ if (levelPriority[level] >= levelPriority[this.debugLevel]) {
2488
+ switch (level) {
2489
+ case "DEBUG":
2490
+ console.log(...args);
2491
+ break;
2492
+ case "WARN":
2493
+ console.warn(...args);
2494
+ break;
2495
+ case "ERROR":
2496
+ console.error(...args);
2497
+ break;
2498
+ }
2499
+ }
2500
+ }
2354
2501
  /**
2355
2502
  * Get the CashuSpender instance
2356
2503
  */
@@ -2414,7 +2561,7 @@ var RoutstrClient = class {
2414
2561
  amount: requiredSats,
2415
2562
  baseUrl
2416
2563
  });
2417
- console.log(token, baseUrl);
2564
+ this._log("DEBUG", token, baseUrl);
2418
2565
  let requestBody = body;
2419
2566
  if (body && typeof body === "object") {
2420
2567
  const bodyObj = body;
@@ -2438,8 +2585,9 @@ var RoutstrClient = class {
2438
2585
  });
2439
2586
  const tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
2440
2587
  const baseUrlUsed = response.baseUrl || baseUrl;
2588
+ const tokenUsed = response.token || token;
2441
2589
  await this._handlePostResponseBalanceUpdate({
2442
- token,
2590
+ token: tokenUsed,
2443
2591
  baseUrl: baseUrlUsed,
2444
2592
  initialTokenBalance: tokenBalanceInSats,
2445
2593
  response
@@ -2575,14 +2723,15 @@ var RoutstrClient = class {
2575
2723
  const { path, method, body, baseUrl, token, headers } = params;
2576
2724
  try {
2577
2725
  const url = `${baseUrl.replace(/\/$/, "")}${path}`;
2578
- if (this.mode === "xcashu") console.log("HEADERS,", headers);
2726
+ if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
2579
2727
  const response = await fetch(url, {
2580
2728
  method,
2581
2729
  headers,
2582
2730
  body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
2583
2731
  });
2584
- if (this.mode === "xcashu") console.log("response,", response);
2732
+ if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
2585
2733
  response.baseUrl = baseUrl;
2734
+ response.token = token;
2586
2735
  if (!response.ok) {
2587
2736
  const requestId = response.headers.get("x-routstr-request-id") || void 0;
2588
2737
  return await this._handleErrorResponse(
@@ -2612,38 +2761,44 @@ var RoutstrClient = class {
2612
2761
  async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken) {
2613
2762
  const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
2614
2763
  let tryNextProvider = false;
2615
- console.log(
2764
+ this._log(
2765
+ "DEBUG",
2616
2766
  `[RoutstrClient] _handleErrorResponse: status=${status}, baseUrl=${baseUrl}, mode=${this.mode}, token preview=${token}, requestId=${requestId}`
2617
2767
  );
2618
- console.log(
2768
+ this._log(
2769
+ "DEBUG",
2619
2770
  `[RoutstrClient] _handleErrorResponse: Attempting to receive/restore token for ${baseUrl}`
2620
2771
  );
2621
2772
  if (params.token.startsWith("cashu")) {
2622
- const tryReceiveTokenResult = await this.walletAdapter.receiveToken(
2773
+ const tryReceiveTokenResult = await this.cashuSpender.receiveToken(
2623
2774
  params.token
2624
2775
  );
2625
2776
  if (tryReceiveTokenResult.success) {
2626
- console.log(
2777
+ this._log(
2778
+ "DEBUG",
2627
2779
  `[RoutstrClient] _handleErrorResponse: Token restored successfully, amount=${tryReceiveTokenResult.amount}`
2628
2780
  );
2629
2781
  tryNextProvider = true;
2630
2782
  if (this.mode === "lazyrefund")
2631
2783
  this.storageAdapter.removeToken(baseUrl);
2632
2784
  } else {
2633
- console.log(
2634
- `[RoutstrClient] _handleErrorResponse: Token restore failed or not needed`
2785
+ this._log(
2786
+ "DEBUG",
2787
+ `[RoutstrClient] _handleErrorResponse: Failed to receive token. `
2635
2788
  );
2636
2789
  }
2637
2790
  }
2638
2791
  if (this.mode === "xcashu") {
2639
2792
  if (xCashuRefundToken) {
2640
- console.log(
2793
+ this._log(
2794
+ "DEBUG",
2641
2795
  `[RoutstrClient] _handleErrorResponse: Attempting to receive xcashu refund token, preview=${xCashuRefundToken.substring(0, 20)}...`
2642
2796
  );
2643
2797
  try {
2644
- const receiveResult = await this.walletAdapter.receiveToken(xCashuRefundToken);
2798
+ const receiveResult = await this.cashuSpender.receiveToken(xCashuRefundToken);
2645
2799
  if (receiveResult.success) {
2646
- console.log(
2800
+ this._log(
2801
+ "DEBUG",
2647
2802
  `[RoutstrClient] _handleErrorResponse: xcashu refund received, amount=${receiveResult.amount}`
2648
2803
  );
2649
2804
  tryNextProvider = true;
@@ -2655,7 +2810,7 @@ var RoutstrClient = class {
2655
2810
  requestId
2656
2811
  );
2657
2812
  } catch (error) {
2658
- console.error("[xcashu] Failed to receive refund token:", error);
2813
+ this._log("ERROR", "[xcashu] Failed to receive refund token:", error);
2659
2814
  throw new ProviderError(
2660
2815
  baseUrl,
2661
2816
  status,
@@ -2680,7 +2835,8 @@ var RoutstrClient = class {
2680
2835
  amount: params.requiredSats * TOPUP_MARGIN,
2681
2836
  token: params.token
2682
2837
  });
2683
- console.log(
2838
+ this._log(
2839
+ "DEBUG",
2684
2840
  `[RoutstrClient] _handleErrorResponse: Topup result for ${baseUrl}: success=${topupResult.success}, message=${topupResult.message}`
2685
2841
  );
2686
2842
  if (!topupResult.success) {
@@ -2690,18 +2846,21 @@ var RoutstrClient = class {
2690
2846
  const haveMatch = message.match(/have (\d+)/);
2691
2847
  const required = needMatch ? parseInt(needMatch[1], 10) : params.requiredSats;
2692
2848
  const available = haveMatch ? parseInt(haveMatch[1], 10) : 0;
2693
- console.log(
2849
+ this._log(
2850
+ "DEBUG",
2694
2851
  `[RoutstrClient] _handleErrorResponse: Insufficient balance, need=${required}, have=${available}`
2695
2852
  );
2696
2853
  throw new InsufficientBalanceError(required, available);
2697
2854
  } else {
2698
- console.log(
2855
+ this._log(
2856
+ "DEBUG",
2699
2857
  `[RoutstrClient] _handleErrorResponse: Topup failed with non-insufficient-balance error, will try next provider`
2700
2858
  );
2701
2859
  tryNextProvider = true;
2702
2860
  }
2703
2861
  } else {
2704
- console.log(
2862
+ this._log(
2863
+ "DEBUG",
2705
2864
  `[RoutstrClient] _handleErrorResponse: Topup successful, will retry with new token`
2706
2865
  );
2707
2866
  }
@@ -2712,8 +2871,9 @@ var RoutstrClient = class {
2712
2871
  headers: this._withAuthHeader(params.baseHeaders, params.token)
2713
2872
  });
2714
2873
  }
2715
- if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 521) && !tryNextProvider) {
2716
- console.log(
2874
+ if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
2875
+ this._log(
2876
+ "DEBUG",
2717
2877
  `[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`
2718
2878
  );
2719
2879
  if (this.mode === "lazyrefund") {
@@ -2723,7 +2883,8 @@ var RoutstrClient = class {
2723
2883
  baseUrl,
2724
2884
  token: params.token
2725
2885
  });
2726
- console.log(
2886
+ this._log(
2887
+ "DEBUG",
2727
2888
  `[RoutstrClient] _handleErrorResponse: Lazyrefund result: success=${refundResult.success}`
2728
2889
  );
2729
2890
  if (refundResult.success) this.storageAdapter.removeToken(baseUrl);
@@ -2743,14 +2904,16 @@ var RoutstrClient = class {
2743
2904
  );
2744
2905
  }
2745
2906
  } else if (this.mode === "apikeys") {
2746
- console.log(
2907
+ this._log(
2908
+ "DEBUG",
2747
2909
  `[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
2748
2910
  );
2749
2911
  const initialBalance = await this.balanceManager.getTokenBalance(
2750
2912
  token,
2751
2913
  baseUrl
2752
2914
  );
2753
- console.log(
2915
+ this._log(
2916
+ "DEBUG",
2754
2917
  `[RoutstrClient] _handleErrorResponse: Initial API key balance: ${initialBalance.amount}`
2755
2918
  );
2756
2919
  const refundResult = await this.balanceManager.refundApiKey({
@@ -2758,7 +2921,8 @@ var RoutstrClient = class {
2758
2921
  baseUrl,
2759
2922
  apiKey: token
2760
2923
  });
2761
- console.log(
2924
+ this._log(
2925
+ "DEBUG",
2762
2926
  `[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
2763
2927
  );
2764
2928
  if (!refundResult.success && initialBalance.amount > 0) {
@@ -2773,7 +2937,8 @@ var RoutstrClient = class {
2773
2937
  }
2774
2938
  }
2775
2939
  this.providerManager.markFailed(baseUrl);
2776
- console.log(
2940
+ this._log(
2941
+ "DEBUG",
2777
2942
  `[RoutstrClient] _handleErrorResponse: Marked provider ${baseUrl} as failed`
2778
2943
  );
2779
2944
  if (!selectedModel) {
@@ -2788,7 +2953,8 @@ var RoutstrClient = class {
2788
2953
  baseUrl
2789
2954
  );
2790
2955
  if (nextProvider) {
2791
- console.log(
2956
+ this._log(
2957
+ "DEBUG",
2792
2958
  `[RoutstrClient] _handleErrorResponse: Failing over to next provider: ${nextProvider}, model: ${selectedModel.id}`
2793
2959
  );
2794
2960
  const newModel = await this.providerManager.getModelForProvider(
@@ -2803,7 +2969,8 @@ var RoutstrClient = class {
2803
2969
  messagesForPricing,
2804
2970
  params.maxTokens
2805
2971
  );
2806
- console.log(
2972
+ this._log(
2973
+ "DEBUG",
2807
2974
  `[RoutstrClient] _handleErrorResponse: Creating new token for failover provider ${nextProvider}, required sats: ${newRequiredSats}`
2808
2975
  );
2809
2976
  const spendResult = await this._spendToken({
@@ -2835,10 +3002,10 @@ var RoutstrClient = class {
2835
3002
  const refundToken = response.headers.get("x-cashu") ?? void 0;
2836
3003
  if (refundToken) {
2837
3004
  try {
2838
- const receiveResult = await this.walletAdapter.receiveToken(refundToken);
3005
+ const receiveResult = await this.cashuSpender.receiveToken(refundToken);
2839
3006
  satsSpent = initialTokenBalance - receiveResult.amount * (receiveResult.unit == "sat" ? 1 : 1e3);
2840
3007
  } catch (error) {
2841
- console.error("[xcashu] Failed to receive refund token:", error);
3008
+ this._log("ERROR", "[xcashu] Failed to receive refund token:", error);
2842
3009
  }
2843
3010
  }
2844
3011
  } else if (this.mode === "lazyrefund") {
@@ -2855,7 +3022,8 @@ var RoutstrClient = class {
2855
3022
  token,
2856
3023
  baseUrl
2857
3024
  );
2858
- console.log(
3025
+ this._log(
3026
+ "DEBUG",
2859
3027
  "LATEST Balance",
2860
3028
  latestBalanceInfo.amount,
2861
3029
  latestBalanceInfo.reserved,
@@ -2867,7 +3035,7 @@ var RoutstrClient = class {
2867
3035
  this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
2868
3036
  satsSpent = initialTokenBalance - latestTokenBalance;
2869
3037
  } catch (e) {
2870
- console.warn("Could not get updated API key balance:", e);
3038
+ this._log("WARN", "Could not get updated API key balance:", e);
2871
3039
  satsSpent = fallbackSatsSpent ?? initialTokenBalance;
2872
3040
  }
2873
3041
  }
@@ -2978,11 +3146,12 @@ var RoutstrClient = class {
2978
3146
  * Handle errors and notify callbacks
2979
3147
  */
2980
3148
  _handleError(error, callbacks) {
2981
- console.error("[RoutstrClient] _handleError: Error occurred", error);
3149
+ this._log("ERROR", "[RoutstrClient] _handleError: Error occurred", error);
2982
3150
  if (error instanceof Error) {
2983
3151
  const isStreamError = error.message.includes("Error in input stream") || error.message.includes("Load failed");
2984
3152
  const modifiedErrorMsg = isStreamError ? "AI stream was cut off, turn on Keep Active or please try again" : error.message;
2985
- console.error(
3153
+ this._log(
3154
+ "ERROR",
2986
3155
  `[RoutstrClient] _handleError: Error type=${error.constructor.name}, message=${modifiedErrorMsg}, isStreamError=${isStreamError}`
2987
3156
  );
2988
3157
  callbacks.onMessageAppend({
@@ -3011,13 +3180,15 @@ var RoutstrClient = class {
3011
3180
  */
3012
3181
  async _spendToken(params) {
3013
3182
  const { mintUrl, amount, baseUrl } = params;
3014
- console.log(
3183
+ this._log(
3184
+ "DEBUG",
3015
3185
  `[RoutstrClient] _spendToken: mode=${this.mode}, amount=${amount}, baseUrl=${baseUrl}, mintUrl=${mintUrl}`
3016
3186
  );
3017
3187
  if (this.mode === "apikeys") {
3018
3188
  let parentApiKey = this.storageAdapter.getApiKey(baseUrl);
3019
3189
  if (!parentApiKey) {
3020
- console.log(
3190
+ this._log(
3191
+ "DEBUG",
3021
3192
  `[RoutstrClient] _spendToken: No existing API key for ${baseUrl}, creating new one via Cashu`
3022
3193
  );
3023
3194
  const spendResult2 = await this.cashuSpender.spend({
@@ -3027,7 +3198,8 @@ var RoutstrClient = class {
3027
3198
  reuseToken: false
3028
3199
  });
3029
3200
  if (!spendResult2.token) {
3030
- console.error(
3201
+ this._log(
3202
+ "ERROR",
3031
3203
  `[RoutstrClient] _spendToken: Failed to create Cashu token for API key creation, error:`,
3032
3204
  spendResult2.error
3033
3205
  );
@@ -3035,30 +3207,35 @@ var RoutstrClient = class {
3035
3207
  `[RoutstrClient] _spendToken: Failed to create Cashu token for API key creation, error: ${spendResult2.error}`
3036
3208
  );
3037
3209
  } else {
3038
- console.log(
3210
+ this._log(
3211
+ "DEBUG",
3039
3212
  `[RoutstrClient] _spendToken: Cashu token created, token preview: ${spendResult2.token}`
3040
3213
  );
3041
3214
  }
3042
- console.log(
3215
+ this._log(
3216
+ "DEBUG",
3043
3217
  `[RoutstrClient] _spendToken: Created API key for ${baseUrl}, key preview: ${spendResult2.token}, balance: ${spendResult2.balance}`
3044
3218
  );
3045
3219
  try {
3046
3220
  this.storageAdapter.setApiKey(baseUrl, spendResult2.token);
3047
3221
  } catch (error) {
3048
3222
  if (error instanceof Error && error.message.includes("ApiKey already exists")) {
3049
- const tryReceiveTokenResult = await this.walletAdapter.receiveToken(
3223
+ const tryReceiveTokenResult = await this.cashuSpender.receiveToken(
3050
3224
  spendResult2.token
3051
3225
  );
3052
3226
  if (tryReceiveTokenResult.success) {
3053
- console.log(
3227
+ this._log(
3228
+ "DEBUG",
3054
3229
  `[RoutstrClient] _handleErrorResponse: Token restored successfully, amount=${tryReceiveTokenResult.amount}`
3055
3230
  );
3056
3231
  } else {
3057
- console.log(
3232
+ this._log(
3233
+ "DEBUG",
3058
3234
  `[RoutstrClient] _handleErrorResponse: Token restore failed or not needed`
3059
3235
  );
3060
3236
  }
3061
- console.log(
3237
+ this._log(
3238
+ "DEBUG",
3062
3239
  `[RoutstrClient] _spendToken: API key already exists for ${baseUrl}, using existing key`
3063
3240
  );
3064
3241
  } else {
@@ -3067,7 +3244,8 @@ var RoutstrClient = class {
3067
3244
  }
3068
3245
  parentApiKey = this.storageAdapter.getApiKey(baseUrl);
3069
3246
  } else {
3070
- console.log(
3247
+ this._log(
3248
+ "DEBUG",
3071
3249
  `[RoutstrClient] _spendToken: Using existing API key for ${baseUrl}, key preview: ${parentApiKey.key}`
3072
3250
  );
3073
3251
  }
@@ -3089,10 +3267,11 @@ var RoutstrClient = class {
3089
3267
  tokenBalance = balanceInfo.amount;
3090
3268
  tokenBalanceUnit = balanceInfo.unit;
3091
3269
  } catch (e) {
3092
- console.warn("Could not get initial API key balance:", e);
3270
+ this._log("WARN", "Could not get initial API key balance:", e);
3093
3271
  }
3094
3272
  }
3095
- console.log(
3273
+ this._log(
3274
+ "DEBUG",
3096
3275
  `[RoutstrClient] _spendToken: Returning token with balance=${tokenBalance} ${tokenBalanceUnit}`
3097
3276
  );
3098
3277
  return {
@@ -3101,7 +3280,8 @@ var RoutstrClient = class {
3101
3280
  tokenBalanceUnit
3102
3281
  };
3103
3282
  }
3104
- console.log(
3283
+ this._log(
3284
+ "DEBUG",
3105
3285
  `[RoutstrClient] _spendToken: Calling CashuSpender.spend for amount=${amount}, mintUrl=${mintUrl}, mode=${this.mode}`
3106
3286
  );
3107
3287
  const spendResult = await this.cashuSpender.spend({
@@ -3111,12 +3291,14 @@ var RoutstrClient = class {
3111
3291
  reuseToken: this.mode === "lazyrefund"
3112
3292
  });
3113
3293
  if (!spendResult.token) {
3114
- console.error(
3294
+ this._log(
3295
+ "ERROR",
3115
3296
  `[RoutstrClient] _spendToken: CashuSpender.spend failed, error:`,
3116
3297
  spendResult.error
3117
3298
  );
3118
3299
  } else {
3119
- console.log(
3300
+ this._log(
3301
+ "DEBUG",
3120
3302
  `[RoutstrClient] _spendToken: Cashu token created, token preview: ${spendResult.token}, balance: ${spendResult.balance} ${spendResult.unit ?? "sat"}`
3121
3303
  );
3122
3304
  }
@@ -3428,7 +3610,9 @@ var SDK_STORAGE_KEYS = {
3428
3610
  LAST_BASE_URLS_UPDATE: "lastBaseUrlsUpdate",
3429
3611
  LOCAL_CASHU_TOKENS: "local_cashu_tokens",
3430
3612
  API_KEYS: "api_keys",
3431
- CHILD_KEYS: "child_keys"
3613
+ CHILD_KEYS: "child_keys",
3614
+ ROUTSTR21_MODELS: "routstr21Models",
3615
+ CACHED_RECEIVE_TOKENS: "cached_receive_tokens"
3432
3616
  };
3433
3617
 
3434
3618
  // storage/store.ts
@@ -3460,7 +3644,9 @@ var createSdkStore = async ({
3460
3644
  rawLastModelsUpdate,
3461
3645
  rawCachedTokens,
3462
3646
  rawApiKeys,
3463
- rawChildKeys
3647
+ rawChildKeys,
3648
+ rawRoutstr21Models,
3649
+ rawCachedReceiveTokens
3464
3650
  ] = await Promise.all([
3465
3651
  driver.getItem(
3466
3652
  SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
@@ -3484,7 +3670,9 @@ var createSdkStore = async ({
3484
3670
  ),
3485
3671
  driver.getItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, []),
3486
3672
  driver.getItem(SDK_STORAGE_KEYS.API_KEYS, []),
3487
- driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, [])
3673
+ driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, []),
3674
+ driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
3675
+ driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, [])
3488
3676
  ]);
3489
3677
  const modelsFromAllProviders = Object.fromEntries(
3490
3678
  Object.entries(rawModels).map(([baseUrl, models]) => [
@@ -3534,6 +3722,13 @@ var createSdkStore = async ({
3534
3722
  validityDate: entry.validityDate,
3535
3723
  createdAt: entry.createdAt ?? Date.now()
3536
3724
  }));
3725
+ const routstr21Models = rawRoutstr21Models;
3726
+ const cachedReceiveTokens = rawCachedReceiveTokens.map((entry) => ({
3727
+ token: entry.token,
3728
+ amount: entry.amount,
3729
+ unit: entry.unit || "sat",
3730
+ createdAt: entry.createdAt ?? Date.now()
3731
+ }));
3537
3732
  return createStore((set, get) => ({
3538
3733
  modelsFromAllProviders,
3539
3734
  lastUsedModel,
@@ -3546,6 +3741,8 @@ var createSdkStore = async ({
3546
3741
  cachedTokens,
3547
3742
  apiKeys,
3548
3743
  childKeys,
3744
+ routstr21Models,
3745
+ cachedReceiveTokens,
3549
3746
  setModelsFromAllProviders: (value) => {
3550
3747
  const normalized = {};
3551
3748
  for (const [baseUrl, models] of Object.entries(value)) {
@@ -3644,6 +3841,20 @@ var createSdkStore = async ({
3644
3841
  void driver.setItem(SDK_STORAGE_KEYS.CHILD_KEYS, normalized);
3645
3842
  return { childKeys: normalized };
3646
3843
  });
3844
+ },
3845
+ setRoutstr21Models: (value) => {
3846
+ void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, value);
3847
+ set({ routstr21Models: value });
3848
+ },
3849
+ setCachedReceiveTokens: (value) => {
3850
+ const normalized = value.map((entry) => ({
3851
+ token: entry.token,
3852
+ amount: entry.amount,
3853
+ unit: entry.unit || "sat",
3854
+ createdAt: entry.createdAt ?? Date.now()
3855
+ }));
3856
+ void driver.setItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, normalized);
3857
+ set({ cachedReceiveTokens: normalized });
3647
3858
  }
3648
3859
  }));
3649
3860
  };
@@ -3671,7 +3882,9 @@ var createDiscoveryAdapterFromStore = (store) => ({
3671
3882
  getBaseUrlsList: () => store.getState().baseUrlsList,
3672
3883
  setBaseUrlsList: (urls) => store.getState().setBaseUrlsList(urls),
3673
3884
  getBaseUrlsLastUpdate: () => store.getState().lastBaseUrlsUpdate,
3674
- setBaseUrlsLastUpdate: (timestamp) => store.getState().setBaseUrlsLastUpdate(timestamp)
3885
+ setBaseUrlsLastUpdate: (timestamp) => store.getState().setBaseUrlsLastUpdate(timestamp),
3886
+ getRoutstr21Models: () => store.getState().routstr21Models,
3887
+ setRoutstr21Models: (models) => store.getState().setRoutstr21Models(models)
3675
3888
  });
3676
3889
  var createStorageAdapterFromStore = (store) => ({
3677
3890
  getToken: (baseUrl) => {
@@ -3865,6 +4078,12 @@ var createStorageAdapterFromStore = (store) => ({
3865
4078
  validityDate: entry.validityDate,
3866
4079
  createdAt: entry.createdAt
3867
4080
  }));
4081
+ },
4082
+ getCachedReceiveTokens: () => {
4083
+ return store.getState().cachedReceiveTokens;
4084
+ },
4085
+ setCachedReceiveTokens: (tokens) => {
4086
+ store.getState().setCachedReceiveTokens(tokens);
3868
4087
  }
3869
4088
  });
3870
4089
  var createProviderRegistryFromStore = (store) => ({