@routstr/sdk 0.2.5 → 0.2.6
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/README.md +1 -2
- package/dist/client/index.d.mts +19 -5
- package/dist/client/index.d.ts +19 -5
- package/dist/client/index.js +506 -451
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +506 -451
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.d.mts +16 -8
- package/dist/index.d.ts +16 -8
- package/dist/index.js +627 -528
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +625 -529
- package/dist/index.mjs.map +1 -1
- package/dist/{interfaces-CluftN4z.d.ts → interfaces-B62Rw-dd.d.ts} +19 -14
- package/dist/{interfaces-C6Dr6hKy.d.mts → interfaces-C5fLD3jB.d.mts} +19 -14
- package/dist/storage/index.d.mts +36 -162
- package/dist/storage/index.d.ts +36 -162
- package/dist/storage/index.js +434 -189
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +432 -190
- package/dist/storage/index.mjs.map +1 -1
- package/dist/store-BJlwiDX5.d.ts +151 -0
- package/dist/store-C5lnyX8k.d.mts +151 -0
- package/dist/wallet/index.d.mts +18 -24
- package/dist/wallet/index.d.ts +18 -24
- package/dist/wallet/index.js +106 -255
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +106 -255
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,16 +4,8 @@ var applesauceRelay = require('applesauce-relay');
|
|
|
4
4
|
var applesauceCore = require('applesauce-core');
|
|
5
5
|
var rxjs = require('rxjs');
|
|
6
6
|
var vanilla = require('zustand/vanilla');
|
|
7
|
-
var cashuTs = require('@cashu/cashu-ts');
|
|
8
7
|
var stream = require('stream');
|
|
9
8
|
|
|
10
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
11
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
12
|
-
}) : x)(function(x) {
|
|
13
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
14
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
9
|
// core/errors.ts
|
|
18
10
|
var InsufficientBalanceError = class extends Error {
|
|
19
11
|
constructor(required, available, maxMintBalance = 0, maxMintUrl = "", customMessage) {
|
|
@@ -788,13 +780,8 @@ var CashuSpender = class {
|
|
|
788
780
|
normalizedMintBalances[url] = balanceInSats;
|
|
789
781
|
totalMintBalance += balanceInSats;
|
|
790
782
|
}
|
|
791
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
792
783
|
const providerBalances = {};
|
|
793
784
|
let totalProviderBalance = 0;
|
|
794
|
-
for (const pending of pendingDistribution) {
|
|
795
|
-
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
796
|
-
totalProviderBalance += pending.amount;
|
|
797
|
-
}
|
|
798
785
|
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
799
786
|
for (const apiKey of apiKeys) {
|
|
800
787
|
if (!providerBalances[apiKey.baseUrl]) {
|
|
@@ -1015,27 +1002,11 @@ var CashuSpender = class {
|
|
|
1015
1002
|
};
|
|
1016
1003
|
}
|
|
1017
1004
|
}
|
|
1018
|
-
if (token
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
this._log(
|
|
1024
|
-
"DEBUG",
|
|
1025
|
-
`[CashuSpender] _spendInternal: Token already exists for ${baseUrl}, receiving newly created token and using existing`
|
|
1026
|
-
);
|
|
1027
|
-
const receiveResult = await this.receiveToken(token);
|
|
1028
|
-
if (receiveResult.success) {
|
|
1029
|
-
this._log(
|
|
1030
|
-
"DEBUG",
|
|
1031
|
-
`[CashuSpender] _spendInternal: Token restored successfully, amount=${receiveResult.amount}`
|
|
1032
|
-
);
|
|
1033
|
-
}
|
|
1034
|
-
token = this.storageAdapter.getToken(baseUrl);
|
|
1035
|
-
} else {
|
|
1036
|
-
throw error;
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1005
|
+
if (token) {
|
|
1006
|
+
this._log(
|
|
1007
|
+
"DEBUG",
|
|
1008
|
+
`[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
|
|
1009
|
+
);
|
|
1039
1010
|
}
|
|
1040
1011
|
this._logTransaction("spend", {
|
|
1041
1012
|
amount: spentAmount,
|
|
@@ -1056,19 +1027,19 @@ var CashuSpender = class {
|
|
|
1056
1027
|
};
|
|
1057
1028
|
}
|
|
1058
1029
|
/**
|
|
1059
|
-
* Try to reuse an existing
|
|
1030
|
+
* Try to reuse an existing API key
|
|
1060
1031
|
*/
|
|
1061
1032
|
async _tryReuseToken(baseUrl, amount, mintUrl) {
|
|
1062
|
-
const
|
|
1063
|
-
if (!
|
|
1064
|
-
const
|
|
1065
|
-
const balanceForBaseUrl =
|
|
1066
|
-
this._log("DEBUG", "
|
|
1033
|
+
const apiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
|
|
1034
|
+
if (!apiKeyEntry) return null;
|
|
1035
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1036
|
+
const balanceForBaseUrl = apiKeyDistribution.find((b) => b.baseUrl === baseUrl)?.amount || 0;
|
|
1037
|
+
this._log("DEBUG", "Reusing API key", balanceForBaseUrl, amount);
|
|
1067
1038
|
if (balanceForBaseUrl > amount) {
|
|
1068
1039
|
const units = this.walletAdapter.getMintUnits();
|
|
1069
1040
|
const unit = units[mintUrl] || "sat";
|
|
1070
1041
|
return {
|
|
1071
|
-
token:
|
|
1042
|
+
token: apiKeyEntry.key,
|
|
1072
1043
|
status: "success",
|
|
1073
1044
|
balance: balanceForBaseUrl,
|
|
1074
1045
|
unit
|
|
@@ -1079,7 +1050,8 @@ var CashuSpender = class {
|
|
|
1079
1050
|
const topUpResult = await this.balanceManager.topUp({
|
|
1080
1051
|
mintUrl,
|
|
1081
1052
|
baseUrl,
|
|
1082
|
-
amount: topUpAmount
|
|
1053
|
+
amount: topUpAmount,
|
|
1054
|
+
token: apiKeyEntry.key
|
|
1083
1055
|
});
|
|
1084
1056
|
this._log("DEBUG", "TOPUP ", topUpResult);
|
|
1085
1057
|
if (topUpResult.success && topUpResult.toppedUpAmount) {
|
|
@@ -1093,7 +1065,7 @@ var CashuSpender = class {
|
|
|
1093
1065
|
status: "success"
|
|
1094
1066
|
});
|
|
1095
1067
|
return {
|
|
1096
|
-
token:
|
|
1068
|
+
token: apiKeyEntry.key,
|
|
1097
1069
|
status: "success",
|
|
1098
1070
|
balance: newBalance,
|
|
1099
1071
|
unit
|
|
@@ -1101,84 +1073,108 @@ var CashuSpender = class {
|
|
|
1101
1073
|
}
|
|
1102
1074
|
const providerBalance = await this._getProviderTokenBalance(
|
|
1103
1075
|
baseUrl,
|
|
1104
|
-
|
|
1076
|
+
apiKeyEntry.key
|
|
1105
1077
|
);
|
|
1106
1078
|
this._log("DEBUG", providerBalance);
|
|
1107
1079
|
if (providerBalance <= 0) {
|
|
1108
|
-
this.storageAdapter.
|
|
1080
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
1109
1081
|
}
|
|
1110
1082
|
}
|
|
1111
1083
|
return null;
|
|
1112
1084
|
}
|
|
1113
1085
|
/**
|
|
1114
|
-
* Refund
|
|
1086
|
+
* Refund all xcashu tokens from storage and increment tryCounts on failure.
|
|
1087
|
+
* Reuses receiveToken from BalanceManager/CashuSpender for receiving refunds.
|
|
1088
|
+
* @param mintUrl - The mint URL for receiving tokens
|
|
1089
|
+
* @param excludeBaseUrls - Base URLs to exclude from refund (optional)
|
|
1090
|
+
* @returns Results for each xcashu token refund attempt
|
|
1115
1091
|
*/
|
|
1116
|
-
async
|
|
1092
|
+
async refundXcashuTokens(mintUrl, excludeBaseUrls) {
|
|
1117
1093
|
const results = [];
|
|
1118
|
-
const
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
(r) => r.status === "fulfilled" ? r.value : { baseUrl: "", success: false }
|
|
1151
|
-
)
|
|
1152
|
-
);
|
|
1153
|
-
if (refundApiKeys) {
|
|
1154
|
-
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1155
|
-
const apiKeysToRefund = apiKeyDistribution.filter(
|
|
1156
|
-
(p) => baseUrls.includes(p.baseUrl)
|
|
1157
|
-
);
|
|
1158
|
-
for (const apiKeyEntry of apiKeysToRefund) {
|
|
1159
|
-
const apiKeyEntryFull = this.storageAdapter.getApiKey(
|
|
1160
|
-
apiKeyEntry.baseUrl
|
|
1161
|
-
);
|
|
1162
|
-
if (apiKeyEntryFull && this.balanceManager) {
|
|
1163
|
-
const refundResult = await this.balanceManager.refundApiKey({
|
|
1164
|
-
mintUrl,
|
|
1165
|
-
baseUrl: apiKeyEntry.baseUrl,
|
|
1166
|
-
apiKey: apiKeyEntryFull.key,
|
|
1167
|
-
forceRefund
|
|
1168
|
-
});
|
|
1169
|
-
if (refundResult.success) {
|
|
1170
|
-
this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, 0);
|
|
1094
|
+
const xcashuTokens = this.storageAdapter.getXcashuTokens();
|
|
1095
|
+
const excludedUrls = new Set(excludeBaseUrls || []);
|
|
1096
|
+
for (const [baseUrl, tokens] of Object.entries(xcashuTokens)) {
|
|
1097
|
+
if (excludedUrls.has(baseUrl)) continue;
|
|
1098
|
+
for (const xcashuToken of tokens) {
|
|
1099
|
+
try {
|
|
1100
|
+
const receiveResult = await this.receiveToken(xcashuToken.token);
|
|
1101
|
+
if (receiveResult.success) {
|
|
1102
|
+
this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
|
|
1103
|
+
results.push({
|
|
1104
|
+
baseUrl,
|
|
1105
|
+
token: xcashuToken.token,
|
|
1106
|
+
success: true
|
|
1107
|
+
});
|
|
1108
|
+
this._log(
|
|
1109
|
+
"DEBUG",
|
|
1110
|
+
`[CashuSpender] refundXcashuTokens: Successfully refunded xcashu token for ${baseUrl}, amount=${receiveResult.amount}`
|
|
1111
|
+
);
|
|
1112
|
+
} else {
|
|
1113
|
+
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
1114
|
+
const newTryCount = currentTryCount + 1;
|
|
1115
|
+
this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
|
|
1116
|
+
results.push({
|
|
1117
|
+
baseUrl,
|
|
1118
|
+
token: xcashuToken.token,
|
|
1119
|
+
success: false,
|
|
1120
|
+
error: receiveResult.message ?? "Refund failed"
|
|
1121
|
+
});
|
|
1122
|
+
this._log(
|
|
1123
|
+
"DEBUG",
|
|
1124
|
+
`[CashuSpender] refundXcashuTokens: Failed to refund xcashu token for ${baseUrl}, incremented tryCount to ${newTryCount}`
|
|
1125
|
+
);
|
|
1171
1126
|
}
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
1129
|
+
const newTryCount = currentTryCount + 1;
|
|
1130
|
+
this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
|
|
1131
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1172
1132
|
results.push({
|
|
1173
|
-
baseUrl
|
|
1174
|
-
|
|
1133
|
+
baseUrl,
|
|
1134
|
+
token: xcashuToken.token,
|
|
1135
|
+
success: false,
|
|
1136
|
+
error: errorMessage
|
|
1175
1137
|
});
|
|
1138
|
+
this._log(
|
|
1139
|
+
"ERROR",
|
|
1140
|
+
`[CashuSpender] refundXcashuTokens: Exception during refund for ${baseUrl}: ${errorMessage}, incremented tryCount to ${newTryCount}`
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return results;
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Refund specific providers without retrying spend
|
|
1149
|
+
*/
|
|
1150
|
+
async refundProviders(mintUrl, forceRefund) {
|
|
1151
|
+
const results = [];
|
|
1152
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1153
|
+
for (const apiKeyEntry of apiKeyDistribution) {
|
|
1154
|
+
const apiKeyEntryFull = this.storageAdapter.getApiKey(
|
|
1155
|
+
apiKeyEntry.baseUrl
|
|
1156
|
+
);
|
|
1157
|
+
if (apiKeyEntryFull && this.balanceManager) {
|
|
1158
|
+
const refundResult = await this.balanceManager.refundApiKey({
|
|
1159
|
+
mintUrl,
|
|
1160
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
1161
|
+
apiKey: apiKeyEntryFull.key,
|
|
1162
|
+
forceRefund
|
|
1163
|
+
});
|
|
1164
|
+
if (refundResult.success) {
|
|
1165
|
+
this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
|
|
1176
1166
|
} else {
|
|
1177
|
-
|
|
1178
|
-
baseUrl: apiKeyEntry.baseUrl,
|
|
1179
|
-
success: false
|
|
1180
|
-
});
|
|
1167
|
+
this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, apiKeyEntry.amount);
|
|
1181
1168
|
}
|
|
1169
|
+
results.push({
|
|
1170
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
1171
|
+
success: refundResult.success
|
|
1172
|
+
});
|
|
1173
|
+
} else {
|
|
1174
|
+
results.push({
|
|
1175
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
1176
|
+
success: false
|
|
1177
|
+
});
|
|
1182
1178
|
}
|
|
1183
1179
|
}
|
|
1184
1180
|
return results;
|
|
@@ -1263,13 +1259,8 @@ var BalanceManager = class {
|
|
|
1263
1259
|
normalizedMintBalances[url] = balanceInSats;
|
|
1264
1260
|
totalMintBalance += balanceInSats;
|
|
1265
1261
|
}
|
|
1266
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1267
1262
|
const providerBalances = {};
|
|
1268
1263
|
let totalProviderBalance = 0;
|
|
1269
|
-
for (const pending of pendingDistribution) {
|
|
1270
|
-
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
1271
|
-
totalProviderBalance += pending.amount;
|
|
1272
|
-
}
|
|
1273
1264
|
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
1274
1265
|
for (const apiKey of apiKeys) {
|
|
1275
1266
|
if (!providerBalances[apiKey.baseUrl]) {
|
|
@@ -1284,57 +1275,6 @@ var BalanceManager = class {
|
|
|
1284
1275
|
mintBalances: normalizedMintBalances
|
|
1285
1276
|
};
|
|
1286
1277
|
}
|
|
1287
|
-
/**
|
|
1288
|
-
* Unified refund - handles both NIP-60 and legacy wallet refunds
|
|
1289
|
-
*/
|
|
1290
|
-
async refund(options) {
|
|
1291
|
-
const { mintUrl, baseUrl, token: providedToken } = options;
|
|
1292
|
-
const storedToken = providedToken || this.storageAdapter.getToken(baseUrl);
|
|
1293
|
-
if (!storedToken) {
|
|
1294
|
-
console.log("[BalanceManager] No token to refund, returning early");
|
|
1295
|
-
return { success: true, message: "No API key to refund" };
|
|
1296
|
-
}
|
|
1297
|
-
let fetchResult;
|
|
1298
|
-
try {
|
|
1299
|
-
fetchResult = await this._fetchRefundToken(baseUrl, storedToken);
|
|
1300
|
-
if (!fetchResult.success) {
|
|
1301
|
-
return {
|
|
1302
|
-
success: false,
|
|
1303
|
-
message: fetchResult.error || "Refund failed",
|
|
1304
|
-
requestId: fetchResult.requestId
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
1307
|
-
if (!fetchResult.token) {
|
|
1308
|
-
return {
|
|
1309
|
-
success: false,
|
|
1310
|
-
message: "No token received from refund",
|
|
1311
|
-
requestId: fetchResult.requestId
|
|
1312
|
-
};
|
|
1313
|
-
}
|
|
1314
|
-
if (fetchResult.error === "No balance to refund") {
|
|
1315
|
-
console.log(
|
|
1316
|
-
"[BalanceManager] No balance to refund, removing stored token"
|
|
1317
|
-
);
|
|
1318
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
1319
|
-
return { success: true, message: "No balance to refund" };
|
|
1320
|
-
}
|
|
1321
|
-
const receiveResult = await this.cashuSpender.receiveToken(
|
|
1322
|
-
fetchResult.token
|
|
1323
|
-
);
|
|
1324
|
-
const totalAmountMsat = receiveResult.unit === "msat" ? receiveResult.amount : receiveResult.amount * 1e3;
|
|
1325
|
-
if (!providedToken) {
|
|
1326
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
1327
|
-
}
|
|
1328
|
-
return {
|
|
1329
|
-
success: receiveResult.success,
|
|
1330
|
-
refundedAmount: totalAmountMsat,
|
|
1331
|
-
requestId: fetchResult.requestId
|
|
1332
|
-
};
|
|
1333
|
-
} catch (error) {
|
|
1334
|
-
console.error("[BalanceManager] Refund error", error);
|
|
1335
|
-
return this._handleRefundError(error, mintUrl, fetchResult?.requestId);
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
1278
|
/**
|
|
1339
1279
|
* Refund API key balance - convert remaining API key balance to cashu token
|
|
1340
1280
|
* @param options - Refund options including forceRefund flag
|
|
@@ -1470,8 +1410,9 @@ var BalanceManager = class {
|
|
|
1470
1410
|
if (!amount || amount <= 0) {
|
|
1471
1411
|
return { success: false, message: "Invalid top up amount" };
|
|
1472
1412
|
}
|
|
1473
|
-
const
|
|
1474
|
-
|
|
1413
|
+
const apiKeyEntry = providedToken ? null : this.storageAdapter.getApiKey(baseUrl);
|
|
1414
|
+
const apiKey = providedToken || apiKeyEntry?.key;
|
|
1415
|
+
if (!apiKey) {
|
|
1475
1416
|
return { success: false, message: "No API key available for top up" };
|
|
1476
1417
|
}
|
|
1477
1418
|
let cashuToken = null;
|
|
@@ -1491,7 +1432,7 @@ var BalanceManager = class {
|
|
|
1491
1432
|
cashuToken = tokenResult.token;
|
|
1492
1433
|
const topUpResult = await this._postTopUp(
|
|
1493
1434
|
baseUrl,
|
|
1494
|
-
|
|
1435
|
+
apiKey,
|
|
1495
1436
|
cashuToken
|
|
1496
1437
|
);
|
|
1497
1438
|
requestId = topUpResult.requestId;
|
|
@@ -1706,38 +1647,11 @@ var BalanceManager = class {
|
|
|
1706
1647
|
return candidates;
|
|
1707
1648
|
}
|
|
1708
1649
|
async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount) {
|
|
1709
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1710
1650
|
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1711
1651
|
const forceRefund = retryCount >= 2;
|
|
1712
|
-
const toRefund = pendingDistribution.filter(
|
|
1713
|
-
(pending) => pending.baseUrl !== baseUrl
|
|
1714
|
-
);
|
|
1715
1652
|
const apiKeysToRefund = apiKeyDistribution.filter(
|
|
1716
1653
|
(apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
|
|
1717
1654
|
);
|
|
1718
|
-
const tokenRefundResults = await Promise.allSettled(
|
|
1719
|
-
toRefund.map(async (pending) => {
|
|
1720
|
-
const token = this.storageAdapter.getToken(pending.baseUrl);
|
|
1721
|
-
if (!token) {
|
|
1722
|
-
return { baseUrl: pending.baseUrl, success: false };
|
|
1723
|
-
}
|
|
1724
|
-
const tokenBalance = await this.getTokenBalance(token, pending.baseUrl);
|
|
1725
|
-
if (tokenBalance.reserved > 0) {
|
|
1726
|
-
return { baseUrl: pending.baseUrl, success: false };
|
|
1727
|
-
}
|
|
1728
|
-
const result = await this.refund({
|
|
1729
|
-
mintUrl,
|
|
1730
|
-
baseUrl: pending.baseUrl,
|
|
1731
|
-
token
|
|
1732
|
-
});
|
|
1733
|
-
return { baseUrl: pending.baseUrl, success: result.success };
|
|
1734
|
-
})
|
|
1735
|
-
);
|
|
1736
|
-
for (const result of tokenRefundResults) {
|
|
1737
|
-
if (result.status === "fulfilled" && result.value.success) {
|
|
1738
|
-
this.storageAdapter.removeToken(result.value.baseUrl);
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
1655
|
const apiKeyRefundResults = await Promise.allSettled(
|
|
1742
1656
|
apiKeysToRefund.map(async (apiKeyEntry) => {
|
|
1743
1657
|
const fullApiKeyEntry = this.storageAdapter.getApiKey(
|
|
@@ -1761,77 +1675,6 @@ var BalanceManager = class {
|
|
|
1761
1675
|
}
|
|
1762
1676
|
}
|
|
1763
1677
|
}
|
|
1764
|
-
/**
|
|
1765
|
-
* Fetch refund token from provider API
|
|
1766
|
-
*/
|
|
1767
|
-
async _fetchRefundToken(baseUrl, storedToken) {
|
|
1768
|
-
if (!baseUrl) {
|
|
1769
|
-
return {
|
|
1770
|
-
success: false,
|
|
1771
|
-
error: "No base URL configured"
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
1775
|
-
const url = `${normalizedBaseUrl}v1/wallet/refund`;
|
|
1776
|
-
const controller = new AbortController();
|
|
1777
|
-
const timeoutId = setTimeout(() => {
|
|
1778
|
-
controller.abort();
|
|
1779
|
-
}, 6e4);
|
|
1780
|
-
try {
|
|
1781
|
-
const response = await fetch(url, {
|
|
1782
|
-
method: "POST",
|
|
1783
|
-
headers: {
|
|
1784
|
-
Authorization: `Bearer ${storedToken}`,
|
|
1785
|
-
"Content-Type": "application/json"
|
|
1786
|
-
},
|
|
1787
|
-
signal: controller.signal
|
|
1788
|
-
});
|
|
1789
|
-
clearTimeout(timeoutId);
|
|
1790
|
-
const requestId = response.headers.get("x-routstr-request-id") || void 0;
|
|
1791
|
-
if (!response.ok) {
|
|
1792
|
-
const errorData = await response.json().catch(() => ({}));
|
|
1793
|
-
if (response.status === 400 && errorData?.detail === "No balance to refund") {
|
|
1794
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
1795
|
-
return {
|
|
1796
|
-
success: false,
|
|
1797
|
-
requestId,
|
|
1798
|
-
error: "No balance to refund"
|
|
1799
|
-
};
|
|
1800
|
-
}
|
|
1801
|
-
return {
|
|
1802
|
-
success: false,
|
|
1803
|
-
requestId,
|
|
1804
|
-
error: `Refund request failed with status ${response.status}: ${errorData?.detail || response.statusText}`
|
|
1805
|
-
};
|
|
1806
|
-
}
|
|
1807
|
-
const data = await response.json();
|
|
1808
|
-
console.log("refund rsule", data);
|
|
1809
|
-
return {
|
|
1810
|
-
success: true,
|
|
1811
|
-
token: data.token,
|
|
1812
|
-
requestId
|
|
1813
|
-
};
|
|
1814
|
-
} catch (error) {
|
|
1815
|
-
clearTimeout(timeoutId);
|
|
1816
|
-
console.error("[BalanceManager._fetchRefundToken] Fetch error", error);
|
|
1817
|
-
if (error instanceof Error) {
|
|
1818
|
-
if (error.name === "AbortError") {
|
|
1819
|
-
return {
|
|
1820
|
-
success: false,
|
|
1821
|
-
error: "Request timed out after 1 minute"
|
|
1822
|
-
};
|
|
1823
|
-
}
|
|
1824
|
-
return {
|
|
1825
|
-
success: false,
|
|
1826
|
-
error: error.message
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
1829
|
-
return {
|
|
1830
|
-
success: false,
|
|
1831
|
-
error: "Unknown error occurred during refund request"
|
|
1832
|
-
};
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
1678
|
/**
|
|
1836
1679
|
* Post topup request to provider API
|
|
1837
1680
|
*/
|
|
@@ -2876,38 +2719,54 @@ var createMemoryDriver = (seed) => {
|
|
|
2876
2719
|
var isBun = () => {
|
|
2877
2720
|
return typeof process.versions.bun !== "undefined";
|
|
2878
2721
|
};
|
|
2879
|
-
var
|
|
2722
|
+
var cachedDbModule = null;
|
|
2723
|
+
var loadDatabase = async (dbPath) => {
|
|
2880
2724
|
if (isBun()) {
|
|
2881
2725
|
throw new Error(
|
|
2882
|
-
"SQLite driver not supported in Bun. Use
|
|
2726
|
+
"SQLite driver not supported in Bun. Use createBunSqliteDriver() instead."
|
|
2883
2727
|
);
|
|
2884
2728
|
}
|
|
2885
|
-
let Database = null;
|
|
2886
2729
|
try {
|
|
2887
|
-
|
|
2730
|
+
if (!cachedDbModule) {
|
|
2731
|
+
cachedDbModule = (await import('better-sqlite3')).default;
|
|
2732
|
+
}
|
|
2733
|
+
return new cachedDbModule(dbPath);
|
|
2888
2734
|
} catch (error) {
|
|
2889
2735
|
throw new Error(
|
|
2890
2736
|
`better-sqlite3 is required for sqlite storage. Install it to use sqlite storage. (${error})`
|
|
2891
2737
|
);
|
|
2892
2738
|
}
|
|
2893
|
-
return new Database(dbPath);
|
|
2894
2739
|
};
|
|
2895
2740
|
var createSqliteDriver = (options = {}) => {
|
|
2896
2741
|
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2897
2742
|
const tableName = options.tableName || "sdk_storage";
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
const
|
|
2903
|
-
|
|
2904
|
-
|
|
2743
|
+
let db;
|
|
2744
|
+
let selectStmt;
|
|
2745
|
+
let upsertStmt;
|
|
2746
|
+
let deleteStmt;
|
|
2747
|
+
const initDb = async () => {
|
|
2748
|
+
if (!db) {
|
|
2749
|
+
db = await loadDatabase(dbPath);
|
|
2750
|
+
db.exec(
|
|
2751
|
+
`CREATE TABLE IF NOT EXISTS ${tableName} (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
|
|
2752
|
+
);
|
|
2753
|
+
selectStmt = db.prepare(`SELECT value FROM ${tableName} WHERE key = ?`);
|
|
2754
|
+
upsertStmt = db.prepare(
|
|
2755
|
+
`INSERT INTO ${tableName} (key, value) VALUES (?, ?)
|
|
2905
2756
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
2906
|
-
|
|
2907
|
-
|
|
2757
|
+
);
|
|
2758
|
+
deleteStmt = db.prepare(`DELETE FROM ${tableName} WHERE key = ?`);
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
const ensureInit = async () => {
|
|
2762
|
+
if (!db) {
|
|
2763
|
+
await initDb();
|
|
2764
|
+
}
|
|
2765
|
+
};
|
|
2908
2766
|
return {
|
|
2909
2767
|
async getItem(key, defaultValue) {
|
|
2910
2768
|
try {
|
|
2769
|
+
await ensureInit();
|
|
2911
2770
|
const row = selectStmt.get(key);
|
|
2912
2771
|
if (!row || typeof row.value !== "string") return defaultValue;
|
|
2913
2772
|
try {
|
|
@@ -2925,6 +2784,7 @@ var createSqliteDriver = (options = {}) => {
|
|
|
2925
2784
|
},
|
|
2926
2785
|
async setItem(key, value) {
|
|
2927
2786
|
try {
|
|
2787
|
+
await ensureInit();
|
|
2928
2788
|
upsertStmt.run(key, JSON.stringify(value));
|
|
2929
2789
|
} catch (error) {
|
|
2930
2790
|
console.error(`SQLite setItem failed for key "${key}":`, error);
|
|
@@ -2932,6 +2792,7 @@ var createSqliteDriver = (options = {}) => {
|
|
|
2932
2792
|
},
|
|
2933
2793
|
async removeItem(key) {
|
|
2934
2794
|
try {
|
|
2795
|
+
await ensureInit();
|
|
2935
2796
|
deleteStmt.run(key);
|
|
2936
2797
|
} catch (error) {
|
|
2937
2798
|
console.error(`SQLite removeItem failed for key "${key}":`, error);
|
|
@@ -2939,6 +2800,54 @@ var createSqliteDriver = (options = {}) => {
|
|
|
2939
2800
|
}
|
|
2940
2801
|
};
|
|
2941
2802
|
};
|
|
2803
|
+
async function createBunSqliteDriver(dbPath) {
|
|
2804
|
+
const SQLite = (await import(
|
|
2805
|
+
/* webpackIgnore: true */
|
|
2806
|
+
'bun:sqlite'
|
|
2807
|
+
)).default;
|
|
2808
|
+
const db = new SQLite(dbPath);
|
|
2809
|
+
db.run(`
|
|
2810
|
+
CREATE TABLE IF NOT EXISTS sdk_storage (
|
|
2811
|
+
key TEXT PRIMARY KEY,
|
|
2812
|
+
value TEXT NOT NULL
|
|
2813
|
+
)
|
|
2814
|
+
`);
|
|
2815
|
+
return {
|
|
2816
|
+
async getItem(key, defaultValue) {
|
|
2817
|
+
try {
|
|
2818
|
+
const row = db.query("SELECT value FROM sdk_storage WHERE key = ?").get(key);
|
|
2819
|
+
if (!row || typeof row.value !== "string") return defaultValue;
|
|
2820
|
+
try {
|
|
2821
|
+
return JSON.parse(row.value);
|
|
2822
|
+
} catch (parseError) {
|
|
2823
|
+
if (typeof defaultValue === "string") {
|
|
2824
|
+
return row.value;
|
|
2825
|
+
}
|
|
2826
|
+
throw parseError;
|
|
2827
|
+
}
|
|
2828
|
+
} catch (error) {
|
|
2829
|
+
console.error(`SQLite getItem failed for key "${key}":`, error);
|
|
2830
|
+
return defaultValue;
|
|
2831
|
+
}
|
|
2832
|
+
},
|
|
2833
|
+
async setItem(key, value) {
|
|
2834
|
+
try {
|
|
2835
|
+
db.query(
|
|
2836
|
+
"INSERT INTO sdk_storage (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
2837
|
+
).run(key, JSON.stringify(value));
|
|
2838
|
+
} catch (error) {
|
|
2839
|
+
console.error(`SQLite setItem failed for key "${key}":`, error);
|
|
2840
|
+
}
|
|
2841
|
+
},
|
|
2842
|
+
async removeItem(key) {
|
|
2843
|
+
try {
|
|
2844
|
+
db.query("DELETE FROM sdk_storage WHERE key = ?").run(key);
|
|
2845
|
+
} catch (error) {
|
|
2846
|
+
console.error(`SQLite removeItem failed for key "${key}":`, error);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2942
2851
|
|
|
2943
2852
|
// storage/drivers/indexedDB.ts
|
|
2944
2853
|
var isBrowser = typeof indexedDB !== "undefined";
|
|
@@ -3044,9 +2953,9 @@ var SDK_STORAGE_KEYS = {
|
|
|
3044
2953
|
INFO_FROM_ALL_PROVIDERS: "info_from_all_providers",
|
|
3045
2954
|
LAST_MODELS_UPDATE: "lastModelsUpdate",
|
|
3046
2955
|
LAST_BASE_URLS_UPDATE: "lastBaseUrlsUpdate",
|
|
3047
|
-
LOCAL_CASHU_TOKENS: "local_cashu_tokens",
|
|
3048
2956
|
API_KEYS: "api_keys",
|
|
3049
2957
|
CHILD_KEYS: "child_keys",
|
|
2958
|
+
XCASHU_TOKENS: "xcashu_tokens",
|
|
3050
2959
|
ROUTSTR21_MODELS: "routstr21Models",
|
|
3051
2960
|
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
3052
2961
|
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
@@ -3237,21 +3146,23 @@ var normalizeBaseUrl2 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUr
|
|
|
3237
3146
|
var isBun2 = () => {
|
|
3238
3147
|
return typeof process.versions.bun !== "undefined";
|
|
3239
3148
|
};
|
|
3240
|
-
var
|
|
3149
|
+
var cachedDbModule2 = null;
|
|
3150
|
+
var loadDatabase2 = async (dbPath) => {
|
|
3241
3151
|
if (isBun2()) {
|
|
3242
3152
|
throw new Error(
|
|
3243
3153
|
"SQLite driver not supported in Bun. Use createMemoryDriver() instead."
|
|
3244
3154
|
);
|
|
3245
3155
|
}
|
|
3246
|
-
let Database = null;
|
|
3247
3156
|
try {
|
|
3248
|
-
|
|
3157
|
+
if (!cachedDbModule2) {
|
|
3158
|
+
cachedDbModule2 = (await import('better-sqlite3')).default;
|
|
3159
|
+
}
|
|
3160
|
+
return new cachedDbModule2(dbPath);
|
|
3249
3161
|
} catch (error) {
|
|
3250
3162
|
throw new Error(
|
|
3251
3163
|
`better-sqlite3 is required for sqlite usage tracking. Install it to use sqlite storage. (${error})`
|
|
3252
3164
|
);
|
|
3253
3165
|
}
|
|
3254
|
-
return new Database(dbPath);
|
|
3255
3166
|
};
|
|
3256
3167
|
var buildWhereClause = (options = {}) => {
|
|
3257
3168
|
const clauses = [];
|
|
@@ -3288,38 +3199,49 @@ var buildWhereClause = (options = {}) => {
|
|
|
3288
3199
|
var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
3289
3200
|
const dbPath = options.dbPath || "routstr.sqlite";
|
|
3290
3201
|
const tableName = options.tableName || "usage_tracking";
|
|
3291
|
-
const db = createDatabase2(dbPath);
|
|
3292
3202
|
const legacyStorageDriver = options.legacyStorageDriver;
|
|
3293
|
-
db
|
|
3294
|
-
|
|
3295
|
-
id TEXT PRIMARY KEY,
|
|
3296
|
-
timestamp INTEGER NOT NULL,
|
|
3297
|
-
model_id TEXT NOT NULL,
|
|
3298
|
-
base_url TEXT NOT NULL,
|
|
3299
|
-
request_id TEXT NOT NULL,
|
|
3300
|
-
cost REAL NOT NULL,
|
|
3301
|
-
sats_cost REAL NOT NULL,
|
|
3302
|
-
prompt_tokens INTEGER NOT NULL,
|
|
3303
|
-
completion_tokens INTEGER NOT NULL,
|
|
3304
|
-
total_tokens INTEGER NOT NULL,
|
|
3305
|
-
client TEXT,
|
|
3306
|
-
session_id TEXT,
|
|
3307
|
-
tags TEXT
|
|
3308
|
-
);
|
|
3309
|
-
CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp);
|
|
3310
|
-
CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id);
|
|
3311
|
-
CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url);
|
|
3312
|
-
CREATE INDEX IF NOT EXISTS idx_${tableName}_session_id ON ${tableName}(session_id);
|
|
3313
|
-
CREATE INDEX IF NOT EXISTS idx_${tableName}_client ON ${tableName}(client);
|
|
3314
|
-
`);
|
|
3315
|
-
const insertStmt = db.prepare(`
|
|
3316
|
-
INSERT OR REPLACE INTO ${tableName} (
|
|
3317
|
-
id, timestamp, model_id, base_url, request_id,
|
|
3318
|
-
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
3319
|
-
client, session_id, tags
|
|
3320
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3321
|
-
`);
|
|
3203
|
+
let db;
|
|
3204
|
+
let insertStmt;
|
|
3322
3205
|
let migrationComplete = false;
|
|
3206
|
+
const initDb = async () => {
|
|
3207
|
+
if (!db) {
|
|
3208
|
+
db = await loadDatabase2(dbPath);
|
|
3209
|
+
db.exec(`
|
|
3210
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
3211
|
+
id TEXT PRIMARY KEY,
|
|
3212
|
+
timestamp INTEGER NOT NULL,
|
|
3213
|
+
model_id TEXT NOT NULL,
|
|
3214
|
+
base_url TEXT NOT NULL,
|
|
3215
|
+
request_id TEXT NOT NULL,
|
|
3216
|
+
cost REAL NOT NULL,
|
|
3217
|
+
sats_cost REAL NOT NULL,
|
|
3218
|
+
prompt_tokens INTEGER NOT NULL,
|
|
3219
|
+
completion_tokens INTEGER NOT NULL,
|
|
3220
|
+
total_tokens INTEGER NOT NULL,
|
|
3221
|
+
client TEXT,
|
|
3222
|
+
session_id TEXT,
|
|
3223
|
+
tags TEXT
|
|
3224
|
+
);
|
|
3225
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp);
|
|
3226
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id);
|
|
3227
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url);
|
|
3228
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_session_id ON ${tableName}(session_id);
|
|
3229
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_client ON ${tableName}(client);
|
|
3230
|
+
`);
|
|
3231
|
+
insertStmt = db.prepare(`
|
|
3232
|
+
INSERT OR REPLACE INTO ${tableName} (
|
|
3233
|
+
id, timestamp, model_id, base_url, request_id,
|
|
3234
|
+
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
3235
|
+
client, session_id, tags
|
|
3236
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3237
|
+
`);
|
|
3238
|
+
}
|
|
3239
|
+
};
|
|
3240
|
+
const ensureInit = async () => {
|
|
3241
|
+
if (!db) {
|
|
3242
|
+
await initDb();
|
|
3243
|
+
}
|
|
3244
|
+
};
|
|
3323
3245
|
const appendOne = (entry) => {
|
|
3324
3246
|
insertStmt.run(
|
|
3325
3247
|
entry.id,
|
|
@@ -3377,19 +3299,23 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
3377
3299
|
});
|
|
3378
3300
|
return {
|
|
3379
3301
|
async migrate() {
|
|
3302
|
+
await ensureInit();
|
|
3380
3303
|
await ensureMigrated();
|
|
3381
3304
|
},
|
|
3382
3305
|
async append(entry) {
|
|
3306
|
+
await ensureInit();
|
|
3383
3307
|
await ensureMigrated();
|
|
3384
3308
|
appendOne(entry);
|
|
3385
3309
|
},
|
|
3386
3310
|
async appendMany(entries) {
|
|
3311
|
+
await ensureInit();
|
|
3387
3312
|
await ensureMigrated();
|
|
3388
3313
|
for (const entry of entries) {
|
|
3389
3314
|
appendOne(entry);
|
|
3390
3315
|
}
|
|
3391
3316
|
},
|
|
3392
3317
|
async list(options2 = {}) {
|
|
3318
|
+
await ensureInit();
|
|
3393
3319
|
await ensureMigrated();
|
|
3394
3320
|
const { sql, params } = buildWhereClause(options2);
|
|
3395
3321
|
const limitSql = typeof options2.limit === "number" ? " LIMIT ?" : "";
|
|
@@ -3402,6 +3328,7 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
3402
3328
|
return rows.map(mapRow);
|
|
3403
3329
|
},
|
|
3404
3330
|
async count(options2 = {}) {
|
|
3331
|
+
await ensureInit();
|
|
3405
3332
|
await ensureMigrated();
|
|
3406
3333
|
const { sql, params } = buildWhereClause(options2);
|
|
3407
3334
|
const stmt = db.prepare(`SELECT COUNT(*) as count FROM ${tableName} ${sql}`);
|
|
@@ -3409,20 +3336,197 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
3409
3336
|
return Number(row?.count ?? 0);
|
|
3410
3337
|
},
|
|
3411
3338
|
async deleteOlderThan(timestamp) {
|
|
3339
|
+
await ensureInit();
|
|
3412
3340
|
await ensureMigrated();
|
|
3413
3341
|
const stmt = db.prepare(`DELETE FROM ${tableName} WHERE timestamp < ?`);
|
|
3414
3342
|
const result = stmt.run(timestamp);
|
|
3415
3343
|
return result.changes;
|
|
3416
3344
|
},
|
|
3417
3345
|
async clear() {
|
|
3346
|
+
await ensureInit();
|
|
3418
3347
|
await ensureMigrated();
|
|
3419
3348
|
db.prepare(`DELETE FROM ${tableName}`).run();
|
|
3420
3349
|
}
|
|
3421
3350
|
};
|
|
3422
3351
|
};
|
|
3423
3352
|
|
|
3424
|
-
// storage/usageTracking/
|
|
3353
|
+
// storage/usageTracking/bunSqlite.ts
|
|
3354
|
+
var MIGRATION_MARKER_KEY3 = "usage_tracking_migration_v1";
|
|
3425
3355
|
var normalizeBaseUrl3 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
3356
|
+
var buildWhereClause2 = (options = {}) => {
|
|
3357
|
+
const clauses = [];
|
|
3358
|
+
const params = [];
|
|
3359
|
+
if (typeof options.before === "number") {
|
|
3360
|
+
clauses.push("timestamp < ?");
|
|
3361
|
+
params.push(options.before);
|
|
3362
|
+
}
|
|
3363
|
+
if (typeof options.after === "number") {
|
|
3364
|
+
clauses.push("timestamp > ?");
|
|
3365
|
+
params.push(options.after);
|
|
3366
|
+
}
|
|
3367
|
+
if (options.modelId) {
|
|
3368
|
+
clauses.push("model_id = ?");
|
|
3369
|
+
params.push(options.modelId);
|
|
3370
|
+
}
|
|
3371
|
+
if (options.baseUrl) {
|
|
3372
|
+
clauses.push("base_url = ?");
|
|
3373
|
+
params.push(normalizeBaseUrl3(options.baseUrl));
|
|
3374
|
+
}
|
|
3375
|
+
if (options.sessionId) {
|
|
3376
|
+
clauses.push("session_id = ?");
|
|
3377
|
+
params.push(options.sessionId);
|
|
3378
|
+
}
|
|
3379
|
+
if (options.client) {
|
|
3380
|
+
clauses.push("client = ?");
|
|
3381
|
+
params.push(options.client);
|
|
3382
|
+
}
|
|
3383
|
+
return {
|
|
3384
|
+
sql: clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "",
|
|
3385
|
+
params
|
|
3386
|
+
};
|
|
3387
|
+
};
|
|
3388
|
+
var createBunSqliteUsageTrackingDriver = (options = {}) => {
|
|
3389
|
+
const dbPath = options.dbPath || "routstr.sqlite";
|
|
3390
|
+
const tableName = options.tableName || "usage_tracking";
|
|
3391
|
+
const legacyStorageDriver = options.legacyStorageDriver;
|
|
3392
|
+
const SQLiteDatabase = options.sqlite?.Database;
|
|
3393
|
+
let migrationPromise = null;
|
|
3394
|
+
if (!SQLiteDatabase) {
|
|
3395
|
+
throw new Error(
|
|
3396
|
+
"Bun SQLite Database constructor is required. Pass { sqlite: { Database } } when creating the driver."
|
|
3397
|
+
);
|
|
3398
|
+
}
|
|
3399
|
+
const db = new SQLiteDatabase(dbPath);
|
|
3400
|
+
db.run(`
|
|
3401
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
3402
|
+
id TEXT PRIMARY KEY,
|
|
3403
|
+
timestamp INTEGER NOT NULL,
|
|
3404
|
+
model_id TEXT NOT NULL,
|
|
3405
|
+
base_url TEXT NOT NULL,
|
|
3406
|
+
request_id TEXT NOT NULL,
|
|
3407
|
+
cost REAL NOT NULL,
|
|
3408
|
+
sats_cost REAL NOT NULL,
|
|
3409
|
+
prompt_tokens INTEGER NOT NULL,
|
|
3410
|
+
completion_tokens INTEGER NOT NULL,
|
|
3411
|
+
total_tokens INTEGER NOT NULL,
|
|
3412
|
+
client TEXT,
|
|
3413
|
+
session_id TEXT,
|
|
3414
|
+
tags TEXT
|
|
3415
|
+
)
|
|
3416
|
+
`);
|
|
3417
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp)`);
|
|
3418
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id)`);
|
|
3419
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url)`);
|
|
3420
|
+
const appendOne = (entry) => {
|
|
3421
|
+
db.query(`
|
|
3422
|
+
INSERT OR REPLACE INTO ${tableName} (
|
|
3423
|
+
id, timestamp, model_id, base_url, request_id,
|
|
3424
|
+
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
3425
|
+
client, session_id, tags
|
|
3426
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3427
|
+
`).run(
|
|
3428
|
+
entry.id,
|
|
3429
|
+
entry.timestamp,
|
|
3430
|
+
entry.modelId,
|
|
3431
|
+
normalizeBaseUrl3(entry.baseUrl),
|
|
3432
|
+
entry.requestId,
|
|
3433
|
+
entry.cost,
|
|
3434
|
+
entry.satsCost,
|
|
3435
|
+
entry.promptTokens,
|
|
3436
|
+
entry.completionTokens,
|
|
3437
|
+
entry.totalTokens,
|
|
3438
|
+
entry.client ?? null,
|
|
3439
|
+
entry.sessionId ?? null,
|
|
3440
|
+
JSON.stringify(entry.tags ?? [])
|
|
3441
|
+
);
|
|
3442
|
+
};
|
|
3443
|
+
const mapRow = (row) => ({
|
|
3444
|
+
id: row.id,
|
|
3445
|
+
timestamp: row.timestamp,
|
|
3446
|
+
modelId: row.model_id,
|
|
3447
|
+
baseUrl: row.base_url,
|
|
3448
|
+
requestId: row.request_id,
|
|
3449
|
+
cost: row.cost,
|
|
3450
|
+
satsCost: row.sats_cost,
|
|
3451
|
+
promptTokens: row.prompt_tokens,
|
|
3452
|
+
completionTokens: row.completion_tokens,
|
|
3453
|
+
totalTokens: row.total_tokens,
|
|
3454
|
+
client: row.client ?? void 0,
|
|
3455
|
+
sessionId: row.session_id ?? void 0,
|
|
3456
|
+
tags: typeof row.tags === "string" ? JSON.parse(row.tags) : void 0
|
|
3457
|
+
});
|
|
3458
|
+
const ensureMigrated = async () => {
|
|
3459
|
+
if (!legacyStorageDriver) return;
|
|
3460
|
+
if (!migrationPromise) {
|
|
3461
|
+
migrationPromise = (async () => {
|
|
3462
|
+
const migrated = await legacyStorageDriver.getItem(
|
|
3463
|
+
MIGRATION_MARKER_KEY3,
|
|
3464
|
+
false
|
|
3465
|
+
);
|
|
3466
|
+
if (migrated) return;
|
|
3467
|
+
const legacyEntries = await legacyStorageDriver.getItem(
|
|
3468
|
+
SDK_STORAGE_KEYS.USAGE_TRACKING,
|
|
3469
|
+
[]
|
|
3470
|
+
);
|
|
3471
|
+
if (legacyEntries.length > 0) {
|
|
3472
|
+
for (const entry of legacyEntries) {
|
|
3473
|
+
appendOne(entry);
|
|
3474
|
+
}
|
|
3475
|
+
await legacyStorageDriver.removeItem(SDK_STORAGE_KEYS.USAGE_TRACKING);
|
|
3476
|
+
}
|
|
3477
|
+
await legacyStorageDriver.setItem(MIGRATION_MARKER_KEY3, true);
|
|
3478
|
+
})();
|
|
3479
|
+
}
|
|
3480
|
+
await migrationPromise;
|
|
3481
|
+
};
|
|
3482
|
+
return {
|
|
3483
|
+
async migrate() {
|
|
3484
|
+
await ensureMigrated();
|
|
3485
|
+
},
|
|
3486
|
+
async append(entry) {
|
|
3487
|
+
await ensureMigrated();
|
|
3488
|
+
appendOne(entry);
|
|
3489
|
+
},
|
|
3490
|
+
async appendMany(entries) {
|
|
3491
|
+
await ensureMigrated();
|
|
3492
|
+
for (const entry of entries) {
|
|
3493
|
+
appendOne(entry);
|
|
3494
|
+
}
|
|
3495
|
+
},
|
|
3496
|
+
async list(options2 = {}) {
|
|
3497
|
+
await ensureMigrated();
|
|
3498
|
+
const { sql, params } = buildWhereClause2(options2);
|
|
3499
|
+
const limitSql = typeof options2.limit === "number" ? " LIMIT ?" : "";
|
|
3500
|
+
const query = `SELECT * FROM ${tableName} ${sql} ORDER BY timestamp DESC${limitSql}`;
|
|
3501
|
+
let rows;
|
|
3502
|
+
if (typeof options2.limit === "number") {
|
|
3503
|
+
rows = db.query(query).all(...params, options2.limit);
|
|
3504
|
+
} else {
|
|
3505
|
+
rows = db.query(query).all(...params);
|
|
3506
|
+
}
|
|
3507
|
+
return rows.map(mapRow);
|
|
3508
|
+
},
|
|
3509
|
+
async count(options2 = {}) {
|
|
3510
|
+
const { sql, params } = buildWhereClause2(options2);
|
|
3511
|
+
const query = `SELECT COUNT(*) as count FROM ${tableName} ${sql}`;
|
|
3512
|
+
const row = db.query(query).get(...params);
|
|
3513
|
+
return Number(row?.count ?? 0);
|
|
3514
|
+
},
|
|
3515
|
+
async deleteOlderThan(timestamp) {
|
|
3516
|
+
await ensureMigrated();
|
|
3517
|
+
const before = timestamp;
|
|
3518
|
+
const result = db.query(`DELETE FROM ${tableName} WHERE timestamp < ?`).run(before);
|
|
3519
|
+
return result.changes ?? 0;
|
|
3520
|
+
},
|
|
3521
|
+
async clear() {
|
|
3522
|
+
await ensureMigrated();
|
|
3523
|
+
db.query(`DELETE FROM ${tableName}`).run();
|
|
3524
|
+
}
|
|
3525
|
+
};
|
|
3526
|
+
};
|
|
3527
|
+
|
|
3528
|
+
// storage/usageTracking/memory.ts
|
|
3529
|
+
var normalizeBaseUrl4 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
3426
3530
|
var matchesFilters2 = (entry, options = {}) => {
|
|
3427
3531
|
if (typeof options.before === "number" && entry.timestamp >= options.before) {
|
|
3428
3532
|
return false;
|
|
@@ -3433,7 +3537,7 @@ var matchesFilters2 = (entry, options = {}) => {
|
|
|
3433
3537
|
if (options.modelId && entry.modelId !== options.modelId) {
|
|
3434
3538
|
return false;
|
|
3435
3539
|
}
|
|
3436
|
-
if (options.baseUrl &&
|
|
3540
|
+
if (options.baseUrl && normalizeBaseUrl4(entry.baseUrl) !== normalizeBaseUrl4(options.baseUrl)) {
|
|
3437
3541
|
return false;
|
|
3438
3542
|
}
|
|
3439
3543
|
if (options.sessionId && entry.sessionId !== options.sessionId) {
|
|
@@ -3447,18 +3551,18 @@ var matchesFilters2 = (entry, options = {}) => {
|
|
|
3447
3551
|
var createMemoryUsageTrackingDriver = (seed = []) => {
|
|
3448
3552
|
const store = /* @__PURE__ */ new Map();
|
|
3449
3553
|
for (const entry of seed) {
|
|
3450
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
3554
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
3451
3555
|
}
|
|
3452
3556
|
return {
|
|
3453
3557
|
async migrate() {
|
|
3454
3558
|
return;
|
|
3455
3559
|
},
|
|
3456
3560
|
async append(entry) {
|
|
3457
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
3561
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
3458
3562
|
},
|
|
3459
3563
|
async appendMany(entries) {
|
|
3460
3564
|
for (const entry of entries) {
|
|
3461
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
3565
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
3462
3566
|
}
|
|
3463
3567
|
},
|
|
3464
3568
|
async list(options = {}) {
|
|
@@ -3486,20 +3590,7 @@ var createMemoryUsageTrackingDriver = (seed = []) => {
|
|
|
3486
3590
|
}
|
|
3487
3591
|
};
|
|
3488
3592
|
};
|
|
3489
|
-
var
|
|
3490
|
-
var getCashuTokenBalance = (token) => {
|
|
3491
|
-
try {
|
|
3492
|
-
const decoded = cashuTs.getDecodedToken(token);
|
|
3493
|
-
const unitDivisor = decoded.unit === "msat" ? 1e3 : 1;
|
|
3494
|
-
let sum = 0;
|
|
3495
|
-
for (const proof of decoded.proofs) {
|
|
3496
|
-
sum += proof.amount / unitDivisor;
|
|
3497
|
-
}
|
|
3498
|
-
return sum;
|
|
3499
|
-
} catch {
|
|
3500
|
-
return 0;
|
|
3501
|
-
}
|
|
3502
|
-
};
|
|
3593
|
+
var normalizeBaseUrl5 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
3503
3594
|
var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
3504
3595
|
modelsFromAllProviders: {},
|
|
3505
3596
|
lastUsedModel: null,
|
|
@@ -3509,9 +3600,9 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3509
3600
|
mintsFromAllProviders: {},
|
|
3510
3601
|
infoFromAllProviders: {},
|
|
3511
3602
|
lastModelsUpdate: {},
|
|
3512
|
-
cachedTokens: [],
|
|
3513
3603
|
apiKeys: [],
|
|
3514
3604
|
childKeys: [],
|
|
3605
|
+
xcashuTokens: {},
|
|
3515
3606
|
routstr21Models: [],
|
|
3516
3607
|
lastRoutstr21ModelsUpdate: null,
|
|
3517
3608
|
cachedReceiveTokens: [],
|
|
@@ -3519,7 +3610,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3519
3610
|
setModelsFromAllProviders: (value) => {
|
|
3520
3611
|
const normalized = {};
|
|
3521
3612
|
for (const [baseUrl, models] of Object.entries(value)) {
|
|
3522
|
-
normalized[
|
|
3613
|
+
normalized[normalizeBaseUrl5(baseUrl)] = models;
|
|
3523
3614
|
}
|
|
3524
3615
|
void driver.setItem(
|
|
3525
3616
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -3532,7 +3623,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3532
3623
|
set({ lastUsedModel: value });
|
|
3533
3624
|
},
|
|
3534
3625
|
setBaseUrlsList: (value) => {
|
|
3535
|
-
const normalized = value.map((url) =>
|
|
3626
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
3536
3627
|
void driver.setItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, normalized);
|
|
3537
3628
|
set({ baseUrlsList: normalized });
|
|
3538
3629
|
},
|
|
@@ -3541,14 +3632,14 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3541
3632
|
set({ lastBaseUrlsUpdate: value });
|
|
3542
3633
|
},
|
|
3543
3634
|
setDisabledProviders: (value) => {
|
|
3544
|
-
const normalized = value.map((url) =>
|
|
3635
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
3545
3636
|
void driver.setItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, normalized);
|
|
3546
3637
|
set({ disabledProviders: normalized });
|
|
3547
3638
|
},
|
|
3548
3639
|
setMintsFromAllProviders: (value) => {
|
|
3549
3640
|
const normalized = {};
|
|
3550
3641
|
for (const [baseUrl, mints] of Object.entries(value)) {
|
|
3551
|
-
normalized[
|
|
3642
|
+
normalized[normalizeBaseUrl5(baseUrl)] = mints.map(
|
|
3552
3643
|
(mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint
|
|
3553
3644
|
);
|
|
3554
3645
|
}
|
|
@@ -3561,7 +3652,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3561
3652
|
setInfoFromAllProviders: (value) => {
|
|
3562
3653
|
const normalized = {};
|
|
3563
3654
|
for (const [baseUrl, info] of Object.entries(value)) {
|
|
3564
|
-
normalized[
|
|
3655
|
+
normalized[normalizeBaseUrl5(baseUrl)] = info;
|
|
3565
3656
|
}
|
|
3566
3657
|
void driver.setItem(SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS, normalized);
|
|
3567
3658
|
set({ infoFromAllProviders: normalized });
|
|
@@ -3569,30 +3660,17 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3569
3660
|
setLastModelsUpdate: (value) => {
|
|
3570
3661
|
const normalized = {};
|
|
3571
3662
|
for (const [baseUrl, timestamp] of Object.entries(value)) {
|
|
3572
|
-
normalized[
|
|
3663
|
+
normalized[normalizeBaseUrl5(baseUrl)] = timestamp;
|
|
3573
3664
|
}
|
|
3574
3665
|
void driver.setItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE, normalized);
|
|
3575
3666
|
set({ lastModelsUpdate: normalized });
|
|
3576
3667
|
},
|
|
3577
|
-
setCachedTokens: (value) => {
|
|
3578
|
-
set((state) => {
|
|
3579
|
-
const updates = typeof value === "function" ? value(state.cachedTokens) : value;
|
|
3580
|
-
const normalized = updates.map((entry) => ({
|
|
3581
|
-
...entry,
|
|
3582
|
-
baseUrl: normalizeBaseUrl4(entry.baseUrl),
|
|
3583
|
-
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
3584
|
-
lastUsed: entry.lastUsed ?? null
|
|
3585
|
-
}));
|
|
3586
|
-
void driver.setItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, normalized);
|
|
3587
|
-
return { cachedTokens: normalized };
|
|
3588
|
-
});
|
|
3589
|
-
},
|
|
3590
3668
|
setApiKeys: (value) => {
|
|
3591
3669
|
set((state) => {
|
|
3592
3670
|
const updates = typeof value === "function" ? value(state.apiKeys) : value;
|
|
3593
3671
|
const normalized = updates.map((entry) => ({
|
|
3594
3672
|
...entry,
|
|
3595
|
-
baseUrl:
|
|
3673
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3596
3674
|
balance: entry.balance ?? 0,
|
|
3597
3675
|
lastUsed: entry.lastUsed ?? null
|
|
3598
3676
|
}));
|
|
@@ -3604,7 +3682,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3604
3682
|
set((state) => {
|
|
3605
3683
|
const updates = typeof value === "function" ? value(state.childKeys) : value;
|
|
3606
3684
|
const normalized = updates.map((entry) => ({
|
|
3607
|
-
parentBaseUrl:
|
|
3685
|
+
parentBaseUrl: normalizeBaseUrl5(entry.parentBaseUrl),
|
|
3608
3686
|
childKey: entry.childKey,
|
|
3609
3687
|
balance: entry.balance ?? 0,
|
|
3610
3688
|
balanceLimit: entry.balanceLimit,
|
|
@@ -3615,6 +3693,30 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3615
3693
|
return { childKeys: normalized };
|
|
3616
3694
|
});
|
|
3617
3695
|
},
|
|
3696
|
+
setXcashuTokens: (value) => {
|
|
3697
|
+
const normalized = {};
|
|
3698
|
+
for (const [baseUrl, tokens] of Object.entries(value)) {
|
|
3699
|
+
normalized[normalizeBaseUrl5(baseUrl)] = tokens.map((entry) => ({
|
|
3700
|
+
...entry,
|
|
3701
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3702
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
3703
|
+
tryCount: entry.tryCount ?? 0
|
|
3704
|
+
}));
|
|
3705
|
+
}
|
|
3706
|
+
void driver.setItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, normalized);
|
|
3707
|
+
set({ xcashuTokens: normalized });
|
|
3708
|
+
},
|
|
3709
|
+
updateXcashuTokenTryCount: (token, tryCount) => {
|
|
3710
|
+
const currentTokens = get().xcashuTokens;
|
|
3711
|
+
const updatedTokens = {};
|
|
3712
|
+
for (const [baseUrl, tokens] of Object.entries(currentTokens)) {
|
|
3713
|
+
updatedTokens[baseUrl] = tokens.map(
|
|
3714
|
+
(entry) => entry.token === token ? { ...entry, tryCount } : entry
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
void driver.setItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, updatedTokens);
|
|
3718
|
+
set({ xcashuTokens: updatedTokens });
|
|
3719
|
+
},
|
|
3618
3720
|
setRoutstr21Models: (value) => {
|
|
3619
3721
|
void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, value);
|
|
3620
3722
|
set({ routstr21Models: value });
|
|
@@ -3656,9 +3758,9 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3656
3758
|
rawMints,
|
|
3657
3759
|
rawInfo,
|
|
3658
3760
|
rawLastModelsUpdate,
|
|
3659
|
-
rawCachedTokens,
|
|
3660
3761
|
rawApiKeys,
|
|
3661
3762
|
rawChildKeys,
|
|
3763
|
+
rawXcashuTokens,
|
|
3662
3764
|
rawRoutstr21Models,
|
|
3663
3765
|
rawLastRoutstr21ModelsUpdate,
|
|
3664
3766
|
rawCachedReceiveTokens,
|
|
@@ -3684,9 +3786,9 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3684
3786
|
SDK_STORAGE_KEYS.LAST_MODELS_UPDATE,
|
|
3685
3787
|
{}
|
|
3686
3788
|
),
|
|
3687
|
-
driver.getItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, []),
|
|
3688
3789
|
driver.getItem(SDK_STORAGE_KEYS.API_KEYS, []),
|
|
3689
3790
|
driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, []),
|
|
3791
|
+
driver.getItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, {}),
|
|
3690
3792
|
driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
|
|
3691
3793
|
driver.getItem(
|
|
3692
3794
|
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
@@ -3697,52 +3799,57 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3697
3799
|
]);
|
|
3698
3800
|
const modelsFromAllProviders = Object.fromEntries(
|
|
3699
3801
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
3700
|
-
|
|
3802
|
+
normalizeBaseUrl5(baseUrl),
|
|
3701
3803
|
models
|
|
3702
3804
|
])
|
|
3703
3805
|
);
|
|
3704
|
-
const baseUrlsList = rawBaseUrls.map((url) =>
|
|
3806
|
+
const baseUrlsList = rawBaseUrls.map((url) => normalizeBaseUrl5(url));
|
|
3705
3807
|
const disabledProviders = rawDisabledProviders.map(
|
|
3706
|
-
(url) =>
|
|
3808
|
+
(url) => normalizeBaseUrl5(url)
|
|
3707
3809
|
);
|
|
3708
3810
|
const mintsFromAllProviders = Object.fromEntries(
|
|
3709
3811
|
Object.entries(rawMints).map(([baseUrl, mints]) => [
|
|
3710
|
-
|
|
3812
|
+
normalizeBaseUrl5(baseUrl),
|
|
3711
3813
|
mints.map((mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint)
|
|
3712
3814
|
])
|
|
3713
3815
|
);
|
|
3714
3816
|
const infoFromAllProviders = Object.fromEntries(
|
|
3715
3817
|
Object.entries(rawInfo).map(([baseUrl, info]) => [
|
|
3716
|
-
|
|
3818
|
+
normalizeBaseUrl5(baseUrl),
|
|
3717
3819
|
info
|
|
3718
3820
|
])
|
|
3719
3821
|
);
|
|
3720
3822
|
const lastModelsUpdate = Object.fromEntries(
|
|
3721
3823
|
Object.entries(rawLastModelsUpdate).map(([baseUrl, timestamp]) => [
|
|
3722
|
-
|
|
3824
|
+
normalizeBaseUrl5(baseUrl),
|
|
3723
3825
|
timestamp
|
|
3724
3826
|
])
|
|
3725
3827
|
);
|
|
3726
|
-
const cachedTokens = rawCachedTokens.map((entry) => ({
|
|
3727
|
-
...entry,
|
|
3728
|
-
baseUrl: normalizeBaseUrl4(entry.baseUrl),
|
|
3729
|
-
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
3730
|
-
lastUsed: entry.lastUsed ?? null
|
|
3731
|
-
}));
|
|
3732
3828
|
const apiKeys = rawApiKeys.map((entry) => ({
|
|
3733
3829
|
...entry,
|
|
3734
|
-
baseUrl:
|
|
3830
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3735
3831
|
balance: entry.balance ?? 0,
|
|
3736
3832
|
lastUsed: entry.lastUsed ?? null
|
|
3737
3833
|
}));
|
|
3738
3834
|
const childKeys = rawChildKeys.map((entry) => ({
|
|
3739
|
-
parentBaseUrl:
|
|
3835
|
+
parentBaseUrl: normalizeBaseUrl5(entry.parentBaseUrl),
|
|
3740
3836
|
childKey: entry.childKey,
|
|
3741
3837
|
balance: entry.balance ?? 0,
|
|
3742
3838
|
balanceLimit: entry.balanceLimit,
|
|
3743
3839
|
validityDate: entry.validityDate,
|
|
3744
3840
|
createdAt: entry.createdAt ?? Date.now()
|
|
3745
3841
|
}));
|
|
3842
|
+
const xcashuTokens = Object.fromEntries(
|
|
3843
|
+
Object.entries(rawXcashuTokens).map(([baseUrl, tokens]) => [
|
|
3844
|
+
normalizeBaseUrl5(baseUrl),
|
|
3845
|
+
tokens.map((entry) => ({
|
|
3846
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3847
|
+
token: entry.token,
|
|
3848
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
3849
|
+
tryCount: entry.tryCount ?? 0
|
|
3850
|
+
}))
|
|
3851
|
+
])
|
|
3852
|
+
);
|
|
3746
3853
|
const routstr21Models = rawRoutstr21Models;
|
|
3747
3854
|
const lastRoutstr21ModelsUpdate = rawLastRoutstr21ModelsUpdate;
|
|
3748
3855
|
const cachedReceiveTokens = rawCachedReceiveTokens?.map((entry) => ({
|
|
@@ -3765,9 +3872,9 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3765
3872
|
mintsFromAllProviders,
|
|
3766
3873
|
infoFromAllProviders,
|
|
3767
3874
|
lastModelsUpdate,
|
|
3768
|
-
cachedTokens,
|
|
3769
3875
|
apiKeys,
|
|
3770
3876
|
childKeys,
|
|
3877
|
+
xcashuTokens,
|
|
3771
3878
|
routstr21Models,
|
|
3772
3879
|
lastRoutstr21ModelsUpdate,
|
|
3773
3880
|
cachedReceiveTokens,
|
|
@@ -3791,12 +3898,12 @@ var createDiscoveryAdapterFromStore = (store) => ({
|
|
|
3791
3898
|
getCachedProviderInfo: () => store.getState().infoFromAllProviders,
|
|
3792
3899
|
setCachedProviderInfo: (info) => store.getState().setInfoFromAllProviders(info),
|
|
3793
3900
|
getProviderLastUpdate: (baseUrl) => {
|
|
3794
|
-
const normalized =
|
|
3901
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3795
3902
|
const timestamps = store.getState().lastModelsUpdate;
|
|
3796
3903
|
return timestamps[normalized] || null;
|
|
3797
3904
|
},
|
|
3798
3905
|
setProviderLastUpdate: (baseUrl, timestamp) => {
|
|
3799
|
-
const normalized =
|
|
3906
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3800
3907
|
const timestamps = { ...store.getState().lastModelsUpdate };
|
|
3801
3908
|
timestamps[normalized] = timestamp;
|
|
3802
3909
|
store.getState().setLastModelsUpdate(timestamps);
|
|
@@ -3814,59 +3921,6 @@ var createDiscoveryAdapterFromStore = (store) => ({
|
|
|
3814
3921
|
setRoutstr21ModelsLastUpdate: (timestamp) => store.getState().setRoutstr21ModelsLastUpdate(timestamp)
|
|
3815
3922
|
});
|
|
3816
3923
|
var createStorageAdapterFromStore = (store) => ({
|
|
3817
|
-
getToken: (baseUrl) => {
|
|
3818
|
-
const normalized = normalizeBaseUrl4(baseUrl);
|
|
3819
|
-
const entry = store.getState().cachedTokens.find((token) => token.baseUrl === normalized);
|
|
3820
|
-
if (!entry) return null;
|
|
3821
|
-
const next = store.getState().cachedTokens.map(
|
|
3822
|
-
(token) => token.baseUrl === normalized ? { ...token, lastUsed: Date.now() } : token
|
|
3823
|
-
);
|
|
3824
|
-
store.getState().setCachedTokens(next);
|
|
3825
|
-
return entry.token;
|
|
3826
|
-
},
|
|
3827
|
-
setToken: (baseUrl, token) => {
|
|
3828
|
-
const normalized = normalizeBaseUrl4(baseUrl);
|
|
3829
|
-
const tokens = store.getState().cachedTokens;
|
|
3830
|
-
const balance = getCashuTokenBalance(token);
|
|
3831
|
-
const existingIndex = tokens.findIndex(
|
|
3832
|
-
(entry) => entry.baseUrl === normalized
|
|
3833
|
-
);
|
|
3834
|
-
if (existingIndex !== -1) {
|
|
3835
|
-
throw new Error(`Token already exists for baseUrl: ${normalized}`);
|
|
3836
|
-
}
|
|
3837
|
-
const next = [...tokens];
|
|
3838
|
-
next.push({
|
|
3839
|
-
baseUrl: normalized,
|
|
3840
|
-
token,
|
|
3841
|
-
balance,
|
|
3842
|
-
lastUsed: Date.now()
|
|
3843
|
-
});
|
|
3844
|
-
store.getState().setCachedTokens(next);
|
|
3845
|
-
},
|
|
3846
|
-
removeToken: (baseUrl) => {
|
|
3847
|
-
const normalized = normalizeBaseUrl4(baseUrl);
|
|
3848
|
-
const next = store.getState().cachedTokens.filter((entry) => entry.baseUrl !== normalized);
|
|
3849
|
-
store.getState().setCachedTokens(next);
|
|
3850
|
-
},
|
|
3851
|
-
updateTokenBalance: (baseUrl, balance) => {
|
|
3852
|
-
const normalized = normalizeBaseUrl4(baseUrl);
|
|
3853
|
-
const tokens = store.getState().cachedTokens;
|
|
3854
|
-
const next = tokens.map(
|
|
3855
|
-
(entry) => entry.baseUrl === normalized ? { ...entry, balance } : entry
|
|
3856
|
-
);
|
|
3857
|
-
store.getState().setCachedTokens(next);
|
|
3858
|
-
},
|
|
3859
|
-
getCachedTokenDistribution: () => {
|
|
3860
|
-
const cachedTokens = store.getState().cachedTokens;
|
|
3861
|
-
const distributionMap = {};
|
|
3862
|
-
for (const entry of cachedTokens) {
|
|
3863
|
-
const sum = entry.balance || 0;
|
|
3864
|
-
if (sum > 0) {
|
|
3865
|
-
distributionMap[entry.baseUrl] = (distributionMap[entry.baseUrl] || 0) + sum;
|
|
3866
|
-
}
|
|
3867
|
-
}
|
|
3868
|
-
return Object.entries(distributionMap).map(([baseUrl, amt]) => ({ baseUrl, amount: amt })).sort((a, b) => b.amount - a.amount);
|
|
3869
|
-
},
|
|
3870
3924
|
getApiKeyDistribution: () => {
|
|
3871
3925
|
const apiKeys = store.getState().apiKeys;
|
|
3872
3926
|
const distributionMap = {};
|
|
@@ -3879,28 +3933,24 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3879
3933
|
return Object.entries(distributionMap).map(([baseUrl, amt]) => ({ baseUrl, amount: amt })).sort((a, b) => b.amount - a.amount);
|
|
3880
3934
|
},
|
|
3881
3935
|
saveProviderInfo: (baseUrl, info) => {
|
|
3882
|
-
const normalized =
|
|
3936
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3883
3937
|
const next = { ...store.getState().infoFromAllProviders };
|
|
3884
3938
|
next[normalized] = info;
|
|
3885
3939
|
store.getState().setInfoFromAllProviders(next);
|
|
3886
3940
|
},
|
|
3887
3941
|
getProviderInfo: (baseUrl) => {
|
|
3888
|
-
const normalized =
|
|
3942
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3889
3943
|
return store.getState().infoFromAllProviders[normalized] || null;
|
|
3890
3944
|
},
|
|
3891
3945
|
// ========== API Keys (for apikeys mode) ==========
|
|
3892
3946
|
getApiKey: (baseUrl) => {
|
|
3893
|
-
const normalized =
|
|
3947
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3894
3948
|
const entry = store.getState().apiKeys.find((key) => key.baseUrl === normalized);
|
|
3895
3949
|
if (!entry) return null;
|
|
3896
|
-
const next = store.getState().apiKeys.map(
|
|
3897
|
-
(key) => key.baseUrl === normalized ? { ...key, lastUsed: Date.now() } : key
|
|
3898
|
-
);
|
|
3899
|
-
store.getState().setApiKeys(next);
|
|
3900
3950
|
return entry;
|
|
3901
3951
|
},
|
|
3902
3952
|
setApiKey: (baseUrl, key) => {
|
|
3903
|
-
const normalized =
|
|
3953
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3904
3954
|
const keys = store.getState().apiKeys;
|
|
3905
3955
|
const existingIndex = keys.findIndex(
|
|
3906
3956
|
(entry) => entry.baseUrl === normalized
|
|
@@ -3918,15 +3968,15 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3918
3968
|
store.getState().setApiKeys(next);
|
|
3919
3969
|
},
|
|
3920
3970
|
updateApiKeyBalance: (baseUrl, balance) => {
|
|
3921
|
-
const normalized =
|
|
3971
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3922
3972
|
const keys = store.getState().apiKeys;
|
|
3923
3973
|
const next = keys.map(
|
|
3924
|
-
(entry) => entry.baseUrl === normalized ? { ...entry, balance } : entry
|
|
3974
|
+
(entry) => entry.baseUrl === normalized ? { ...entry, balance, lastUsed: Date.now() } : entry
|
|
3925
3975
|
);
|
|
3926
3976
|
store.getState().setApiKeys(next);
|
|
3927
3977
|
},
|
|
3928
3978
|
removeApiKey: (baseUrl) => {
|
|
3929
|
-
const normalized =
|
|
3979
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3930
3980
|
const next = store.getState().apiKeys.filter((entry) => entry.baseUrl !== normalized);
|
|
3931
3981
|
store.getState().setApiKeys(next);
|
|
3932
3982
|
},
|
|
@@ -3940,7 +3990,7 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3940
3990
|
},
|
|
3941
3991
|
// ========== Child Keys ==========
|
|
3942
3992
|
getChildKey: (parentBaseUrl) => {
|
|
3943
|
-
const normalized =
|
|
3993
|
+
const normalized = normalizeBaseUrl5(parentBaseUrl);
|
|
3944
3994
|
const entry = store.getState().childKeys.find((key) => key.parentBaseUrl === normalized);
|
|
3945
3995
|
if (!entry) return null;
|
|
3946
3996
|
return {
|
|
@@ -3953,7 +4003,7 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3953
4003
|
};
|
|
3954
4004
|
},
|
|
3955
4005
|
setChildKey: (parentBaseUrl, childKey, balance, validityDate, balanceLimit) => {
|
|
3956
|
-
const normalized =
|
|
4006
|
+
const normalized = normalizeBaseUrl5(parentBaseUrl);
|
|
3957
4007
|
const keys = store.getState().childKeys;
|
|
3958
4008
|
const existingIndex = keys.findIndex(
|
|
3959
4009
|
(entry) => entry.parentBaseUrl === normalized
|
|
@@ -3984,7 +4034,7 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3984
4034
|
}
|
|
3985
4035
|
},
|
|
3986
4036
|
updateChildKeyBalance: (parentBaseUrl, balance) => {
|
|
3987
|
-
const normalized =
|
|
4037
|
+
const normalized = normalizeBaseUrl5(parentBaseUrl);
|
|
3988
4038
|
const keys = store.getState().childKeys;
|
|
3989
4039
|
const next = keys.map(
|
|
3990
4040
|
(entry) => entry.parentBaseUrl === normalized ? { ...entry, balance } : entry
|
|
@@ -3992,7 +4042,7 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3992
4042
|
store.getState().setChildKeys(next);
|
|
3993
4043
|
},
|
|
3994
4044
|
removeChildKey: (parentBaseUrl) => {
|
|
3995
|
-
const normalized =
|
|
4045
|
+
const normalized = normalizeBaseUrl5(parentBaseUrl);
|
|
3996
4046
|
const next = store.getState().childKeys.filter((entry) => entry.parentBaseUrl !== normalized);
|
|
3997
4047
|
store.getState().setChildKeys(next);
|
|
3998
4048
|
},
|
|
@@ -4011,20 +4061,60 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
4011
4061
|
},
|
|
4012
4062
|
setCachedReceiveTokens: (tokens) => {
|
|
4013
4063
|
store.getState().setCachedReceiveTokens(tokens);
|
|
4064
|
+
},
|
|
4065
|
+
// ========== XCashu Tokens (multiple tokens per baseUrl) ==========
|
|
4066
|
+
getXcashuTokens: () => {
|
|
4067
|
+
return store.getState().xcashuTokens;
|
|
4068
|
+
},
|
|
4069
|
+
getXcashuTokensForBaseUrl: (baseUrl) => {
|
|
4070
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
4071
|
+
return store.getState().xcashuTokens[normalized] || [];
|
|
4072
|
+
},
|
|
4073
|
+
addXcashuToken: (baseUrl, token) => {
|
|
4074
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
4075
|
+
const tokens = store.getState().xcashuTokens;
|
|
4076
|
+
const existing = tokens[normalized] || [];
|
|
4077
|
+
const next = { ...tokens };
|
|
4078
|
+
next[normalized] = [
|
|
4079
|
+
...existing,
|
|
4080
|
+
{ baseUrl: normalized, token, createdAt: Date.now(), tryCount: 0 }
|
|
4081
|
+
];
|
|
4082
|
+
store.getState().setXcashuTokens(next);
|
|
4083
|
+
},
|
|
4084
|
+
removeXcashuToken: (baseUrl, token) => {
|
|
4085
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
4086
|
+
const tokens = store.getState().xcashuTokens;
|
|
4087
|
+
const existing = tokens[normalized] || [];
|
|
4088
|
+
const next = { ...tokens };
|
|
4089
|
+
next[normalized] = existing.filter((entry) => entry.token !== token);
|
|
4090
|
+
if (next[normalized].length === 0) {
|
|
4091
|
+
delete next[normalized];
|
|
4092
|
+
}
|
|
4093
|
+
store.getState().setXcashuTokens(next);
|
|
4094
|
+
},
|
|
4095
|
+
clearXcashuTokensForBaseUrl: (baseUrl) => {
|
|
4096
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
4097
|
+
const tokens = store.getState().xcashuTokens;
|
|
4098
|
+
const next = { ...tokens };
|
|
4099
|
+
delete next[normalized];
|
|
4100
|
+
store.getState().setXcashuTokens(next);
|
|
4101
|
+
},
|
|
4102
|
+
updateXcashuTokenTryCount: (token, tryCount) => {
|
|
4103
|
+
store.getState().updateXcashuTokenTryCount(token, tryCount);
|
|
4014
4104
|
}
|
|
4015
4105
|
});
|
|
4016
4106
|
var createProviderRegistryFromStore = (store) => ({
|
|
4017
4107
|
getModelsForProvider: (baseUrl) => {
|
|
4018
|
-
const normalized =
|
|
4108
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
4019
4109
|
return store.getState().modelsFromAllProviders[normalized] || [];
|
|
4020
4110
|
},
|
|
4021
4111
|
getDisabledProviders: () => store.getState().disabledProviders,
|
|
4022
4112
|
getProviderMints: (baseUrl) => {
|
|
4023
|
-
const normalized =
|
|
4113
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
4024
4114
|
return store.getState().mintsFromAllProviders[normalized] || [];
|
|
4025
4115
|
},
|
|
4026
4116
|
getProviderInfo: async (baseUrl) => {
|
|
4027
|
-
const normalized =
|
|
4117
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
4028
4118
|
const cached = store.getState().infoFromAllProviders[normalized];
|
|
4029
4119
|
if (cached) return cached;
|
|
4030
4120
|
try {
|
|
@@ -4099,7 +4189,7 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
4099
4189
|
return defaultUsageTrackingDriver;
|
|
4100
4190
|
}
|
|
4101
4191
|
if (isBun3()) {
|
|
4102
|
-
defaultUsageTrackingDriver =
|
|
4192
|
+
defaultUsageTrackingDriver = createBunSqliteUsageTrackingDriver();
|
|
4103
4193
|
return defaultUsageTrackingDriver;
|
|
4104
4194
|
}
|
|
4105
4195
|
if (isNode()) {
|
|
@@ -4111,21 +4201,28 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
4111
4201
|
defaultUsageTrackingDriver = createMemoryUsageTrackingDriver();
|
|
4112
4202
|
return defaultUsageTrackingDriver;
|
|
4113
4203
|
};
|
|
4204
|
+
var setDefaultUsageTrackingDriver = (driver) => {
|
|
4205
|
+
defaultUsageTrackingDriver = driver;
|
|
4206
|
+
};
|
|
4114
4207
|
var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
|
|
4115
4208
|
var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
|
|
4116
4209
|
var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
|
|
4117
4210
|
function createSSEParserTransform(onUsage, onResponseId) {
|
|
4118
4211
|
let buffer = "";
|
|
4212
|
+
let usageCaptured = false;
|
|
4213
|
+
let responseIdCaptured = false;
|
|
4119
4214
|
const maybeCaptureUsageFromJson = (jsonText) => {
|
|
4120
4215
|
try {
|
|
4121
4216
|
const data = JSON.parse(jsonText);
|
|
4122
4217
|
const responseId = data.id;
|
|
4123
4218
|
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
4124
4219
|
onResponseId?.(responseId.trim());
|
|
4220
|
+
responseIdCaptured = true;
|
|
4125
4221
|
}
|
|
4126
4222
|
const usage = extractUsageFromSSEJson(data);
|
|
4127
4223
|
if (usage) {
|
|
4128
4224
|
onUsage(usage);
|
|
4225
|
+
usageCaptured = true;
|
|
4129
4226
|
}
|
|
4130
4227
|
} catch {
|
|
4131
4228
|
}
|
|
@@ -4181,7 +4278,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4181
4278
|
}
|
|
4182
4279
|
var TOPUP_MARGIN = 1.2;
|
|
4183
4280
|
var RoutstrClient = class {
|
|
4184
|
-
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu") {
|
|
4281
|
+
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
|
|
4185
4282
|
this.walletAdapter = walletAdapter;
|
|
4186
4283
|
this.storageAdapter = storageAdapter;
|
|
4187
4284
|
this.providerRegistry = providerRegistry;
|
|
@@ -4199,13 +4296,9 @@ var RoutstrClient = class {
|
|
|
4199
4296
|
this.streamProcessor = new StreamProcessor();
|
|
4200
4297
|
this.providerManager = new ProviderManager(providerRegistry);
|
|
4201
4298
|
this.alertLevel = alertLevel;
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
this.mode = "lazyrefund";
|
|
4206
|
-
} else {
|
|
4207
|
-
this.mode = mode;
|
|
4208
|
-
}
|
|
4299
|
+
this.mode = mode;
|
|
4300
|
+
this.usageTrackingDriver = options.usageTrackingDriver;
|
|
4301
|
+
this.sdkStore = options.sdkStore;
|
|
4209
4302
|
}
|
|
4210
4303
|
cashuSpender;
|
|
4211
4304
|
balanceManager;
|
|
@@ -4214,6 +4307,8 @@ var RoutstrClient = class {
|
|
|
4214
4307
|
alertLevel;
|
|
4215
4308
|
mode;
|
|
4216
4309
|
debugLevel = "WARN";
|
|
4310
|
+
usageTrackingDriver;
|
|
4311
|
+
sdkStore;
|
|
4217
4312
|
/**
|
|
4218
4313
|
* Get the current client mode
|
|
4219
4314
|
*/
|
|
@@ -4283,11 +4378,13 @@ var RoutstrClient = class {
|
|
|
4283
4378
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4284
4379
|
token: prepared.tokenUsed,
|
|
4285
4380
|
baseUrl: prepared.baseUrlUsed,
|
|
4381
|
+
mintUrl: params.mintUrl,
|
|
4286
4382
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4287
4383
|
response: prepared.response,
|
|
4288
4384
|
modelId: prepared.modelId,
|
|
4289
4385
|
usage: prepared.capturedUsage,
|
|
4290
|
-
requestId: prepared.capturedResponseId
|
|
4386
|
+
requestId: prepared.capturedResponseId,
|
|
4387
|
+
clientApiKey: prepared.clientApiKey
|
|
4291
4388
|
});
|
|
4292
4389
|
prepared.response.satsSpent = satsSpent;
|
|
4293
4390
|
prepared.response.usage = prepared.capturedUsage;
|
|
@@ -4306,11 +4403,13 @@ var RoutstrClient = class {
|
|
|
4306
4403
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4307
4404
|
token: prepared.tokenUsed,
|
|
4308
4405
|
baseUrl: prepared.baseUrlUsed,
|
|
4406
|
+
mintUrl: params.mintUrl,
|
|
4309
4407
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4310
4408
|
response: prepared.response,
|
|
4311
4409
|
modelId: prepared.modelId,
|
|
4312
4410
|
usage: prepared.capturedUsage,
|
|
4313
|
-
requestId: prepared.capturedResponseId
|
|
4411
|
+
requestId: prepared.capturedResponseId,
|
|
4412
|
+
clientApiKey: prepared.clientApiKey
|
|
4314
4413
|
});
|
|
4315
4414
|
prepared.response.satsSpent = satsSpent;
|
|
4316
4415
|
res.end();
|
|
@@ -4326,11 +4425,13 @@ var RoutstrClient = class {
|
|
|
4326
4425
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4327
4426
|
token: prepared.tokenUsed,
|
|
4328
4427
|
baseUrl: prepared.baseUrlUsed,
|
|
4428
|
+
mintUrl: params.mintUrl,
|
|
4329
4429
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
4330
4430
|
response: prepared.response,
|
|
4331
4431
|
modelId: prepared.modelId,
|
|
4332
4432
|
usage: prepared.capturedUsage,
|
|
4333
|
-
requestId: prepared.capturedResponseId
|
|
4433
|
+
requestId: prepared.capturedResponseId,
|
|
4434
|
+
clientApiKey: prepared.clientApiKey
|
|
4334
4435
|
});
|
|
4335
4436
|
prepared.response.satsSpent = satsSpent;
|
|
4336
4437
|
prepared.response.usage = prepared.capturedUsage;
|
|
@@ -4360,8 +4461,10 @@ var RoutstrClient = class {
|
|
|
4360
4461
|
headers = {},
|
|
4361
4462
|
baseUrl,
|
|
4362
4463
|
mintUrl,
|
|
4363
|
-
modelId
|
|
4464
|
+
modelId,
|
|
4465
|
+
clientApiKey: providedClientApiKey
|
|
4364
4466
|
} = params;
|
|
4467
|
+
const clientApiKey = providedClientApiKey ?? this._extractClientApiKey(headers);
|
|
4365
4468
|
await this._checkBalance();
|
|
4366
4469
|
let requiredSats = 1;
|
|
4367
4470
|
let selectedModel;
|
|
@@ -4383,7 +4486,6 @@ var RoutstrClient = class {
|
|
|
4383
4486
|
amount: requiredSats,
|
|
4384
4487
|
baseUrl
|
|
4385
4488
|
});
|
|
4386
|
-
this._log("DEBUG", token, baseUrl);
|
|
4387
4489
|
let requestBody = body;
|
|
4388
4490
|
if (body && typeof body === "object") {
|
|
4389
4491
|
const bodyObj = body;
|
|
@@ -4391,7 +4493,7 @@ var RoutstrClient = class {
|
|
|
4391
4493
|
requestBody = { ...bodyObj, stream: false };
|
|
4392
4494
|
}
|
|
4393
4495
|
}
|
|
4394
|
-
const baseHeaders = this._buildBaseHeaders(
|
|
4496
|
+
const baseHeaders = this._buildBaseHeaders();
|
|
4395
4497
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
4396
4498
|
const response = await this._makeRequest({
|
|
4397
4499
|
path,
|
|
@@ -4443,9 +4545,21 @@ var RoutstrClient = class {
|
|
|
4443
4545
|
tokenBalanceInSats,
|
|
4444
4546
|
modelId,
|
|
4445
4547
|
capturedUsage,
|
|
4446
|
-
capturedResponseId
|
|
4548
|
+
capturedResponseId,
|
|
4549
|
+
clientApiKey
|
|
4447
4550
|
};
|
|
4448
4551
|
}
|
|
4552
|
+
/**
|
|
4553
|
+
* Extract clientApiKey from Authorization Bearer token if present
|
|
4554
|
+
*/
|
|
4555
|
+
_extractClientApiKey(headers) {
|
|
4556
|
+
const authHeader = headers["Authorization"] || headers["authorization"];
|
|
4557
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
4558
|
+
const extractedKey = authHeader.slice(7);
|
|
4559
|
+
return extractedKey;
|
|
4560
|
+
}
|
|
4561
|
+
return void 0;
|
|
4562
|
+
}
|
|
4449
4563
|
/**
|
|
4450
4564
|
* Fetch AI response with streaming
|
|
4451
4565
|
*/
|
|
@@ -4549,6 +4663,7 @@ var RoutstrClient = class {
|
|
|
4549
4663
|
let satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
4550
4664
|
token,
|
|
4551
4665
|
baseUrl: baseUrlUsed,
|
|
4666
|
+
mintUrl,
|
|
4552
4667
|
initialTokenBalance: tokenBalanceInSats,
|
|
4553
4668
|
fallbackSatsSpent: isApikeysEstimate ? this._getEstimatedCosts(selectedModel, streamingResult) : void 0,
|
|
4554
4669
|
response,
|
|
@@ -4587,7 +4702,6 @@ var RoutstrClient = class {
|
|
|
4587
4702
|
try {
|
|
4588
4703
|
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
4589
4704
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
4590
|
-
this._log("DEBUG", "HEADERS,", headers);
|
|
4591
4705
|
const response = await fetch(url, {
|
|
4592
4706
|
method,
|
|
4593
4707
|
headers,
|
|
@@ -4656,8 +4770,6 @@ var RoutstrClient = class {
|
|
|
4656
4770
|
`[RoutstrClient] _handleErrorResponse: Token restored successfully, amount=${tryReceiveTokenResult.amount}`
|
|
4657
4771
|
);
|
|
4658
4772
|
tryNextProvider = true;
|
|
4659
|
-
if (this.mode === "lazyrefund")
|
|
4660
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
4661
4773
|
} else {
|
|
4662
4774
|
this._log(
|
|
4663
4775
|
"DEBUG",
|
|
@@ -4705,22 +4817,15 @@ var RoutstrClient = class {
|
|
|
4705
4817
|
);
|
|
4706
4818
|
}
|
|
4707
4819
|
}
|
|
4708
|
-
if (status === 402 && !tryNextProvider &&
|
|
4820
|
+
if (status === 402 && !tryNextProvider && this.mode === "apikeys") {
|
|
4709
4821
|
this.storageAdapter.getApiKey(baseUrl);
|
|
4710
4822
|
let topupAmount = params.requiredSats;
|
|
4711
4823
|
try {
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
);
|
|
4718
|
-
currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
4719
|
-
} else if (this.mode === "lazyrefund") {
|
|
4720
|
-
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
4721
|
-
const tokenEntry = distribution.find((t) => t.baseUrl === baseUrl);
|
|
4722
|
-
currentBalance = tokenEntry?.amount ?? 0;
|
|
4723
|
-
}
|
|
4824
|
+
const currentBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
4825
|
+
params.token,
|
|
4826
|
+
baseUrl
|
|
4827
|
+
);
|
|
4828
|
+
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
4724
4829
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
4725
4830
|
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
4726
4831
|
} catch (e) {
|
|
@@ -4857,34 +4962,7 @@ var RoutstrClient = class {
|
|
|
4857
4962
|
"DEBUG",
|
|
4858
4963
|
`[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`
|
|
4859
4964
|
);
|
|
4860
|
-
if (this.mode === "
|
|
4861
|
-
try {
|
|
4862
|
-
const refundResult = await this.balanceManager.refund({
|
|
4863
|
-
mintUrl,
|
|
4864
|
-
baseUrl,
|
|
4865
|
-
token: params.token
|
|
4866
|
-
});
|
|
4867
|
-
this._log(
|
|
4868
|
-
"DEBUG",
|
|
4869
|
-
`[RoutstrClient] _handleErrorResponse: Lazyrefund result: success=${refundResult.success}`
|
|
4870
|
-
);
|
|
4871
|
-
if (refundResult.success) this.storageAdapter.removeToken(baseUrl);
|
|
4872
|
-
else
|
|
4873
|
-
throw new ProviderError(
|
|
4874
|
-
baseUrl,
|
|
4875
|
-
status,
|
|
4876
|
-
"refund failed",
|
|
4877
|
-
requestId
|
|
4878
|
-
);
|
|
4879
|
-
} catch (error) {
|
|
4880
|
-
throw new ProviderError(
|
|
4881
|
-
baseUrl,
|
|
4882
|
-
status,
|
|
4883
|
-
"Failed to refund token",
|
|
4884
|
-
requestId
|
|
4885
|
-
);
|
|
4886
|
-
}
|
|
4887
|
-
} else if (this.mode === "apikeys") {
|
|
4965
|
+
if (this.mode === "apikeys") {
|
|
4888
4966
|
this._log(
|
|
4889
4967
|
"DEBUG",
|
|
4890
4968
|
`[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
|
|
@@ -4900,7 +4978,8 @@ var RoutstrClient = class {
|
|
|
4900
4978
|
const refundResult = await this.balanceManager.refundApiKey({
|
|
4901
4979
|
mintUrl,
|
|
4902
4980
|
baseUrl,
|
|
4903
|
-
apiKey: token
|
|
4981
|
+
apiKey: token,
|
|
4982
|
+
forceRefund: true
|
|
4904
4983
|
});
|
|
4905
4984
|
this._log(
|
|
4906
4985
|
"DEBUG",
|
|
@@ -4982,12 +5061,14 @@ var RoutstrClient = class {
|
|
|
4982
5061
|
const {
|
|
4983
5062
|
token,
|
|
4984
5063
|
baseUrl,
|
|
5064
|
+
mintUrl,
|
|
4985
5065
|
initialTokenBalance,
|
|
4986
5066
|
fallbackSatsSpent,
|
|
4987
5067
|
response,
|
|
4988
5068
|
modelId,
|
|
4989
5069
|
usage,
|
|
4990
|
-
requestId
|
|
5070
|
+
requestId,
|
|
5071
|
+
clientApiKey
|
|
4991
5072
|
} = params;
|
|
4992
5073
|
let satsSpent = initialTokenBalance;
|
|
4993
5074
|
if (this.mode === "xcashu" && response) {
|
|
@@ -4995,19 +5076,14 @@ var RoutstrClient = class {
|
|
|
4995
5076
|
if (refundToken) {
|
|
4996
5077
|
try {
|
|
4997
5078
|
const receiveResult = await this.cashuSpender.receiveToken(refundToken);
|
|
4998
|
-
|
|
5079
|
+
if (receiveResult.success) {
|
|
5080
|
+
this.storageAdapter.removeXcashuToken(baseUrl, token);
|
|
5081
|
+
satsSpent = initialTokenBalance - receiveResult.amount * (receiveResult.unit == "sat" ? 1 : 1e3);
|
|
5082
|
+
}
|
|
4999
5083
|
} catch (error) {
|
|
5000
5084
|
this._log("ERROR", "[xcashu] Failed to receive refund token:", error);
|
|
5001
5085
|
}
|
|
5002
5086
|
}
|
|
5003
|
-
} else if (this.mode === "lazyrefund") {
|
|
5004
|
-
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
5005
|
-
token,
|
|
5006
|
-
baseUrl
|
|
5007
|
-
);
|
|
5008
|
-
const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
5009
|
-
this.storageAdapter.updateTokenBalance(baseUrl, latestTokenBalance);
|
|
5010
|
-
satsSpent = initialTokenBalance - latestTokenBalance;
|
|
5011
5087
|
} else if (this.mode === "apikeys") {
|
|
5012
5088
|
try {
|
|
5013
5089
|
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
@@ -5042,8 +5118,18 @@ var RoutstrClient = class {
|
|
|
5042
5118
|
modelId,
|
|
5043
5119
|
satsSpent,
|
|
5044
5120
|
usage,
|
|
5045
|
-
requestId
|
|
5121
|
+
requestId,
|
|
5122
|
+
clientApiKey
|
|
5046
5123
|
});
|
|
5124
|
+
(async () => {
|
|
5125
|
+
try {
|
|
5126
|
+
const xcashuResults = await this.cashuSpender.refundXcashuTokens(mintUrl);
|
|
5127
|
+
this._log("DEBUG", "Refund xcashu tokens results:", xcashuResults);
|
|
5128
|
+
const results = await this.cashuSpender.refundProviders(mintUrl);
|
|
5129
|
+
} catch (error) {
|
|
5130
|
+
this._log("ERROR", "Failed to refund providers:", error);
|
|
5131
|
+
}
|
|
5132
|
+
})();
|
|
5047
5133
|
return satsSpent;
|
|
5048
5134
|
}
|
|
5049
5135
|
async _trackResponseUsage(params) {
|
|
@@ -5054,7 +5140,8 @@ var RoutstrClient = class {
|
|
|
5054
5140
|
modelId,
|
|
5055
5141
|
satsSpent,
|
|
5056
5142
|
usage: providedUsage,
|
|
5057
|
-
requestId: providedRequestId
|
|
5143
|
+
requestId: providedRequestId,
|
|
5144
|
+
clientApiKey
|
|
5058
5145
|
} = params;
|
|
5059
5146
|
if (!response || !modelId) {
|
|
5060
5147
|
return;
|
|
@@ -5081,13 +5168,14 @@ var RoutstrClient = class {
|
|
|
5081
5168
|
return;
|
|
5082
5169
|
}
|
|
5083
5170
|
const finalRequestId = requestId || "unknown";
|
|
5084
|
-
const store = await getDefaultSdkStore();
|
|
5171
|
+
const store = this.sdkStore ?? await getDefaultSdkStore();
|
|
5085
5172
|
const state = store.getState();
|
|
5173
|
+
const matchKey = clientApiKey ?? token;
|
|
5086
5174
|
const matchingClient = state.clientIds.find(
|
|
5087
|
-
(client) => client.apiKey ===
|
|
5175
|
+
(client) => client.apiKey === matchKey
|
|
5088
5176
|
);
|
|
5089
5177
|
const entryId = finalRequestId === "unknown" ? `req-${Date.now()}-${modelId}` : finalRequestId;
|
|
5090
|
-
const usageTracking = getDefaultUsageTrackingDriver();
|
|
5178
|
+
const usageTracking = this.usageTrackingDriver ?? getDefaultUsageTrackingDriver();
|
|
5091
5179
|
const entry = {
|
|
5092
5180
|
id: entryId,
|
|
5093
5181
|
timestamp: Date.now(),
|
|
@@ -5162,11 +5250,11 @@ var RoutstrClient = class {
|
|
|
5162
5250
|
return estimatedCosts;
|
|
5163
5251
|
}
|
|
5164
5252
|
/**
|
|
5165
|
-
* Get pending
|
|
5253
|
+
* Get pending API key amount
|
|
5166
5254
|
*/
|
|
5167
5255
|
_getPendingCashuTokenAmount() {
|
|
5168
|
-
const
|
|
5169
|
-
return
|
|
5256
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
5257
|
+
return apiKeyDistribution.reduce((total, item) => total + item.amount, 0);
|
|
5170
5258
|
}
|
|
5171
5259
|
/**
|
|
5172
5260
|
* Handle errors and notify callbacks
|
|
@@ -5313,8 +5401,8 @@ var RoutstrClient = class {
|
|
|
5313
5401
|
const spendResult = await this.cashuSpender.spend({
|
|
5314
5402
|
mintUrl,
|
|
5315
5403
|
amount,
|
|
5316
|
-
baseUrl:
|
|
5317
|
-
reuseToken:
|
|
5404
|
+
baseUrl: "",
|
|
5405
|
+
reuseToken: false
|
|
5318
5406
|
});
|
|
5319
5407
|
if (!spendResult.token) {
|
|
5320
5408
|
this._log(
|
|
@@ -5327,6 +5415,7 @@ var RoutstrClient = class {
|
|
|
5327
5415
|
"DEBUG",
|
|
5328
5416
|
`[RoutstrClient] _spendToken: Cashu token created, token preview: ${spendResult.token}, balance: ${spendResult.balance} ${spendResult.unit ?? "sat"}`
|
|
5329
5417
|
);
|
|
5418
|
+
this.storageAdapter.addXcashuToken(baseUrl, spendResult.token);
|
|
5330
5419
|
}
|
|
5331
5420
|
return {
|
|
5332
5421
|
token: spendResult.token,
|
|
@@ -5364,6 +5453,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5364
5453
|
modelId,
|
|
5365
5454
|
requestBody,
|
|
5366
5455
|
path = "/v1/chat/completions",
|
|
5456
|
+
headers = {},
|
|
5367
5457
|
forcedProvider,
|
|
5368
5458
|
walletAdapter,
|
|
5369
5459
|
storageAdapter,
|
|
@@ -5374,7 +5464,9 @@ async function resolveRouteRequestContext(options) {
|
|
|
5374
5464
|
forceRefresh = false,
|
|
5375
5465
|
modelManager: providedModelManager,
|
|
5376
5466
|
debugLevel,
|
|
5377
|
-
mode = "apikeys"
|
|
5467
|
+
mode = "apikeys",
|
|
5468
|
+
usageTrackingDriver,
|
|
5469
|
+
sdkStore
|
|
5378
5470
|
} = options;
|
|
5379
5471
|
let modelManager;
|
|
5380
5472
|
let providers;
|
|
@@ -5438,7 +5530,8 @@ async function resolveRouteRequestContext(options) {
|
|
|
5438
5530
|
storageAdapter,
|
|
5439
5531
|
providerRegistry,
|
|
5440
5532
|
"min",
|
|
5441
|
-
mode
|
|
5533
|
+
mode,
|
|
5534
|
+
{ usageTrackingDriver, sdkStore }
|
|
5442
5535
|
);
|
|
5443
5536
|
if (debugLevel) {
|
|
5444
5537
|
client.setDebugLevel(debugLevel);
|
|
@@ -5458,17 +5551,19 @@ async function resolveRouteRequestContext(options) {
|
|
|
5458
5551
|
baseUrl,
|
|
5459
5552
|
mintUrl,
|
|
5460
5553
|
path,
|
|
5554
|
+
headers,
|
|
5461
5555
|
modelId,
|
|
5462
5556
|
proxiedBody
|
|
5463
5557
|
};
|
|
5464
5558
|
}
|
|
5465
5559
|
async function routeRequests(options) {
|
|
5466
|
-
const { client, baseUrl, mintUrl, path, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5560
|
+
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5467
5561
|
try {
|
|
5468
5562
|
const response = await client.routeRequest({
|
|
5469
5563
|
path,
|
|
5470
5564
|
method: "POST",
|
|
5471
5565
|
body: proxiedBody,
|
|
5566
|
+
headers,
|
|
5472
5567
|
baseUrl,
|
|
5473
5568
|
mintUrl,
|
|
5474
5569
|
modelId
|
|
@@ -5486,12 +5581,13 @@ async function routeRequests(options) {
|
|
|
5486
5581
|
}
|
|
5487
5582
|
async function routeRequestsToNodeResponse(options) {
|
|
5488
5583
|
const { res } = options;
|
|
5489
|
-
const { client, baseUrl, mintUrl, path, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5584
|
+
const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
|
|
5490
5585
|
try {
|
|
5491
5586
|
await client.routeRequestToNodeResponse({
|
|
5492
5587
|
path,
|
|
5493
5588
|
method: "POST",
|
|
5494
5589
|
body: proxiedBody,
|
|
5590
|
+
headers,
|
|
5495
5591
|
baseUrl,
|
|
5496
5592
|
mintUrl,
|
|
5497
5593
|
modelId,
|
|
@@ -5539,6 +5635,8 @@ exports.SDK_STORAGE_KEYS = SDK_STORAGE_KEYS;
|
|
|
5539
5635
|
exports.StreamProcessor = StreamProcessor;
|
|
5540
5636
|
exports.StreamingError = StreamingError;
|
|
5541
5637
|
exports.TokenOperationError = TokenOperationError;
|
|
5638
|
+
exports.createBunSqliteDriver = createBunSqliteDriver;
|
|
5639
|
+
exports.createBunSqliteUsageTrackingDriver = createBunSqliteUsageTrackingDriver;
|
|
5542
5640
|
exports.createDiscoveryAdapterFromStore = createDiscoveryAdapterFromStore;
|
|
5543
5641
|
exports.createIndexedDBDriver = createIndexedDBDriver;
|
|
5544
5642
|
exports.createIndexedDBUsageTrackingDriver = createIndexedDBUsageTrackingDriver;
|
|
@@ -5564,5 +5662,6 @@ exports.localStorageDriver = localStorageDriver;
|
|
|
5564
5662
|
exports.normalizeProviderUrl = normalizeProviderUrl;
|
|
5565
5663
|
exports.routeRequests = routeRequests;
|
|
5566
5664
|
exports.routeRequestsToNodeResponse = routeRequestsToNodeResponse;
|
|
5665
|
+
exports.setDefaultUsageTrackingDriver = setDefaultUsageTrackingDriver;
|
|
5567
5666
|
//# sourceMappingURL=index.js.map
|
|
5568
5667
|
//# sourceMappingURL=index.js.map
|