@routstr/sdk 0.2.3 → 0.2.5
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 +9 -0
- package/dist/client/index.d.mts +21 -8
- package/dist/client/index.d.ts +21 -8
- package/dist/client/index.js +1406 -69
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +1406 -70
- package/dist/client/index.mjs.map +1 -1
- package/dist/discovery/index.d.mts +2 -2
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +1 -4
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +1 -4
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.d.mts +15 -19
- package/dist/index.d.ts +15 -19
- package/dist/index.js +2385 -1574
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2380 -1575
- package/dist/index.mjs.map +1 -1
- package/dist/{interfaces-DGdP8fQp.d.mts → interfaces-BWJJTCXO.d.mts} +1 -1
- package/dist/{interfaces-CC0LT9p9.d.ts → interfaces-BxDEka72.d.ts} +1 -1
- package/dist/{interfaces-B85Wx7ni.d.mts → interfaces-C6Dr6hKy.d.mts} +1 -1
- package/dist/{interfaces-BVNyAmKu.d.ts → interfaces-CluftN4z.d.ts} +1 -1
- package/dist/storage/index.d.mts +56 -34
- package/dist/storage/index.d.ts +56 -34
- package/dist/storage/index.js +500 -51
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +497 -52
- package/dist/storage/index.mjs.map +1 -1
- package/dist/{types-BlHjmWRK.d.mts → types-BYj_8c5c.d.mts} +3 -0
- package/dist/{types-BlHjmWRK.d.ts → types-BYj_8c5c.d.ts} +3 -0
- package/dist/wallet/index.d.mts +9 -5
- package/dist/wallet/index.d.ts +9 -5
- package/dist/wallet/index.js +54 -22
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +54 -22
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var vanilla = require('zustand/vanilla');
|
|
4
|
+
var cashuTs = require('@cashu/cashu-ts');
|
|
5
|
+
var stream = require('stream');
|
|
6
|
+
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
13
|
+
|
|
3
14
|
// core/errors.ts
|
|
4
15
|
var InsufficientBalanceError = class extends Error {
|
|
5
16
|
constructor(required, available, maxMintBalance = 0, maxMintUrl = "", customMessage) {
|
|
@@ -468,7 +479,7 @@ var CashuSpender = class {
|
|
|
468
479
|
/**
|
|
469
480
|
* Refund specific providers without retrying spend
|
|
470
481
|
*/
|
|
471
|
-
async refundProviders(baseUrls, mintUrl, refundApiKeys = false) {
|
|
482
|
+
async refundProviders(baseUrls, mintUrl, refundApiKeys = false, forceRefund) {
|
|
472
483
|
const results = [];
|
|
473
484
|
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
474
485
|
const toRefund = pendingDistribution.filter(
|
|
@@ -518,7 +529,8 @@ var CashuSpender = class {
|
|
|
518
529
|
const refundResult = await this.balanceManager.refundApiKey({
|
|
519
530
|
mintUrl,
|
|
520
531
|
baseUrl: apiKeyEntry.baseUrl,
|
|
521
|
-
apiKey: apiKeyEntryFull.key
|
|
532
|
+
apiKey: apiKeyEntryFull.key,
|
|
533
|
+
forceRefund
|
|
522
534
|
});
|
|
523
535
|
if (refundResult.success) {
|
|
524
536
|
this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, 0);
|
|
@@ -691,12 +703,29 @@ var BalanceManager = class {
|
|
|
691
703
|
}
|
|
692
704
|
/**
|
|
693
705
|
* Refund API key balance - convert remaining API key balance to cashu token
|
|
706
|
+
* @param options - Refund options including forceRefund flag
|
|
707
|
+
* @returns Refund result
|
|
694
708
|
*/
|
|
695
709
|
async refundApiKey(options) {
|
|
696
|
-
const { mintUrl, baseUrl, apiKey } = options;
|
|
710
|
+
const { mintUrl, baseUrl, apiKey, forceRefund } = options;
|
|
697
711
|
if (!apiKey) {
|
|
698
712
|
return { success: false, message: "No API key to refund" };
|
|
699
713
|
}
|
|
714
|
+
if (!forceRefund) {
|
|
715
|
+
const apiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
|
|
716
|
+
if (apiKeyEntry?.lastUsed) {
|
|
717
|
+
const fiveMinutesAgo = Date.now() - 5 * 60 * 1e3;
|
|
718
|
+
if (apiKeyEntry.lastUsed > fiveMinutesAgo) {
|
|
719
|
+
console.log(
|
|
720
|
+
`[BalanceManager] Skipping refund for ${baseUrl} - used ${Math.round((Date.now() - apiKeyEntry.lastUsed) / 1e3)}s ago`
|
|
721
|
+
);
|
|
722
|
+
return {
|
|
723
|
+
success: false,
|
|
724
|
+
message: "API key was used recently, skipping refund"
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
700
729
|
let fetchResult;
|
|
701
730
|
try {
|
|
702
731
|
fetchResult = await this._fetchRefundTokenWithApiKey(baseUrl, apiKey);
|
|
@@ -868,9 +897,13 @@ var BalanceManager = class {
|
|
|
868
897
|
p2pkPubkey
|
|
869
898
|
} = options;
|
|
870
899
|
const adjustedAmount = Math.ceil(amount);
|
|
871
|
-
console.log(
|
|
900
|
+
console.log(
|
|
901
|
+
`[BalanceManager.createProviderToken] Starting: baseUrl=${baseUrl}, mintUrl=${mintUrl}, amount=${amount}, adjustedAmount=${adjustedAmount}, retryCount=${retryCount}`
|
|
902
|
+
);
|
|
872
903
|
if (!adjustedAmount || isNaN(adjustedAmount)) {
|
|
873
|
-
console.error(
|
|
904
|
+
console.error(
|
|
905
|
+
`[BalanceManager.createProviderToken] FAILURE: Invalid amount - amount=${amount}, adjustedAmount=${adjustedAmount}`
|
|
906
|
+
);
|
|
874
907
|
return { success: false, error: "Invalid top up amount" };
|
|
875
908
|
}
|
|
876
909
|
const balanceState = await this.getBalanceState();
|
|
@@ -884,8 +917,8 @@ var BalanceManager = class {
|
|
|
884
917
|
const refundableProviderBalance = Object.entries(
|
|
885
918
|
balanceState.providerBalances
|
|
886
919
|
).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
|
|
887
|
-
if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount <
|
|
888
|
-
await this._refundOtherProvidersForTopUp(baseUrl, mintUrl);
|
|
920
|
+
if (totalMintBalance + targetProviderBalance < adjustedAmount && totalMintBalance + targetProviderBalance + refundableProviderBalance >= adjustedAmount && retryCount < 2) {
|
|
921
|
+
await this._refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount);
|
|
889
922
|
return this.createProviderToken({
|
|
890
923
|
...options,
|
|
891
924
|
retryCount: retryCount + 1
|
|
@@ -901,15 +934,11 @@ var BalanceManager = class {
|
|
|
901
934
|
{ url: "", balance: 0 }
|
|
902
935
|
).url
|
|
903
936
|
);
|
|
904
|
-
console.error(
|
|
937
|
+
console.error(
|
|
938
|
+
`[BalanceManager.createProviderToken] FAILURE: Insufficient balance - required=${adjustedAmount}, available=${totalMintBalance + targetProviderBalance}, totalMintBalance=${totalMintBalance}, targetProviderBalance=${targetProviderBalance}, refundableProviderBalance=${refundableProviderBalance}`
|
|
939
|
+
);
|
|
905
940
|
return { success: false, error: error.message };
|
|
906
941
|
}
|
|
907
|
-
if (targetProviderBalance >= adjustedAmount) {
|
|
908
|
-
return {
|
|
909
|
-
success: true,
|
|
910
|
-
amountSpent: 0
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
942
|
const providerMints = baseUrl && this.providerRegistry ? this.providerRegistry.getProviderMints(baseUrl) : [];
|
|
914
943
|
let requiredAmount = adjustedAmount;
|
|
915
944
|
const supportedMintsOnly = providerMints.length > 0;
|
|
@@ -943,7 +972,9 @@ var BalanceManager = class {
|
|
|
943
972
|
maxMintUrl = mintUrl2;
|
|
944
973
|
}
|
|
945
974
|
}
|
|
946
|
-
console.error(
|
|
975
|
+
console.error(
|
|
976
|
+
`[BalanceManager.createProviderToken] FAILURE: No candidate mints found - requiredAmount=${requiredAmount}, totalMintBalance=${totalMintBalance}, maxBalance=${maxBalance}, maxMintUrl=${maxMintUrl}, providerMints=${JSON.stringify(providerMints)}`
|
|
977
|
+
);
|
|
947
978
|
const error = new InsufficientBalanceError(
|
|
948
979
|
adjustedAmount,
|
|
949
980
|
totalMintBalance,
|
|
@@ -955,13 +986,17 @@ var BalanceManager = class {
|
|
|
955
986
|
let lastError;
|
|
956
987
|
for (const candidateMint of candidates) {
|
|
957
988
|
try {
|
|
958
|
-
console.log(
|
|
989
|
+
console.log(
|
|
990
|
+
`[BalanceManager.createProviderToken] Attempting mint: ${candidateMint}, amount: ${requiredAmount}`
|
|
991
|
+
);
|
|
959
992
|
const token = await this.walletAdapter.sendToken(
|
|
960
993
|
candidateMint,
|
|
961
994
|
requiredAmount,
|
|
962
995
|
p2pkPubkey
|
|
963
996
|
);
|
|
964
|
-
console.log(
|
|
997
|
+
console.log(
|
|
998
|
+
`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
|
|
999
|
+
);
|
|
965
1000
|
return {
|
|
966
1001
|
success: true,
|
|
967
1002
|
token,
|
|
@@ -970,11 +1005,15 @@ var BalanceManager = class {
|
|
|
970
1005
|
};
|
|
971
1006
|
} catch (error) {
|
|
972
1007
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
973
|
-
console.error(
|
|
1008
|
+
console.error(
|
|
1009
|
+
`[BalanceManager.createProviderToken] FAILURE: Mint ${candidateMint} failed with error: ${errorMsg}`
|
|
1010
|
+
);
|
|
974
1011
|
if (error instanceof Error) {
|
|
975
1012
|
lastError = errorMsg;
|
|
976
1013
|
if (isNetworkErrorMessage(error.message)) {
|
|
977
|
-
console.warn(
|
|
1014
|
+
console.warn(
|
|
1015
|
+
`[BalanceManager.createProviderToken] Network error from ${candidateMint}, trying next mint...`
|
|
1016
|
+
);
|
|
978
1017
|
continue;
|
|
979
1018
|
}
|
|
980
1019
|
}
|
|
@@ -984,7 +1023,9 @@ var BalanceManager = class {
|
|
|
984
1023
|
};
|
|
985
1024
|
}
|
|
986
1025
|
}
|
|
987
|
-
console.error(
|
|
1026
|
+
console.error(
|
|
1027
|
+
`[BalanceManager.createProviderToken] FAILURE: All candidate mints exhausted - lastError=${lastError}, candidates=${JSON.stringify(candidates)}`
|
|
1028
|
+
);
|
|
988
1029
|
return {
|
|
989
1030
|
success: false,
|
|
990
1031
|
error: lastError || "All candidate mints failed while creating top up token"
|
|
@@ -1030,9 +1071,10 @@ var BalanceManager = class {
|
|
|
1030
1071
|
}
|
|
1031
1072
|
return candidates;
|
|
1032
1073
|
}
|
|
1033
|
-
async _refundOtherProvidersForTopUp(baseUrl, mintUrl) {
|
|
1074
|
+
async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount) {
|
|
1034
1075
|
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1035
1076
|
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1077
|
+
const forceRefund = retryCount >= 2;
|
|
1036
1078
|
const toRefund = pendingDistribution.filter(
|
|
1037
1079
|
(pending) => pending.baseUrl !== baseUrl
|
|
1038
1080
|
);
|
|
@@ -1073,7 +1115,8 @@ var BalanceManager = class {
|
|
|
1073
1115
|
const result = await this.refundApiKey({
|
|
1074
1116
|
mintUrl,
|
|
1075
1117
|
baseUrl: apiKeyEntry.baseUrl,
|
|
1076
|
-
apiKey: fullApiKeyEntry.key
|
|
1118
|
+
apiKey: fullApiKeyEntry.key,
|
|
1119
|
+
forceRefund
|
|
1077
1120
|
});
|
|
1078
1121
|
return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
|
|
1079
1122
|
})
|
|
@@ -1327,6 +1370,77 @@ var BalanceManager = class {
|
|
|
1327
1370
|
}
|
|
1328
1371
|
};
|
|
1329
1372
|
|
|
1373
|
+
// client/usage.ts
|
|
1374
|
+
function extractUsageFromResponseBody(body, fallbackSatsCost = 0) {
|
|
1375
|
+
if (!body || typeof body !== "object") return null;
|
|
1376
|
+
const usage = body.usage;
|
|
1377
|
+
if (!usage || typeof usage !== "object") return null;
|
|
1378
|
+
const promptTokens = Number(usage.prompt_tokens ?? 0);
|
|
1379
|
+
const completionTokens = Number(usage.completion_tokens ?? 0);
|
|
1380
|
+
const totalTokens = Number(usage.total_tokens ?? 0);
|
|
1381
|
+
const costValue = usage.cost;
|
|
1382
|
+
let cost = 0;
|
|
1383
|
+
let satsCost = fallbackSatsCost;
|
|
1384
|
+
if (typeof costValue === "number") {
|
|
1385
|
+
cost = costValue;
|
|
1386
|
+
} else if (costValue && typeof costValue === "object") {
|
|
1387
|
+
const costObj = costValue;
|
|
1388
|
+
const totalUsd = costObj.total_usd;
|
|
1389
|
+
const totalMsats = costObj.total_msats;
|
|
1390
|
+
cost = typeof totalUsd === "number" ? totalUsd : 0;
|
|
1391
|
+
if (typeof totalMsats === "number") {
|
|
1392
|
+
satsCost = totalMsats / 1e3;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (promptTokens === 0 && completionTokens === 0 && totalTokens === 0 && cost === 0 && satsCost === 0) {
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
return {
|
|
1399
|
+
promptTokens,
|
|
1400
|
+
completionTokens,
|
|
1401
|
+
totalTokens,
|
|
1402
|
+
cost,
|
|
1403
|
+
satsCost
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
function extractResponseId(body) {
|
|
1407
|
+
if (!body || typeof body !== "object") return void 0;
|
|
1408
|
+
const id = body.id;
|
|
1409
|
+
if (typeof id !== "string") return void 0;
|
|
1410
|
+
const trimmed = id.trim();
|
|
1411
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
1412
|
+
}
|
|
1413
|
+
function extractUsageFromSSEJson(parsed, fallbackSatsCost = 0) {
|
|
1414
|
+
if (!parsed || typeof parsed !== "object" || !parsed.usage) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
const usage = parsed.usage;
|
|
1418
|
+
const usageCost = usage.cost;
|
|
1419
|
+
const cost = typeof usageCost === "number" ? usageCost : usageCost?.total_usd ?? parsed.metadata?.routstr?.cost?.total_usd ?? 0;
|
|
1420
|
+
const msats = parsed.metadata?.routstr?.cost?.total_msats ?? (typeof usage.cost_sats === "number" ? usage.cost_sats * 1e3 : 0);
|
|
1421
|
+
const result = {
|
|
1422
|
+
promptTokens: Number(usage.prompt_tokens ?? 0),
|
|
1423
|
+
completionTokens: Number(usage.completion_tokens ?? 0),
|
|
1424
|
+
totalTokens: Number(usage.total_tokens ?? 0),
|
|
1425
|
+
cost: Number(cost ?? 0),
|
|
1426
|
+
satsCost: msats > 0 ? msats / 1e3 : fallbackSatsCost
|
|
1427
|
+
};
|
|
1428
|
+
if (result.promptTokens === 0 && result.completionTokens === 0 && result.totalTokens === 0 && result.cost === 0 && result.satsCost === 0) {
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
return result;
|
|
1432
|
+
}
|
|
1433
|
+
function toUsageStats(usage) {
|
|
1434
|
+
if (!usage) return void 0;
|
|
1435
|
+
return {
|
|
1436
|
+
total_tokens: usage.totalTokens,
|
|
1437
|
+
prompt_tokens: usage.promptTokens,
|
|
1438
|
+
completion_tokens: usage.completionTokens,
|
|
1439
|
+
cost: usage.cost,
|
|
1440
|
+
sats_cost: usage.satsCost
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1330
1444
|
// client/StreamProcessor.ts
|
|
1331
1445
|
var StreamProcessor = class {
|
|
1332
1446
|
accumulatedContent = "";
|
|
@@ -1354,6 +1468,7 @@ var StreamProcessor = class {
|
|
|
1354
1468
|
let finish_reason;
|
|
1355
1469
|
let citations;
|
|
1356
1470
|
let annotations;
|
|
1471
|
+
let responseId;
|
|
1357
1472
|
try {
|
|
1358
1473
|
while (true) {
|
|
1359
1474
|
const { done, value } = await reader.read();
|
|
@@ -1382,6 +1497,9 @@ var StreamProcessor = class {
|
|
|
1382
1497
|
if (parsed.finish_reason) {
|
|
1383
1498
|
finish_reason = parsed.finish_reason;
|
|
1384
1499
|
}
|
|
1500
|
+
if (parsed.responseId) {
|
|
1501
|
+
responseId = parsed.responseId;
|
|
1502
|
+
}
|
|
1385
1503
|
if (parsed.citations) {
|
|
1386
1504
|
citations = parsed.citations;
|
|
1387
1505
|
}
|
|
@@ -1402,6 +1520,7 @@ var StreamProcessor = class {
|
|
|
1402
1520
|
images: this.accumulatedImages.length > 0 ? this.accumulatedImages : void 0,
|
|
1403
1521
|
usage,
|
|
1404
1522
|
model,
|
|
1523
|
+
responseId,
|
|
1405
1524
|
finish_reason,
|
|
1406
1525
|
citations,
|
|
1407
1526
|
annotations
|
|
@@ -1429,12 +1548,15 @@ var StreamProcessor = class {
|
|
|
1429
1548
|
result.reasoning = parsed.choices[0].delta.reasoning;
|
|
1430
1549
|
}
|
|
1431
1550
|
if (parsed.usage) {
|
|
1432
|
-
result.usage = {
|
|
1551
|
+
result.usage = toUsageStats(extractUsageFromSSEJson(parsed)) ?? {
|
|
1433
1552
|
total_tokens: parsed.usage.total_tokens,
|
|
1434
1553
|
prompt_tokens: parsed.usage.prompt_tokens,
|
|
1435
1554
|
completion_tokens: parsed.usage.completion_tokens
|
|
1436
1555
|
};
|
|
1437
1556
|
}
|
|
1557
|
+
if (parsed.id) {
|
|
1558
|
+
result.responseId = parsed.id;
|
|
1559
|
+
}
|
|
1438
1560
|
if (parsed.model) {
|
|
1439
1561
|
result.model = parsed.model;
|
|
1440
1562
|
}
|
|
@@ -1826,7 +1948,6 @@ var ProviderManager = class _ProviderManager {
|
|
|
1826
1948
|
* Get providers for a model sorted by prompt+completion pricing
|
|
1827
1949
|
*/
|
|
1828
1950
|
getProviderPriceRankingForModel(modelId, options = {}) {
|
|
1829
|
-
const normalizedId = this.normalizeModelId(modelId);
|
|
1830
1951
|
const includeDisabled = options.includeDisabled ?? false;
|
|
1831
1952
|
const torMode = options.torMode ?? false;
|
|
1832
1953
|
const disabledProviders = new Set(
|
|
@@ -1840,9 +1961,7 @@ var ProviderManager = class _ProviderManager {
|
|
|
1840
1961
|
if (torMode && !baseUrl.includes(".onion")) continue;
|
|
1841
1962
|
if (!torMode && (baseUrl.includes(".onion") || isInsecureHttpUrl(baseUrl)))
|
|
1842
1963
|
continue;
|
|
1843
|
-
const match = models.find(
|
|
1844
|
-
(model) => this.normalizeModelId(model.id) === normalizedId
|
|
1845
|
-
);
|
|
1964
|
+
const match = models.find((model) => model.id === modelId);
|
|
1846
1965
|
if (!match?.sats_pricing) continue;
|
|
1847
1966
|
const prompt = match.sats_pricing.prompt;
|
|
1848
1967
|
const completion = match.sats_pricing.completion;
|
|
@@ -1955,7 +2074,1058 @@ var ProviderManager = class _ProviderManager {
|
|
|
1955
2074
|
}
|
|
1956
2075
|
};
|
|
1957
2076
|
|
|
1958
|
-
//
|
|
2077
|
+
// storage/drivers/localStorage.ts
|
|
2078
|
+
var canUseLocalStorage = () => {
|
|
2079
|
+
return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
|
|
2080
|
+
};
|
|
2081
|
+
var isQuotaExceeded = (error) => {
|
|
2082
|
+
const e = error;
|
|
2083
|
+
return !!e && (e?.name === "QuotaExceededError" || e?.code === 22 || e?.code === 1014);
|
|
2084
|
+
};
|
|
2085
|
+
var NON_CRITICAL_KEYS = /* @__PURE__ */ new Set(["modelsFromAllProviders"]);
|
|
2086
|
+
var localStorageDriver = {
|
|
2087
|
+
async getItem(key, defaultValue) {
|
|
2088
|
+
if (!canUseLocalStorage()) return defaultValue;
|
|
2089
|
+
try {
|
|
2090
|
+
const item = window.localStorage.getItem(key);
|
|
2091
|
+
if (item === null) return defaultValue;
|
|
2092
|
+
try {
|
|
2093
|
+
return JSON.parse(item);
|
|
2094
|
+
} catch (parseError) {
|
|
2095
|
+
if (typeof defaultValue === "string") {
|
|
2096
|
+
return item;
|
|
2097
|
+
}
|
|
2098
|
+
throw parseError;
|
|
2099
|
+
}
|
|
2100
|
+
} catch (error) {
|
|
2101
|
+
console.error(`Error retrieving item with key "${key}":`, error);
|
|
2102
|
+
if (canUseLocalStorage()) {
|
|
2103
|
+
try {
|
|
2104
|
+
window.localStorage.removeItem(key);
|
|
2105
|
+
} catch (removeError) {
|
|
2106
|
+
console.error(
|
|
2107
|
+
`Error removing corrupted item with key "${key}":`,
|
|
2108
|
+
removeError
|
|
2109
|
+
);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return defaultValue;
|
|
2113
|
+
}
|
|
2114
|
+
},
|
|
2115
|
+
async setItem(key, value) {
|
|
2116
|
+
if (!canUseLocalStorage()) return;
|
|
2117
|
+
try {
|
|
2118
|
+
window.localStorage.setItem(key, JSON.stringify(value));
|
|
2119
|
+
} catch (error) {
|
|
2120
|
+
if (isQuotaExceeded(error)) {
|
|
2121
|
+
if (NON_CRITICAL_KEYS.has(key)) {
|
|
2122
|
+
console.warn(
|
|
2123
|
+
`Storage quota exceeded; skipping non-critical key "${key}".`
|
|
2124
|
+
);
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
try {
|
|
2128
|
+
window.localStorage.removeItem("modelsFromAllProviders");
|
|
2129
|
+
} catch {
|
|
2130
|
+
}
|
|
2131
|
+
try {
|
|
2132
|
+
window.localStorage.setItem(key, JSON.stringify(value));
|
|
2133
|
+
return;
|
|
2134
|
+
} catch (retryError) {
|
|
2135
|
+
console.warn(
|
|
2136
|
+
`Storage quota exceeded; unable to persist key "${key}" after cleanup attempt.`,
|
|
2137
|
+
retryError
|
|
2138
|
+
);
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
console.error(`Error storing item with key "${key}":`, error);
|
|
2143
|
+
}
|
|
2144
|
+
},
|
|
2145
|
+
async removeItem(key) {
|
|
2146
|
+
if (!canUseLocalStorage()) return;
|
|
2147
|
+
try {
|
|
2148
|
+
window.localStorage.removeItem(key);
|
|
2149
|
+
} catch (error) {
|
|
2150
|
+
console.error(`Error removing item with key "${key}":`, error);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
// storage/drivers/memory.ts
|
|
2156
|
+
var createMemoryDriver = (seed) => {
|
|
2157
|
+
const store = /* @__PURE__ */ new Map();
|
|
2158
|
+
return {
|
|
2159
|
+
async getItem(key, defaultValue) {
|
|
2160
|
+
const item = store.get(key);
|
|
2161
|
+
if (item === void 0) return defaultValue;
|
|
2162
|
+
try {
|
|
2163
|
+
return JSON.parse(item);
|
|
2164
|
+
} catch (parseError) {
|
|
2165
|
+
if (typeof defaultValue === "string") {
|
|
2166
|
+
return item;
|
|
2167
|
+
}
|
|
2168
|
+
throw parseError;
|
|
2169
|
+
}
|
|
2170
|
+
},
|
|
2171
|
+
async setItem(key, value) {
|
|
2172
|
+
store.set(key, JSON.stringify(value));
|
|
2173
|
+
},
|
|
2174
|
+
async removeItem(key) {
|
|
2175
|
+
store.delete(key);
|
|
2176
|
+
}
|
|
2177
|
+
};
|
|
2178
|
+
};
|
|
2179
|
+
|
|
2180
|
+
// storage/drivers/sqlite.ts
|
|
2181
|
+
var isBun = () => {
|
|
2182
|
+
return typeof process.versions.bun !== "undefined";
|
|
2183
|
+
};
|
|
2184
|
+
var createDatabase = (dbPath) => {
|
|
2185
|
+
if (isBun()) {
|
|
2186
|
+
throw new Error(
|
|
2187
|
+
"SQLite driver not supported in Bun. Use createMemoryDriver() instead."
|
|
2188
|
+
);
|
|
2189
|
+
}
|
|
2190
|
+
let Database = null;
|
|
2191
|
+
try {
|
|
2192
|
+
Database = __require("better-sqlite3");
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
throw new Error(
|
|
2195
|
+
`better-sqlite3 is required for sqlite storage. Install it to use sqlite storage. (${error})`
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
return new Database(dbPath);
|
|
2199
|
+
};
|
|
2200
|
+
var createSqliteDriver = (options = {}) => {
|
|
2201
|
+
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2202
|
+
const tableName = options.tableName || "sdk_storage";
|
|
2203
|
+
const db = createDatabase(dbPath);
|
|
2204
|
+
db.exec(
|
|
2205
|
+
`CREATE TABLE IF NOT EXISTS ${tableName} (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
|
|
2206
|
+
);
|
|
2207
|
+
const selectStmt = db.prepare(`SELECT value FROM ${tableName} WHERE key = ?`);
|
|
2208
|
+
const upsertStmt = db.prepare(
|
|
2209
|
+
`INSERT INTO ${tableName} (key, value) VALUES (?, ?)
|
|
2210
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
2211
|
+
);
|
|
2212
|
+
const deleteStmt = db.prepare(`DELETE FROM ${tableName} WHERE key = ?`);
|
|
2213
|
+
return {
|
|
2214
|
+
async getItem(key, defaultValue) {
|
|
2215
|
+
try {
|
|
2216
|
+
const row = selectStmt.get(key);
|
|
2217
|
+
if (!row || typeof row.value !== "string") return defaultValue;
|
|
2218
|
+
try {
|
|
2219
|
+
return JSON.parse(row.value);
|
|
2220
|
+
} catch (parseError) {
|
|
2221
|
+
if (typeof defaultValue === "string") {
|
|
2222
|
+
return row.value;
|
|
2223
|
+
}
|
|
2224
|
+
throw parseError;
|
|
2225
|
+
}
|
|
2226
|
+
} catch (error) {
|
|
2227
|
+
console.error(`SQLite getItem failed for key "${key}":`, error);
|
|
2228
|
+
return defaultValue;
|
|
2229
|
+
}
|
|
2230
|
+
},
|
|
2231
|
+
async setItem(key, value) {
|
|
2232
|
+
try {
|
|
2233
|
+
upsertStmt.run(key, JSON.stringify(value));
|
|
2234
|
+
} catch (error) {
|
|
2235
|
+
console.error(`SQLite setItem failed for key "${key}":`, error);
|
|
2236
|
+
}
|
|
2237
|
+
},
|
|
2238
|
+
async removeItem(key) {
|
|
2239
|
+
try {
|
|
2240
|
+
deleteStmt.run(key);
|
|
2241
|
+
} catch (error) {
|
|
2242
|
+
console.error(`SQLite removeItem failed for key "${key}":`, error);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
};
|
|
2246
|
+
};
|
|
2247
|
+
|
|
2248
|
+
// storage/keys.ts
|
|
2249
|
+
var SDK_STORAGE_KEYS = {
|
|
2250
|
+
MODELS_FROM_ALL_PROVIDERS: "modelsFromAllProviders",
|
|
2251
|
+
LAST_USED_MODEL: "lastUsedModel",
|
|
2252
|
+
BASE_URLS_LIST: "base_urls_list",
|
|
2253
|
+
DISABLED_PROVIDERS: "disabled_providers",
|
|
2254
|
+
MINTS_FROM_ALL_PROVIDERS: "mints_from_all_providers",
|
|
2255
|
+
INFO_FROM_ALL_PROVIDERS: "info_from_all_providers",
|
|
2256
|
+
LAST_MODELS_UPDATE: "lastModelsUpdate",
|
|
2257
|
+
LAST_BASE_URLS_UPDATE: "lastBaseUrlsUpdate",
|
|
2258
|
+
LOCAL_CASHU_TOKENS: "local_cashu_tokens",
|
|
2259
|
+
API_KEYS: "api_keys",
|
|
2260
|
+
CHILD_KEYS: "child_keys",
|
|
2261
|
+
ROUTSTR21_MODELS: "routstr21Models",
|
|
2262
|
+
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
2263
|
+
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
2264
|
+
USAGE_TRACKING: "usage_tracking",
|
|
2265
|
+
CLIENT_IDS: "client_ids"
|
|
2266
|
+
};
|
|
2267
|
+
|
|
2268
|
+
// storage/usageTracking/indexedDB.ts
|
|
2269
|
+
var DEFAULT_DB_NAME = "routstr-sdk";
|
|
2270
|
+
var DEFAULT_STORE_NAME = "usage_tracking";
|
|
2271
|
+
var MIGRATION_MARKER_KEY = "usage_tracking_migration_v1";
|
|
2272
|
+
var isBrowser = typeof indexedDB !== "undefined";
|
|
2273
|
+
var normalizeBaseUrl = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2274
|
+
var openDatabase = (dbName, storeName) => {
|
|
2275
|
+
if (!isBrowser) {
|
|
2276
|
+
return Promise.reject(new Error("IndexedDB is not available"));
|
|
2277
|
+
}
|
|
2278
|
+
return new Promise((resolve, reject) => {
|
|
2279
|
+
const request = indexedDB.open(dbName, 1);
|
|
2280
|
+
request.onupgradeneeded = () => {
|
|
2281
|
+
const db = request.result;
|
|
2282
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
2283
|
+
const store = db.createObjectStore(storeName, { keyPath: "id" });
|
|
2284
|
+
store.createIndex("timestamp", "timestamp", { unique: false });
|
|
2285
|
+
store.createIndex("modelId", "modelId", { unique: false });
|
|
2286
|
+
store.createIndex("baseUrl", "baseUrl", { unique: false });
|
|
2287
|
+
store.createIndex("sessionId", "sessionId", { unique: false });
|
|
2288
|
+
store.createIndex("client", "client", { unique: false });
|
|
2289
|
+
}
|
|
2290
|
+
};
|
|
2291
|
+
request.onsuccess = () => resolve(request.result);
|
|
2292
|
+
request.onerror = () => reject(request.error);
|
|
2293
|
+
});
|
|
2294
|
+
};
|
|
2295
|
+
var matchesFilters = (entry, options = {}) => {
|
|
2296
|
+
if (typeof options.before === "number" && entry.timestamp >= options.before) {
|
|
2297
|
+
return false;
|
|
2298
|
+
}
|
|
2299
|
+
if (typeof options.after === "number" && entry.timestamp <= options.after) {
|
|
2300
|
+
return false;
|
|
2301
|
+
}
|
|
2302
|
+
if (options.modelId && entry.modelId !== options.modelId) {
|
|
2303
|
+
return false;
|
|
2304
|
+
}
|
|
2305
|
+
if (options.baseUrl && normalizeBaseUrl(entry.baseUrl) !== normalizeBaseUrl(options.baseUrl)) {
|
|
2306
|
+
return false;
|
|
2307
|
+
}
|
|
2308
|
+
if (options.sessionId && entry.sessionId !== options.sessionId) {
|
|
2309
|
+
return false;
|
|
2310
|
+
}
|
|
2311
|
+
if (options.client && entry.client !== options.client) {
|
|
2312
|
+
return false;
|
|
2313
|
+
}
|
|
2314
|
+
return true;
|
|
2315
|
+
};
|
|
2316
|
+
var createIndexedDBUsageTrackingDriver = (options = {}) => {
|
|
2317
|
+
const dbName = options.dbName || DEFAULT_DB_NAME;
|
|
2318
|
+
const storeName = options.storeName || DEFAULT_STORE_NAME;
|
|
2319
|
+
const legacyStorageDriver = options.legacyStorageDriver;
|
|
2320
|
+
let dbPromise = null;
|
|
2321
|
+
let migrationPromise = null;
|
|
2322
|
+
const getDb = () => {
|
|
2323
|
+
if (!dbPromise) {
|
|
2324
|
+
dbPromise = openDatabase(dbName, storeName);
|
|
2325
|
+
}
|
|
2326
|
+
return dbPromise;
|
|
2327
|
+
};
|
|
2328
|
+
const putMany = async (entries) => {
|
|
2329
|
+
if (entries.length === 0) return;
|
|
2330
|
+
const db = await getDb();
|
|
2331
|
+
await new Promise((resolve, reject) => {
|
|
2332
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
2333
|
+
const store = tx.objectStore(storeName);
|
|
2334
|
+
for (const entry of entries) {
|
|
2335
|
+
store.put({ ...entry, baseUrl: normalizeBaseUrl(entry.baseUrl) });
|
|
2336
|
+
}
|
|
2337
|
+
tx.oncomplete = () => resolve();
|
|
2338
|
+
tx.onerror = () => reject(tx.error);
|
|
2339
|
+
});
|
|
2340
|
+
};
|
|
2341
|
+
const ensureMigrated = async () => {
|
|
2342
|
+
if (!legacyStorageDriver) return;
|
|
2343
|
+
if (!migrationPromise) {
|
|
2344
|
+
migrationPromise = (async () => {
|
|
2345
|
+
const migrated = await legacyStorageDriver.getItem(
|
|
2346
|
+
MIGRATION_MARKER_KEY,
|
|
2347
|
+
false
|
|
2348
|
+
);
|
|
2349
|
+
if (migrated) return;
|
|
2350
|
+
const legacyEntries = await legacyStorageDriver.getItem(
|
|
2351
|
+
SDK_STORAGE_KEYS.USAGE_TRACKING,
|
|
2352
|
+
[]
|
|
2353
|
+
);
|
|
2354
|
+
if (legacyEntries.length > 0) {
|
|
2355
|
+
await putMany(legacyEntries);
|
|
2356
|
+
await legacyStorageDriver.removeItem(SDK_STORAGE_KEYS.USAGE_TRACKING);
|
|
2357
|
+
}
|
|
2358
|
+
await legacyStorageDriver.setItem(MIGRATION_MARKER_KEY, true);
|
|
2359
|
+
})();
|
|
2360
|
+
}
|
|
2361
|
+
await migrationPromise;
|
|
2362
|
+
};
|
|
2363
|
+
return {
|
|
2364
|
+
async migrate() {
|
|
2365
|
+
await ensureMigrated();
|
|
2366
|
+
},
|
|
2367
|
+
async append(entry) {
|
|
2368
|
+
await ensureMigrated();
|
|
2369
|
+
await putMany([entry]);
|
|
2370
|
+
},
|
|
2371
|
+
async appendMany(entries) {
|
|
2372
|
+
await ensureMigrated();
|
|
2373
|
+
await putMany(entries);
|
|
2374
|
+
},
|
|
2375
|
+
async list(options2 = {}) {
|
|
2376
|
+
await ensureMigrated();
|
|
2377
|
+
const db = await getDb();
|
|
2378
|
+
return new Promise((resolve, reject) => {
|
|
2379
|
+
const tx = db.transaction(storeName, "readonly");
|
|
2380
|
+
const store = tx.objectStore(storeName);
|
|
2381
|
+
const index = store.index("timestamp");
|
|
2382
|
+
const direction = "prev";
|
|
2383
|
+
const request = index.openCursor(null, direction);
|
|
2384
|
+
const results = [];
|
|
2385
|
+
const limit = options2.limit;
|
|
2386
|
+
request.onsuccess = () => {
|
|
2387
|
+
const cursor = request.result;
|
|
2388
|
+
if (!cursor) {
|
|
2389
|
+
resolve(results);
|
|
2390
|
+
return;
|
|
2391
|
+
}
|
|
2392
|
+
const value = cursor.value;
|
|
2393
|
+
if (matchesFilters(value, options2)) {
|
|
2394
|
+
results.push(value);
|
|
2395
|
+
if (typeof limit === "number" && results.length >= limit) {
|
|
2396
|
+
resolve(results);
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
cursor.continue();
|
|
2401
|
+
};
|
|
2402
|
+
request.onerror = () => reject(request.error);
|
|
2403
|
+
});
|
|
2404
|
+
},
|
|
2405
|
+
async count(options2 = {}) {
|
|
2406
|
+
const results = await this.list(options2);
|
|
2407
|
+
return results.length;
|
|
2408
|
+
},
|
|
2409
|
+
async deleteOlderThan(timestamp) {
|
|
2410
|
+
await ensureMigrated();
|
|
2411
|
+
const db = await getDb();
|
|
2412
|
+
return new Promise((resolve, reject) => {
|
|
2413
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
2414
|
+
const store = tx.objectStore(storeName);
|
|
2415
|
+
const index = store.index("timestamp");
|
|
2416
|
+
const range = IDBKeyRange.upperBound(timestamp, true);
|
|
2417
|
+
const request = index.openCursor(range);
|
|
2418
|
+
let deleted = 0;
|
|
2419
|
+
request.onsuccess = () => {
|
|
2420
|
+
const cursor = request.result;
|
|
2421
|
+
if (!cursor) {
|
|
2422
|
+
resolve(deleted);
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
deleted += 1;
|
|
2426
|
+
cursor.delete();
|
|
2427
|
+
cursor.continue();
|
|
2428
|
+
};
|
|
2429
|
+
request.onerror = () => reject(request.error);
|
|
2430
|
+
});
|
|
2431
|
+
},
|
|
2432
|
+
async clear() {
|
|
2433
|
+
await ensureMigrated();
|
|
2434
|
+
const db = await getDb();
|
|
2435
|
+
await new Promise((resolve, reject) => {
|
|
2436
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
2437
|
+
tx.objectStore(storeName).clear();
|
|
2438
|
+
tx.oncomplete = () => resolve();
|
|
2439
|
+
tx.onerror = () => reject(tx.error);
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
};
|
|
2444
|
+
|
|
2445
|
+
// storage/usageTracking/sqlite.ts
|
|
2446
|
+
var MIGRATION_MARKER_KEY2 = "usage_tracking_migration_v1";
|
|
2447
|
+
var normalizeBaseUrl2 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2448
|
+
var isBun2 = () => {
|
|
2449
|
+
return typeof process.versions.bun !== "undefined";
|
|
2450
|
+
};
|
|
2451
|
+
var createDatabase2 = (dbPath) => {
|
|
2452
|
+
if (isBun2()) {
|
|
2453
|
+
throw new Error(
|
|
2454
|
+
"SQLite driver not supported in Bun. Use createMemoryDriver() instead."
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2457
|
+
let Database = null;
|
|
2458
|
+
try {
|
|
2459
|
+
Database = __require("better-sqlite3");
|
|
2460
|
+
} catch (error) {
|
|
2461
|
+
throw new Error(
|
|
2462
|
+
`better-sqlite3 is required for sqlite usage tracking. Install it to use sqlite storage. (${error})`
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
return new Database(dbPath);
|
|
2466
|
+
};
|
|
2467
|
+
var buildWhereClause = (options = {}) => {
|
|
2468
|
+
const clauses = [];
|
|
2469
|
+
const params = [];
|
|
2470
|
+
if (typeof options.before === "number") {
|
|
2471
|
+
clauses.push("timestamp < ?");
|
|
2472
|
+
params.push(options.before);
|
|
2473
|
+
}
|
|
2474
|
+
if (typeof options.after === "number") {
|
|
2475
|
+
clauses.push("timestamp > ?");
|
|
2476
|
+
params.push(options.after);
|
|
2477
|
+
}
|
|
2478
|
+
if (options.modelId) {
|
|
2479
|
+
clauses.push("model_id = ?");
|
|
2480
|
+
params.push(options.modelId);
|
|
2481
|
+
}
|
|
2482
|
+
if (options.baseUrl) {
|
|
2483
|
+
clauses.push("base_url = ?");
|
|
2484
|
+
params.push(normalizeBaseUrl2(options.baseUrl));
|
|
2485
|
+
}
|
|
2486
|
+
if (options.sessionId) {
|
|
2487
|
+
clauses.push("session_id = ?");
|
|
2488
|
+
params.push(options.sessionId);
|
|
2489
|
+
}
|
|
2490
|
+
if (options.client) {
|
|
2491
|
+
clauses.push("client = ?");
|
|
2492
|
+
params.push(options.client);
|
|
2493
|
+
}
|
|
2494
|
+
return {
|
|
2495
|
+
sql: clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "",
|
|
2496
|
+
params
|
|
2497
|
+
};
|
|
2498
|
+
};
|
|
2499
|
+
var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
2500
|
+
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2501
|
+
const tableName = options.tableName || "usage_tracking";
|
|
2502
|
+
const db = createDatabase2(dbPath);
|
|
2503
|
+
const legacyStorageDriver = options.legacyStorageDriver;
|
|
2504
|
+
db.exec(`
|
|
2505
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
2506
|
+
id TEXT PRIMARY KEY,
|
|
2507
|
+
timestamp INTEGER NOT NULL,
|
|
2508
|
+
model_id TEXT NOT NULL,
|
|
2509
|
+
base_url TEXT NOT NULL,
|
|
2510
|
+
request_id TEXT NOT NULL,
|
|
2511
|
+
cost REAL NOT NULL,
|
|
2512
|
+
sats_cost REAL NOT NULL,
|
|
2513
|
+
prompt_tokens INTEGER NOT NULL,
|
|
2514
|
+
completion_tokens INTEGER NOT NULL,
|
|
2515
|
+
total_tokens INTEGER NOT NULL,
|
|
2516
|
+
client TEXT,
|
|
2517
|
+
session_id TEXT,
|
|
2518
|
+
tags TEXT
|
|
2519
|
+
);
|
|
2520
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp);
|
|
2521
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id);
|
|
2522
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url);
|
|
2523
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_session_id ON ${tableName}(session_id);
|
|
2524
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_client ON ${tableName}(client);
|
|
2525
|
+
`);
|
|
2526
|
+
const insertStmt = db.prepare(`
|
|
2527
|
+
INSERT OR REPLACE INTO ${tableName} (
|
|
2528
|
+
id, timestamp, model_id, base_url, request_id,
|
|
2529
|
+
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
2530
|
+
client, session_id, tags
|
|
2531
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2532
|
+
`);
|
|
2533
|
+
let migrationComplete = false;
|
|
2534
|
+
const appendOne = (entry) => {
|
|
2535
|
+
insertStmt.run(
|
|
2536
|
+
entry.id,
|
|
2537
|
+
entry.timestamp,
|
|
2538
|
+
entry.modelId,
|
|
2539
|
+
normalizeBaseUrl2(entry.baseUrl),
|
|
2540
|
+
entry.requestId,
|
|
2541
|
+
entry.cost,
|
|
2542
|
+
entry.satsCost,
|
|
2543
|
+
entry.promptTokens,
|
|
2544
|
+
entry.completionTokens,
|
|
2545
|
+
entry.totalTokens,
|
|
2546
|
+
entry.client ?? null,
|
|
2547
|
+
entry.sessionId ?? null,
|
|
2548
|
+
JSON.stringify(entry.tags ?? [])
|
|
2549
|
+
);
|
|
2550
|
+
};
|
|
2551
|
+
const ensureMigrated = async () => {
|
|
2552
|
+
if (!legacyStorageDriver || migrationComplete) return;
|
|
2553
|
+
const migrated = await legacyStorageDriver.getItem(
|
|
2554
|
+
MIGRATION_MARKER_KEY2,
|
|
2555
|
+
false
|
|
2556
|
+
);
|
|
2557
|
+
if (migrated) {
|
|
2558
|
+
migrationComplete = true;
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
const legacyEntries = await legacyStorageDriver.getItem(
|
|
2562
|
+
SDK_STORAGE_KEYS.USAGE_TRACKING,
|
|
2563
|
+
[]
|
|
2564
|
+
);
|
|
2565
|
+
for (const entry of legacyEntries) {
|
|
2566
|
+
appendOne(entry);
|
|
2567
|
+
}
|
|
2568
|
+
if (legacyEntries.length > 0) {
|
|
2569
|
+
await legacyStorageDriver.removeItem(SDK_STORAGE_KEYS.USAGE_TRACKING);
|
|
2570
|
+
}
|
|
2571
|
+
await legacyStorageDriver.setItem(MIGRATION_MARKER_KEY2, true);
|
|
2572
|
+
migrationComplete = true;
|
|
2573
|
+
};
|
|
2574
|
+
const mapRow = (row) => ({
|
|
2575
|
+
id: row.id,
|
|
2576
|
+
timestamp: row.timestamp,
|
|
2577
|
+
modelId: row.model_id,
|
|
2578
|
+
baseUrl: row.base_url,
|
|
2579
|
+
requestId: row.request_id,
|
|
2580
|
+
cost: row.cost,
|
|
2581
|
+
satsCost: row.sats_cost,
|
|
2582
|
+
promptTokens: row.prompt_tokens,
|
|
2583
|
+
completionTokens: row.completion_tokens,
|
|
2584
|
+
totalTokens: row.total_tokens,
|
|
2585
|
+
client: row.client ?? void 0,
|
|
2586
|
+
sessionId: row.session_id ?? void 0,
|
|
2587
|
+
tags: typeof row.tags === "string" ? JSON.parse(row.tags) : void 0
|
|
2588
|
+
});
|
|
2589
|
+
return {
|
|
2590
|
+
async migrate() {
|
|
2591
|
+
await ensureMigrated();
|
|
2592
|
+
},
|
|
2593
|
+
async append(entry) {
|
|
2594
|
+
await ensureMigrated();
|
|
2595
|
+
appendOne(entry);
|
|
2596
|
+
},
|
|
2597
|
+
async appendMany(entries) {
|
|
2598
|
+
await ensureMigrated();
|
|
2599
|
+
for (const entry of entries) {
|
|
2600
|
+
appendOne(entry);
|
|
2601
|
+
}
|
|
2602
|
+
},
|
|
2603
|
+
async list(options2 = {}) {
|
|
2604
|
+
await ensureMigrated();
|
|
2605
|
+
const { sql, params } = buildWhereClause(options2);
|
|
2606
|
+
const limitSql = typeof options2.limit === "number" ? " LIMIT ?" : "";
|
|
2607
|
+
const stmt = db.prepare(
|
|
2608
|
+
`SELECT * FROM ${tableName} ${sql} ORDER BY timestamp DESC${limitSql}`
|
|
2609
|
+
);
|
|
2610
|
+
const rows = stmt.all(
|
|
2611
|
+
...typeof options2.limit === "number" ? [...params, options2.limit] : params
|
|
2612
|
+
);
|
|
2613
|
+
return rows.map(mapRow);
|
|
2614
|
+
},
|
|
2615
|
+
async count(options2 = {}) {
|
|
2616
|
+
await ensureMigrated();
|
|
2617
|
+
const { sql, params } = buildWhereClause(options2);
|
|
2618
|
+
const stmt = db.prepare(`SELECT COUNT(*) as count FROM ${tableName} ${sql}`);
|
|
2619
|
+
const row = stmt.get(...params);
|
|
2620
|
+
return Number(row?.count ?? 0);
|
|
2621
|
+
},
|
|
2622
|
+
async deleteOlderThan(timestamp) {
|
|
2623
|
+
await ensureMigrated();
|
|
2624
|
+
const stmt = db.prepare(`DELETE FROM ${tableName} WHERE timestamp < ?`);
|
|
2625
|
+
const result = stmt.run(timestamp);
|
|
2626
|
+
return result.changes;
|
|
2627
|
+
},
|
|
2628
|
+
async clear() {
|
|
2629
|
+
await ensureMigrated();
|
|
2630
|
+
db.prepare(`DELETE FROM ${tableName}`).run();
|
|
2631
|
+
}
|
|
2632
|
+
};
|
|
2633
|
+
};
|
|
2634
|
+
|
|
2635
|
+
// storage/usageTracking/memory.ts
|
|
2636
|
+
var normalizeBaseUrl3 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2637
|
+
var matchesFilters2 = (entry, options = {}) => {
|
|
2638
|
+
if (typeof options.before === "number" && entry.timestamp >= options.before) {
|
|
2639
|
+
return false;
|
|
2640
|
+
}
|
|
2641
|
+
if (typeof options.after === "number" && entry.timestamp <= options.after) {
|
|
2642
|
+
return false;
|
|
2643
|
+
}
|
|
2644
|
+
if (options.modelId && entry.modelId !== options.modelId) {
|
|
2645
|
+
return false;
|
|
2646
|
+
}
|
|
2647
|
+
if (options.baseUrl && normalizeBaseUrl3(entry.baseUrl) !== normalizeBaseUrl3(options.baseUrl)) {
|
|
2648
|
+
return false;
|
|
2649
|
+
}
|
|
2650
|
+
if (options.sessionId && entry.sessionId !== options.sessionId) {
|
|
2651
|
+
return false;
|
|
2652
|
+
}
|
|
2653
|
+
if (options.client && entry.client !== options.client) {
|
|
2654
|
+
return false;
|
|
2655
|
+
}
|
|
2656
|
+
return true;
|
|
2657
|
+
};
|
|
2658
|
+
var createMemoryUsageTrackingDriver = (seed = []) => {
|
|
2659
|
+
const store = /* @__PURE__ */ new Map();
|
|
2660
|
+
for (const entry of seed) {
|
|
2661
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl3(entry.baseUrl) });
|
|
2662
|
+
}
|
|
2663
|
+
return {
|
|
2664
|
+
async migrate() {
|
|
2665
|
+
return;
|
|
2666
|
+
},
|
|
2667
|
+
async append(entry) {
|
|
2668
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl3(entry.baseUrl) });
|
|
2669
|
+
},
|
|
2670
|
+
async appendMany(entries) {
|
|
2671
|
+
for (const entry of entries) {
|
|
2672
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl3(entry.baseUrl) });
|
|
2673
|
+
}
|
|
2674
|
+
},
|
|
2675
|
+
async list(options = {}) {
|
|
2676
|
+
const entries = [...store.values()].filter((entry) => matchesFilters2(entry, options)).sort((a, b) => b.timestamp - a.timestamp);
|
|
2677
|
+
if (typeof options.limit === "number") {
|
|
2678
|
+
return entries.slice(0, options.limit);
|
|
2679
|
+
}
|
|
2680
|
+
return entries;
|
|
2681
|
+
},
|
|
2682
|
+
async count(options = {}) {
|
|
2683
|
+
return (await this.list(options)).length;
|
|
2684
|
+
},
|
|
2685
|
+
async deleteOlderThan(timestamp) {
|
|
2686
|
+
let deleted = 0;
|
|
2687
|
+
for (const [id, entry] of store.entries()) {
|
|
2688
|
+
if (entry.timestamp < timestamp) {
|
|
2689
|
+
store.delete(id);
|
|
2690
|
+
deleted += 1;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
return deleted;
|
|
2694
|
+
},
|
|
2695
|
+
async clear() {
|
|
2696
|
+
store.clear();
|
|
2697
|
+
}
|
|
2698
|
+
};
|
|
2699
|
+
};
|
|
2700
|
+
var normalizeBaseUrl4 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2701
|
+
var getCashuTokenBalance = (token) => {
|
|
2702
|
+
try {
|
|
2703
|
+
const decoded = cashuTs.getDecodedToken(token);
|
|
2704
|
+
const unitDivisor = decoded.unit === "msat" ? 1e3 : 1;
|
|
2705
|
+
let sum = 0;
|
|
2706
|
+
for (const proof of decoded.proofs) {
|
|
2707
|
+
sum += proof.amount / unitDivisor;
|
|
2708
|
+
}
|
|
2709
|
+
return sum;
|
|
2710
|
+
} catch {
|
|
2711
|
+
return 0;
|
|
2712
|
+
}
|
|
2713
|
+
};
|
|
2714
|
+
var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
2715
|
+
modelsFromAllProviders: {},
|
|
2716
|
+
lastUsedModel: null,
|
|
2717
|
+
baseUrlsList: [],
|
|
2718
|
+
lastBaseUrlsUpdate: null,
|
|
2719
|
+
disabledProviders: [],
|
|
2720
|
+
mintsFromAllProviders: {},
|
|
2721
|
+
infoFromAllProviders: {},
|
|
2722
|
+
lastModelsUpdate: {},
|
|
2723
|
+
cachedTokens: [],
|
|
2724
|
+
apiKeys: [],
|
|
2725
|
+
childKeys: [],
|
|
2726
|
+
routstr21Models: [],
|
|
2727
|
+
lastRoutstr21ModelsUpdate: null,
|
|
2728
|
+
cachedReceiveTokens: [],
|
|
2729
|
+
clientIds: [],
|
|
2730
|
+
setModelsFromAllProviders: (value) => {
|
|
2731
|
+
const normalized = {};
|
|
2732
|
+
for (const [baseUrl, models] of Object.entries(value)) {
|
|
2733
|
+
normalized[normalizeBaseUrl4(baseUrl)] = models;
|
|
2734
|
+
}
|
|
2735
|
+
void driver.setItem(
|
|
2736
|
+
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
2737
|
+
normalized
|
|
2738
|
+
);
|
|
2739
|
+
set({ modelsFromAllProviders: normalized });
|
|
2740
|
+
},
|
|
2741
|
+
setLastUsedModel: (value) => {
|
|
2742
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, value);
|
|
2743
|
+
set({ lastUsedModel: value });
|
|
2744
|
+
},
|
|
2745
|
+
setBaseUrlsList: (value) => {
|
|
2746
|
+
const normalized = value.map((url) => normalizeBaseUrl4(url));
|
|
2747
|
+
void driver.setItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, normalized);
|
|
2748
|
+
set({ baseUrlsList: normalized });
|
|
2749
|
+
},
|
|
2750
|
+
setBaseUrlsLastUpdate: (value) => {
|
|
2751
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, value);
|
|
2752
|
+
set({ lastBaseUrlsUpdate: value });
|
|
2753
|
+
},
|
|
2754
|
+
setDisabledProviders: (value) => {
|
|
2755
|
+
const normalized = value.map((url) => normalizeBaseUrl4(url));
|
|
2756
|
+
void driver.setItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, normalized);
|
|
2757
|
+
set({ disabledProviders: normalized });
|
|
2758
|
+
},
|
|
2759
|
+
setMintsFromAllProviders: (value) => {
|
|
2760
|
+
const normalized = {};
|
|
2761
|
+
for (const [baseUrl, mints] of Object.entries(value)) {
|
|
2762
|
+
normalized[normalizeBaseUrl4(baseUrl)] = mints.map(
|
|
2763
|
+
(mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint
|
|
2764
|
+
);
|
|
2765
|
+
}
|
|
2766
|
+
void driver.setItem(
|
|
2767
|
+
SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS,
|
|
2768
|
+
normalized
|
|
2769
|
+
);
|
|
2770
|
+
set({ mintsFromAllProviders: normalized });
|
|
2771
|
+
},
|
|
2772
|
+
setInfoFromAllProviders: (value) => {
|
|
2773
|
+
const normalized = {};
|
|
2774
|
+
for (const [baseUrl, info] of Object.entries(value)) {
|
|
2775
|
+
normalized[normalizeBaseUrl4(baseUrl)] = info;
|
|
2776
|
+
}
|
|
2777
|
+
void driver.setItem(SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS, normalized);
|
|
2778
|
+
set({ infoFromAllProviders: normalized });
|
|
2779
|
+
},
|
|
2780
|
+
setLastModelsUpdate: (value) => {
|
|
2781
|
+
const normalized = {};
|
|
2782
|
+
for (const [baseUrl, timestamp] of Object.entries(value)) {
|
|
2783
|
+
normalized[normalizeBaseUrl4(baseUrl)] = timestamp;
|
|
2784
|
+
}
|
|
2785
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE, normalized);
|
|
2786
|
+
set({ lastModelsUpdate: normalized });
|
|
2787
|
+
},
|
|
2788
|
+
setCachedTokens: (value) => {
|
|
2789
|
+
set((state) => {
|
|
2790
|
+
const updates = typeof value === "function" ? value(state.cachedTokens) : value;
|
|
2791
|
+
const normalized = updates.map((entry) => ({
|
|
2792
|
+
...entry,
|
|
2793
|
+
baseUrl: normalizeBaseUrl4(entry.baseUrl),
|
|
2794
|
+
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
2795
|
+
lastUsed: entry.lastUsed ?? null
|
|
2796
|
+
}));
|
|
2797
|
+
void driver.setItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, normalized);
|
|
2798
|
+
return { cachedTokens: normalized };
|
|
2799
|
+
});
|
|
2800
|
+
},
|
|
2801
|
+
setApiKeys: (value) => {
|
|
2802
|
+
set((state) => {
|
|
2803
|
+
const updates = typeof value === "function" ? value(state.apiKeys) : value;
|
|
2804
|
+
const normalized = updates.map((entry) => ({
|
|
2805
|
+
...entry,
|
|
2806
|
+
baseUrl: normalizeBaseUrl4(entry.baseUrl),
|
|
2807
|
+
balance: entry.balance ?? 0,
|
|
2808
|
+
lastUsed: entry.lastUsed ?? null
|
|
2809
|
+
}));
|
|
2810
|
+
void driver.setItem(SDK_STORAGE_KEYS.API_KEYS, normalized);
|
|
2811
|
+
return { apiKeys: normalized };
|
|
2812
|
+
});
|
|
2813
|
+
},
|
|
2814
|
+
setChildKeys: (value) => {
|
|
2815
|
+
set((state) => {
|
|
2816
|
+
const updates = typeof value === "function" ? value(state.childKeys) : value;
|
|
2817
|
+
const normalized = updates.map((entry) => ({
|
|
2818
|
+
parentBaseUrl: normalizeBaseUrl4(entry.parentBaseUrl),
|
|
2819
|
+
childKey: entry.childKey,
|
|
2820
|
+
balance: entry.balance ?? 0,
|
|
2821
|
+
balanceLimit: entry.balanceLimit,
|
|
2822
|
+
validityDate: entry.validityDate,
|
|
2823
|
+
createdAt: entry.createdAt ?? Date.now()
|
|
2824
|
+
}));
|
|
2825
|
+
void driver.setItem(SDK_STORAGE_KEYS.CHILD_KEYS, normalized);
|
|
2826
|
+
return { childKeys: normalized };
|
|
2827
|
+
});
|
|
2828
|
+
},
|
|
2829
|
+
setRoutstr21Models: (value) => {
|
|
2830
|
+
void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, value);
|
|
2831
|
+
set({ routstr21Models: value });
|
|
2832
|
+
},
|
|
2833
|
+
setRoutstr21ModelsLastUpdate: (value) => {
|
|
2834
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE, value);
|
|
2835
|
+
set({ lastRoutstr21ModelsUpdate: value });
|
|
2836
|
+
},
|
|
2837
|
+
setCachedReceiveTokens: (value) => {
|
|
2838
|
+
const normalized = value.map((entry) => ({
|
|
2839
|
+
token: entry.token,
|
|
2840
|
+
amount: entry.amount,
|
|
2841
|
+
unit: entry.unit || "sat",
|
|
2842
|
+
createdAt: entry.createdAt ?? Date.now()
|
|
2843
|
+
}));
|
|
2844
|
+
void driver.setItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, normalized);
|
|
2845
|
+
set({ cachedReceiveTokens: normalized });
|
|
2846
|
+
},
|
|
2847
|
+
setClientIds: (value) => {
|
|
2848
|
+
set((state) => {
|
|
2849
|
+
const updates = typeof value === "function" ? value(state.clientIds) : value;
|
|
2850
|
+
const normalized = updates.map((entry) => ({
|
|
2851
|
+
...entry,
|
|
2852
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
2853
|
+
lastUsed: entry.lastUsed ?? null
|
|
2854
|
+
}));
|
|
2855
|
+
void driver.setItem(SDK_STORAGE_KEYS.CLIENT_IDS, normalized);
|
|
2856
|
+
return { clientIds: normalized };
|
|
2857
|
+
});
|
|
2858
|
+
}
|
|
2859
|
+
}));
|
|
2860
|
+
var hydrateStoreFromDriver = async (store, driver) => {
|
|
2861
|
+
const [
|
|
2862
|
+
rawModels,
|
|
2863
|
+
lastUsedModel,
|
|
2864
|
+
rawBaseUrls,
|
|
2865
|
+
lastBaseUrlsUpdate,
|
|
2866
|
+
rawDisabledProviders,
|
|
2867
|
+
rawMints,
|
|
2868
|
+
rawInfo,
|
|
2869
|
+
rawLastModelsUpdate,
|
|
2870
|
+
rawCachedTokens,
|
|
2871
|
+
rawApiKeys,
|
|
2872
|
+
rawChildKeys,
|
|
2873
|
+
rawRoutstr21Models,
|
|
2874
|
+
rawLastRoutstr21ModelsUpdate,
|
|
2875
|
+
rawCachedReceiveTokens,
|
|
2876
|
+
rawClientIds
|
|
2877
|
+
] = await Promise.all([
|
|
2878
|
+
driver.getItem(
|
|
2879
|
+
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
2880
|
+
{}
|
|
2881
|
+
),
|
|
2882
|
+
driver.getItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, null),
|
|
2883
|
+
driver.getItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, []),
|
|
2884
|
+
driver.getItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, null),
|
|
2885
|
+
driver.getItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, []),
|
|
2886
|
+
driver.getItem(
|
|
2887
|
+
SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS,
|
|
2888
|
+
{}
|
|
2889
|
+
),
|
|
2890
|
+
driver.getItem(
|
|
2891
|
+
SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS,
|
|
2892
|
+
{}
|
|
2893
|
+
),
|
|
2894
|
+
driver.getItem(
|
|
2895
|
+
SDK_STORAGE_KEYS.LAST_MODELS_UPDATE,
|
|
2896
|
+
{}
|
|
2897
|
+
),
|
|
2898
|
+
driver.getItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, []),
|
|
2899
|
+
driver.getItem(SDK_STORAGE_KEYS.API_KEYS, []),
|
|
2900
|
+
driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, []),
|
|
2901
|
+
driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
|
|
2902
|
+
driver.getItem(
|
|
2903
|
+
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
2904
|
+
null
|
|
2905
|
+
),
|
|
2906
|
+
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
|
|
2907
|
+
driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, [])
|
|
2908
|
+
]);
|
|
2909
|
+
const modelsFromAllProviders = Object.fromEntries(
|
|
2910
|
+
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
2911
|
+
normalizeBaseUrl4(baseUrl),
|
|
2912
|
+
models
|
|
2913
|
+
])
|
|
2914
|
+
);
|
|
2915
|
+
const baseUrlsList = rawBaseUrls.map((url) => normalizeBaseUrl4(url));
|
|
2916
|
+
const disabledProviders = rawDisabledProviders.map(
|
|
2917
|
+
(url) => normalizeBaseUrl4(url)
|
|
2918
|
+
);
|
|
2919
|
+
const mintsFromAllProviders = Object.fromEntries(
|
|
2920
|
+
Object.entries(rawMints).map(([baseUrl, mints]) => [
|
|
2921
|
+
normalizeBaseUrl4(baseUrl),
|
|
2922
|
+
mints.map((mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint)
|
|
2923
|
+
])
|
|
2924
|
+
);
|
|
2925
|
+
const infoFromAllProviders = Object.fromEntries(
|
|
2926
|
+
Object.entries(rawInfo).map(([baseUrl, info]) => [
|
|
2927
|
+
normalizeBaseUrl4(baseUrl),
|
|
2928
|
+
info
|
|
2929
|
+
])
|
|
2930
|
+
);
|
|
2931
|
+
const lastModelsUpdate = Object.fromEntries(
|
|
2932
|
+
Object.entries(rawLastModelsUpdate).map(([baseUrl, timestamp]) => [
|
|
2933
|
+
normalizeBaseUrl4(baseUrl),
|
|
2934
|
+
timestamp
|
|
2935
|
+
])
|
|
2936
|
+
);
|
|
2937
|
+
const cachedTokens = rawCachedTokens.map((entry) => ({
|
|
2938
|
+
...entry,
|
|
2939
|
+
baseUrl: normalizeBaseUrl4(entry.baseUrl),
|
|
2940
|
+
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
2941
|
+
lastUsed: entry.lastUsed ?? null
|
|
2942
|
+
}));
|
|
2943
|
+
const apiKeys = rawApiKeys.map((entry) => ({
|
|
2944
|
+
...entry,
|
|
2945
|
+
baseUrl: normalizeBaseUrl4(entry.baseUrl),
|
|
2946
|
+
balance: entry.balance ?? 0,
|
|
2947
|
+
lastUsed: entry.lastUsed ?? null
|
|
2948
|
+
}));
|
|
2949
|
+
const childKeys = rawChildKeys.map((entry) => ({
|
|
2950
|
+
parentBaseUrl: normalizeBaseUrl4(entry.parentBaseUrl),
|
|
2951
|
+
childKey: entry.childKey,
|
|
2952
|
+
balance: entry.balance ?? 0,
|
|
2953
|
+
balanceLimit: entry.balanceLimit,
|
|
2954
|
+
validityDate: entry.validityDate,
|
|
2955
|
+
createdAt: entry.createdAt ?? Date.now()
|
|
2956
|
+
}));
|
|
2957
|
+
const routstr21Models = rawRoutstr21Models;
|
|
2958
|
+
const lastRoutstr21ModelsUpdate = rawLastRoutstr21ModelsUpdate;
|
|
2959
|
+
const cachedReceiveTokens = rawCachedReceiveTokens?.map((entry) => ({
|
|
2960
|
+
token: entry.token,
|
|
2961
|
+
amount: entry.amount,
|
|
2962
|
+
unit: entry.unit || "sat",
|
|
2963
|
+
createdAt: entry.createdAt ?? Date.now()
|
|
2964
|
+
}));
|
|
2965
|
+
const clientIds = rawClientIds.map((entry) => ({
|
|
2966
|
+
...entry,
|
|
2967
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
2968
|
+
lastUsed: entry.lastUsed ?? null
|
|
2969
|
+
}));
|
|
2970
|
+
store.setState({
|
|
2971
|
+
modelsFromAllProviders,
|
|
2972
|
+
lastUsedModel,
|
|
2973
|
+
baseUrlsList,
|
|
2974
|
+
lastBaseUrlsUpdate,
|
|
2975
|
+
disabledProviders,
|
|
2976
|
+
mintsFromAllProviders,
|
|
2977
|
+
infoFromAllProviders,
|
|
2978
|
+
lastModelsUpdate,
|
|
2979
|
+
cachedTokens,
|
|
2980
|
+
apiKeys,
|
|
2981
|
+
childKeys,
|
|
2982
|
+
routstr21Models,
|
|
2983
|
+
lastRoutstr21ModelsUpdate,
|
|
2984
|
+
cachedReceiveTokens,
|
|
2985
|
+
clientIds
|
|
2986
|
+
});
|
|
2987
|
+
};
|
|
2988
|
+
var createSdkStore = ({
|
|
2989
|
+
driver
|
|
2990
|
+
}) => {
|
|
2991
|
+
const store = createEmptyStore(driver);
|
|
2992
|
+
return {
|
|
2993
|
+
store,
|
|
2994
|
+
hydrate: hydrateStoreFromDriver(store, driver)
|
|
2995
|
+
};
|
|
2996
|
+
};
|
|
2997
|
+
|
|
2998
|
+
// storage/index.ts
|
|
2999
|
+
var isBrowser2 = () => {
|
|
3000
|
+
try {
|
|
3001
|
+
return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
|
|
3002
|
+
} catch {
|
|
3003
|
+
return false;
|
|
3004
|
+
}
|
|
3005
|
+
};
|
|
3006
|
+
var isNode = () => {
|
|
3007
|
+
try {
|
|
3008
|
+
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
3009
|
+
} catch {
|
|
3010
|
+
return false;
|
|
3011
|
+
}
|
|
3012
|
+
};
|
|
3013
|
+
var defaultDriver = null;
|
|
3014
|
+
var isBun3 = () => {
|
|
3015
|
+
return typeof process.versions.bun !== "undefined";
|
|
3016
|
+
};
|
|
3017
|
+
var getDefaultSdkDriver = () => {
|
|
3018
|
+
if (defaultDriver) return defaultDriver;
|
|
3019
|
+
if (isBrowser2()) {
|
|
3020
|
+
defaultDriver = localStorageDriver;
|
|
3021
|
+
return defaultDriver;
|
|
3022
|
+
}
|
|
3023
|
+
if (isBun3()) {
|
|
3024
|
+
defaultDriver = createMemoryDriver();
|
|
3025
|
+
return defaultDriver;
|
|
3026
|
+
}
|
|
3027
|
+
if (isNode()) {
|
|
3028
|
+
defaultDriver = createSqliteDriver();
|
|
3029
|
+
return defaultDriver;
|
|
3030
|
+
}
|
|
3031
|
+
defaultDriver = createMemoryDriver();
|
|
3032
|
+
return defaultDriver;
|
|
3033
|
+
};
|
|
3034
|
+
var defaultStore = null;
|
|
3035
|
+
var defaultUsageTrackingDriver = null;
|
|
3036
|
+
var getDefaultSdkStore = () => {
|
|
3037
|
+
if (!defaultStore) {
|
|
3038
|
+
defaultStore = createSdkStore({ driver: getDefaultSdkDriver() });
|
|
3039
|
+
}
|
|
3040
|
+
return defaultStore.hydrate.then(() => defaultStore.store);
|
|
3041
|
+
};
|
|
3042
|
+
var getDefaultUsageTrackingDriver = () => {
|
|
3043
|
+
if (defaultUsageTrackingDriver) return defaultUsageTrackingDriver;
|
|
3044
|
+
const storageDriver = getDefaultSdkDriver();
|
|
3045
|
+
if (isBrowser2()) {
|
|
3046
|
+
defaultUsageTrackingDriver = createIndexedDBUsageTrackingDriver({
|
|
3047
|
+
legacyStorageDriver: storageDriver
|
|
3048
|
+
});
|
|
3049
|
+
return defaultUsageTrackingDriver;
|
|
3050
|
+
}
|
|
3051
|
+
if (isBun3()) {
|
|
3052
|
+
defaultUsageTrackingDriver = createMemoryUsageTrackingDriver();
|
|
3053
|
+
return defaultUsageTrackingDriver;
|
|
3054
|
+
}
|
|
3055
|
+
if (isNode()) {
|
|
3056
|
+
defaultUsageTrackingDriver = createSqliteUsageTrackingDriver({
|
|
3057
|
+
legacyStorageDriver: storageDriver
|
|
3058
|
+
});
|
|
3059
|
+
return defaultUsageTrackingDriver;
|
|
3060
|
+
}
|
|
3061
|
+
defaultUsageTrackingDriver = createMemoryUsageTrackingDriver();
|
|
3062
|
+
return defaultUsageTrackingDriver;
|
|
3063
|
+
};
|
|
3064
|
+
function createSSEParserTransform(onUsage, onResponseId) {
|
|
3065
|
+
let buffer = "";
|
|
3066
|
+
const maybeCaptureUsageFromJson = (jsonText) => {
|
|
3067
|
+
try {
|
|
3068
|
+
const data = JSON.parse(jsonText);
|
|
3069
|
+
const responseId = data.id;
|
|
3070
|
+
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
3071
|
+
onResponseId?.(responseId.trim());
|
|
3072
|
+
}
|
|
3073
|
+
const usage = extractUsageFromSSEJson(data);
|
|
3074
|
+
if (usage) {
|
|
3075
|
+
onUsage(usage);
|
|
3076
|
+
}
|
|
3077
|
+
} catch {
|
|
3078
|
+
}
|
|
3079
|
+
};
|
|
3080
|
+
const processLine = (self, line) => {
|
|
3081
|
+
const trimmed = line.trim();
|
|
3082
|
+
if (!trimmed) {
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
if (trimmed === "data: [DONE]" || trimmed === "[DONE]") {
|
|
3086
|
+
self.push("data: [DONE]\n\n");
|
|
3087
|
+
return;
|
|
3088
|
+
}
|
|
3089
|
+
if (trimmed.startsWith("data:")) {
|
|
3090
|
+
const dataStr = trimmed.startsWith("data: ") ? trimmed.slice(6) : trimmed.slice(5).trimStart();
|
|
3091
|
+
if (dataStr === "[DONE]") {
|
|
3092
|
+
self.push("data: [DONE]\n\n");
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
maybeCaptureUsageFromJson(dataStr);
|
|
3096
|
+
self.push(`data: ${dataStr}
|
|
3097
|
+
|
|
3098
|
+
`);
|
|
3099
|
+
return;
|
|
3100
|
+
}
|
|
3101
|
+
if (trimmed.startsWith("{")) {
|
|
3102
|
+
maybeCaptureUsageFromJson(trimmed);
|
|
3103
|
+
self.push(`data: ${trimmed}
|
|
3104
|
+
|
|
3105
|
+
`);
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
self.push(line + "\n");
|
|
3109
|
+
};
|
|
3110
|
+
return new stream.Transform({
|
|
3111
|
+
transform(chunk, encoding, callback) {
|
|
3112
|
+
buffer += chunk.toString();
|
|
3113
|
+
const lines = buffer.split(/\r?\n/);
|
|
3114
|
+
buffer = lines.pop() || "";
|
|
3115
|
+
for (const line of lines) {
|
|
3116
|
+
processLine(this, line);
|
|
3117
|
+
}
|
|
3118
|
+
callback();
|
|
3119
|
+
},
|
|
3120
|
+
flush(callback) {
|
|
3121
|
+
if (buffer.trim()) {
|
|
3122
|
+
processLine(this, buffer);
|
|
3123
|
+
}
|
|
3124
|
+
buffer = "";
|
|
3125
|
+
callback();
|
|
3126
|
+
}
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
1959
3129
|
var TOPUP_MARGIN = 1.2;
|
|
1960
3130
|
var RoutstrClient = class {
|
|
1961
3131
|
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu") {
|
|
@@ -1976,7 +3146,13 @@ var RoutstrClient = class {
|
|
|
1976
3146
|
this.streamProcessor = new StreamProcessor();
|
|
1977
3147
|
this.providerManager = new ProviderManager(providerRegistry);
|
|
1978
3148
|
this.alertLevel = alertLevel;
|
|
1979
|
-
|
|
3149
|
+
if (mode === "lazyrefund") {
|
|
3150
|
+
this.mode = "apikeys";
|
|
3151
|
+
} else if (mode === "apikeys") {
|
|
3152
|
+
this.mode = "lazyrefund";
|
|
3153
|
+
} else {
|
|
3154
|
+
this.mode = mode;
|
|
3155
|
+
}
|
|
1980
3156
|
}
|
|
1981
3157
|
cashuSpender;
|
|
1982
3158
|
balanceManager;
|
|
@@ -2050,6 +3226,80 @@ var RoutstrClient = class {
|
|
|
2050
3226
|
* requests and get responses back.
|
|
2051
3227
|
*/
|
|
2052
3228
|
async routeRequest(params) {
|
|
3229
|
+
const prepared = await this._prepareRoutedRequest(params);
|
|
3230
|
+
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3231
|
+
token: prepared.tokenUsed,
|
|
3232
|
+
baseUrl: prepared.baseUrlUsed,
|
|
3233
|
+
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3234
|
+
response: prepared.response,
|
|
3235
|
+
modelId: prepared.modelId,
|
|
3236
|
+
usage: prepared.capturedUsage,
|
|
3237
|
+
requestId: prepared.capturedResponseId
|
|
3238
|
+
});
|
|
3239
|
+
prepared.response.satsSpent = satsSpent;
|
|
3240
|
+
prepared.response.usage = prepared.capturedUsage;
|
|
3241
|
+
prepared.response.requestId = prepared.capturedResponseId;
|
|
3242
|
+
return prepared.response;
|
|
3243
|
+
}
|
|
3244
|
+
async routeRequestToNodeResponse(params) {
|
|
3245
|
+
const { res } = params;
|
|
3246
|
+
const prepared = await this._prepareRoutedRequest(params);
|
|
3247
|
+
res.statusCode = prepared.response.status;
|
|
3248
|
+
prepared.response.headers.forEach((value, key) => {
|
|
3249
|
+
res.setHeader(key, value);
|
|
3250
|
+
});
|
|
3251
|
+
const body = prepared.response.body;
|
|
3252
|
+
if (!body) {
|
|
3253
|
+
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3254
|
+
token: prepared.tokenUsed,
|
|
3255
|
+
baseUrl: prepared.baseUrlUsed,
|
|
3256
|
+
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3257
|
+
response: prepared.response,
|
|
3258
|
+
modelId: prepared.modelId,
|
|
3259
|
+
usage: prepared.capturedUsage,
|
|
3260
|
+
requestId: prepared.capturedResponseId
|
|
3261
|
+
});
|
|
3262
|
+
prepared.response.satsSpent = satsSpent;
|
|
3263
|
+
res.end();
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3266
|
+
const nodeReadable = stream.Readable.fromWeb(body);
|
|
3267
|
+
await new Promise((resolve, reject) => {
|
|
3268
|
+
let settled = false;
|
|
3269
|
+
const finish = async () => {
|
|
3270
|
+
if (settled) return;
|
|
3271
|
+
settled = true;
|
|
3272
|
+
try {
|
|
3273
|
+
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3274
|
+
token: prepared.tokenUsed,
|
|
3275
|
+
baseUrl: prepared.baseUrlUsed,
|
|
3276
|
+
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3277
|
+
response: prepared.response,
|
|
3278
|
+
modelId: prepared.modelId,
|
|
3279
|
+
usage: prepared.capturedUsage,
|
|
3280
|
+
requestId: prepared.capturedResponseId
|
|
3281
|
+
});
|
|
3282
|
+
prepared.response.satsSpent = satsSpent;
|
|
3283
|
+
prepared.response.usage = prepared.capturedUsage;
|
|
3284
|
+
prepared.response.requestId = prepared.capturedResponseId;
|
|
3285
|
+
resolve();
|
|
3286
|
+
} catch (error) {
|
|
3287
|
+
reject(error);
|
|
3288
|
+
}
|
|
3289
|
+
};
|
|
3290
|
+
const fail = (error) => {
|
|
3291
|
+
if (settled) return;
|
|
3292
|
+
settled = true;
|
|
3293
|
+
reject(error);
|
|
3294
|
+
};
|
|
3295
|
+
res.once("finish", finish);
|
|
3296
|
+
res.once("close", finish);
|
|
3297
|
+
res.once("error", fail);
|
|
3298
|
+
nodeReadable.once("error", fail);
|
|
3299
|
+
nodeReadable.pipe(res);
|
|
3300
|
+
});
|
|
3301
|
+
}
|
|
3302
|
+
async _prepareRoutedRequest(params) {
|
|
2053
3303
|
const {
|
|
2054
3304
|
path,
|
|
2055
3305
|
method,
|
|
@@ -2105,13 +3355,43 @@ var RoutstrClient = class {
|
|
|
2105
3355
|
const tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
|
|
2106
3356
|
const baseUrlUsed = response.baseUrl || baseUrl;
|
|
2107
3357
|
const tokenUsed = response.token || token;
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
3358
|
+
const contentType = response.headers.get("content-type") || "";
|
|
3359
|
+
let processedResponse = response;
|
|
3360
|
+
let capturedUsage;
|
|
3361
|
+
let capturedResponseId;
|
|
3362
|
+
if (contentType.includes("text/event-stream") && response.body) {
|
|
3363
|
+
const nodeReadable = stream.Readable.fromWeb(response.body);
|
|
3364
|
+
const sseParser = createSSEParserTransform(
|
|
3365
|
+
(usage) => {
|
|
3366
|
+
capturedUsage = usage;
|
|
3367
|
+
processedResponse.usage = usage;
|
|
3368
|
+
},
|
|
3369
|
+
(responseId) => {
|
|
3370
|
+
capturedResponseId = responseId;
|
|
3371
|
+
processedResponse.requestId = responseId;
|
|
3372
|
+
}
|
|
3373
|
+
);
|
|
3374
|
+
const transformed = nodeReadable.pipe(sseParser, { end: true });
|
|
3375
|
+
const webStream = stream.Readable.toWeb(
|
|
3376
|
+
transformed
|
|
3377
|
+
);
|
|
3378
|
+
processedResponse = new Response(webStream, {
|
|
3379
|
+
status: response.status,
|
|
3380
|
+
statusText: response.statusText,
|
|
3381
|
+
headers: response.headers
|
|
3382
|
+
});
|
|
3383
|
+
processedResponse.baseUrl = response.baseUrl;
|
|
3384
|
+
processedResponse.token = response.token;
|
|
3385
|
+
}
|
|
3386
|
+
return {
|
|
3387
|
+
response: processedResponse,
|
|
3388
|
+
tokenUsed,
|
|
3389
|
+
baseUrlUsed,
|
|
3390
|
+
tokenBalanceInSats,
|
|
3391
|
+
modelId,
|
|
3392
|
+
capturedUsage,
|
|
3393
|
+
capturedResponseId
|
|
3394
|
+
};
|
|
2115
3395
|
}
|
|
2116
3396
|
/**
|
|
2117
3397
|
* Fetch AI response with streaming
|
|
@@ -2218,7 +3498,18 @@ var RoutstrClient = class {
|
|
|
2218
3498
|
baseUrl: baseUrlUsed,
|
|
2219
3499
|
initialTokenBalance: tokenBalanceInSats,
|
|
2220
3500
|
fallbackSatsSpent: isApikeysEstimate ? this._getEstimatedCosts(selectedModel, streamingResult) : void 0,
|
|
2221
|
-
response
|
|
3501
|
+
response,
|
|
3502
|
+
modelId: selectedModel.id,
|
|
3503
|
+
usage: streamingResult.usage ? {
|
|
3504
|
+
promptTokens: Number(streamingResult.usage.prompt_tokens ?? 0),
|
|
3505
|
+
completionTokens: Number(
|
|
3506
|
+
streamingResult.usage.completion_tokens ?? 0
|
|
3507
|
+
),
|
|
3508
|
+
totalTokens: Number(streamingResult.usage.total_tokens ?? 0),
|
|
3509
|
+
cost: Number(streamingResult.usage.cost ?? 0),
|
|
3510
|
+
satsCost: Number(streamingResult.usage.sats_cost ?? 0)
|
|
3511
|
+
} : void 0,
|
|
3512
|
+
requestId: streamingResult.responseId
|
|
2222
3513
|
});
|
|
2223
3514
|
const estimatedCosts = this._getEstimatedCosts(
|
|
2224
3515
|
selectedModel,
|
|
@@ -2635,7 +3926,16 @@ var RoutstrClient = class {
|
|
|
2635
3926
|
* Handle post-response balance update for all modes
|
|
2636
3927
|
*/
|
|
2637
3928
|
async _handlePostResponseBalanceUpdate(params) {
|
|
2638
|
-
const {
|
|
3929
|
+
const {
|
|
3930
|
+
token,
|
|
3931
|
+
baseUrl,
|
|
3932
|
+
initialTokenBalance,
|
|
3933
|
+
fallbackSatsSpent,
|
|
3934
|
+
response,
|
|
3935
|
+
modelId,
|
|
3936
|
+
usage,
|
|
3937
|
+
requestId
|
|
3938
|
+
} = params;
|
|
2639
3939
|
let satsSpent = initialTokenBalance;
|
|
2640
3940
|
if (this.mode === "xcashu" && response) {
|
|
2641
3941
|
const refundToken = response.headers.get("x-cashu") ?? void 0;
|
|
@@ -2682,8 +3982,75 @@ var RoutstrClient = class {
|
|
|
2682
3982
|
satsSpent = fallbackSatsSpent ?? initialTokenBalance;
|
|
2683
3983
|
}
|
|
2684
3984
|
}
|
|
3985
|
+
await this._trackResponseUsage({
|
|
3986
|
+
token,
|
|
3987
|
+
baseUrl,
|
|
3988
|
+
response,
|
|
3989
|
+
modelId,
|
|
3990
|
+
satsSpent,
|
|
3991
|
+
usage,
|
|
3992
|
+
requestId
|
|
3993
|
+
});
|
|
2685
3994
|
return satsSpent;
|
|
2686
3995
|
}
|
|
3996
|
+
async _trackResponseUsage(params) {
|
|
3997
|
+
const {
|
|
3998
|
+
token,
|
|
3999
|
+
baseUrl,
|
|
4000
|
+
response,
|
|
4001
|
+
modelId,
|
|
4002
|
+
satsSpent,
|
|
4003
|
+
usage: providedUsage,
|
|
4004
|
+
requestId: providedRequestId
|
|
4005
|
+
} = params;
|
|
4006
|
+
if (!response || !modelId) {
|
|
4007
|
+
return;
|
|
4008
|
+
}
|
|
4009
|
+
try {
|
|
4010
|
+
let usage = providedUsage;
|
|
4011
|
+
let requestId = providedRequestId;
|
|
4012
|
+
if (!usage || !requestId) {
|
|
4013
|
+
const contentType = response.headers.get("content-type") || "";
|
|
4014
|
+
if (contentType.includes("text/event-stream")) {
|
|
4015
|
+
usage = usage ?? response.usage;
|
|
4016
|
+
requestId = requestId ?? response.requestId ?? response.headers.get("x-routstr-request-id") ?? void 0;
|
|
4017
|
+
if (!usage) {
|
|
4018
|
+
return;
|
|
4019
|
+
}
|
|
4020
|
+
} else {
|
|
4021
|
+
const cloned = response.clone();
|
|
4022
|
+
const responseBody = await cloned.json();
|
|
4023
|
+
usage = usage ?? extractUsageFromResponseBody(responseBody, satsSpent) ?? void 0;
|
|
4024
|
+
requestId = requestId ?? extractResponseId(responseBody) ?? response.headers.get("x-routstr-request-id") ?? void 0;
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
if (!usage) {
|
|
4028
|
+
return;
|
|
4029
|
+
}
|
|
4030
|
+
const finalRequestId = requestId || "unknown";
|
|
4031
|
+
const store = await getDefaultSdkStore();
|
|
4032
|
+
const state = store.getState();
|
|
4033
|
+
const matchingClient = state.clientIds.find(
|
|
4034
|
+
(client) => client.apiKey === token
|
|
4035
|
+
);
|
|
4036
|
+
const entryId = finalRequestId === "unknown" ? `req-${Date.now()}-${modelId}` : finalRequestId;
|
|
4037
|
+
const usageTracking = getDefaultUsageTrackingDriver();
|
|
4038
|
+
const entry = {
|
|
4039
|
+
id: entryId,
|
|
4040
|
+
timestamp: Date.now(),
|
|
4041
|
+
modelId,
|
|
4042
|
+
baseUrl,
|
|
4043
|
+
requestId: finalRequestId,
|
|
4044
|
+
client: matchingClient?.clientId,
|
|
4045
|
+
...usage
|
|
4046
|
+
};
|
|
4047
|
+
if (this.mode === "xcashu") {
|
|
4048
|
+
entry.satsCost = satsSpent;
|
|
4049
|
+
}
|
|
4050
|
+
await usageTracking.append(entry);
|
|
4051
|
+
} catch (error) {
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
2687
4054
|
/**
|
|
2688
4055
|
* Convert messages for API format
|
|
2689
4056
|
*/
|
|
@@ -2728,37 +4095,6 @@ var RoutstrClient = class {
|
|
|
2728
4095
|
content: result.content || ""
|
|
2729
4096
|
};
|
|
2730
4097
|
}
|
|
2731
|
-
/**
|
|
2732
|
-
* Create a child key for a parent API key via the provider's API
|
|
2733
|
-
* POST /v1/balance/child-key
|
|
2734
|
-
*/
|
|
2735
|
-
async _createChildKey(baseUrl, parentApiKey, options) {
|
|
2736
|
-
const response = await fetch(`${baseUrl}v1/balance/child-key`, {
|
|
2737
|
-
method: "POST",
|
|
2738
|
-
headers: {
|
|
2739
|
-
"Content-Type": "application/json",
|
|
2740
|
-
Authorization: `Bearer ${parentApiKey}`
|
|
2741
|
-
},
|
|
2742
|
-
body: JSON.stringify({
|
|
2743
|
-
count: options?.count ?? 1,
|
|
2744
|
-
balance_limit: options?.balanceLimit,
|
|
2745
|
-
balance_limit_reset: options?.balanceLimitReset,
|
|
2746
|
-
validity_date: options?.validityDate
|
|
2747
|
-
})
|
|
2748
|
-
});
|
|
2749
|
-
if (!response.ok) {
|
|
2750
|
-
throw new Error(
|
|
2751
|
-
`Failed to create child key: ${response.status} ${await response.text()}`
|
|
2752
|
-
);
|
|
2753
|
-
}
|
|
2754
|
-
const data = await response.json();
|
|
2755
|
-
return {
|
|
2756
|
-
childKey: data.api_keys?.[0],
|
|
2757
|
-
balance: data.balance ?? 0,
|
|
2758
|
-
balanceLimit: data.balance_limit,
|
|
2759
|
-
validityDate: data.validity_date
|
|
2760
|
-
};
|
|
2761
|
-
}
|
|
2762
4098
|
/**
|
|
2763
4099
|
* Calculate estimated costs from usage
|
|
2764
4100
|
*/
|
|
@@ -2972,5 +4308,6 @@ var RoutstrClient = class {
|
|
|
2972
4308
|
exports.ProviderManager = ProviderManager;
|
|
2973
4309
|
exports.RoutstrClient = RoutstrClient;
|
|
2974
4310
|
exports.StreamProcessor = StreamProcessor;
|
|
4311
|
+
exports.createSSEParserTransform = createSSEParserTransform;
|
|
2975
4312
|
//# sourceMappingURL=index.js.map
|
|
2976
4313
|
//# sourceMappingURL=index.js.map
|