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