@routstr/sdk 0.1.6 → 0.1.8
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/client/index.js +190 -66
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +190 -66
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +206 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +206 -73
- package/dist/index.mjs.map +1 -1
- package/dist/storage/index.d.mts +31 -0
- package/dist/storage/index.d.ts +31 -0
- package/dist/storage/index.js +16 -7
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +16 -7
- package/dist/storage/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +8 -1
- package/dist/wallet/index.d.ts +8 -1
- package/dist/wallet/index.js +93 -39
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +93 -39
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import { ModelManager } from './discovery/index.mjs';
|
|
|
5
5
|
export { MintDiscovery, ModelManagerConfig } from './discovery/index.mjs';
|
|
6
6
|
import { W as WalletAdapter, S as StorageAdapter, P as ProviderRegistry } from './interfaces-B85Wx7ni.mjs';
|
|
7
7
|
export { A as ApiKeyEntry, C as ChildKeyEntry, R as RoutstrClientOptions, a as StreamingCallbacks } from './interfaces-B85Wx7ni.mjs';
|
|
8
|
-
export { BalanceManager, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.mjs';
|
|
8
|
+
export { BalanceManager, BalanceState, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.mjs';
|
|
9
9
|
import { DebugLevel } from './client/index.mjs';
|
|
10
10
|
export { AlertLevel, FetchOptions, ModelProviderPrice, ProviderManager, RouteRequestParams, RoutstrClient, RoutstrClientMode, StreamCallbacks, StreamProcessor } from './client/index.mjs';
|
|
11
11
|
export { SDK_STORAGE_KEYS, SdkStore, StorageDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createMemoryDriver, createProviderRegistryFromStore, createSdkStore, createSqliteDriver, createStorageAdapterFromStore, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, localStorageDriver } from './storage/index.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { ModelManager } from './discovery/index.js';
|
|
|
5
5
|
export { MintDiscovery, ModelManagerConfig } from './discovery/index.js';
|
|
6
6
|
import { W as WalletAdapter, S as StorageAdapter, P as ProviderRegistry } from './interfaces-BVNyAmKu.js';
|
|
7
7
|
export { A as ApiKeyEntry, C as ChildKeyEntry, R as RoutstrClientOptions, a as StreamingCallbacks } from './interfaces-BVNyAmKu.js';
|
|
8
|
-
export { BalanceManager, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.js';
|
|
8
|
+
export { BalanceManager, BalanceState, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.js';
|
|
9
9
|
import { DebugLevel } from './client/index.js';
|
|
10
10
|
export { AlertLevel, FetchOptions, ModelProviderPrice, ProviderManager, RouteRequestParams, RoutstrClient, RoutstrClientMode, StreamCallbacks, StreamProcessor } from './client/index.js';
|
|
11
11
|
export { SDK_STORAGE_KEYS, SdkStore, StorageDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createMemoryDriver, createProviderRegistryFromStore, createSdkStore, createSqliteDriver, createStorageAdapterFromStore, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, localStorageDriver } from './storage/index.js';
|
package/dist/index.js
CHANGED
|
@@ -768,6 +768,9 @@ var CashuSpender = class {
|
|
|
768
768
|
return result;
|
|
769
769
|
}
|
|
770
770
|
async _getBalanceState() {
|
|
771
|
+
if (this.balanceManager) {
|
|
772
|
+
return this.balanceManager.getBalanceState();
|
|
773
|
+
}
|
|
771
774
|
const mintBalances = await this.walletAdapter.getBalances();
|
|
772
775
|
const units = this.walletAdapter.getMintUnits();
|
|
773
776
|
let totalMintBalance = 0;
|
|
@@ -783,15 +786,16 @@ var CashuSpender = class {
|
|
|
783
786
|
const providerBalances = {};
|
|
784
787
|
let totalProviderBalance = 0;
|
|
785
788
|
for (const pending of pendingDistribution) {
|
|
786
|
-
providerBalances[pending.baseUrl] = pending.amount;
|
|
789
|
+
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
787
790
|
totalProviderBalance += pending.amount;
|
|
788
791
|
}
|
|
789
792
|
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
790
793
|
for (const apiKey of apiKeys) {
|
|
791
794
|
if (!providerBalances[apiKey.baseUrl]) {
|
|
792
|
-
providerBalances[apiKey.baseUrl] =
|
|
793
|
-
totalProviderBalance += apiKey.balance;
|
|
795
|
+
providerBalances[apiKey.baseUrl] = 0;
|
|
794
796
|
}
|
|
797
|
+
providerBalances[apiKey.baseUrl] += apiKey.balance;
|
|
798
|
+
totalProviderBalance += apiKey.balance;
|
|
795
799
|
}
|
|
796
800
|
return {
|
|
797
801
|
totalBalance: totalMintBalance + totalProviderBalance,
|
|
@@ -940,25 +944,12 @@ var CashuSpender = class {
|
|
|
940
944
|
`[CashuSpender] _spendInternal: Could not reuse token, will create new token`
|
|
941
945
|
);
|
|
942
946
|
}
|
|
943
|
-
const
|
|
944
|
-
const
|
|
945
|
-
let totalBalance = 0;
|
|
946
|
-
for (const url in balances) {
|
|
947
|
-
const balance = balances[url];
|
|
948
|
-
const unit = units[url];
|
|
949
|
-
const balanceInSats = getBalanceInSats(balance, unit);
|
|
950
|
-
totalBalance += balanceInSats;
|
|
951
|
-
}
|
|
952
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
953
|
-
const totalPending = pendingDistribution.reduce(
|
|
954
|
-
(sum, item) => sum + item.amount,
|
|
955
|
-
0
|
|
956
|
-
);
|
|
947
|
+
const balanceState = await this._getBalanceState();
|
|
948
|
+
const totalAvailableBalance = balanceState.totalBalance;
|
|
957
949
|
this._log(
|
|
958
950
|
"DEBUG",
|
|
959
|
-
`[CashuSpender] _spendInternal:
|
|
951
|
+
`[CashuSpender] _spendInternal: totalAvailableBalance=${totalAvailableBalance}, adjustedAmount=${adjustedAmount}`
|
|
960
952
|
);
|
|
961
|
-
const totalAvailableBalance = totalBalance + totalPending;
|
|
962
953
|
if (totalAvailableBalance < adjustedAmount) {
|
|
963
954
|
this._log(
|
|
964
955
|
"ERROR",
|
|
@@ -966,8 +957,7 @@ var CashuSpender = class {
|
|
|
966
957
|
);
|
|
967
958
|
return this._createInsufficientBalanceError(
|
|
968
959
|
adjustedAmount,
|
|
969
|
-
|
|
970
|
-
units,
|
|
960
|
+
balanceState.mintBalances,
|
|
971
961
|
totalAvailableBalance
|
|
972
962
|
);
|
|
973
963
|
}
|
|
@@ -987,8 +977,7 @@ var CashuSpender = class {
|
|
|
987
977
|
if ((tokenResult.error || "").includes("Insufficient balance")) {
|
|
988
978
|
return this._createInsufficientBalanceError(
|
|
989
979
|
adjustedAmount,
|
|
990
|
-
|
|
991
|
-
units,
|
|
980
|
+
balanceState.mintBalances,
|
|
992
981
|
totalAvailableBalance
|
|
993
982
|
);
|
|
994
983
|
}
|
|
@@ -1033,6 +1022,7 @@ var CashuSpender = class {
|
|
|
1033
1022
|
"DEBUG",
|
|
1034
1023
|
`[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
|
|
1035
1024
|
);
|
|
1025
|
+
const units = this.walletAdapter.getMintUnits();
|
|
1036
1026
|
return {
|
|
1037
1027
|
token,
|
|
1038
1028
|
status: "success",
|
|
@@ -1170,13 +1160,11 @@ var CashuSpender = class {
|
|
|
1170
1160
|
/**
|
|
1171
1161
|
* Create an insufficient balance error result
|
|
1172
1162
|
*/
|
|
1173
|
-
_createInsufficientBalanceError(required,
|
|
1163
|
+
_createInsufficientBalanceError(required, normalizedBalances, availableBalance) {
|
|
1174
1164
|
let maxBalance = 0;
|
|
1175
1165
|
let maxMintUrl = "";
|
|
1176
|
-
for (const mintUrl in
|
|
1177
|
-
const
|
|
1178
|
-
const unit = units[mintUrl];
|
|
1179
|
-
const balanceInSats = getBalanceInSats(balance, unit);
|
|
1166
|
+
for (const mintUrl in normalizedBalances) {
|
|
1167
|
+
const balanceInSats = normalizedBalances[mintUrl];
|
|
1180
1168
|
if (balanceInSats > maxBalance) {
|
|
1181
1169
|
maxBalance = balanceInSats;
|
|
1182
1170
|
maxMintUrl = mintUrl;
|
|
@@ -1237,6 +1225,39 @@ var BalanceManager = class {
|
|
|
1237
1225
|
}
|
|
1238
1226
|
}
|
|
1239
1227
|
cashuSpender;
|
|
1228
|
+
async getBalanceState() {
|
|
1229
|
+
const mintBalances = await this.walletAdapter.getBalances();
|
|
1230
|
+
const units = this.walletAdapter.getMintUnits();
|
|
1231
|
+
let totalMintBalance = 0;
|
|
1232
|
+
const normalizedMintBalances = {};
|
|
1233
|
+
for (const url in mintBalances) {
|
|
1234
|
+
const balance = mintBalances[url];
|
|
1235
|
+
const unit = units[url];
|
|
1236
|
+
const balanceInSats = getBalanceInSats(balance, unit);
|
|
1237
|
+
normalizedMintBalances[url] = balanceInSats;
|
|
1238
|
+
totalMintBalance += balanceInSats;
|
|
1239
|
+
}
|
|
1240
|
+
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1241
|
+
const providerBalances = {};
|
|
1242
|
+
let totalProviderBalance = 0;
|
|
1243
|
+
for (const pending of pendingDistribution) {
|
|
1244
|
+
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
1245
|
+
totalProviderBalance += pending.amount;
|
|
1246
|
+
}
|
|
1247
|
+
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
1248
|
+
for (const apiKey of apiKeys) {
|
|
1249
|
+
if (!providerBalances[apiKey.baseUrl]) {
|
|
1250
|
+
providerBalances[apiKey.baseUrl] = 0;
|
|
1251
|
+
}
|
|
1252
|
+
providerBalances[apiKey.baseUrl] += apiKey.balance;
|
|
1253
|
+
totalProviderBalance += apiKey.balance;
|
|
1254
|
+
}
|
|
1255
|
+
return {
|
|
1256
|
+
totalBalance: totalMintBalance + totalProviderBalance,
|
|
1257
|
+
providerBalances,
|
|
1258
|
+
mintBalances: normalizedMintBalances
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1240
1261
|
/**
|
|
1241
1262
|
* Unified refund - handles both NIP-60 and legacy wallet refunds
|
|
1242
1263
|
*/
|
|
@@ -1466,17 +1487,17 @@ var BalanceManager = class {
|
|
|
1466
1487
|
if (!adjustedAmount || isNaN(adjustedAmount)) {
|
|
1467
1488
|
return { success: false, error: "Invalid top up amount" };
|
|
1468
1489
|
}
|
|
1490
|
+
const balanceState = await this.getBalanceState();
|
|
1469
1491
|
const balances = await this.walletAdapter.getBalances();
|
|
1470
1492
|
const units = this.walletAdapter.getMintUnits();
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
if (totalMintBalance < adjustedAmount && totalMintBalance + refundablePending >= adjustedAmount && retryCount < 1) {
|
|
1493
|
+
const totalMintBalance = Object.values(balanceState.mintBalances).reduce(
|
|
1494
|
+
(sum, value) => sum + value,
|
|
1495
|
+
0
|
|
1496
|
+
);
|
|
1497
|
+
const refundableProviderBalance = Object.entries(
|
|
1498
|
+
balanceState.providerBalances
|
|
1499
|
+
).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
|
|
1500
|
+
if (totalMintBalance < adjustedAmount && totalMintBalance + refundableProviderBalance >= adjustedAmount && retryCount < 1) {
|
|
1480
1501
|
await this._refundOtherProvidersForTopUp(baseUrl, mintUrl);
|
|
1481
1502
|
return this.createProviderToken({
|
|
1482
1503
|
...options,
|
|
@@ -1598,10 +1619,14 @@ var BalanceManager = class {
|
|
|
1598
1619
|
}
|
|
1599
1620
|
async _refundOtherProvidersForTopUp(baseUrl, mintUrl) {
|
|
1600
1621
|
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1622
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1601
1623
|
const toRefund = pendingDistribution.filter(
|
|
1602
1624
|
(pending) => pending.baseUrl !== baseUrl
|
|
1603
1625
|
);
|
|
1604
|
-
const
|
|
1626
|
+
const apiKeysToRefund = apiKeyDistribution.filter(
|
|
1627
|
+
(apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
|
|
1628
|
+
);
|
|
1629
|
+
const tokenRefundResults = await Promise.allSettled(
|
|
1605
1630
|
toRefund.map(async (pending) => {
|
|
1606
1631
|
const token = this.storageAdapter.getToken(pending.baseUrl);
|
|
1607
1632
|
if (!token) {
|
|
@@ -1619,11 +1644,32 @@ var BalanceManager = class {
|
|
|
1619
1644
|
return { baseUrl: pending.baseUrl, success: result.success };
|
|
1620
1645
|
})
|
|
1621
1646
|
);
|
|
1622
|
-
for (const result of
|
|
1647
|
+
for (const result of tokenRefundResults) {
|
|
1623
1648
|
if (result.status === "fulfilled" && result.value.success) {
|
|
1624
1649
|
this.storageAdapter.removeToken(result.value.baseUrl);
|
|
1625
1650
|
}
|
|
1626
1651
|
}
|
|
1652
|
+
const apiKeyRefundResults = await Promise.allSettled(
|
|
1653
|
+
apiKeysToRefund.map(async (apiKeyEntry) => {
|
|
1654
|
+
const fullApiKeyEntry = this.storageAdapter.getApiKey(
|
|
1655
|
+
apiKeyEntry.baseUrl
|
|
1656
|
+
);
|
|
1657
|
+
if (!fullApiKeyEntry) {
|
|
1658
|
+
return { baseUrl: apiKeyEntry.baseUrl, success: false };
|
|
1659
|
+
}
|
|
1660
|
+
const result = await this.refundApiKey({
|
|
1661
|
+
mintUrl,
|
|
1662
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
1663
|
+
apiKey: fullApiKeyEntry.key
|
|
1664
|
+
});
|
|
1665
|
+
return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
|
|
1666
|
+
})
|
|
1667
|
+
);
|
|
1668
|
+
for (const result of apiKeyRefundResults) {
|
|
1669
|
+
if (result.status === "fulfilled" && result.value.success) {
|
|
1670
|
+
this.storageAdapter.updateApiKeyBalance(result.value.baseUrl, 0);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1627
1673
|
}
|
|
1628
1674
|
/**
|
|
1629
1675
|
* Fetch refund token from provider API
|
|
@@ -1822,6 +1868,14 @@ var BalanceManager = class {
|
|
|
1822
1868
|
console.log(response.status);
|
|
1823
1869
|
const data = await response.json();
|
|
1824
1870
|
console.log("FAILED ", data);
|
|
1871
|
+
const isInvalidApiKey = response.status === 401 && data?.code === "invalid_api_key" && data?.message?.includes("proofs already spent");
|
|
1872
|
+
return {
|
|
1873
|
+
amount: -1,
|
|
1874
|
+
reserved: data.reserved ?? 0,
|
|
1875
|
+
unit: "msat",
|
|
1876
|
+
apiKey: data.api_key,
|
|
1877
|
+
isInvalidApiKey
|
|
1878
|
+
};
|
|
1825
1879
|
}
|
|
1826
1880
|
} catch (error) {
|
|
1827
1881
|
console.error("ERRORR IN RESTPONSE", error);
|
|
@@ -2770,7 +2824,8 @@ var RoutstrClient = class {
|
|
|
2770
2824
|
response.status,
|
|
2771
2825
|
requestId,
|
|
2772
2826
|
this.mode === "xcashu" ? response.headers.get("x-cashu") ?? void 0 : void 0,
|
|
2773
|
-
bodyText
|
|
2827
|
+
bodyText,
|
|
2828
|
+
params.retryCount ?? 0
|
|
2774
2829
|
);
|
|
2775
2830
|
}
|
|
2776
2831
|
return response;
|
|
@@ -2779,8 +2834,12 @@ var RoutstrClient = class {
|
|
|
2779
2834
|
return await this._handleErrorResponse(
|
|
2780
2835
|
params,
|
|
2781
2836
|
token,
|
|
2782
|
-
-1
|
|
2837
|
+
-1,
|
|
2783
2838
|
// just for Network Error to skip all statuses
|
|
2839
|
+
void 0,
|
|
2840
|
+
void 0,
|
|
2841
|
+
void 0,
|
|
2842
|
+
params.retryCount ?? 0
|
|
2784
2843
|
);
|
|
2785
2844
|
}
|
|
2786
2845
|
throw error;
|
|
@@ -2789,7 +2848,8 @@ var RoutstrClient = class {
|
|
|
2789
2848
|
/**
|
|
2790
2849
|
* Handle error responses with failover
|
|
2791
2850
|
*/
|
|
2792
|
-
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody) {
|
|
2851
|
+
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
2852
|
+
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
2793
2853
|
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
2794
2854
|
let tryNextProvider = false;
|
|
2795
2855
|
this._log(
|
|
@@ -2860,10 +2920,34 @@ var RoutstrClient = class {
|
|
|
2860
2920
|
}
|
|
2861
2921
|
}
|
|
2862
2922
|
if (status === 402 && !tryNextProvider && (this.mode === "apikeys" || this.mode === "lazyrefund")) {
|
|
2923
|
+
this.storageAdapter.getApiKey(baseUrl);
|
|
2924
|
+
let topupAmount = params.requiredSats;
|
|
2925
|
+
try {
|
|
2926
|
+
let currentBalance = 0;
|
|
2927
|
+
if (this.mode === "apikeys") {
|
|
2928
|
+
const currentBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
2929
|
+
params.token,
|
|
2930
|
+
baseUrl
|
|
2931
|
+
);
|
|
2932
|
+
currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
2933
|
+
} else if (this.mode === "lazyrefund") {
|
|
2934
|
+
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
2935
|
+
const tokenEntry = distribution.find((t) => t.baseUrl === baseUrl);
|
|
2936
|
+
currentBalance = tokenEntry?.amount ?? 0;
|
|
2937
|
+
}
|
|
2938
|
+
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
2939
|
+
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
2940
|
+
} catch (e) {
|
|
2941
|
+
this._log(
|
|
2942
|
+
"WARN",
|
|
2943
|
+
"Could not get current token balance for topup calculation:",
|
|
2944
|
+
e
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2863
2947
|
const topupResult = await this.balanceManager.topUp({
|
|
2864
2948
|
mintUrl,
|
|
2865
2949
|
baseUrl,
|
|
2866
|
-
amount:
|
|
2950
|
+
amount: topupAmount * TOPUP_MARGIN,
|
|
2867
2951
|
token: params.token
|
|
2868
2952
|
});
|
|
2869
2953
|
this._log(
|
|
@@ -2895,12 +2979,26 @@ var RoutstrClient = class {
|
|
|
2895
2979
|
`[RoutstrClient] _handleErrorResponse: Topup successful, will retry with new token`
|
|
2896
2980
|
);
|
|
2897
2981
|
}
|
|
2898
|
-
if (!tryNextProvider)
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2982
|
+
if (!tryNextProvider) {
|
|
2983
|
+
if (retryCount < MAX_RETRIES_PER_PROVIDER) {
|
|
2984
|
+
this._log(
|
|
2985
|
+
"DEBUG",
|
|
2986
|
+
`[RoutstrClient] _handleErrorResponse: Retrying 402 (attempt ${retryCount + 1}/${MAX_RETRIES_PER_PROVIDER})`
|
|
2987
|
+
);
|
|
2988
|
+
return this._makeRequest({
|
|
2989
|
+
...params,
|
|
2990
|
+
token: params.token,
|
|
2991
|
+
headers: this._withAuthHeader(params.baseHeaders, params.token),
|
|
2992
|
+
retryCount: retryCount + 1
|
|
2993
|
+
});
|
|
2994
|
+
} else {
|
|
2995
|
+
this._log(
|
|
2996
|
+
"DEBUG",
|
|
2997
|
+
`[RoutstrClient] _handleErrorResponse: 402 retry limit reached (${retryCount}/${MAX_RETRIES_PER_PROVIDER}), failing over to next provider`
|
|
2998
|
+
);
|
|
2999
|
+
tryNextProvider = true;
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
2904
3002
|
}
|
|
2905
3003
|
const isInsufficientBalance413 = status === 413 && responseBody?.includes("Insufficient balance");
|
|
2906
3004
|
if (isInsufficientBalance413 && !tryNextProvider && this.mode === "apikeys") {
|
|
@@ -2910,19 +3008,31 @@ var RoutstrClient = class {
|
|
|
2910
3008
|
params.token,
|
|
2911
3009
|
baseUrl
|
|
2912
3010
|
);
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
3011
|
+
if (latestBalanceInfo.isInvalidApiKey) {
|
|
3012
|
+
this._log(
|
|
3013
|
+
"DEBUG",
|
|
3014
|
+
`[RoutstrClient] _handleErrorResponse: Invalid API key (proofs already spent), removing for ${baseUrl}`
|
|
3015
|
+
);
|
|
3016
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
3017
|
+
tryNextProvider = true;
|
|
3018
|
+
} else {
|
|
3019
|
+
const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
3020
|
+
if (latestBalanceInfo.apiKey) {
|
|
3021
|
+
const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
|
|
3022
|
+
if (storedApiKeyEntry?.key !== latestBalanceInfo.apiKey) {
|
|
3023
|
+
if (storedApiKeyEntry) {
|
|
3024
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
3025
|
+
}
|
|
3026
|
+
this.storageAdapter.setApiKey(baseUrl, latestBalanceInfo.apiKey);
|
|
2919
3027
|
}
|
|
2920
|
-
|
|
3028
|
+
retryToken = latestBalanceInfo.apiKey;
|
|
3029
|
+
}
|
|
3030
|
+
if (latestTokenBalance >= 0) {
|
|
3031
|
+
this.storageAdapter.updateApiKeyBalance(
|
|
3032
|
+
baseUrl,
|
|
3033
|
+
latestTokenBalance
|
|
3034
|
+
);
|
|
2921
3035
|
}
|
|
2922
|
-
retryToken = latestBalanceInfo.apiKey;
|
|
2923
|
-
}
|
|
2924
|
-
if (latestTokenBalance >= 0) {
|
|
2925
|
-
this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
|
|
2926
3036
|
}
|
|
2927
3037
|
} catch (error) {
|
|
2928
3038
|
this._log(
|
|
@@ -2931,11 +3041,24 @@ var RoutstrClient = class {
|
|
|
2931
3041
|
error
|
|
2932
3042
|
);
|
|
2933
3043
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
3044
|
+
if (retryCount < MAX_RETRIES_PER_PROVIDER) {
|
|
3045
|
+
this._log(
|
|
3046
|
+
"DEBUG",
|
|
3047
|
+
`[RoutstrClient] _handleErrorResponse: Retrying 413 (attempt ${retryCount + 1}/${MAX_RETRIES_PER_PROVIDER})`
|
|
3048
|
+
);
|
|
3049
|
+
return this._makeRequest({
|
|
3050
|
+
...params,
|
|
3051
|
+
token: retryToken,
|
|
3052
|
+
headers: this._withAuthHeader(params.baseHeaders, retryToken),
|
|
3053
|
+
retryCount: retryCount + 1
|
|
3054
|
+
});
|
|
3055
|
+
} else {
|
|
3056
|
+
this._log(
|
|
3057
|
+
"DEBUG",
|
|
3058
|
+
`[RoutstrClient] _handleErrorResponse: 413 retry limit reached (${retryCount}/${MAX_RETRIES_PER_PROVIDER}), failing over to next provider`
|
|
3059
|
+
);
|
|
3060
|
+
tryNextProvider = true;
|
|
3061
|
+
}
|
|
2939
3062
|
}
|
|
2940
3063
|
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
2941
3064
|
this._log(
|
|
@@ -3051,7 +3174,8 @@ var RoutstrClient = class {
|
|
|
3051
3174
|
selectedModel: newModel,
|
|
3052
3175
|
token: spendResult.token,
|
|
3053
3176
|
requiredSats: newRequiredSats,
|
|
3054
|
-
headers: this._withAuthHeader(params.baseHeaders, spendResult.token)
|
|
3177
|
+
headers: this._withAuthHeader(params.baseHeaders, spendResult.token),
|
|
3178
|
+
retryCount: 0
|
|
3055
3179
|
});
|
|
3056
3180
|
}
|
|
3057
3181
|
throw new FailoverError(baseUrl, Array.from(this.providerManager));
|
|
@@ -3681,12 +3805,13 @@ var SDK_STORAGE_KEYS = {
|
|
|
3681
3805
|
CHILD_KEYS: "child_keys",
|
|
3682
3806
|
ROUTSTR21_MODELS: "routstr21Models",
|
|
3683
3807
|
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
3684
|
-
CACHED_RECEIVE_TOKENS: "cached_receive_tokens"
|
|
3808
|
+
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
3809
|
+
USAGE_TRACKING: "usage_tracking"
|
|
3685
3810
|
};
|
|
3686
3811
|
|
|
3687
3812
|
// storage/store.ts
|
|
3688
3813
|
var normalizeBaseUrl = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
3689
|
-
var
|
|
3814
|
+
var getCashuTokenBalance = (token) => {
|
|
3690
3815
|
try {
|
|
3691
3816
|
const decoded = cashuTs.getDecodedToken(token);
|
|
3692
3817
|
const unitDivisor = decoded.unit === "msat" ? 1e3 : 1;
|
|
@@ -3716,7 +3841,8 @@ var createSdkStore = async ({
|
|
|
3716
3841
|
rawChildKeys,
|
|
3717
3842
|
rawRoutstr21Models,
|
|
3718
3843
|
rawLastRoutstr21ModelsUpdate,
|
|
3719
|
-
rawCachedReceiveTokens
|
|
3844
|
+
rawCachedReceiveTokens,
|
|
3845
|
+
rawUsageTracking
|
|
3720
3846
|
] = await Promise.all([
|
|
3721
3847
|
driver.getItem(
|
|
3722
3848
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -3746,7 +3872,8 @@ var createSdkStore = async ({
|
|
|
3746
3872
|
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
3747
3873
|
null
|
|
3748
3874
|
),
|
|
3749
|
-
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, [])
|
|
3875
|
+
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
|
|
3876
|
+
driver.getItem(SDK_STORAGE_KEYS.USAGE_TRACKING, [])
|
|
3750
3877
|
]);
|
|
3751
3878
|
const modelsFromAllProviders = Object.fromEntries(
|
|
3752
3879
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
@@ -3779,7 +3906,7 @@ var createSdkStore = async ({
|
|
|
3779
3906
|
const cachedTokens = rawCachedTokens.map((entry) => ({
|
|
3780
3907
|
...entry,
|
|
3781
3908
|
baseUrl: normalizeBaseUrl(entry.baseUrl),
|
|
3782
|
-
balance: typeof entry.balance === "number" ? entry.balance :
|
|
3909
|
+
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
3783
3910
|
lastUsed: entry.lastUsed ?? null
|
|
3784
3911
|
}));
|
|
3785
3912
|
const apiKeys = rawApiKeys.map((entry) => ({
|
|
@@ -3804,6 +3931,7 @@ var createSdkStore = async ({
|
|
|
3804
3931
|
unit: entry.unit || "sat",
|
|
3805
3932
|
createdAt: entry.createdAt ?? Date.now()
|
|
3806
3933
|
}));
|
|
3934
|
+
const usageTracking = rawUsageTracking;
|
|
3807
3935
|
return vanilla.createStore((set, get) => ({
|
|
3808
3936
|
modelsFromAllProviders,
|
|
3809
3937
|
lastUsedModel,
|
|
@@ -3819,6 +3947,7 @@ var createSdkStore = async ({
|
|
|
3819
3947
|
routstr21Models,
|
|
3820
3948
|
lastRoutstr21ModelsUpdate,
|
|
3821
3949
|
cachedReceiveTokens,
|
|
3950
|
+
usageTracking,
|
|
3822
3951
|
setModelsFromAllProviders: (value) => {
|
|
3823
3952
|
const normalized = {};
|
|
3824
3953
|
for (const [baseUrl, models] of Object.entries(value)) {
|
|
@@ -3883,7 +4012,7 @@ var createSdkStore = async ({
|
|
|
3883
4012
|
const normalized = updates.map((entry) => ({
|
|
3884
4013
|
...entry,
|
|
3885
4014
|
baseUrl: normalizeBaseUrl(entry.baseUrl),
|
|
3886
|
-
balance: typeof entry.balance === "number" ? entry.balance :
|
|
4015
|
+
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
3887
4016
|
lastUsed: entry.lastUsed ?? null
|
|
3888
4017
|
}));
|
|
3889
4018
|
void driver.setItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, normalized);
|
|
@@ -3935,6 +4064,10 @@ var createSdkStore = async ({
|
|
|
3935
4064
|
}));
|
|
3936
4065
|
void driver.setItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, normalized);
|
|
3937
4066
|
set({ cachedReceiveTokens: normalized });
|
|
4067
|
+
},
|
|
4068
|
+
setUsageTracking: (value) => {
|
|
4069
|
+
void driver.setItem(SDK_STORAGE_KEYS.USAGE_TRACKING, value);
|
|
4070
|
+
set({ usageTracking: value });
|
|
3938
4071
|
}
|
|
3939
4072
|
}));
|
|
3940
4073
|
};
|
|
@@ -3982,7 +4115,7 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3982
4115
|
setToken: (baseUrl, token) => {
|
|
3983
4116
|
const normalized = normalizeBaseUrl(baseUrl);
|
|
3984
4117
|
const tokens = store.getState().cachedTokens;
|
|
3985
|
-
const balance =
|
|
4118
|
+
const balance = getCashuTokenBalance(token);
|
|
3986
4119
|
const existingIndex = tokens.findIndex(
|
|
3987
4120
|
(entry) => entry.baseUrl === normalized
|
|
3988
4121
|
);
|