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