@routstr/sdk 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/client/index.d.mts +19 -5
- package/dist/client/index.d.ts +19 -5
- package/dist/client/index.js +506 -451
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +506 -451
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.d.mts +16 -8
- package/dist/index.d.ts +16 -8
- package/dist/index.js +627 -528
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +625 -529
- package/dist/index.mjs.map +1 -1
- package/dist/{interfaces-CluftN4z.d.ts → interfaces-B62Rw-dd.d.ts} +19 -14
- package/dist/{interfaces-C6Dr6hKy.d.mts → interfaces-C5fLD3jB.d.mts} +19 -14
- package/dist/storage/index.d.mts +36 -162
- package/dist/storage/index.d.ts +36 -162
- package/dist/storage/index.js +434 -189
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +432 -190
- package/dist/storage/index.mjs.map +1 -1
- package/dist/store-BJlwiDX5.d.ts +151 -0
- package/dist/store-C5lnyX8k.d.mts +151 -0
- package/dist/wallet/index.d.mts +18 -24
- package/dist/wallet/index.d.ts +18 -24
- package/dist/wallet/index.js +106 -255
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +106 -255
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var vanilla = require('zustand/vanilla');
|
|
4
|
-
var cashuTs = require('@cashu/cashu-ts');
|
|
5
4
|
var stream = require('stream');
|
|
6
5
|
|
|
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
|
-
|
|
14
6
|
// core/errors.ts
|
|
15
7
|
var InsufficientBalanceError = class extends Error {
|
|
16
8
|
constructor(required, available, maxMintBalance = 0, maxMintUrl = "", customMessage) {
|
|
@@ -154,13 +146,8 @@ var CashuSpender = class {
|
|
|
154
146
|
normalizedMintBalances[url] = balanceInSats;
|
|
155
147
|
totalMintBalance += balanceInSats;
|
|
156
148
|
}
|
|
157
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
158
149
|
const providerBalances = {};
|
|
159
150
|
let totalProviderBalance = 0;
|
|
160
|
-
for (const pending of pendingDistribution) {
|
|
161
|
-
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
162
|
-
totalProviderBalance += pending.amount;
|
|
163
|
-
}
|
|
164
151
|
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
165
152
|
for (const apiKey of apiKeys) {
|
|
166
153
|
if (!providerBalances[apiKey.baseUrl]) {
|
|
@@ -381,27 +368,11 @@ var CashuSpender = class {
|
|
|
381
368
|
};
|
|
382
369
|
}
|
|
383
370
|
}
|
|
384
|
-
if (token
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
this._log(
|
|
390
|
-
"DEBUG",
|
|
391
|
-
`[CashuSpender] _spendInternal: Token already exists for ${baseUrl}, receiving newly created token and using existing`
|
|
392
|
-
);
|
|
393
|
-
const receiveResult = await this.receiveToken(token);
|
|
394
|
-
if (receiveResult.success) {
|
|
395
|
-
this._log(
|
|
396
|
-
"DEBUG",
|
|
397
|
-
`[CashuSpender] _spendInternal: Token restored successfully, amount=${receiveResult.amount}`
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
token = this.storageAdapter.getToken(baseUrl);
|
|
401
|
-
} else {
|
|
402
|
-
throw error;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
371
|
+
if (token) {
|
|
372
|
+
this._log(
|
|
373
|
+
"DEBUG",
|
|
374
|
+
`[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
|
|
375
|
+
);
|
|
405
376
|
}
|
|
406
377
|
this._logTransaction("spend", {
|
|
407
378
|
amount: spentAmount,
|
|
@@ -422,19 +393,19 @@ var CashuSpender = class {
|
|
|
422
393
|
};
|
|
423
394
|
}
|
|
424
395
|
/**
|
|
425
|
-
* Try to reuse an existing
|
|
396
|
+
* Try to reuse an existing API key
|
|
426
397
|
*/
|
|
427
398
|
async _tryReuseToken(baseUrl, amount, mintUrl) {
|
|
428
|
-
const
|
|
429
|
-
if (!
|
|
430
|
-
const
|
|
431
|
-
const balanceForBaseUrl =
|
|
432
|
-
this._log("DEBUG", "
|
|
399
|
+
const apiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
|
|
400
|
+
if (!apiKeyEntry) return null;
|
|
401
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
402
|
+
const balanceForBaseUrl = apiKeyDistribution.find((b) => b.baseUrl === baseUrl)?.amount || 0;
|
|
403
|
+
this._log("DEBUG", "Reusing API key", balanceForBaseUrl, amount);
|
|
433
404
|
if (balanceForBaseUrl > amount) {
|
|
434
405
|
const units = this.walletAdapter.getMintUnits();
|
|
435
406
|
const unit = units[mintUrl] || "sat";
|
|
436
407
|
return {
|
|
437
|
-
token:
|
|
408
|
+
token: apiKeyEntry.key,
|
|
438
409
|
status: "success",
|
|
439
410
|
balance: balanceForBaseUrl,
|
|
440
411
|
unit
|
|
@@ -445,7 +416,8 @@ var CashuSpender = class {
|
|
|
445
416
|
const topUpResult = await this.balanceManager.topUp({
|
|
446
417
|
mintUrl,
|
|
447
418
|
baseUrl,
|
|
448
|
-
amount: topUpAmount
|
|
419
|
+
amount: topUpAmount,
|
|
420
|
+
token: apiKeyEntry.key
|
|
449
421
|
});
|
|
450
422
|
this._log("DEBUG", "TOPUP ", topUpResult);
|
|
451
423
|
if (topUpResult.success && topUpResult.toppedUpAmount) {
|
|
@@ -459,7 +431,7 @@ var CashuSpender = class {
|
|
|
459
431
|
status: "success"
|
|
460
432
|
});
|
|
461
433
|
return {
|
|
462
|
-
token:
|
|
434
|
+
token: apiKeyEntry.key,
|
|
463
435
|
status: "success",
|
|
464
436
|
balance: newBalance,
|
|
465
437
|
unit
|
|
@@ -467,84 +439,108 @@ var CashuSpender = class {
|
|
|
467
439
|
}
|
|
468
440
|
const providerBalance = await this._getProviderTokenBalance(
|
|
469
441
|
baseUrl,
|
|
470
|
-
|
|
442
|
+
apiKeyEntry.key
|
|
471
443
|
);
|
|
472
444
|
this._log("DEBUG", providerBalance);
|
|
473
445
|
if (providerBalance <= 0) {
|
|
474
|
-
this.storageAdapter.
|
|
446
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
475
447
|
}
|
|
476
448
|
}
|
|
477
449
|
return null;
|
|
478
450
|
}
|
|
479
451
|
/**
|
|
480
|
-
* Refund
|
|
452
|
+
* Refund all xcashu tokens from storage and increment tryCounts on failure.
|
|
453
|
+
* Reuses receiveToken from BalanceManager/CashuSpender for receiving refunds.
|
|
454
|
+
* @param mintUrl - The mint URL for receiving tokens
|
|
455
|
+
* @param excludeBaseUrls - Base URLs to exclude from refund (optional)
|
|
456
|
+
* @returns Results for each xcashu token refund attempt
|
|
481
457
|
*/
|
|
482
|
-
async
|
|
458
|
+
async refundXcashuTokens(mintUrl, excludeBaseUrls) {
|
|
483
459
|
const results = [];
|
|
484
|
-
const
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
(r) => r.status === "fulfilled" ? r.value : { baseUrl: "", success: false }
|
|
517
|
-
)
|
|
518
|
-
);
|
|
519
|
-
if (refundApiKeys) {
|
|
520
|
-
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
521
|
-
const apiKeysToRefund = apiKeyDistribution.filter(
|
|
522
|
-
(p) => baseUrls.includes(p.baseUrl)
|
|
523
|
-
);
|
|
524
|
-
for (const apiKeyEntry of apiKeysToRefund) {
|
|
525
|
-
const apiKeyEntryFull = this.storageAdapter.getApiKey(
|
|
526
|
-
apiKeyEntry.baseUrl
|
|
527
|
-
);
|
|
528
|
-
if (apiKeyEntryFull && this.balanceManager) {
|
|
529
|
-
const refundResult = await this.balanceManager.refundApiKey({
|
|
530
|
-
mintUrl,
|
|
531
|
-
baseUrl: apiKeyEntry.baseUrl,
|
|
532
|
-
apiKey: apiKeyEntryFull.key,
|
|
533
|
-
forceRefund
|
|
534
|
-
});
|
|
535
|
-
if (refundResult.success) {
|
|
536
|
-
this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, 0);
|
|
460
|
+
const xcashuTokens = this.storageAdapter.getXcashuTokens();
|
|
461
|
+
const excludedUrls = new Set(excludeBaseUrls || []);
|
|
462
|
+
for (const [baseUrl, tokens] of Object.entries(xcashuTokens)) {
|
|
463
|
+
if (excludedUrls.has(baseUrl)) continue;
|
|
464
|
+
for (const xcashuToken of tokens) {
|
|
465
|
+
try {
|
|
466
|
+
const receiveResult = await this.receiveToken(xcashuToken.token);
|
|
467
|
+
if (receiveResult.success) {
|
|
468
|
+
this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
|
|
469
|
+
results.push({
|
|
470
|
+
baseUrl,
|
|
471
|
+
token: xcashuToken.token,
|
|
472
|
+
success: true
|
|
473
|
+
});
|
|
474
|
+
this._log(
|
|
475
|
+
"DEBUG",
|
|
476
|
+
`[CashuSpender] refundXcashuTokens: Successfully refunded xcashu token for ${baseUrl}, amount=${receiveResult.amount}`
|
|
477
|
+
);
|
|
478
|
+
} else {
|
|
479
|
+
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
480
|
+
const newTryCount = currentTryCount + 1;
|
|
481
|
+
this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
|
|
482
|
+
results.push({
|
|
483
|
+
baseUrl,
|
|
484
|
+
token: xcashuToken.token,
|
|
485
|
+
success: false,
|
|
486
|
+
error: receiveResult.message ?? "Refund failed"
|
|
487
|
+
});
|
|
488
|
+
this._log(
|
|
489
|
+
"DEBUG",
|
|
490
|
+
`[CashuSpender] refundXcashuTokens: Failed to refund xcashu token for ${baseUrl}, incremented tryCount to ${newTryCount}`
|
|
491
|
+
);
|
|
537
492
|
}
|
|
493
|
+
} catch (error) {
|
|
494
|
+
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
495
|
+
const newTryCount = currentTryCount + 1;
|
|
496
|
+
this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
|
|
497
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
538
498
|
results.push({
|
|
539
|
-
baseUrl
|
|
540
|
-
|
|
499
|
+
baseUrl,
|
|
500
|
+
token: xcashuToken.token,
|
|
501
|
+
success: false,
|
|
502
|
+
error: errorMessage
|
|
541
503
|
});
|
|
504
|
+
this._log(
|
|
505
|
+
"ERROR",
|
|
506
|
+
`[CashuSpender] refundXcashuTokens: Exception during refund for ${baseUrl}: ${errorMessage}, incremented tryCount to ${newTryCount}`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return results;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Refund specific providers without retrying spend
|
|
515
|
+
*/
|
|
516
|
+
async refundProviders(mintUrl, forceRefund) {
|
|
517
|
+
const results = [];
|
|
518
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
519
|
+
for (const apiKeyEntry of apiKeyDistribution) {
|
|
520
|
+
const apiKeyEntryFull = this.storageAdapter.getApiKey(
|
|
521
|
+
apiKeyEntry.baseUrl
|
|
522
|
+
);
|
|
523
|
+
if (apiKeyEntryFull && this.balanceManager) {
|
|
524
|
+
const refundResult = await this.balanceManager.refundApiKey({
|
|
525
|
+
mintUrl,
|
|
526
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
527
|
+
apiKey: apiKeyEntryFull.key,
|
|
528
|
+
forceRefund
|
|
529
|
+
});
|
|
530
|
+
if (refundResult.success) {
|
|
531
|
+
this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
|
|
542
532
|
} else {
|
|
543
|
-
|
|
544
|
-
baseUrl: apiKeyEntry.baseUrl,
|
|
545
|
-
success: false
|
|
546
|
-
});
|
|
533
|
+
this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, apiKeyEntry.amount);
|
|
547
534
|
}
|
|
535
|
+
results.push({
|
|
536
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
537
|
+
success: refundResult.success
|
|
538
|
+
});
|
|
539
|
+
} else {
|
|
540
|
+
results.push({
|
|
541
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
542
|
+
success: false
|
|
543
|
+
});
|
|
548
544
|
}
|
|
549
545
|
}
|
|
550
546
|
return results;
|
|
@@ -629,13 +625,8 @@ var BalanceManager = class {
|
|
|
629
625
|
normalizedMintBalances[url] = balanceInSats;
|
|
630
626
|
totalMintBalance += balanceInSats;
|
|
631
627
|
}
|
|
632
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
633
628
|
const providerBalances = {};
|
|
634
629
|
let totalProviderBalance = 0;
|
|
635
|
-
for (const pending of pendingDistribution) {
|
|
636
|
-
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
637
|
-
totalProviderBalance += pending.amount;
|
|
638
|
-
}
|
|
639
630
|
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
640
631
|
for (const apiKey of apiKeys) {
|
|
641
632
|
if (!providerBalances[apiKey.baseUrl]) {
|
|
@@ -650,57 +641,6 @@ var BalanceManager = class {
|
|
|
650
641
|
mintBalances: normalizedMintBalances
|
|
651
642
|
};
|
|
652
643
|
}
|
|
653
|
-
/**
|
|
654
|
-
* Unified refund - handles both NIP-60 and legacy wallet refunds
|
|
655
|
-
*/
|
|
656
|
-
async refund(options) {
|
|
657
|
-
const { mintUrl, baseUrl, token: providedToken } = options;
|
|
658
|
-
const storedToken = providedToken || this.storageAdapter.getToken(baseUrl);
|
|
659
|
-
if (!storedToken) {
|
|
660
|
-
console.log("[BalanceManager] No token to refund, returning early");
|
|
661
|
-
return { success: true, message: "No API key to refund" };
|
|
662
|
-
}
|
|
663
|
-
let fetchResult;
|
|
664
|
-
try {
|
|
665
|
-
fetchResult = await this._fetchRefundToken(baseUrl, storedToken);
|
|
666
|
-
if (!fetchResult.success) {
|
|
667
|
-
return {
|
|
668
|
-
success: false,
|
|
669
|
-
message: fetchResult.error || "Refund failed",
|
|
670
|
-
requestId: fetchResult.requestId
|
|
671
|
-
};
|
|
672
|
-
}
|
|
673
|
-
if (!fetchResult.token) {
|
|
674
|
-
return {
|
|
675
|
-
success: false,
|
|
676
|
-
message: "No token received from refund",
|
|
677
|
-
requestId: fetchResult.requestId
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
if (fetchResult.error === "No balance to refund") {
|
|
681
|
-
console.log(
|
|
682
|
-
"[BalanceManager] No balance to refund, removing stored token"
|
|
683
|
-
);
|
|
684
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
685
|
-
return { success: true, message: "No balance to refund" };
|
|
686
|
-
}
|
|
687
|
-
const receiveResult = await this.cashuSpender.receiveToken(
|
|
688
|
-
fetchResult.token
|
|
689
|
-
);
|
|
690
|
-
const totalAmountMsat = receiveResult.unit === "msat" ? receiveResult.amount : receiveResult.amount * 1e3;
|
|
691
|
-
if (!providedToken) {
|
|
692
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
693
|
-
}
|
|
694
|
-
return {
|
|
695
|
-
success: receiveResult.success,
|
|
696
|
-
refundedAmount: totalAmountMsat,
|
|
697
|
-
requestId: fetchResult.requestId
|
|
698
|
-
};
|
|
699
|
-
} catch (error) {
|
|
700
|
-
console.error("[BalanceManager] Refund error", error);
|
|
701
|
-
return this._handleRefundError(error, mintUrl, fetchResult?.requestId);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
644
|
/**
|
|
705
645
|
* Refund API key balance - convert remaining API key balance to cashu token
|
|
706
646
|
* @param options - Refund options including forceRefund flag
|
|
@@ -836,8 +776,9 @@ var BalanceManager = class {
|
|
|
836
776
|
if (!amount || amount <= 0) {
|
|
837
777
|
return { success: false, message: "Invalid top up amount" };
|
|
838
778
|
}
|
|
839
|
-
const
|
|
840
|
-
|
|
779
|
+
const apiKeyEntry = providedToken ? null : this.storageAdapter.getApiKey(baseUrl);
|
|
780
|
+
const apiKey = providedToken || apiKeyEntry?.key;
|
|
781
|
+
if (!apiKey) {
|
|
841
782
|
return { success: false, message: "No API key available for top up" };
|
|
842
783
|
}
|
|
843
784
|
let cashuToken = null;
|
|
@@ -857,7 +798,7 @@ var BalanceManager = class {
|
|
|
857
798
|
cashuToken = tokenResult.token;
|
|
858
799
|
const topUpResult = await this._postTopUp(
|
|
859
800
|
baseUrl,
|
|
860
|
-
|
|
801
|
+
apiKey,
|
|
861
802
|
cashuToken
|
|
862
803
|
);
|
|
863
804
|
requestId = topUpResult.requestId;
|
|
@@ -1072,38 +1013,11 @@ var BalanceManager = class {
|
|
|
1072
1013
|
return candidates;
|
|
1073
1014
|
}
|
|
1074
1015
|
async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount) {
|
|
1075
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1076
1016
|
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1077
1017
|
const forceRefund = retryCount >= 2;
|
|
1078
|
-
const toRefund = pendingDistribution.filter(
|
|
1079
|
-
(pending) => pending.baseUrl !== baseUrl
|
|
1080
|
-
);
|
|
1081
1018
|
const apiKeysToRefund = apiKeyDistribution.filter(
|
|
1082
1019
|
(apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
|
|
1083
1020
|
);
|
|
1084
|
-
const tokenRefundResults = await Promise.allSettled(
|
|
1085
|
-
toRefund.map(async (pending) => {
|
|
1086
|
-
const token = this.storageAdapter.getToken(pending.baseUrl);
|
|
1087
|
-
if (!token) {
|
|
1088
|
-
return { baseUrl: pending.baseUrl, success: false };
|
|
1089
|
-
}
|
|
1090
|
-
const tokenBalance = await this.getTokenBalance(token, pending.baseUrl);
|
|
1091
|
-
if (tokenBalance.reserved > 0) {
|
|
1092
|
-
return { baseUrl: pending.baseUrl, success: false };
|
|
1093
|
-
}
|
|
1094
|
-
const result = await this.refund({
|
|
1095
|
-
mintUrl,
|
|
1096
|
-
baseUrl: pending.baseUrl,
|
|
1097
|
-
token
|
|
1098
|
-
});
|
|
1099
|
-
return { baseUrl: pending.baseUrl, success: result.success };
|
|
1100
|
-
})
|
|
1101
|
-
);
|
|
1102
|
-
for (const result of tokenRefundResults) {
|
|
1103
|
-
if (result.status === "fulfilled" && result.value.success) {
|
|
1104
|
-
this.storageAdapter.removeToken(result.value.baseUrl);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
1021
|
const apiKeyRefundResults = await Promise.allSettled(
|
|
1108
1022
|
apiKeysToRefund.map(async (apiKeyEntry) => {
|
|
1109
1023
|
const fullApiKeyEntry = this.storageAdapter.getApiKey(
|
|
@@ -1127,77 +1041,6 @@ var BalanceManager = class {
|
|
|
1127
1041
|
}
|
|
1128
1042
|
}
|
|
1129
1043
|
}
|
|
1130
|
-
/**
|
|
1131
|
-
* Fetch refund token from provider API
|
|
1132
|
-
*/
|
|
1133
|
-
async _fetchRefundToken(baseUrl, storedToken) {
|
|
1134
|
-
if (!baseUrl) {
|
|
1135
|
-
return {
|
|
1136
|
-
success: false,
|
|
1137
|
-
error: "No base URL configured"
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
1141
|
-
const url = `${normalizedBaseUrl}v1/wallet/refund`;
|
|
1142
|
-
const controller = new AbortController();
|
|
1143
|
-
const timeoutId = setTimeout(() => {
|
|
1144
|
-
controller.abort();
|
|
1145
|
-
}, 6e4);
|
|
1146
|
-
try {
|
|
1147
|
-
const response = await fetch(url, {
|
|
1148
|
-
method: "POST",
|
|
1149
|
-
headers: {
|
|
1150
|
-
Authorization: `Bearer ${storedToken}`,
|
|
1151
|
-
"Content-Type": "application/json"
|
|
1152
|
-
},
|
|
1153
|
-
signal: controller.signal
|
|
1154
|
-
});
|
|
1155
|
-
clearTimeout(timeoutId);
|
|
1156
|
-
const requestId = response.headers.get("x-routstr-request-id") || void 0;
|
|
1157
|
-
if (!response.ok) {
|
|
1158
|
-
const errorData = await response.json().catch(() => ({}));
|
|
1159
|
-
if (response.status === 400 && errorData?.detail === "No balance to refund") {
|
|
1160
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
1161
|
-
return {
|
|
1162
|
-
success: false,
|
|
1163
|
-
requestId,
|
|
1164
|
-
error: "No balance to refund"
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
return {
|
|
1168
|
-
success: false,
|
|
1169
|
-
requestId,
|
|
1170
|
-
error: `Refund request failed with status ${response.status}: ${errorData?.detail || response.statusText}`
|
|
1171
|
-
};
|
|
1172
|
-
}
|
|
1173
|
-
const data = await response.json();
|
|
1174
|
-
console.log("refund rsule", data);
|
|
1175
|
-
return {
|
|
1176
|
-
success: true,
|
|
1177
|
-
token: data.token,
|
|
1178
|
-
requestId
|
|
1179
|
-
};
|
|
1180
|
-
} catch (error) {
|
|
1181
|
-
clearTimeout(timeoutId);
|
|
1182
|
-
console.error("[BalanceManager._fetchRefundToken] Fetch error", error);
|
|
1183
|
-
if (error instanceof Error) {
|
|
1184
|
-
if (error.name === "AbortError") {
|
|
1185
|
-
return {
|
|
1186
|
-
success: false,
|
|
1187
|
-
error: "Request timed out after 1 minute"
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
|
-
return {
|
|
1191
|
-
success: false,
|
|
1192
|
-
error: error.message
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
return {
|
|
1196
|
-
success: false,
|
|
1197
|
-
error: "Unknown error occurred during refund request"
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
1044
|
/**
|
|
1202
1045
|
* Post topup request to provider API
|
|
1203
1046
|
*/
|
|
@@ -2181,38 +2024,54 @@ var createMemoryDriver = (seed) => {
|
|
|
2181
2024
|
var isBun = () => {
|
|
2182
2025
|
return typeof process.versions.bun !== "undefined";
|
|
2183
2026
|
};
|
|
2184
|
-
var
|
|
2027
|
+
var cachedDbModule = null;
|
|
2028
|
+
var loadDatabase = async (dbPath) => {
|
|
2185
2029
|
if (isBun()) {
|
|
2186
2030
|
throw new Error(
|
|
2187
|
-
"SQLite driver not supported in Bun. Use
|
|
2031
|
+
"SQLite driver not supported in Bun. Use createBunSqliteDriver() instead."
|
|
2188
2032
|
);
|
|
2189
2033
|
}
|
|
2190
|
-
let Database = null;
|
|
2191
2034
|
try {
|
|
2192
|
-
|
|
2035
|
+
if (!cachedDbModule) {
|
|
2036
|
+
cachedDbModule = (await import('better-sqlite3')).default;
|
|
2037
|
+
}
|
|
2038
|
+
return new cachedDbModule(dbPath);
|
|
2193
2039
|
} catch (error) {
|
|
2194
2040
|
throw new Error(
|
|
2195
2041
|
`better-sqlite3 is required for sqlite storage. Install it to use sqlite storage. (${error})`
|
|
2196
2042
|
);
|
|
2197
2043
|
}
|
|
2198
|
-
return new Database(dbPath);
|
|
2199
2044
|
};
|
|
2200
2045
|
var createSqliteDriver = (options = {}) => {
|
|
2201
2046
|
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2202
2047
|
const tableName = options.tableName || "sdk_storage";
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
const
|
|
2208
|
-
|
|
2209
|
-
|
|
2048
|
+
let db;
|
|
2049
|
+
let selectStmt;
|
|
2050
|
+
let upsertStmt;
|
|
2051
|
+
let deleteStmt;
|
|
2052
|
+
const initDb = async () => {
|
|
2053
|
+
if (!db) {
|
|
2054
|
+
db = await loadDatabase(dbPath);
|
|
2055
|
+
db.exec(
|
|
2056
|
+
`CREATE TABLE IF NOT EXISTS ${tableName} (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
|
|
2057
|
+
);
|
|
2058
|
+
selectStmt = db.prepare(`SELECT value FROM ${tableName} WHERE key = ?`);
|
|
2059
|
+
upsertStmt = db.prepare(
|
|
2060
|
+
`INSERT INTO ${tableName} (key, value) VALUES (?, ?)
|
|
2210
2061
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
2211
|
-
|
|
2212
|
-
|
|
2062
|
+
);
|
|
2063
|
+
deleteStmt = db.prepare(`DELETE FROM ${tableName} WHERE key = ?`);
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
const ensureInit = async () => {
|
|
2067
|
+
if (!db) {
|
|
2068
|
+
await initDb();
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2213
2071
|
return {
|
|
2214
2072
|
async getItem(key, defaultValue) {
|
|
2215
2073
|
try {
|
|
2074
|
+
await ensureInit();
|
|
2216
2075
|
const row = selectStmt.get(key);
|
|
2217
2076
|
if (!row || typeof row.value !== "string") return defaultValue;
|
|
2218
2077
|
try {
|
|
@@ -2230,6 +2089,7 @@ var createSqliteDriver = (options = {}) => {
|
|
|
2230
2089
|
},
|
|
2231
2090
|
async setItem(key, value) {
|
|
2232
2091
|
try {
|
|
2092
|
+
await ensureInit();
|
|
2233
2093
|
upsertStmt.run(key, JSON.stringify(value));
|
|
2234
2094
|
} catch (error) {
|
|
2235
2095
|
console.error(`SQLite setItem failed for key "${key}":`, error);
|
|
@@ -2237,6 +2097,7 @@ var createSqliteDriver = (options = {}) => {
|
|
|
2237
2097
|
},
|
|
2238
2098
|
async removeItem(key) {
|
|
2239
2099
|
try {
|
|
2100
|
+
await ensureInit();
|
|
2240
2101
|
deleteStmt.run(key);
|
|
2241
2102
|
} catch (error) {
|
|
2242
2103
|
console.error(`SQLite removeItem failed for key "${key}":`, error);
|
|
@@ -2255,9 +2116,9 @@ var SDK_STORAGE_KEYS = {
|
|
|
2255
2116
|
INFO_FROM_ALL_PROVIDERS: "info_from_all_providers",
|
|
2256
2117
|
LAST_MODELS_UPDATE: "lastModelsUpdate",
|
|
2257
2118
|
LAST_BASE_URLS_UPDATE: "lastBaseUrlsUpdate",
|
|
2258
|
-
LOCAL_CASHU_TOKENS: "local_cashu_tokens",
|
|
2259
2119
|
API_KEYS: "api_keys",
|
|
2260
2120
|
CHILD_KEYS: "child_keys",
|
|
2121
|
+
XCASHU_TOKENS: "xcashu_tokens",
|
|
2261
2122
|
ROUTSTR21_MODELS: "routstr21Models",
|
|
2262
2123
|
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
2263
2124
|
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
@@ -2448,21 +2309,23 @@ var normalizeBaseUrl2 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUr
|
|
|
2448
2309
|
var isBun2 = () => {
|
|
2449
2310
|
return typeof process.versions.bun !== "undefined";
|
|
2450
2311
|
};
|
|
2451
|
-
var
|
|
2312
|
+
var cachedDbModule2 = null;
|
|
2313
|
+
var loadDatabase2 = async (dbPath) => {
|
|
2452
2314
|
if (isBun2()) {
|
|
2453
2315
|
throw new Error(
|
|
2454
2316
|
"SQLite driver not supported in Bun. Use createMemoryDriver() instead."
|
|
2455
2317
|
);
|
|
2456
2318
|
}
|
|
2457
|
-
let Database = null;
|
|
2458
2319
|
try {
|
|
2459
|
-
|
|
2320
|
+
if (!cachedDbModule2) {
|
|
2321
|
+
cachedDbModule2 = (await import('better-sqlite3')).default;
|
|
2322
|
+
}
|
|
2323
|
+
return new cachedDbModule2(dbPath);
|
|
2460
2324
|
} catch (error) {
|
|
2461
2325
|
throw new Error(
|
|
2462
2326
|
`better-sqlite3 is required for sqlite usage tracking. Install it to use sqlite storage. (${error})`
|
|
2463
2327
|
);
|
|
2464
2328
|
}
|
|
2465
|
-
return new Database(dbPath);
|
|
2466
2329
|
};
|
|
2467
2330
|
var buildWhereClause = (options = {}) => {
|
|
2468
2331
|
const clauses = [];
|
|
@@ -2499,38 +2362,49 @@ var buildWhereClause = (options = {}) => {
|
|
|
2499
2362
|
var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
2500
2363
|
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2501
2364
|
const tableName = options.tableName || "usage_tracking";
|
|
2502
|
-
const db = createDatabase2(dbPath);
|
|
2503
2365
|
const legacyStorageDriver = options.legacyStorageDriver;
|
|
2504
|
-
db
|
|
2505
|
-
|
|
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
|
-
`);
|
|
2366
|
+
let db;
|
|
2367
|
+
let insertStmt;
|
|
2533
2368
|
let migrationComplete = false;
|
|
2369
|
+
const initDb = async () => {
|
|
2370
|
+
if (!db) {
|
|
2371
|
+
db = await loadDatabase2(dbPath);
|
|
2372
|
+
db.exec(`
|
|
2373
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
2374
|
+
id TEXT PRIMARY KEY,
|
|
2375
|
+
timestamp INTEGER NOT NULL,
|
|
2376
|
+
model_id TEXT NOT NULL,
|
|
2377
|
+
base_url TEXT NOT NULL,
|
|
2378
|
+
request_id TEXT NOT NULL,
|
|
2379
|
+
cost REAL NOT NULL,
|
|
2380
|
+
sats_cost REAL NOT NULL,
|
|
2381
|
+
prompt_tokens INTEGER NOT NULL,
|
|
2382
|
+
completion_tokens INTEGER NOT NULL,
|
|
2383
|
+
total_tokens INTEGER NOT NULL,
|
|
2384
|
+
client TEXT,
|
|
2385
|
+
session_id TEXT,
|
|
2386
|
+
tags TEXT
|
|
2387
|
+
);
|
|
2388
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp);
|
|
2389
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id);
|
|
2390
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url);
|
|
2391
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_session_id ON ${tableName}(session_id);
|
|
2392
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_client ON ${tableName}(client);
|
|
2393
|
+
`);
|
|
2394
|
+
insertStmt = db.prepare(`
|
|
2395
|
+
INSERT OR REPLACE INTO ${tableName} (
|
|
2396
|
+
id, timestamp, model_id, base_url, request_id,
|
|
2397
|
+
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
2398
|
+
client, session_id, tags
|
|
2399
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2400
|
+
`);
|
|
2401
|
+
}
|
|
2402
|
+
};
|
|
2403
|
+
const ensureInit = async () => {
|
|
2404
|
+
if (!db) {
|
|
2405
|
+
await initDb();
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2534
2408
|
const appendOne = (entry) => {
|
|
2535
2409
|
insertStmt.run(
|
|
2536
2410
|
entry.id,
|
|
@@ -2588,19 +2462,23 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
2588
2462
|
});
|
|
2589
2463
|
return {
|
|
2590
2464
|
async migrate() {
|
|
2465
|
+
await ensureInit();
|
|
2591
2466
|
await ensureMigrated();
|
|
2592
2467
|
},
|
|
2593
2468
|
async append(entry) {
|
|
2469
|
+
await ensureInit();
|
|
2594
2470
|
await ensureMigrated();
|
|
2595
2471
|
appendOne(entry);
|
|
2596
2472
|
},
|
|
2597
2473
|
async appendMany(entries) {
|
|
2474
|
+
await ensureInit();
|
|
2598
2475
|
await ensureMigrated();
|
|
2599
2476
|
for (const entry of entries) {
|
|
2600
2477
|
appendOne(entry);
|
|
2601
2478
|
}
|
|
2602
2479
|
},
|
|
2603
2480
|
async list(options2 = {}) {
|
|
2481
|
+
await ensureInit();
|
|
2604
2482
|
await ensureMigrated();
|
|
2605
2483
|
const { sql, params } = buildWhereClause(options2);
|
|
2606
2484
|
const limitSql = typeof options2.limit === "number" ? " LIMIT ?" : "";
|
|
@@ -2613,6 +2491,7 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
2613
2491
|
return rows.map(mapRow);
|
|
2614
2492
|
},
|
|
2615
2493
|
async count(options2 = {}) {
|
|
2494
|
+
await ensureInit();
|
|
2616
2495
|
await ensureMigrated();
|
|
2617
2496
|
const { sql, params } = buildWhereClause(options2);
|
|
2618
2497
|
const stmt = db.prepare(`SELECT COUNT(*) as count FROM ${tableName} ${sql}`);
|
|
@@ -2620,20 +2499,197 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
2620
2499
|
return Number(row?.count ?? 0);
|
|
2621
2500
|
},
|
|
2622
2501
|
async deleteOlderThan(timestamp) {
|
|
2502
|
+
await ensureInit();
|
|
2623
2503
|
await ensureMigrated();
|
|
2624
2504
|
const stmt = db.prepare(`DELETE FROM ${tableName} WHERE timestamp < ?`);
|
|
2625
2505
|
const result = stmt.run(timestamp);
|
|
2626
2506
|
return result.changes;
|
|
2627
2507
|
},
|
|
2628
2508
|
async clear() {
|
|
2509
|
+
await ensureInit();
|
|
2629
2510
|
await ensureMigrated();
|
|
2630
2511
|
db.prepare(`DELETE FROM ${tableName}`).run();
|
|
2631
2512
|
}
|
|
2632
2513
|
};
|
|
2633
2514
|
};
|
|
2634
2515
|
|
|
2635
|
-
// storage/usageTracking/
|
|
2516
|
+
// storage/usageTracking/bunSqlite.ts
|
|
2517
|
+
var MIGRATION_MARKER_KEY3 = "usage_tracking_migration_v1";
|
|
2636
2518
|
var normalizeBaseUrl3 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2519
|
+
var buildWhereClause2 = (options = {}) => {
|
|
2520
|
+
const clauses = [];
|
|
2521
|
+
const params = [];
|
|
2522
|
+
if (typeof options.before === "number") {
|
|
2523
|
+
clauses.push("timestamp < ?");
|
|
2524
|
+
params.push(options.before);
|
|
2525
|
+
}
|
|
2526
|
+
if (typeof options.after === "number") {
|
|
2527
|
+
clauses.push("timestamp > ?");
|
|
2528
|
+
params.push(options.after);
|
|
2529
|
+
}
|
|
2530
|
+
if (options.modelId) {
|
|
2531
|
+
clauses.push("model_id = ?");
|
|
2532
|
+
params.push(options.modelId);
|
|
2533
|
+
}
|
|
2534
|
+
if (options.baseUrl) {
|
|
2535
|
+
clauses.push("base_url = ?");
|
|
2536
|
+
params.push(normalizeBaseUrl3(options.baseUrl));
|
|
2537
|
+
}
|
|
2538
|
+
if (options.sessionId) {
|
|
2539
|
+
clauses.push("session_id = ?");
|
|
2540
|
+
params.push(options.sessionId);
|
|
2541
|
+
}
|
|
2542
|
+
if (options.client) {
|
|
2543
|
+
clauses.push("client = ?");
|
|
2544
|
+
params.push(options.client);
|
|
2545
|
+
}
|
|
2546
|
+
return {
|
|
2547
|
+
sql: clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "",
|
|
2548
|
+
params
|
|
2549
|
+
};
|
|
2550
|
+
};
|
|
2551
|
+
var createBunSqliteUsageTrackingDriver = (options = {}) => {
|
|
2552
|
+
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2553
|
+
const tableName = options.tableName || "usage_tracking";
|
|
2554
|
+
const legacyStorageDriver = options.legacyStorageDriver;
|
|
2555
|
+
const SQLiteDatabase = options.sqlite?.Database;
|
|
2556
|
+
let migrationPromise = null;
|
|
2557
|
+
if (!SQLiteDatabase) {
|
|
2558
|
+
throw new Error(
|
|
2559
|
+
"Bun SQLite Database constructor is required. Pass { sqlite: { Database } } when creating the driver."
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
const db = new SQLiteDatabase(dbPath);
|
|
2563
|
+
db.run(`
|
|
2564
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
2565
|
+
id TEXT PRIMARY KEY,
|
|
2566
|
+
timestamp INTEGER NOT NULL,
|
|
2567
|
+
model_id TEXT NOT NULL,
|
|
2568
|
+
base_url TEXT NOT NULL,
|
|
2569
|
+
request_id TEXT NOT NULL,
|
|
2570
|
+
cost REAL NOT NULL,
|
|
2571
|
+
sats_cost REAL NOT NULL,
|
|
2572
|
+
prompt_tokens INTEGER NOT NULL,
|
|
2573
|
+
completion_tokens INTEGER NOT NULL,
|
|
2574
|
+
total_tokens INTEGER NOT NULL,
|
|
2575
|
+
client TEXT,
|
|
2576
|
+
session_id TEXT,
|
|
2577
|
+
tags TEXT
|
|
2578
|
+
)
|
|
2579
|
+
`);
|
|
2580
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp)`);
|
|
2581
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id)`);
|
|
2582
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url)`);
|
|
2583
|
+
const appendOne = (entry) => {
|
|
2584
|
+
db.query(`
|
|
2585
|
+
INSERT OR REPLACE INTO ${tableName} (
|
|
2586
|
+
id, timestamp, model_id, base_url, request_id,
|
|
2587
|
+
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
2588
|
+
client, session_id, tags
|
|
2589
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2590
|
+
`).run(
|
|
2591
|
+
entry.id,
|
|
2592
|
+
entry.timestamp,
|
|
2593
|
+
entry.modelId,
|
|
2594
|
+
normalizeBaseUrl3(entry.baseUrl),
|
|
2595
|
+
entry.requestId,
|
|
2596
|
+
entry.cost,
|
|
2597
|
+
entry.satsCost,
|
|
2598
|
+
entry.promptTokens,
|
|
2599
|
+
entry.completionTokens,
|
|
2600
|
+
entry.totalTokens,
|
|
2601
|
+
entry.client ?? null,
|
|
2602
|
+
entry.sessionId ?? null,
|
|
2603
|
+
JSON.stringify(entry.tags ?? [])
|
|
2604
|
+
);
|
|
2605
|
+
};
|
|
2606
|
+
const mapRow = (row) => ({
|
|
2607
|
+
id: row.id,
|
|
2608
|
+
timestamp: row.timestamp,
|
|
2609
|
+
modelId: row.model_id,
|
|
2610
|
+
baseUrl: row.base_url,
|
|
2611
|
+
requestId: row.request_id,
|
|
2612
|
+
cost: row.cost,
|
|
2613
|
+
satsCost: row.sats_cost,
|
|
2614
|
+
promptTokens: row.prompt_tokens,
|
|
2615
|
+
completionTokens: row.completion_tokens,
|
|
2616
|
+
totalTokens: row.total_tokens,
|
|
2617
|
+
client: row.client ?? void 0,
|
|
2618
|
+
sessionId: row.session_id ?? void 0,
|
|
2619
|
+
tags: typeof row.tags === "string" ? JSON.parse(row.tags) : void 0
|
|
2620
|
+
});
|
|
2621
|
+
const ensureMigrated = async () => {
|
|
2622
|
+
if (!legacyStorageDriver) return;
|
|
2623
|
+
if (!migrationPromise) {
|
|
2624
|
+
migrationPromise = (async () => {
|
|
2625
|
+
const migrated = await legacyStorageDriver.getItem(
|
|
2626
|
+
MIGRATION_MARKER_KEY3,
|
|
2627
|
+
false
|
|
2628
|
+
);
|
|
2629
|
+
if (migrated) return;
|
|
2630
|
+
const legacyEntries = await legacyStorageDriver.getItem(
|
|
2631
|
+
SDK_STORAGE_KEYS.USAGE_TRACKING,
|
|
2632
|
+
[]
|
|
2633
|
+
);
|
|
2634
|
+
if (legacyEntries.length > 0) {
|
|
2635
|
+
for (const entry of legacyEntries) {
|
|
2636
|
+
appendOne(entry);
|
|
2637
|
+
}
|
|
2638
|
+
await legacyStorageDriver.removeItem(SDK_STORAGE_KEYS.USAGE_TRACKING);
|
|
2639
|
+
}
|
|
2640
|
+
await legacyStorageDriver.setItem(MIGRATION_MARKER_KEY3, true);
|
|
2641
|
+
})();
|
|
2642
|
+
}
|
|
2643
|
+
await migrationPromise;
|
|
2644
|
+
};
|
|
2645
|
+
return {
|
|
2646
|
+
async migrate() {
|
|
2647
|
+
await ensureMigrated();
|
|
2648
|
+
},
|
|
2649
|
+
async append(entry) {
|
|
2650
|
+
await ensureMigrated();
|
|
2651
|
+
appendOne(entry);
|
|
2652
|
+
},
|
|
2653
|
+
async appendMany(entries) {
|
|
2654
|
+
await ensureMigrated();
|
|
2655
|
+
for (const entry of entries) {
|
|
2656
|
+
appendOne(entry);
|
|
2657
|
+
}
|
|
2658
|
+
},
|
|
2659
|
+
async list(options2 = {}) {
|
|
2660
|
+
await ensureMigrated();
|
|
2661
|
+
const { sql, params } = buildWhereClause2(options2);
|
|
2662
|
+
const limitSql = typeof options2.limit === "number" ? " LIMIT ?" : "";
|
|
2663
|
+
const query = `SELECT * FROM ${tableName} ${sql} ORDER BY timestamp DESC${limitSql}`;
|
|
2664
|
+
let rows;
|
|
2665
|
+
if (typeof options2.limit === "number") {
|
|
2666
|
+
rows = db.query(query).all(...params, options2.limit);
|
|
2667
|
+
} else {
|
|
2668
|
+
rows = db.query(query).all(...params);
|
|
2669
|
+
}
|
|
2670
|
+
return rows.map(mapRow);
|
|
2671
|
+
},
|
|
2672
|
+
async count(options2 = {}) {
|
|
2673
|
+
const { sql, params } = buildWhereClause2(options2);
|
|
2674
|
+
const query = `SELECT COUNT(*) as count FROM ${tableName} ${sql}`;
|
|
2675
|
+
const row = db.query(query).get(...params);
|
|
2676
|
+
return Number(row?.count ?? 0);
|
|
2677
|
+
},
|
|
2678
|
+
async deleteOlderThan(timestamp) {
|
|
2679
|
+
await ensureMigrated();
|
|
2680
|
+
const before = timestamp;
|
|
2681
|
+
const result = db.query(`DELETE FROM ${tableName} WHERE timestamp < ?`).run(before);
|
|
2682
|
+
return result.changes ?? 0;
|
|
2683
|
+
},
|
|
2684
|
+
async clear() {
|
|
2685
|
+
await ensureMigrated();
|
|
2686
|
+
db.query(`DELETE FROM ${tableName}`).run();
|
|
2687
|
+
}
|
|
2688
|
+
};
|
|
2689
|
+
};
|
|
2690
|
+
|
|
2691
|
+
// storage/usageTracking/memory.ts
|
|
2692
|
+
var normalizeBaseUrl4 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2637
2693
|
var matchesFilters2 = (entry, options = {}) => {
|
|
2638
2694
|
if (typeof options.before === "number" && entry.timestamp >= options.before) {
|
|
2639
2695
|
return false;
|
|
@@ -2644,7 +2700,7 @@ var matchesFilters2 = (entry, options = {}) => {
|
|
|
2644
2700
|
if (options.modelId && entry.modelId !== options.modelId) {
|
|
2645
2701
|
return false;
|
|
2646
2702
|
}
|
|
2647
|
-
if (options.baseUrl &&
|
|
2703
|
+
if (options.baseUrl && normalizeBaseUrl4(entry.baseUrl) !== normalizeBaseUrl4(options.baseUrl)) {
|
|
2648
2704
|
return false;
|
|
2649
2705
|
}
|
|
2650
2706
|
if (options.sessionId && entry.sessionId !== options.sessionId) {
|
|
@@ -2658,18 +2714,18 @@ var matchesFilters2 = (entry, options = {}) => {
|
|
|
2658
2714
|
var createMemoryUsageTrackingDriver = (seed = []) => {
|
|
2659
2715
|
const store = /* @__PURE__ */ new Map();
|
|
2660
2716
|
for (const entry of seed) {
|
|
2661
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
2717
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
2662
2718
|
}
|
|
2663
2719
|
return {
|
|
2664
2720
|
async migrate() {
|
|
2665
2721
|
return;
|
|
2666
2722
|
},
|
|
2667
2723
|
async append(entry) {
|
|
2668
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
2724
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
2669
2725
|
},
|
|
2670
2726
|
async appendMany(entries) {
|
|
2671
2727
|
for (const entry of entries) {
|
|
2672
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
2728
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
2673
2729
|
}
|
|
2674
2730
|
},
|
|
2675
2731
|
async list(options = {}) {
|
|
@@ -2697,20 +2753,7 @@ var createMemoryUsageTrackingDriver = (seed = []) => {
|
|
|
2697
2753
|
}
|
|
2698
2754
|
};
|
|
2699
2755
|
};
|
|
2700
|
-
var
|
|
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
|
-
};
|
|
2756
|
+
var normalizeBaseUrl5 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2714
2757
|
var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
2715
2758
|
modelsFromAllProviders: {},
|
|
2716
2759
|
lastUsedModel: null,
|
|
@@ -2720,9 +2763,9 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2720
2763
|
mintsFromAllProviders: {},
|
|
2721
2764
|
infoFromAllProviders: {},
|
|
2722
2765
|
lastModelsUpdate: {},
|
|
2723
|
-
cachedTokens: [],
|
|
2724
2766
|
apiKeys: [],
|
|
2725
2767
|
childKeys: [],
|
|
2768
|
+
xcashuTokens: {},
|
|
2726
2769
|
routstr21Models: [],
|
|
2727
2770
|
lastRoutstr21ModelsUpdate: null,
|
|
2728
2771
|
cachedReceiveTokens: [],
|
|
@@ -2730,7 +2773,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2730
2773
|
setModelsFromAllProviders: (value) => {
|
|
2731
2774
|
const normalized = {};
|
|
2732
2775
|
for (const [baseUrl, models] of Object.entries(value)) {
|
|
2733
|
-
normalized[
|
|
2776
|
+
normalized[normalizeBaseUrl5(baseUrl)] = models;
|
|
2734
2777
|
}
|
|
2735
2778
|
void driver.setItem(
|
|
2736
2779
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -2743,7 +2786,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2743
2786
|
set({ lastUsedModel: value });
|
|
2744
2787
|
},
|
|
2745
2788
|
setBaseUrlsList: (value) => {
|
|
2746
|
-
const normalized = value.map((url) =>
|
|
2789
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
2747
2790
|
void driver.setItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, normalized);
|
|
2748
2791
|
set({ baseUrlsList: normalized });
|
|
2749
2792
|
},
|
|
@@ -2752,14 +2795,14 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2752
2795
|
set({ lastBaseUrlsUpdate: value });
|
|
2753
2796
|
},
|
|
2754
2797
|
setDisabledProviders: (value) => {
|
|
2755
|
-
const normalized = value.map((url) =>
|
|
2798
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
2756
2799
|
void driver.setItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, normalized);
|
|
2757
2800
|
set({ disabledProviders: normalized });
|
|
2758
2801
|
},
|
|
2759
2802
|
setMintsFromAllProviders: (value) => {
|
|
2760
2803
|
const normalized = {};
|
|
2761
2804
|
for (const [baseUrl, mints] of Object.entries(value)) {
|
|
2762
|
-
normalized[
|
|
2805
|
+
normalized[normalizeBaseUrl5(baseUrl)] = mints.map(
|
|
2763
2806
|
(mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint
|
|
2764
2807
|
);
|
|
2765
2808
|
}
|
|
@@ -2772,7 +2815,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2772
2815
|
setInfoFromAllProviders: (value) => {
|
|
2773
2816
|
const normalized = {};
|
|
2774
2817
|
for (const [baseUrl, info] of Object.entries(value)) {
|
|
2775
|
-
normalized[
|
|
2818
|
+
normalized[normalizeBaseUrl5(baseUrl)] = info;
|
|
2776
2819
|
}
|
|
2777
2820
|
void driver.setItem(SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS, normalized);
|
|
2778
2821
|
set({ infoFromAllProviders: normalized });
|
|
@@ -2780,30 +2823,17 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2780
2823
|
setLastModelsUpdate: (value) => {
|
|
2781
2824
|
const normalized = {};
|
|
2782
2825
|
for (const [baseUrl, timestamp] of Object.entries(value)) {
|
|
2783
|
-
normalized[
|
|
2826
|
+
normalized[normalizeBaseUrl5(baseUrl)] = timestamp;
|
|
2784
2827
|
}
|
|
2785
2828
|
void driver.setItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE, normalized);
|
|
2786
2829
|
set({ lastModelsUpdate: normalized });
|
|
2787
2830
|
},
|
|
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
2831
|
setApiKeys: (value) => {
|
|
2802
2832
|
set((state) => {
|
|
2803
2833
|
const updates = typeof value === "function" ? value(state.apiKeys) : value;
|
|
2804
2834
|
const normalized = updates.map((entry) => ({
|
|
2805
2835
|
...entry,
|
|
2806
|
-
baseUrl:
|
|
2836
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
2807
2837
|
balance: entry.balance ?? 0,
|
|
2808
2838
|
lastUsed: entry.lastUsed ?? null
|
|
2809
2839
|
}));
|
|
@@ -2815,7 +2845,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2815
2845
|
set((state) => {
|
|
2816
2846
|
const updates = typeof value === "function" ? value(state.childKeys) : value;
|
|
2817
2847
|
const normalized = updates.map((entry) => ({
|
|
2818
|
-
parentBaseUrl:
|
|
2848
|
+
parentBaseUrl: normalizeBaseUrl5(entry.parentBaseUrl),
|
|
2819
2849
|
childKey: entry.childKey,
|
|
2820
2850
|
balance: entry.balance ?? 0,
|
|
2821
2851
|
balanceLimit: entry.balanceLimit,
|
|
@@ -2826,6 +2856,30 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2826
2856
|
return { childKeys: normalized };
|
|
2827
2857
|
});
|
|
2828
2858
|
},
|
|
2859
|
+
setXcashuTokens: (value) => {
|
|
2860
|
+
const normalized = {};
|
|
2861
|
+
for (const [baseUrl, tokens] of Object.entries(value)) {
|
|
2862
|
+
normalized[normalizeBaseUrl5(baseUrl)] = tokens.map((entry) => ({
|
|
2863
|
+
...entry,
|
|
2864
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
2865
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
2866
|
+
tryCount: entry.tryCount ?? 0
|
|
2867
|
+
}));
|
|
2868
|
+
}
|
|
2869
|
+
void driver.setItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, normalized);
|
|
2870
|
+
set({ xcashuTokens: normalized });
|
|
2871
|
+
},
|
|
2872
|
+
updateXcashuTokenTryCount: (token, tryCount) => {
|
|
2873
|
+
const currentTokens = get().xcashuTokens;
|
|
2874
|
+
const updatedTokens = {};
|
|
2875
|
+
for (const [baseUrl, tokens] of Object.entries(currentTokens)) {
|
|
2876
|
+
updatedTokens[baseUrl] = tokens.map(
|
|
2877
|
+
(entry) => entry.token === token ? { ...entry, tryCount } : entry
|
|
2878
|
+
);
|
|
2879
|
+
}
|
|
2880
|
+
void driver.setItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, updatedTokens);
|
|
2881
|
+
set({ xcashuTokens: updatedTokens });
|
|
2882
|
+
},
|
|
2829
2883
|
setRoutstr21Models: (value) => {
|
|
2830
2884
|
void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, value);
|
|
2831
2885
|
set({ routstr21Models: value });
|
|
@@ -2867,9 +2921,9 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2867
2921
|
rawMints,
|
|
2868
2922
|
rawInfo,
|
|
2869
2923
|
rawLastModelsUpdate,
|
|
2870
|
-
rawCachedTokens,
|
|
2871
2924
|
rawApiKeys,
|
|
2872
2925
|
rawChildKeys,
|
|
2926
|
+
rawXcashuTokens,
|
|
2873
2927
|
rawRoutstr21Models,
|
|
2874
2928
|
rawLastRoutstr21ModelsUpdate,
|
|
2875
2929
|
rawCachedReceiveTokens,
|
|
@@ -2895,9 +2949,9 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2895
2949
|
SDK_STORAGE_KEYS.LAST_MODELS_UPDATE,
|
|
2896
2950
|
{}
|
|
2897
2951
|
),
|
|
2898
|
-
driver.getItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, []),
|
|
2899
2952
|
driver.getItem(SDK_STORAGE_KEYS.API_KEYS, []),
|
|
2900
2953
|
driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, []),
|
|
2954
|
+
driver.getItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, {}),
|
|
2901
2955
|
driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
|
|
2902
2956
|
driver.getItem(
|
|
2903
2957
|
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
@@ -2908,52 +2962,57 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2908
2962
|
]);
|
|
2909
2963
|
const modelsFromAllProviders = Object.fromEntries(
|
|
2910
2964
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
2911
|
-
|
|
2965
|
+
normalizeBaseUrl5(baseUrl),
|
|
2912
2966
|
models
|
|
2913
2967
|
])
|
|
2914
2968
|
);
|
|
2915
|
-
const baseUrlsList = rawBaseUrls.map((url) =>
|
|
2969
|
+
const baseUrlsList = rawBaseUrls.map((url) => normalizeBaseUrl5(url));
|
|
2916
2970
|
const disabledProviders = rawDisabledProviders.map(
|
|
2917
|
-
(url) =>
|
|
2971
|
+
(url) => normalizeBaseUrl5(url)
|
|
2918
2972
|
);
|
|
2919
2973
|
const mintsFromAllProviders = Object.fromEntries(
|
|
2920
2974
|
Object.entries(rawMints).map(([baseUrl, mints]) => [
|
|
2921
|
-
|
|
2975
|
+
normalizeBaseUrl5(baseUrl),
|
|
2922
2976
|
mints.map((mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint)
|
|
2923
2977
|
])
|
|
2924
2978
|
);
|
|
2925
2979
|
const infoFromAllProviders = Object.fromEntries(
|
|
2926
2980
|
Object.entries(rawInfo).map(([baseUrl, info]) => [
|
|
2927
|
-
|
|
2981
|
+
normalizeBaseUrl5(baseUrl),
|
|
2928
2982
|
info
|
|
2929
2983
|
])
|
|
2930
2984
|
);
|
|
2931
2985
|
const lastModelsUpdate = Object.fromEntries(
|
|
2932
2986
|
Object.entries(rawLastModelsUpdate).map(([baseUrl, timestamp]) => [
|
|
2933
|
-
|
|
2987
|
+
normalizeBaseUrl5(baseUrl),
|
|
2934
2988
|
timestamp
|
|
2935
2989
|
])
|
|
2936
2990
|
);
|
|
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
2991
|
const apiKeys = rawApiKeys.map((entry) => ({
|
|
2944
2992
|
...entry,
|
|
2945
|
-
baseUrl:
|
|
2993
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
2946
2994
|
balance: entry.balance ?? 0,
|
|
2947
2995
|
lastUsed: entry.lastUsed ?? null
|
|
2948
2996
|
}));
|
|
2949
2997
|
const childKeys = rawChildKeys.map((entry) => ({
|
|
2950
|
-
parentBaseUrl:
|
|
2998
|
+
parentBaseUrl: normalizeBaseUrl5(entry.parentBaseUrl),
|
|
2951
2999
|
childKey: entry.childKey,
|
|
2952
3000
|
balance: entry.balance ?? 0,
|
|
2953
3001
|
balanceLimit: entry.balanceLimit,
|
|
2954
3002
|
validityDate: entry.validityDate,
|
|
2955
3003
|
createdAt: entry.createdAt ?? Date.now()
|
|
2956
3004
|
}));
|
|
3005
|
+
const xcashuTokens = Object.fromEntries(
|
|
3006
|
+
Object.entries(rawXcashuTokens).map(([baseUrl, tokens]) => [
|
|
3007
|
+
normalizeBaseUrl5(baseUrl),
|
|
3008
|
+
tokens.map((entry) => ({
|
|
3009
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3010
|
+
token: entry.token,
|
|
3011
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
3012
|
+
tryCount: entry.tryCount ?? 0
|
|
3013
|
+
}))
|
|
3014
|
+
])
|
|
3015
|
+
);
|
|
2957
3016
|
const routstr21Models = rawRoutstr21Models;
|
|
2958
3017
|
const lastRoutstr21ModelsUpdate = rawLastRoutstr21ModelsUpdate;
|
|
2959
3018
|
const cachedReceiveTokens = rawCachedReceiveTokens?.map((entry) => ({
|
|
@@ -2976,9 +3035,9 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2976
3035
|
mintsFromAllProviders,
|
|
2977
3036
|
infoFromAllProviders,
|
|
2978
3037
|
lastModelsUpdate,
|
|
2979
|
-
cachedTokens,
|
|
2980
3038
|
apiKeys,
|
|
2981
3039
|
childKeys,
|
|
3040
|
+
xcashuTokens,
|
|
2982
3041
|
routstr21Models,
|
|
2983
3042
|
lastRoutstr21ModelsUpdate,
|
|
2984
3043
|
cachedReceiveTokens,
|
|
@@ -3049,7 +3108,7 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
3049
3108
|
return defaultUsageTrackingDriver;
|
|
3050
3109
|
}
|
|
3051
3110
|
if (isBun3()) {
|
|
3052
|
-
defaultUsageTrackingDriver =
|
|
3111
|
+
defaultUsageTrackingDriver = createBunSqliteUsageTrackingDriver();
|
|
3053
3112
|
return defaultUsageTrackingDriver;
|
|
3054
3113
|
}
|
|
3055
3114
|
if (isNode()) {
|
|
@@ -3063,16 +3122,20 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
3063
3122
|
};
|
|
3064
3123
|
function createSSEParserTransform(onUsage, onResponseId) {
|
|
3065
3124
|
let buffer = "";
|
|
3125
|
+
let usageCaptured = false;
|
|
3126
|
+
let responseIdCaptured = false;
|
|
3066
3127
|
const maybeCaptureUsageFromJson = (jsonText) => {
|
|
3067
3128
|
try {
|
|
3068
3129
|
const data = JSON.parse(jsonText);
|
|
3069
3130
|
const responseId = data.id;
|
|
3070
3131
|
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
3071
3132
|
onResponseId?.(responseId.trim());
|
|
3133
|
+
responseIdCaptured = true;
|
|
3072
3134
|
}
|
|
3073
3135
|
const usage = extractUsageFromSSEJson(data);
|
|
3074
3136
|
if (usage) {
|
|
3075
3137
|
onUsage(usage);
|
|
3138
|
+
usageCaptured = true;
|
|
3076
3139
|
}
|
|
3077
3140
|
} catch {
|
|
3078
3141
|
}
|
|
@@ -3128,7 +3191,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
3128
3191
|
}
|
|
3129
3192
|
var TOPUP_MARGIN = 1.2;
|
|
3130
3193
|
var RoutstrClient = class {
|
|
3131
|
-
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu") {
|
|
3194
|
+
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
|
|
3132
3195
|
this.walletAdapter = walletAdapter;
|
|
3133
3196
|
this.storageAdapter = storageAdapter;
|
|
3134
3197
|
this.providerRegistry = providerRegistry;
|
|
@@ -3146,13 +3209,9 @@ var RoutstrClient = class {
|
|
|
3146
3209
|
this.streamProcessor = new StreamProcessor();
|
|
3147
3210
|
this.providerManager = new ProviderManager(providerRegistry);
|
|
3148
3211
|
this.alertLevel = alertLevel;
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
this.mode = "lazyrefund";
|
|
3153
|
-
} else {
|
|
3154
|
-
this.mode = mode;
|
|
3155
|
-
}
|
|
3212
|
+
this.mode = mode;
|
|
3213
|
+
this.usageTrackingDriver = options.usageTrackingDriver;
|
|
3214
|
+
this.sdkStore = options.sdkStore;
|
|
3156
3215
|
}
|
|
3157
3216
|
cashuSpender;
|
|
3158
3217
|
balanceManager;
|
|
@@ -3161,6 +3220,8 @@ var RoutstrClient = class {
|
|
|
3161
3220
|
alertLevel;
|
|
3162
3221
|
mode;
|
|
3163
3222
|
debugLevel = "WARN";
|
|
3223
|
+
usageTrackingDriver;
|
|
3224
|
+
sdkStore;
|
|
3164
3225
|
/**
|
|
3165
3226
|
* Get the current client mode
|
|
3166
3227
|
*/
|
|
@@ -3230,11 +3291,13 @@ var RoutstrClient = class {
|
|
|
3230
3291
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3231
3292
|
token: prepared.tokenUsed,
|
|
3232
3293
|
baseUrl: prepared.baseUrlUsed,
|
|
3294
|
+
mintUrl: params.mintUrl,
|
|
3233
3295
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3234
3296
|
response: prepared.response,
|
|
3235
3297
|
modelId: prepared.modelId,
|
|
3236
3298
|
usage: prepared.capturedUsage,
|
|
3237
|
-
requestId: prepared.capturedResponseId
|
|
3299
|
+
requestId: prepared.capturedResponseId,
|
|
3300
|
+
clientApiKey: prepared.clientApiKey
|
|
3238
3301
|
});
|
|
3239
3302
|
prepared.response.satsSpent = satsSpent;
|
|
3240
3303
|
prepared.response.usage = prepared.capturedUsage;
|
|
@@ -3253,11 +3316,13 @@ var RoutstrClient = class {
|
|
|
3253
3316
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3254
3317
|
token: prepared.tokenUsed,
|
|
3255
3318
|
baseUrl: prepared.baseUrlUsed,
|
|
3319
|
+
mintUrl: params.mintUrl,
|
|
3256
3320
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3257
3321
|
response: prepared.response,
|
|
3258
3322
|
modelId: prepared.modelId,
|
|
3259
3323
|
usage: prepared.capturedUsage,
|
|
3260
|
-
requestId: prepared.capturedResponseId
|
|
3324
|
+
requestId: prepared.capturedResponseId,
|
|
3325
|
+
clientApiKey: prepared.clientApiKey
|
|
3261
3326
|
});
|
|
3262
3327
|
prepared.response.satsSpent = satsSpent;
|
|
3263
3328
|
res.end();
|
|
@@ -3273,11 +3338,13 @@ var RoutstrClient = class {
|
|
|
3273
3338
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3274
3339
|
token: prepared.tokenUsed,
|
|
3275
3340
|
baseUrl: prepared.baseUrlUsed,
|
|
3341
|
+
mintUrl: params.mintUrl,
|
|
3276
3342
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3277
3343
|
response: prepared.response,
|
|
3278
3344
|
modelId: prepared.modelId,
|
|
3279
3345
|
usage: prepared.capturedUsage,
|
|
3280
|
-
requestId: prepared.capturedResponseId
|
|
3346
|
+
requestId: prepared.capturedResponseId,
|
|
3347
|
+
clientApiKey: prepared.clientApiKey
|
|
3281
3348
|
});
|
|
3282
3349
|
prepared.response.satsSpent = satsSpent;
|
|
3283
3350
|
prepared.response.usage = prepared.capturedUsage;
|
|
@@ -3307,8 +3374,10 @@ var RoutstrClient = class {
|
|
|
3307
3374
|
headers = {},
|
|
3308
3375
|
baseUrl,
|
|
3309
3376
|
mintUrl,
|
|
3310
|
-
modelId
|
|
3377
|
+
modelId,
|
|
3378
|
+
clientApiKey: providedClientApiKey
|
|
3311
3379
|
} = params;
|
|
3380
|
+
const clientApiKey = providedClientApiKey ?? this._extractClientApiKey(headers);
|
|
3312
3381
|
await this._checkBalance();
|
|
3313
3382
|
let requiredSats = 1;
|
|
3314
3383
|
let selectedModel;
|
|
@@ -3330,7 +3399,6 @@ var RoutstrClient = class {
|
|
|
3330
3399
|
amount: requiredSats,
|
|
3331
3400
|
baseUrl
|
|
3332
3401
|
});
|
|
3333
|
-
this._log("DEBUG", token, baseUrl);
|
|
3334
3402
|
let requestBody = body;
|
|
3335
3403
|
if (body && typeof body === "object") {
|
|
3336
3404
|
const bodyObj = body;
|
|
@@ -3338,7 +3406,7 @@ var RoutstrClient = class {
|
|
|
3338
3406
|
requestBody = { ...bodyObj, stream: false };
|
|
3339
3407
|
}
|
|
3340
3408
|
}
|
|
3341
|
-
const baseHeaders = this._buildBaseHeaders(
|
|
3409
|
+
const baseHeaders = this._buildBaseHeaders();
|
|
3342
3410
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
3343
3411
|
const response = await this._makeRequest({
|
|
3344
3412
|
path,
|
|
@@ -3390,9 +3458,21 @@ var RoutstrClient = class {
|
|
|
3390
3458
|
tokenBalanceInSats,
|
|
3391
3459
|
modelId,
|
|
3392
3460
|
capturedUsage,
|
|
3393
|
-
capturedResponseId
|
|
3461
|
+
capturedResponseId,
|
|
3462
|
+
clientApiKey
|
|
3394
3463
|
};
|
|
3395
3464
|
}
|
|
3465
|
+
/**
|
|
3466
|
+
* Extract clientApiKey from Authorization Bearer token if present
|
|
3467
|
+
*/
|
|
3468
|
+
_extractClientApiKey(headers) {
|
|
3469
|
+
const authHeader = headers["Authorization"] || headers["authorization"];
|
|
3470
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
3471
|
+
const extractedKey = authHeader.slice(7);
|
|
3472
|
+
return extractedKey;
|
|
3473
|
+
}
|
|
3474
|
+
return void 0;
|
|
3475
|
+
}
|
|
3396
3476
|
/**
|
|
3397
3477
|
* Fetch AI response with streaming
|
|
3398
3478
|
*/
|
|
@@ -3496,6 +3576,7 @@ var RoutstrClient = class {
|
|
|
3496
3576
|
let satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3497
3577
|
token,
|
|
3498
3578
|
baseUrl: baseUrlUsed,
|
|
3579
|
+
mintUrl,
|
|
3499
3580
|
initialTokenBalance: tokenBalanceInSats,
|
|
3500
3581
|
fallbackSatsSpent: isApikeysEstimate ? this._getEstimatedCosts(selectedModel, streamingResult) : void 0,
|
|
3501
3582
|
response,
|
|
@@ -3534,7 +3615,6 @@ var RoutstrClient = class {
|
|
|
3534
3615
|
try {
|
|
3535
3616
|
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
3536
3617
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
3537
|
-
this._log("DEBUG", "HEADERS,", headers);
|
|
3538
3618
|
const response = await fetch(url, {
|
|
3539
3619
|
method,
|
|
3540
3620
|
headers,
|
|
@@ -3603,8 +3683,6 @@ var RoutstrClient = class {
|
|
|
3603
3683
|
`[RoutstrClient] _handleErrorResponse: Token restored successfully, amount=${tryReceiveTokenResult.amount}`
|
|
3604
3684
|
);
|
|
3605
3685
|
tryNextProvider = true;
|
|
3606
|
-
if (this.mode === "lazyrefund")
|
|
3607
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
3608
3686
|
} else {
|
|
3609
3687
|
this._log(
|
|
3610
3688
|
"DEBUG",
|
|
@@ -3652,22 +3730,15 @@ var RoutstrClient = class {
|
|
|
3652
3730
|
);
|
|
3653
3731
|
}
|
|
3654
3732
|
}
|
|
3655
|
-
if (status === 402 && !tryNextProvider &&
|
|
3733
|
+
if (status === 402 && !tryNextProvider && this.mode === "apikeys") {
|
|
3656
3734
|
this.storageAdapter.getApiKey(baseUrl);
|
|
3657
3735
|
let topupAmount = params.requiredSats;
|
|
3658
3736
|
try {
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
);
|
|
3665
|
-
currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
3666
|
-
} else if (this.mode === "lazyrefund") {
|
|
3667
|
-
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
3668
|
-
const tokenEntry = distribution.find((t) => t.baseUrl === baseUrl);
|
|
3669
|
-
currentBalance = tokenEntry?.amount ?? 0;
|
|
3670
|
-
}
|
|
3737
|
+
const currentBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
3738
|
+
params.token,
|
|
3739
|
+
baseUrl
|
|
3740
|
+
);
|
|
3741
|
+
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
3671
3742
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
3672
3743
|
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
3673
3744
|
} catch (e) {
|
|
@@ -3804,34 +3875,7 @@ var RoutstrClient = class {
|
|
|
3804
3875
|
"DEBUG",
|
|
3805
3876
|
`[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`
|
|
3806
3877
|
);
|
|
3807
|
-
if (this.mode === "
|
|
3808
|
-
try {
|
|
3809
|
-
const refundResult = await this.balanceManager.refund({
|
|
3810
|
-
mintUrl,
|
|
3811
|
-
baseUrl,
|
|
3812
|
-
token: params.token
|
|
3813
|
-
});
|
|
3814
|
-
this._log(
|
|
3815
|
-
"DEBUG",
|
|
3816
|
-
`[RoutstrClient] _handleErrorResponse: Lazyrefund result: success=${refundResult.success}`
|
|
3817
|
-
);
|
|
3818
|
-
if (refundResult.success) this.storageAdapter.removeToken(baseUrl);
|
|
3819
|
-
else
|
|
3820
|
-
throw new ProviderError(
|
|
3821
|
-
baseUrl,
|
|
3822
|
-
status,
|
|
3823
|
-
"refund failed",
|
|
3824
|
-
requestId
|
|
3825
|
-
);
|
|
3826
|
-
} catch (error) {
|
|
3827
|
-
throw new ProviderError(
|
|
3828
|
-
baseUrl,
|
|
3829
|
-
status,
|
|
3830
|
-
"Failed to refund token",
|
|
3831
|
-
requestId
|
|
3832
|
-
);
|
|
3833
|
-
}
|
|
3834
|
-
} else if (this.mode === "apikeys") {
|
|
3878
|
+
if (this.mode === "apikeys") {
|
|
3835
3879
|
this._log(
|
|
3836
3880
|
"DEBUG",
|
|
3837
3881
|
`[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
|
|
@@ -3847,7 +3891,8 @@ var RoutstrClient = class {
|
|
|
3847
3891
|
const refundResult = await this.balanceManager.refundApiKey({
|
|
3848
3892
|
mintUrl,
|
|
3849
3893
|
baseUrl,
|
|
3850
|
-
apiKey: token
|
|
3894
|
+
apiKey: token,
|
|
3895
|
+
forceRefund: true
|
|
3851
3896
|
});
|
|
3852
3897
|
this._log(
|
|
3853
3898
|
"DEBUG",
|
|
@@ -3929,12 +3974,14 @@ var RoutstrClient = class {
|
|
|
3929
3974
|
const {
|
|
3930
3975
|
token,
|
|
3931
3976
|
baseUrl,
|
|
3977
|
+
mintUrl,
|
|
3932
3978
|
initialTokenBalance,
|
|
3933
3979
|
fallbackSatsSpent,
|
|
3934
3980
|
response,
|
|
3935
3981
|
modelId,
|
|
3936
3982
|
usage,
|
|
3937
|
-
requestId
|
|
3983
|
+
requestId,
|
|
3984
|
+
clientApiKey
|
|
3938
3985
|
} = params;
|
|
3939
3986
|
let satsSpent = initialTokenBalance;
|
|
3940
3987
|
if (this.mode === "xcashu" && response) {
|
|
@@ -3942,19 +3989,14 @@ var RoutstrClient = class {
|
|
|
3942
3989
|
if (refundToken) {
|
|
3943
3990
|
try {
|
|
3944
3991
|
const receiveResult = await this.cashuSpender.receiveToken(refundToken);
|
|
3945
|
-
|
|
3992
|
+
if (receiveResult.success) {
|
|
3993
|
+
this.storageAdapter.removeXcashuToken(baseUrl, token);
|
|
3994
|
+
satsSpent = initialTokenBalance - receiveResult.amount * (receiveResult.unit == "sat" ? 1 : 1e3);
|
|
3995
|
+
}
|
|
3946
3996
|
} catch (error) {
|
|
3947
3997
|
this._log("ERROR", "[xcashu] Failed to receive refund token:", error);
|
|
3948
3998
|
}
|
|
3949
3999
|
}
|
|
3950
|
-
} else if (this.mode === "lazyrefund") {
|
|
3951
|
-
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
3952
|
-
token,
|
|
3953
|
-
baseUrl
|
|
3954
|
-
);
|
|
3955
|
-
const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
3956
|
-
this.storageAdapter.updateTokenBalance(baseUrl, latestTokenBalance);
|
|
3957
|
-
satsSpent = initialTokenBalance - latestTokenBalance;
|
|
3958
4000
|
} else if (this.mode === "apikeys") {
|
|
3959
4001
|
try {
|
|
3960
4002
|
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
@@ -3989,8 +4031,18 @@ var RoutstrClient = class {
|
|
|
3989
4031
|
modelId,
|
|
3990
4032
|
satsSpent,
|
|
3991
4033
|
usage,
|
|
3992
|
-
requestId
|
|
4034
|
+
requestId,
|
|
4035
|
+
clientApiKey
|
|
3993
4036
|
});
|
|
4037
|
+
(async () => {
|
|
4038
|
+
try {
|
|
4039
|
+
const xcashuResults = await this.cashuSpender.refundXcashuTokens(mintUrl);
|
|
4040
|
+
this._log("DEBUG", "Refund xcashu tokens results:", xcashuResults);
|
|
4041
|
+
const results = await this.cashuSpender.refundProviders(mintUrl);
|
|
4042
|
+
} catch (error) {
|
|
4043
|
+
this._log("ERROR", "Failed to refund providers:", error);
|
|
4044
|
+
}
|
|
4045
|
+
})();
|
|
3994
4046
|
return satsSpent;
|
|
3995
4047
|
}
|
|
3996
4048
|
async _trackResponseUsage(params) {
|
|
@@ -4001,7 +4053,8 @@ var RoutstrClient = class {
|
|
|
4001
4053
|
modelId,
|
|
4002
4054
|
satsSpent,
|
|
4003
4055
|
usage: providedUsage,
|
|
4004
|
-
requestId: providedRequestId
|
|
4056
|
+
requestId: providedRequestId,
|
|
4057
|
+
clientApiKey
|
|
4005
4058
|
} = params;
|
|
4006
4059
|
if (!response || !modelId) {
|
|
4007
4060
|
return;
|
|
@@ -4028,13 +4081,14 @@ var RoutstrClient = class {
|
|
|
4028
4081
|
return;
|
|
4029
4082
|
}
|
|
4030
4083
|
const finalRequestId = requestId || "unknown";
|
|
4031
|
-
const store = await getDefaultSdkStore();
|
|
4084
|
+
const store = this.sdkStore ?? await getDefaultSdkStore();
|
|
4032
4085
|
const state = store.getState();
|
|
4086
|
+
const matchKey = clientApiKey ?? token;
|
|
4033
4087
|
const matchingClient = state.clientIds.find(
|
|
4034
|
-
(client) => client.apiKey ===
|
|
4088
|
+
(client) => client.apiKey === matchKey
|
|
4035
4089
|
);
|
|
4036
4090
|
const entryId = finalRequestId === "unknown" ? `req-${Date.now()}-${modelId}` : finalRequestId;
|
|
4037
|
-
const usageTracking = getDefaultUsageTrackingDriver();
|
|
4091
|
+
const usageTracking = this.usageTrackingDriver ?? getDefaultUsageTrackingDriver();
|
|
4038
4092
|
const entry = {
|
|
4039
4093
|
id: entryId,
|
|
4040
4094
|
timestamp: Date.now(),
|
|
@@ -4109,11 +4163,11 @@ var RoutstrClient = class {
|
|
|
4109
4163
|
return estimatedCosts;
|
|
4110
4164
|
}
|
|
4111
4165
|
/**
|
|
4112
|
-
* Get pending
|
|
4166
|
+
* Get pending API key amount
|
|
4113
4167
|
*/
|
|
4114
4168
|
_getPendingCashuTokenAmount() {
|
|
4115
|
-
const
|
|
4116
|
-
return
|
|
4169
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
4170
|
+
return apiKeyDistribution.reduce((total, item) => total + item.amount, 0);
|
|
4117
4171
|
}
|
|
4118
4172
|
/**
|
|
4119
4173
|
* Handle errors and notify callbacks
|
|
@@ -4260,8 +4314,8 @@ var RoutstrClient = class {
|
|
|
4260
4314
|
const spendResult = await this.cashuSpender.spend({
|
|
4261
4315
|
mintUrl,
|
|
4262
4316
|
amount,
|
|
4263
|
-
baseUrl:
|
|
4264
|
-
reuseToken:
|
|
4317
|
+
baseUrl: "",
|
|
4318
|
+
reuseToken: false
|
|
4265
4319
|
});
|
|
4266
4320
|
if (!spendResult.token) {
|
|
4267
4321
|
this._log(
|
|
@@ -4274,6 +4328,7 @@ var RoutstrClient = class {
|
|
|
4274
4328
|
"DEBUG",
|
|
4275
4329
|
`[RoutstrClient] _spendToken: Cashu token created, token preview: ${spendResult.token}, balance: ${spendResult.balance} ${spendResult.unit ?? "sat"}`
|
|
4276
4330
|
);
|
|
4331
|
+
this.storageAdapter.addXcashuToken(baseUrl, spendResult.token);
|
|
4277
4332
|
}
|
|
4278
4333
|
return {
|
|
4279
4334
|
token: spendResult.token,
|