@routstr/sdk 0.2.5 → 0.2.7
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 +34 -6
- package/dist/client/index.d.ts +34 -6
- package/dist/client/index.js +773 -479
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +773 -479
- package/dist/client/index.mjs.map +1 -1
- package/dist/discovery/index.js +1 -1
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +1 -1
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.d.mts +21 -9
- package/dist/index.d.ts +21 -9
- package/dist/index.js +897 -558
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +895 -559
- 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 +39 -162
- package/dist/storage/index.d.ts +39 -162
- package/dist/storage/index.js +529 -193
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +527 -194
- package/dist/storage/index.mjs.map +1 -1
- package/dist/store-DGeLPv9E.d.mts +172 -0
- package/dist/store-h7m23ffq.d.ts +172 -0
- package/dist/wallet/index.d.mts +26 -26
- package/dist/wallet/index.d.ts +26 -26
- package/dist/wallet/index.js +143 -271
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +143 -271
- 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,131 @@ 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 by calling the provider's refund endpoint.
|
|
453
|
+
* The xcashu token acts as an API key to claim the refund, and the response contains
|
|
454
|
+
* the actual refunded Cashu token which is then received into the wallet.
|
|
455
|
+
* @param mintUrl - The mint URL for receiving tokens
|
|
456
|
+
* @param excludeBaseUrls - Base URLs to exclude from refund (optional)
|
|
457
|
+
* @returns Results for each xcashu token refund attempt
|
|
481
458
|
*/
|
|
482
|
-
async
|
|
459
|
+
async refundXcashuTokens(mintUrl, excludeBaseUrls) {
|
|
483
460
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
apiKey: apiKeyEntryFull.key,
|
|
533
|
-
forceRefund
|
|
534
|
-
});
|
|
535
|
-
if (refundResult.success) {
|
|
536
|
-
this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, 0);
|
|
461
|
+
const xcashuTokens = this.storageAdapter.getXcashuTokens();
|
|
462
|
+
const excludedUrls = new Set(excludeBaseUrls || []);
|
|
463
|
+
for (const [baseUrl, tokens] of Object.entries(xcashuTokens)) {
|
|
464
|
+
if (excludedUrls.has(baseUrl)) continue;
|
|
465
|
+
for (const xcashuToken of tokens) {
|
|
466
|
+
try {
|
|
467
|
+
if (!this.balanceManager) {
|
|
468
|
+
throw new Error("BalanceManager not available for xcashu refund");
|
|
469
|
+
}
|
|
470
|
+
const fetchResult = await this.balanceManager.fetchRefundToken(
|
|
471
|
+
baseUrl,
|
|
472
|
+
xcashuToken.token,
|
|
473
|
+
true
|
|
474
|
+
);
|
|
475
|
+
if (!fetchResult.success || !fetchResult.token) {
|
|
476
|
+
throw new Error(
|
|
477
|
+
fetchResult.error || "Failed to fetch refund token from provider"
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
const receiveResult = await this.receiveToken(fetchResult.token);
|
|
481
|
+
if (receiveResult.success) {
|
|
482
|
+
this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
|
|
483
|
+
results.push({
|
|
484
|
+
baseUrl,
|
|
485
|
+
token: xcashuToken.token,
|
|
486
|
+
success: true
|
|
487
|
+
});
|
|
488
|
+
this._log(
|
|
489
|
+
"DEBUG",
|
|
490
|
+
`[CashuSpender] refundXcashuTokens: Successfully refunded xcashu token for ${baseUrl}, amount=${receiveResult.amount}`
|
|
491
|
+
);
|
|
492
|
+
} else {
|
|
493
|
+
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
494
|
+
const newTryCount = currentTryCount + 1;
|
|
495
|
+
this.storageAdapter.updateXcashuTokenTryCount(
|
|
496
|
+
xcashuToken.token,
|
|
497
|
+
newTryCount
|
|
498
|
+
);
|
|
499
|
+
results.push({
|
|
500
|
+
baseUrl,
|
|
501
|
+
token: xcashuToken.token,
|
|
502
|
+
success: false,
|
|
503
|
+
error: receiveResult.message ?? "Refund failed"
|
|
504
|
+
});
|
|
505
|
+
this._log(
|
|
506
|
+
"DEBUG",
|
|
507
|
+
`[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}`
|
|
508
|
+
);
|
|
537
509
|
}
|
|
510
|
+
} catch (error) {
|
|
511
|
+
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
512
|
+
const newTryCount = currentTryCount + 1;
|
|
513
|
+
this.storageAdapter.updateXcashuTokenTryCount(
|
|
514
|
+
xcashuToken.token,
|
|
515
|
+
newTryCount
|
|
516
|
+
);
|
|
517
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
538
518
|
results.push({
|
|
539
|
-
baseUrl
|
|
540
|
-
|
|
519
|
+
baseUrl,
|
|
520
|
+
token: xcashuToken.token,
|
|
521
|
+
success: false,
|
|
522
|
+
error: errorMessage
|
|
541
523
|
});
|
|
524
|
+
this._log(
|
|
525
|
+
"ERROR",
|
|
526
|
+
`[CashuSpender] refundXcashuTokens: Exception during refund for ${baseUrl}: ${errorMessage}, incremented tryCount to ${newTryCount}`
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return results;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Refund specific providers without retrying spend
|
|
535
|
+
*/
|
|
536
|
+
async refundProviders(mintUrl, forceRefund) {
|
|
537
|
+
const results = [];
|
|
538
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
539
|
+
for (const apiKeyEntry of apiKeyDistribution) {
|
|
540
|
+
const apiKeyEntryFull = this.storageAdapter.getApiKey(
|
|
541
|
+
apiKeyEntry.baseUrl
|
|
542
|
+
);
|
|
543
|
+
if (apiKeyEntryFull && this.balanceManager) {
|
|
544
|
+
const refundResult = await this.balanceManager.refundApiKey({
|
|
545
|
+
mintUrl,
|
|
546
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
547
|
+
apiKey: apiKeyEntryFull.key,
|
|
548
|
+
forceRefund
|
|
549
|
+
});
|
|
550
|
+
if (refundResult.success) {
|
|
551
|
+
this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
|
|
542
552
|
} else {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
553
|
+
this.storageAdapter.updateApiKeyBalance(
|
|
554
|
+
apiKeyEntry.baseUrl,
|
|
555
|
+
apiKeyEntry.amount
|
|
556
|
+
);
|
|
547
557
|
}
|
|
558
|
+
results.push({
|
|
559
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
560
|
+
success: refundResult.success
|
|
561
|
+
});
|
|
562
|
+
} else {
|
|
563
|
+
results.push({
|
|
564
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
565
|
+
success: false
|
|
566
|
+
});
|
|
548
567
|
}
|
|
549
568
|
}
|
|
550
569
|
return results;
|
|
@@ -629,13 +648,8 @@ var BalanceManager = class {
|
|
|
629
648
|
normalizedMintBalances[url] = balanceInSats;
|
|
630
649
|
totalMintBalance += balanceInSats;
|
|
631
650
|
}
|
|
632
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
633
651
|
const providerBalances = {};
|
|
634
652
|
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
653
|
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
640
654
|
for (const apiKey of apiKeys) {
|
|
641
655
|
if (!providerBalances[apiKey.baseUrl]) {
|
|
@@ -650,57 +664,6 @@ var BalanceManager = class {
|
|
|
650
664
|
mintBalances: normalizedMintBalances
|
|
651
665
|
};
|
|
652
666
|
}
|
|
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
667
|
/**
|
|
705
668
|
* Refund API key balance - convert remaining API key balance to cashu token
|
|
706
669
|
* @param options - Refund options including forceRefund flag
|
|
@@ -728,7 +691,7 @@ var BalanceManager = class {
|
|
|
728
691
|
}
|
|
729
692
|
let fetchResult;
|
|
730
693
|
try {
|
|
731
|
-
fetchResult = await this.
|
|
694
|
+
fetchResult = await this.fetchRefundToken(baseUrl, apiKey);
|
|
732
695
|
if (!fetchResult.success) {
|
|
733
696
|
return {
|
|
734
697
|
success: false,
|
|
@@ -764,9 +727,9 @@ var BalanceManager = class {
|
|
|
764
727
|
}
|
|
765
728
|
}
|
|
766
729
|
/**
|
|
767
|
-
* Fetch refund token from provider API using API key authentication
|
|
730
|
+
* Fetch refund token from provider API using API key (or xcashu token) authentication
|
|
768
731
|
*/
|
|
769
|
-
async
|
|
732
|
+
async fetchRefundToken(baseUrl, apiKeyOrToken, xCashu = false) {
|
|
770
733
|
if (!baseUrl) {
|
|
771
734
|
return {
|
|
772
735
|
success: false,
|
|
@@ -780,12 +743,17 @@ var BalanceManager = class {
|
|
|
780
743
|
controller.abort();
|
|
781
744
|
}, 6e4);
|
|
782
745
|
try {
|
|
746
|
+
const headers = {
|
|
747
|
+
"Content-Type": "application/json"
|
|
748
|
+
};
|
|
749
|
+
if (xCashu) {
|
|
750
|
+
headers["X-Cashu"] = apiKeyOrToken;
|
|
751
|
+
} else {
|
|
752
|
+
headers["Authorization"] = `Bearer ${apiKeyOrToken}`;
|
|
753
|
+
}
|
|
783
754
|
const response = await fetch(url, {
|
|
784
755
|
method: "POST",
|
|
785
|
-
headers
|
|
786
|
-
Authorization: `Bearer ${apiKey}`,
|
|
787
|
-
"Content-Type": "application/json"
|
|
788
|
-
},
|
|
756
|
+
headers,
|
|
789
757
|
signal: controller.signal
|
|
790
758
|
});
|
|
791
759
|
clearTimeout(timeoutId);
|
|
@@ -806,10 +774,7 @@ var BalanceManager = class {
|
|
|
806
774
|
};
|
|
807
775
|
} catch (error) {
|
|
808
776
|
clearTimeout(timeoutId);
|
|
809
|
-
console.error(
|
|
810
|
-
"[BalanceManager._fetchRefundTokenWithApiKey] Fetch error",
|
|
811
|
-
error
|
|
812
|
-
);
|
|
777
|
+
console.error("[BalanceManager.fetchRefundToken] Fetch error", error);
|
|
813
778
|
if (error instanceof Error) {
|
|
814
779
|
if (error.name === "AbortError") {
|
|
815
780
|
return {
|
|
@@ -836,8 +801,9 @@ var BalanceManager = class {
|
|
|
836
801
|
if (!amount || amount <= 0) {
|
|
837
802
|
return { success: false, message: "Invalid top up amount" };
|
|
838
803
|
}
|
|
839
|
-
const
|
|
840
|
-
|
|
804
|
+
const apiKeyEntry = providedToken ? null : this.storageAdapter.getApiKey(baseUrl);
|
|
805
|
+
const apiKey = providedToken || apiKeyEntry?.key;
|
|
806
|
+
if (!apiKey) {
|
|
841
807
|
return { success: false, message: "No API key available for top up" };
|
|
842
808
|
}
|
|
843
809
|
let cashuToken = null;
|
|
@@ -855,11 +821,7 @@ var BalanceManager = class {
|
|
|
855
821
|
};
|
|
856
822
|
}
|
|
857
823
|
cashuToken = tokenResult.token;
|
|
858
|
-
const topUpResult = await this._postTopUp(
|
|
859
|
-
baseUrl,
|
|
860
|
-
storedToken,
|
|
861
|
-
cashuToken
|
|
862
|
-
);
|
|
824
|
+
const topUpResult = await this._postTopUp(baseUrl, apiKey, cashuToken);
|
|
863
825
|
requestId = topUpResult.requestId;
|
|
864
826
|
console.log(topUpResult);
|
|
865
827
|
if (!topUpResult.success) {
|
|
@@ -1072,38 +1034,11 @@ var BalanceManager = class {
|
|
|
1072
1034
|
return candidates;
|
|
1073
1035
|
}
|
|
1074
1036
|
async _refundOtherProvidersForTopUp(baseUrl, mintUrl, retryCount) {
|
|
1075
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1076
1037
|
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1077
1038
|
const forceRefund = retryCount >= 2;
|
|
1078
|
-
const toRefund = pendingDistribution.filter(
|
|
1079
|
-
(pending) => pending.baseUrl !== baseUrl
|
|
1080
|
-
);
|
|
1081
1039
|
const apiKeysToRefund = apiKeyDistribution.filter(
|
|
1082
1040
|
(apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
|
|
1083
1041
|
);
|
|
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
1042
|
const apiKeyRefundResults = await Promise.allSettled(
|
|
1108
1043
|
apiKeysToRefund.map(async (apiKeyEntry) => {
|
|
1109
1044
|
const fullApiKeyEntry = this.storageAdapter.getApiKey(
|
|
@@ -1127,77 +1062,6 @@ var BalanceManager = class {
|
|
|
1127
1062
|
}
|
|
1128
1063
|
}
|
|
1129
1064
|
}
|
|
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
1065
|
/**
|
|
1202
1066
|
* Post topup request to provider API
|
|
1203
1067
|
*/
|
|
@@ -1323,7 +1187,7 @@ var BalanceManager = class {
|
|
|
1323
1187
|
console.log(response.status);
|
|
1324
1188
|
const data = await response.json();
|
|
1325
1189
|
console.log("FAILED ", data);
|
|
1326
|
-
const isInvalidApiKey = response.status === 401 && data?.code === "invalid_api_key" && data?.message?.includes("proofs already spent");
|
|
1190
|
+
const isInvalidApiKey = response.status === 401 && data?.detail?.error?.code === "invalid_api_key" && data?.detail?.error?.message?.includes("proofs already spent");
|
|
1327
1191
|
return {
|
|
1328
1192
|
amount: -1,
|
|
1329
1193
|
reserved: data.reserved ?? 0,
|
|
@@ -1765,8 +1629,13 @@ function isInsecureHttpUrl(url) {
|
|
|
1765
1629
|
return url.startsWith("http://");
|
|
1766
1630
|
}
|
|
1767
1631
|
var ProviderManager = class _ProviderManager {
|
|
1768
|
-
constructor(providerRegistry) {
|
|
1632
|
+
constructor(providerRegistry, store) {
|
|
1769
1633
|
this.providerRegistry = providerRegistry;
|
|
1634
|
+
this.instanceId = `pm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1635
|
+
if (store) {
|
|
1636
|
+
this.store = store;
|
|
1637
|
+
this.hydrateFromStore();
|
|
1638
|
+
}
|
|
1770
1639
|
}
|
|
1771
1640
|
failedProviders = /* @__PURE__ */ new Set();
|
|
1772
1641
|
/** Track when each provider last failed (provider URL -> timestamp) */
|
|
@@ -1775,14 +1644,57 @@ var ProviderManager = class _ProviderManager {
|
|
|
1775
1644
|
providersOnCoolDown = [];
|
|
1776
1645
|
/** Cooldown duration in milliseconds (5 minutes) */
|
|
1777
1646
|
static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
|
|
1647
|
+
/** Optional persistent store for failure tracking */
|
|
1648
|
+
store = null;
|
|
1649
|
+
/** Instance ID for debugging */
|
|
1650
|
+
instanceId;
|
|
1651
|
+
/**
|
|
1652
|
+
* Hydrate in-memory state from persistent store
|
|
1653
|
+
*/
|
|
1654
|
+
hydrateFromStore() {
|
|
1655
|
+
if (!this.store) return;
|
|
1656
|
+
const state = this.store.getState();
|
|
1657
|
+
this.failedProviders = new Set(state.failedProviders);
|
|
1658
|
+
this.lastFailed = new Map(Object.entries(state.lastFailed));
|
|
1659
|
+
const now = Date.now();
|
|
1660
|
+
this.providersOnCoolDown = state.providersOnCooldown.filter(
|
|
1661
|
+
(entry) => now - entry.timestamp < _ProviderManager.COOLDOWN_DURATION_MS
|
|
1662
|
+
).map((entry) => [entry.baseUrl, entry.timestamp]);
|
|
1663
|
+
console.log(`[ProviderManager:${this.instanceId}] Hydrated from store:`);
|
|
1664
|
+
console.log(` failedProviders: ${this.failedProviders.size}`);
|
|
1665
|
+
console.log(` lastFailed: ${this.lastFailed.size}`);
|
|
1666
|
+
console.log(` providersOnCooldown: ${this.providersOnCoolDown.length}`);
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Get instance ID for debugging
|
|
1670
|
+
*/
|
|
1671
|
+
getInstanceId() {
|
|
1672
|
+
return this.instanceId;
|
|
1673
|
+
}
|
|
1778
1674
|
/**
|
|
1779
1675
|
* Clean up expired cooldown entries
|
|
1780
1676
|
*/
|
|
1781
1677
|
cleanupExpiredCooldowns() {
|
|
1782
1678
|
const now = Date.now();
|
|
1679
|
+
const before = this.providersOnCoolDown.length;
|
|
1783
1680
|
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1784
|
-
([, timestamp]) =>
|
|
1681
|
+
([url, timestamp]) => {
|
|
1682
|
+
const age = now - timestamp;
|
|
1683
|
+
const isExpired = age >= _ProviderManager.COOLDOWN_DURATION_MS;
|
|
1684
|
+
if (isExpired) {
|
|
1685
|
+
console.log(
|
|
1686
|
+
`[cleanupExpiredCooldowns:${this.instanceId}] Removing expired cooldown for ${url} (age: ${age}ms, cooldown: ${_ProviderManager.COOLDOWN_DURATION_MS}ms)`
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
return !isExpired;
|
|
1690
|
+
}
|
|
1785
1691
|
);
|
|
1692
|
+
const after = this.providersOnCoolDown.length;
|
|
1693
|
+
if (before !== after) {
|
|
1694
|
+
console.log(
|
|
1695
|
+
`[cleanupExpiredCooldowns:${this.instanceId}] Cleaned up ${before - after} expired cooldown(s), ${after} remaining`
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1786
1698
|
}
|
|
1787
1699
|
/**
|
|
1788
1700
|
* Get the cooldown duration in milliseconds
|
|
@@ -1795,7 +1707,8 @@ var ProviderManager = class _ProviderManager {
|
|
|
1795
1707
|
*/
|
|
1796
1708
|
isOnCooldown(baseUrl) {
|
|
1797
1709
|
this.cleanupExpiredCooldowns();
|
|
1798
|
-
|
|
1710
|
+
const result = this.providersOnCoolDown.some(([url]) => url === baseUrl);
|
|
1711
|
+
return result;
|
|
1799
1712
|
}
|
|
1800
1713
|
/**
|
|
1801
1714
|
* Get all providers currently on cooldown
|
|
@@ -1809,6 +1722,9 @@ var ProviderManager = class _ProviderManager {
|
|
|
1809
1722
|
*/
|
|
1810
1723
|
resetFailedProviders() {
|
|
1811
1724
|
this.failedProviders.clear();
|
|
1725
|
+
if (this.store) {
|
|
1726
|
+
this.store.getState().setFailedProviders([]);
|
|
1727
|
+
}
|
|
1812
1728
|
}
|
|
1813
1729
|
/**
|
|
1814
1730
|
* Get the last failed timestamp for a provider
|
|
@@ -1829,13 +1745,62 @@ var ProviderManager = class _ProviderManager {
|
|
|
1829
1745
|
markFailed(baseUrl) {
|
|
1830
1746
|
const now = Date.now();
|
|
1831
1747
|
const lastFailure = this.lastFailed.get(baseUrl);
|
|
1748
|
+
console.log(`[markFailed:${this.instanceId}] baseUrl: ${baseUrl}`);
|
|
1749
|
+
console.log(
|
|
1750
|
+
`[markFailed:${this.instanceId}] lastFailure from map: ${lastFailure}`
|
|
1751
|
+
);
|
|
1752
|
+
console.log(
|
|
1753
|
+
`[markFailed:${this.instanceId}] current timestamp (now): ${now}`
|
|
1754
|
+
);
|
|
1755
|
+
console.log(
|
|
1756
|
+
`[markFailed:${this.instanceId}] COOLDOWN_DURATION_MS: ${_ProviderManager.COOLDOWN_DURATION_MS}`
|
|
1757
|
+
);
|
|
1758
|
+
if (lastFailure !== void 0) {
|
|
1759
|
+
const timeSinceLastFailure = now - lastFailure;
|
|
1760
|
+
console.log(
|
|
1761
|
+
`[markFailed:${this.instanceId}] timeSinceLastFailure: ${timeSinceLastFailure}ms`
|
|
1762
|
+
);
|
|
1763
|
+
console.log(
|
|
1764
|
+
`[markFailed:${this.instanceId}] isWithinCooldownWindow: ${timeSinceLastFailure < _ProviderManager.COOLDOWN_DURATION_MS}`
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1832
1767
|
this.lastFailed.set(baseUrl, now);
|
|
1833
1768
|
this.failedProviders.add(baseUrl);
|
|
1769
|
+
if (this.store) {
|
|
1770
|
+
this.store.getState().setLastFailedTimestamp(baseUrl, now);
|
|
1771
|
+
this.store.getState().addFailedProvider(baseUrl);
|
|
1772
|
+
}
|
|
1773
|
+
console.log(
|
|
1774
|
+
`[markFailed:${this.instanceId}] Updated lastFailed map for ${baseUrl} to ${now}`
|
|
1775
|
+
);
|
|
1776
|
+
console.log(
|
|
1777
|
+
`[markFailed:${this.instanceId}] failedProviders set size: ${this.failedProviders.size}`
|
|
1778
|
+
);
|
|
1834
1779
|
if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
|
|
1780
|
+
console.log(
|
|
1781
|
+
`[markFailed:${this.instanceId}] Second failure detected within cooldown window for ${baseUrl}`
|
|
1782
|
+
);
|
|
1835
1783
|
if (!this.isOnCooldown(baseUrl)) {
|
|
1836
1784
|
this.providersOnCoolDown.push([baseUrl, now]);
|
|
1785
|
+
if (this.store) {
|
|
1786
|
+
this.store.getState().addProviderOnCooldown(baseUrl, now);
|
|
1787
|
+
}
|
|
1788
|
+
console.log(
|
|
1789
|
+
`[markFailed:${this.instanceId}] Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
|
|
1790
|
+
);
|
|
1791
|
+
} else {
|
|
1792
|
+
console.log(
|
|
1793
|
+
`[markFailed:${this.instanceId}] Provider ${baseUrl} is already on cooldown`
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
} else {
|
|
1797
|
+
if (lastFailure === void 0) {
|
|
1837
1798
|
console.log(
|
|
1838
|
-
`
|
|
1799
|
+
`[markFailed:${this.instanceId}] First failure for ${baseUrl} - not adding to cooldown yet`
|
|
1800
|
+
);
|
|
1801
|
+
} else {
|
|
1802
|
+
console.log(
|
|
1803
|
+
`[markFailed:${this.instanceId}] Failure outside cooldown window for ${baseUrl} (timeSinceLastFailure: ${now - lastFailure}ms)`
|
|
1839
1804
|
);
|
|
1840
1805
|
}
|
|
1841
1806
|
}
|
|
@@ -1847,18 +1812,27 @@ var ProviderManager = class _ProviderManager {
|
|
|
1847
1812
|
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1848
1813
|
([url]) => url !== baseUrl
|
|
1849
1814
|
);
|
|
1815
|
+
if (this.store) {
|
|
1816
|
+
this.store.getState().removeProviderFromCooldown(baseUrl);
|
|
1817
|
+
}
|
|
1850
1818
|
}
|
|
1851
1819
|
/**
|
|
1852
1820
|
* Clear all cooldown tracking
|
|
1853
1821
|
*/
|
|
1854
1822
|
clearCooldowns() {
|
|
1855
1823
|
this.providersOnCoolDown = [];
|
|
1824
|
+
if (this.store) {
|
|
1825
|
+
this.store.getState().clearProvidersOnCooldown();
|
|
1826
|
+
}
|
|
1856
1827
|
}
|
|
1857
1828
|
/**
|
|
1858
1829
|
* Clear all failure tracking (lastFailed timestamps)
|
|
1859
1830
|
*/
|
|
1860
1831
|
clearFailureHistory() {
|
|
1861
1832
|
this.lastFailed.clear();
|
|
1833
|
+
if (this.store) {
|
|
1834
|
+
this.store.getState().setLastFailed({});
|
|
1835
|
+
}
|
|
1862
1836
|
}
|
|
1863
1837
|
/**
|
|
1864
1838
|
* Check if a provider has failed
|
|
@@ -2181,38 +2155,54 @@ var createMemoryDriver = (seed) => {
|
|
|
2181
2155
|
var isBun = () => {
|
|
2182
2156
|
return typeof process.versions.bun !== "undefined";
|
|
2183
2157
|
};
|
|
2184
|
-
var
|
|
2158
|
+
var cachedDbModule = null;
|
|
2159
|
+
var loadDatabase = async (dbPath) => {
|
|
2185
2160
|
if (isBun()) {
|
|
2186
2161
|
throw new Error(
|
|
2187
|
-
"SQLite driver not supported in Bun. Use
|
|
2162
|
+
"SQLite driver not supported in Bun. Use createBunSqliteDriver() instead."
|
|
2188
2163
|
);
|
|
2189
2164
|
}
|
|
2190
|
-
let Database = null;
|
|
2191
2165
|
try {
|
|
2192
|
-
|
|
2166
|
+
if (!cachedDbModule) {
|
|
2167
|
+
cachedDbModule = (await import('better-sqlite3')).default;
|
|
2168
|
+
}
|
|
2169
|
+
return new cachedDbModule(dbPath);
|
|
2193
2170
|
} catch (error) {
|
|
2194
2171
|
throw new Error(
|
|
2195
2172
|
`better-sqlite3 is required for sqlite storage. Install it to use sqlite storage. (${error})`
|
|
2196
2173
|
);
|
|
2197
2174
|
}
|
|
2198
|
-
return new Database(dbPath);
|
|
2199
2175
|
};
|
|
2200
2176
|
var createSqliteDriver = (options = {}) => {
|
|
2201
2177
|
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2202
2178
|
const tableName = options.tableName || "sdk_storage";
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
const
|
|
2208
|
-
|
|
2209
|
-
|
|
2179
|
+
let db;
|
|
2180
|
+
let selectStmt;
|
|
2181
|
+
let upsertStmt;
|
|
2182
|
+
let deleteStmt;
|
|
2183
|
+
const initDb = async () => {
|
|
2184
|
+
if (!db) {
|
|
2185
|
+
db = await loadDatabase(dbPath);
|
|
2186
|
+
db.exec(
|
|
2187
|
+
`CREATE TABLE IF NOT EXISTS ${tableName} (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
|
|
2188
|
+
);
|
|
2189
|
+
selectStmt = db.prepare(`SELECT value FROM ${tableName} WHERE key = ?`);
|
|
2190
|
+
upsertStmt = db.prepare(
|
|
2191
|
+
`INSERT INTO ${tableName} (key, value) VALUES (?, ?)
|
|
2210
2192
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
2211
|
-
|
|
2212
|
-
|
|
2193
|
+
);
|
|
2194
|
+
deleteStmt = db.prepare(`DELETE FROM ${tableName} WHERE key = ?`);
|
|
2195
|
+
}
|
|
2196
|
+
};
|
|
2197
|
+
const ensureInit = async () => {
|
|
2198
|
+
if (!db) {
|
|
2199
|
+
await initDb();
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2213
2202
|
return {
|
|
2214
2203
|
async getItem(key, defaultValue) {
|
|
2215
2204
|
try {
|
|
2205
|
+
await ensureInit();
|
|
2216
2206
|
const row = selectStmt.get(key);
|
|
2217
2207
|
if (!row || typeof row.value !== "string") return defaultValue;
|
|
2218
2208
|
try {
|
|
@@ -2230,6 +2220,7 @@ var createSqliteDriver = (options = {}) => {
|
|
|
2230
2220
|
},
|
|
2231
2221
|
async setItem(key, value) {
|
|
2232
2222
|
try {
|
|
2223
|
+
await ensureInit();
|
|
2233
2224
|
upsertStmt.run(key, JSON.stringify(value));
|
|
2234
2225
|
} catch (error) {
|
|
2235
2226
|
console.error(`SQLite setItem failed for key "${key}":`, error);
|
|
@@ -2237,6 +2228,7 @@ var createSqliteDriver = (options = {}) => {
|
|
|
2237
2228
|
},
|
|
2238
2229
|
async removeItem(key) {
|
|
2239
2230
|
try {
|
|
2231
|
+
await ensureInit();
|
|
2240
2232
|
deleteStmt.run(key);
|
|
2241
2233
|
} catch (error) {
|
|
2242
2234
|
console.error(`SQLite removeItem failed for key "${key}":`, error);
|
|
@@ -2255,14 +2247,17 @@ var SDK_STORAGE_KEYS = {
|
|
|
2255
2247
|
INFO_FROM_ALL_PROVIDERS: "info_from_all_providers",
|
|
2256
2248
|
LAST_MODELS_UPDATE: "lastModelsUpdate",
|
|
2257
2249
|
LAST_BASE_URLS_UPDATE: "lastBaseUrlsUpdate",
|
|
2258
|
-
LOCAL_CASHU_TOKENS: "local_cashu_tokens",
|
|
2259
2250
|
API_KEYS: "api_keys",
|
|
2260
2251
|
CHILD_KEYS: "child_keys",
|
|
2252
|
+
XCASHU_TOKENS: "xcashu_tokens",
|
|
2261
2253
|
ROUTSTR21_MODELS: "routstr21Models",
|
|
2262
2254
|
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
2263
2255
|
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
2264
2256
|
USAGE_TRACKING: "usage_tracking",
|
|
2265
|
-
CLIENT_IDS: "client_ids"
|
|
2257
|
+
CLIENT_IDS: "client_ids",
|
|
2258
|
+
FAILED_PROVIDERS: "failed_providers",
|
|
2259
|
+
LAST_FAILED: "last_failed",
|
|
2260
|
+
PROVIDERS_ON_COOLDOWN: "providers_on_cooldown"
|
|
2266
2261
|
};
|
|
2267
2262
|
|
|
2268
2263
|
// storage/usageTracking/indexedDB.ts
|
|
@@ -2448,21 +2443,23 @@ var normalizeBaseUrl2 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUr
|
|
|
2448
2443
|
var isBun2 = () => {
|
|
2449
2444
|
return typeof process.versions.bun !== "undefined";
|
|
2450
2445
|
};
|
|
2451
|
-
var
|
|
2446
|
+
var cachedDbModule2 = null;
|
|
2447
|
+
var loadDatabase2 = async (dbPath) => {
|
|
2452
2448
|
if (isBun2()) {
|
|
2453
2449
|
throw new Error(
|
|
2454
2450
|
"SQLite driver not supported in Bun. Use createMemoryDriver() instead."
|
|
2455
2451
|
);
|
|
2456
2452
|
}
|
|
2457
|
-
let Database = null;
|
|
2458
2453
|
try {
|
|
2459
|
-
|
|
2454
|
+
if (!cachedDbModule2) {
|
|
2455
|
+
cachedDbModule2 = (await import('better-sqlite3')).default;
|
|
2456
|
+
}
|
|
2457
|
+
return new cachedDbModule2(dbPath);
|
|
2460
2458
|
} catch (error) {
|
|
2461
2459
|
throw new Error(
|
|
2462
2460
|
`better-sqlite3 is required for sqlite usage tracking. Install it to use sqlite storage. (${error})`
|
|
2463
2461
|
);
|
|
2464
2462
|
}
|
|
2465
|
-
return new Database(dbPath);
|
|
2466
2463
|
};
|
|
2467
2464
|
var buildWhereClause = (options = {}) => {
|
|
2468
2465
|
const clauses = [];
|
|
@@ -2499,38 +2496,49 @@ var buildWhereClause = (options = {}) => {
|
|
|
2499
2496
|
var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
2500
2497
|
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2501
2498
|
const tableName = options.tableName || "usage_tracking";
|
|
2502
|
-
const db = createDatabase2(dbPath);
|
|
2503
2499
|
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
|
-
`);
|
|
2500
|
+
let db;
|
|
2501
|
+
let insertStmt;
|
|
2533
2502
|
let migrationComplete = false;
|
|
2503
|
+
const initDb = async () => {
|
|
2504
|
+
if (!db) {
|
|
2505
|
+
db = await loadDatabase2(dbPath);
|
|
2506
|
+
db.exec(`
|
|
2507
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
2508
|
+
id TEXT PRIMARY KEY,
|
|
2509
|
+
timestamp INTEGER NOT NULL,
|
|
2510
|
+
model_id TEXT NOT NULL,
|
|
2511
|
+
base_url TEXT NOT NULL,
|
|
2512
|
+
request_id TEXT NOT NULL,
|
|
2513
|
+
cost REAL NOT NULL,
|
|
2514
|
+
sats_cost REAL NOT NULL,
|
|
2515
|
+
prompt_tokens INTEGER NOT NULL,
|
|
2516
|
+
completion_tokens INTEGER NOT NULL,
|
|
2517
|
+
total_tokens INTEGER NOT NULL,
|
|
2518
|
+
client TEXT,
|
|
2519
|
+
session_id TEXT,
|
|
2520
|
+
tags TEXT
|
|
2521
|
+
);
|
|
2522
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp);
|
|
2523
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id);
|
|
2524
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url);
|
|
2525
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_session_id ON ${tableName}(session_id);
|
|
2526
|
+
CREATE INDEX IF NOT EXISTS idx_${tableName}_client ON ${tableName}(client);
|
|
2527
|
+
`);
|
|
2528
|
+
insertStmt = db.prepare(`
|
|
2529
|
+
INSERT OR REPLACE INTO ${tableName} (
|
|
2530
|
+
id, timestamp, model_id, base_url, request_id,
|
|
2531
|
+
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
2532
|
+
client, session_id, tags
|
|
2533
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2534
|
+
`);
|
|
2535
|
+
}
|
|
2536
|
+
};
|
|
2537
|
+
const ensureInit = async () => {
|
|
2538
|
+
if (!db) {
|
|
2539
|
+
await initDb();
|
|
2540
|
+
}
|
|
2541
|
+
};
|
|
2534
2542
|
const appendOne = (entry) => {
|
|
2535
2543
|
insertStmt.run(
|
|
2536
2544
|
entry.id,
|
|
@@ -2588,19 +2596,23 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
2588
2596
|
});
|
|
2589
2597
|
return {
|
|
2590
2598
|
async migrate() {
|
|
2599
|
+
await ensureInit();
|
|
2591
2600
|
await ensureMigrated();
|
|
2592
2601
|
},
|
|
2593
2602
|
async append(entry) {
|
|
2603
|
+
await ensureInit();
|
|
2594
2604
|
await ensureMigrated();
|
|
2595
2605
|
appendOne(entry);
|
|
2596
2606
|
},
|
|
2597
2607
|
async appendMany(entries) {
|
|
2608
|
+
await ensureInit();
|
|
2598
2609
|
await ensureMigrated();
|
|
2599
2610
|
for (const entry of entries) {
|
|
2600
2611
|
appendOne(entry);
|
|
2601
2612
|
}
|
|
2602
2613
|
},
|
|
2603
2614
|
async list(options2 = {}) {
|
|
2615
|
+
await ensureInit();
|
|
2604
2616
|
await ensureMigrated();
|
|
2605
2617
|
const { sql, params } = buildWhereClause(options2);
|
|
2606
2618
|
const limitSql = typeof options2.limit === "number" ? " LIMIT ?" : "";
|
|
@@ -2613,6 +2625,7 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
2613
2625
|
return rows.map(mapRow);
|
|
2614
2626
|
},
|
|
2615
2627
|
async count(options2 = {}) {
|
|
2628
|
+
await ensureInit();
|
|
2616
2629
|
await ensureMigrated();
|
|
2617
2630
|
const { sql, params } = buildWhereClause(options2);
|
|
2618
2631
|
const stmt = db.prepare(`SELECT COUNT(*) as count FROM ${tableName} ${sql}`);
|
|
@@ -2620,20 +2633,197 @@ var createSqliteUsageTrackingDriver = (options = {}) => {
|
|
|
2620
2633
|
return Number(row?.count ?? 0);
|
|
2621
2634
|
},
|
|
2622
2635
|
async deleteOlderThan(timestamp) {
|
|
2636
|
+
await ensureInit();
|
|
2623
2637
|
await ensureMigrated();
|
|
2624
2638
|
const stmt = db.prepare(`DELETE FROM ${tableName} WHERE timestamp < ?`);
|
|
2625
2639
|
const result = stmt.run(timestamp);
|
|
2626
2640
|
return result.changes;
|
|
2627
2641
|
},
|
|
2628
2642
|
async clear() {
|
|
2643
|
+
await ensureInit();
|
|
2629
2644
|
await ensureMigrated();
|
|
2630
2645
|
db.prepare(`DELETE FROM ${tableName}`).run();
|
|
2631
2646
|
}
|
|
2632
2647
|
};
|
|
2633
2648
|
};
|
|
2634
2649
|
|
|
2635
|
-
// storage/usageTracking/
|
|
2650
|
+
// storage/usageTracking/bunSqlite.ts
|
|
2651
|
+
var MIGRATION_MARKER_KEY3 = "usage_tracking_migration_v1";
|
|
2636
2652
|
var normalizeBaseUrl3 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2653
|
+
var buildWhereClause2 = (options = {}) => {
|
|
2654
|
+
const clauses = [];
|
|
2655
|
+
const params = [];
|
|
2656
|
+
if (typeof options.before === "number") {
|
|
2657
|
+
clauses.push("timestamp < ?");
|
|
2658
|
+
params.push(options.before);
|
|
2659
|
+
}
|
|
2660
|
+
if (typeof options.after === "number") {
|
|
2661
|
+
clauses.push("timestamp > ?");
|
|
2662
|
+
params.push(options.after);
|
|
2663
|
+
}
|
|
2664
|
+
if (options.modelId) {
|
|
2665
|
+
clauses.push("model_id = ?");
|
|
2666
|
+
params.push(options.modelId);
|
|
2667
|
+
}
|
|
2668
|
+
if (options.baseUrl) {
|
|
2669
|
+
clauses.push("base_url = ?");
|
|
2670
|
+
params.push(normalizeBaseUrl3(options.baseUrl));
|
|
2671
|
+
}
|
|
2672
|
+
if (options.sessionId) {
|
|
2673
|
+
clauses.push("session_id = ?");
|
|
2674
|
+
params.push(options.sessionId);
|
|
2675
|
+
}
|
|
2676
|
+
if (options.client) {
|
|
2677
|
+
clauses.push("client = ?");
|
|
2678
|
+
params.push(options.client);
|
|
2679
|
+
}
|
|
2680
|
+
return {
|
|
2681
|
+
sql: clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "",
|
|
2682
|
+
params
|
|
2683
|
+
};
|
|
2684
|
+
};
|
|
2685
|
+
var createBunSqliteUsageTrackingDriver = (options = {}) => {
|
|
2686
|
+
const dbPath = options.dbPath || "routstr.sqlite";
|
|
2687
|
+
const tableName = options.tableName || "usage_tracking";
|
|
2688
|
+
const legacyStorageDriver = options.legacyStorageDriver;
|
|
2689
|
+
const SQLiteDatabase = options.sqlite?.Database;
|
|
2690
|
+
let migrationPromise = null;
|
|
2691
|
+
if (!SQLiteDatabase) {
|
|
2692
|
+
throw new Error(
|
|
2693
|
+
"Bun SQLite Database constructor is required. Pass { sqlite: { Database } } when creating the driver."
|
|
2694
|
+
);
|
|
2695
|
+
}
|
|
2696
|
+
const db = new SQLiteDatabase(dbPath);
|
|
2697
|
+
db.run(`
|
|
2698
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
2699
|
+
id TEXT PRIMARY KEY,
|
|
2700
|
+
timestamp INTEGER NOT NULL,
|
|
2701
|
+
model_id TEXT NOT NULL,
|
|
2702
|
+
base_url TEXT NOT NULL,
|
|
2703
|
+
request_id TEXT NOT NULL,
|
|
2704
|
+
cost REAL NOT NULL,
|
|
2705
|
+
sats_cost REAL NOT NULL,
|
|
2706
|
+
prompt_tokens INTEGER NOT NULL,
|
|
2707
|
+
completion_tokens INTEGER NOT NULL,
|
|
2708
|
+
total_tokens INTEGER NOT NULL,
|
|
2709
|
+
client TEXT,
|
|
2710
|
+
session_id TEXT,
|
|
2711
|
+
tags TEXT
|
|
2712
|
+
)
|
|
2713
|
+
`);
|
|
2714
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_timestamp ON ${tableName}(timestamp)`);
|
|
2715
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_model_id ON ${tableName}(model_id)`);
|
|
2716
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_${tableName}_base_url ON ${tableName}(base_url)`);
|
|
2717
|
+
const appendOne = (entry) => {
|
|
2718
|
+
db.query(`
|
|
2719
|
+
INSERT OR REPLACE INTO ${tableName} (
|
|
2720
|
+
id, timestamp, model_id, base_url, request_id,
|
|
2721
|
+
cost, sats_cost, prompt_tokens, completion_tokens, total_tokens,
|
|
2722
|
+
client, session_id, tags
|
|
2723
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2724
|
+
`).run(
|
|
2725
|
+
entry.id,
|
|
2726
|
+
entry.timestamp,
|
|
2727
|
+
entry.modelId,
|
|
2728
|
+
normalizeBaseUrl3(entry.baseUrl),
|
|
2729
|
+
entry.requestId,
|
|
2730
|
+
entry.cost,
|
|
2731
|
+
entry.satsCost,
|
|
2732
|
+
entry.promptTokens,
|
|
2733
|
+
entry.completionTokens,
|
|
2734
|
+
entry.totalTokens,
|
|
2735
|
+
entry.client ?? null,
|
|
2736
|
+
entry.sessionId ?? null,
|
|
2737
|
+
JSON.stringify(entry.tags ?? [])
|
|
2738
|
+
);
|
|
2739
|
+
};
|
|
2740
|
+
const mapRow = (row) => ({
|
|
2741
|
+
id: row.id,
|
|
2742
|
+
timestamp: row.timestamp,
|
|
2743
|
+
modelId: row.model_id,
|
|
2744
|
+
baseUrl: row.base_url,
|
|
2745
|
+
requestId: row.request_id,
|
|
2746
|
+
cost: row.cost,
|
|
2747
|
+
satsCost: row.sats_cost,
|
|
2748
|
+
promptTokens: row.prompt_tokens,
|
|
2749
|
+
completionTokens: row.completion_tokens,
|
|
2750
|
+
totalTokens: row.total_tokens,
|
|
2751
|
+
client: row.client ?? void 0,
|
|
2752
|
+
sessionId: row.session_id ?? void 0,
|
|
2753
|
+
tags: typeof row.tags === "string" ? JSON.parse(row.tags) : void 0
|
|
2754
|
+
});
|
|
2755
|
+
const ensureMigrated = async () => {
|
|
2756
|
+
if (!legacyStorageDriver) return;
|
|
2757
|
+
if (!migrationPromise) {
|
|
2758
|
+
migrationPromise = (async () => {
|
|
2759
|
+
const migrated = await legacyStorageDriver.getItem(
|
|
2760
|
+
MIGRATION_MARKER_KEY3,
|
|
2761
|
+
false
|
|
2762
|
+
);
|
|
2763
|
+
if (migrated) return;
|
|
2764
|
+
const legacyEntries = await legacyStorageDriver.getItem(
|
|
2765
|
+
SDK_STORAGE_KEYS.USAGE_TRACKING,
|
|
2766
|
+
[]
|
|
2767
|
+
);
|
|
2768
|
+
if (legacyEntries.length > 0) {
|
|
2769
|
+
for (const entry of legacyEntries) {
|
|
2770
|
+
appendOne(entry);
|
|
2771
|
+
}
|
|
2772
|
+
await legacyStorageDriver.removeItem(SDK_STORAGE_KEYS.USAGE_TRACKING);
|
|
2773
|
+
}
|
|
2774
|
+
await legacyStorageDriver.setItem(MIGRATION_MARKER_KEY3, true);
|
|
2775
|
+
})();
|
|
2776
|
+
}
|
|
2777
|
+
await migrationPromise;
|
|
2778
|
+
};
|
|
2779
|
+
return {
|
|
2780
|
+
async migrate() {
|
|
2781
|
+
await ensureMigrated();
|
|
2782
|
+
},
|
|
2783
|
+
async append(entry) {
|
|
2784
|
+
await ensureMigrated();
|
|
2785
|
+
appendOne(entry);
|
|
2786
|
+
},
|
|
2787
|
+
async appendMany(entries) {
|
|
2788
|
+
await ensureMigrated();
|
|
2789
|
+
for (const entry of entries) {
|
|
2790
|
+
appendOne(entry);
|
|
2791
|
+
}
|
|
2792
|
+
},
|
|
2793
|
+
async list(options2 = {}) {
|
|
2794
|
+
await ensureMigrated();
|
|
2795
|
+
const { sql, params } = buildWhereClause2(options2);
|
|
2796
|
+
const limitSql = typeof options2.limit === "number" ? " LIMIT ?" : "";
|
|
2797
|
+
const query = `SELECT * FROM ${tableName} ${sql} ORDER BY timestamp DESC${limitSql}`;
|
|
2798
|
+
let rows;
|
|
2799
|
+
if (typeof options2.limit === "number") {
|
|
2800
|
+
rows = db.query(query).all(...params, options2.limit);
|
|
2801
|
+
} else {
|
|
2802
|
+
rows = db.query(query).all(...params);
|
|
2803
|
+
}
|
|
2804
|
+
return rows.map(mapRow);
|
|
2805
|
+
},
|
|
2806
|
+
async count(options2 = {}) {
|
|
2807
|
+
const { sql, params } = buildWhereClause2(options2);
|
|
2808
|
+
const query = `SELECT COUNT(*) as count FROM ${tableName} ${sql}`;
|
|
2809
|
+
const row = db.query(query).get(...params);
|
|
2810
|
+
return Number(row?.count ?? 0);
|
|
2811
|
+
},
|
|
2812
|
+
async deleteOlderThan(timestamp) {
|
|
2813
|
+
await ensureMigrated();
|
|
2814
|
+
const before = timestamp;
|
|
2815
|
+
const result = db.query(`DELETE FROM ${tableName} WHERE timestamp < ?`).run(before);
|
|
2816
|
+
return result.changes ?? 0;
|
|
2817
|
+
},
|
|
2818
|
+
async clear() {
|
|
2819
|
+
await ensureMigrated();
|
|
2820
|
+
db.query(`DELETE FROM ${tableName}`).run();
|
|
2821
|
+
}
|
|
2822
|
+
};
|
|
2823
|
+
};
|
|
2824
|
+
|
|
2825
|
+
// storage/usageTracking/memory.ts
|
|
2826
|
+
var normalizeBaseUrl4 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2637
2827
|
var matchesFilters2 = (entry, options = {}) => {
|
|
2638
2828
|
if (typeof options.before === "number" && entry.timestamp >= options.before) {
|
|
2639
2829
|
return false;
|
|
@@ -2644,7 +2834,7 @@ var matchesFilters2 = (entry, options = {}) => {
|
|
|
2644
2834
|
if (options.modelId && entry.modelId !== options.modelId) {
|
|
2645
2835
|
return false;
|
|
2646
2836
|
}
|
|
2647
|
-
if (options.baseUrl &&
|
|
2837
|
+
if (options.baseUrl && normalizeBaseUrl4(entry.baseUrl) !== normalizeBaseUrl4(options.baseUrl)) {
|
|
2648
2838
|
return false;
|
|
2649
2839
|
}
|
|
2650
2840
|
if (options.sessionId && entry.sessionId !== options.sessionId) {
|
|
@@ -2658,18 +2848,18 @@ var matchesFilters2 = (entry, options = {}) => {
|
|
|
2658
2848
|
var createMemoryUsageTrackingDriver = (seed = []) => {
|
|
2659
2849
|
const store = /* @__PURE__ */ new Map();
|
|
2660
2850
|
for (const entry of seed) {
|
|
2661
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
2851
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
2662
2852
|
}
|
|
2663
2853
|
return {
|
|
2664
2854
|
async migrate() {
|
|
2665
2855
|
return;
|
|
2666
2856
|
},
|
|
2667
2857
|
async append(entry) {
|
|
2668
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
2858
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
2669
2859
|
},
|
|
2670
2860
|
async appendMany(entries) {
|
|
2671
2861
|
for (const entry of entries) {
|
|
2672
|
-
store.set(entry.id, { ...entry, baseUrl:
|
|
2862
|
+
store.set(entry.id, { ...entry, baseUrl: normalizeBaseUrl4(entry.baseUrl) });
|
|
2673
2863
|
}
|
|
2674
2864
|
},
|
|
2675
2865
|
async list(options = {}) {
|
|
@@ -2697,20 +2887,7 @@ var createMemoryUsageTrackingDriver = (seed = []) => {
|
|
|
2697
2887
|
}
|
|
2698
2888
|
};
|
|
2699
2889
|
};
|
|
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
|
-
};
|
|
2890
|
+
var normalizeBaseUrl5 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
2714
2891
|
var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
2715
2892
|
modelsFromAllProviders: {},
|
|
2716
2893
|
lastUsedModel: null,
|
|
@@ -2720,17 +2897,20 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2720
2897
|
mintsFromAllProviders: {},
|
|
2721
2898
|
infoFromAllProviders: {},
|
|
2722
2899
|
lastModelsUpdate: {},
|
|
2723
|
-
cachedTokens: [],
|
|
2724
2900
|
apiKeys: [],
|
|
2725
2901
|
childKeys: [],
|
|
2902
|
+
xcashuTokens: {},
|
|
2726
2903
|
routstr21Models: [],
|
|
2727
2904
|
lastRoutstr21ModelsUpdate: null,
|
|
2728
2905
|
cachedReceiveTokens: [],
|
|
2729
2906
|
clientIds: [],
|
|
2907
|
+
failedProviders: [],
|
|
2908
|
+
lastFailed: {},
|
|
2909
|
+
providersOnCooldown: [],
|
|
2730
2910
|
setModelsFromAllProviders: (value) => {
|
|
2731
2911
|
const normalized = {};
|
|
2732
2912
|
for (const [baseUrl, models] of Object.entries(value)) {
|
|
2733
|
-
normalized[
|
|
2913
|
+
normalized[normalizeBaseUrl5(baseUrl)] = models;
|
|
2734
2914
|
}
|
|
2735
2915
|
void driver.setItem(
|
|
2736
2916
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -2743,7 +2923,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2743
2923
|
set({ lastUsedModel: value });
|
|
2744
2924
|
},
|
|
2745
2925
|
setBaseUrlsList: (value) => {
|
|
2746
|
-
const normalized = value.map((url) =>
|
|
2926
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
2747
2927
|
void driver.setItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, normalized);
|
|
2748
2928
|
set({ baseUrlsList: normalized });
|
|
2749
2929
|
},
|
|
@@ -2752,14 +2932,14 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2752
2932
|
set({ lastBaseUrlsUpdate: value });
|
|
2753
2933
|
},
|
|
2754
2934
|
setDisabledProviders: (value) => {
|
|
2755
|
-
const normalized = value.map((url) =>
|
|
2935
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
2756
2936
|
void driver.setItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, normalized);
|
|
2757
2937
|
set({ disabledProviders: normalized });
|
|
2758
2938
|
},
|
|
2759
2939
|
setMintsFromAllProviders: (value) => {
|
|
2760
2940
|
const normalized = {};
|
|
2761
2941
|
for (const [baseUrl, mints] of Object.entries(value)) {
|
|
2762
|
-
normalized[
|
|
2942
|
+
normalized[normalizeBaseUrl5(baseUrl)] = mints.map(
|
|
2763
2943
|
(mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint
|
|
2764
2944
|
);
|
|
2765
2945
|
}
|
|
@@ -2772,7 +2952,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2772
2952
|
setInfoFromAllProviders: (value) => {
|
|
2773
2953
|
const normalized = {};
|
|
2774
2954
|
for (const [baseUrl, info] of Object.entries(value)) {
|
|
2775
|
-
normalized[
|
|
2955
|
+
normalized[normalizeBaseUrl5(baseUrl)] = info;
|
|
2776
2956
|
}
|
|
2777
2957
|
void driver.setItem(SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS, normalized);
|
|
2778
2958
|
set({ infoFromAllProviders: normalized });
|
|
@@ -2780,30 +2960,17 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2780
2960
|
setLastModelsUpdate: (value) => {
|
|
2781
2961
|
const normalized = {};
|
|
2782
2962
|
for (const [baseUrl, timestamp] of Object.entries(value)) {
|
|
2783
|
-
normalized[
|
|
2963
|
+
normalized[normalizeBaseUrl5(baseUrl)] = timestamp;
|
|
2784
2964
|
}
|
|
2785
2965
|
void driver.setItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE, normalized);
|
|
2786
2966
|
set({ lastModelsUpdate: normalized });
|
|
2787
2967
|
},
|
|
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
2968
|
setApiKeys: (value) => {
|
|
2802
2969
|
set((state) => {
|
|
2803
2970
|
const updates = typeof value === "function" ? value(state.apiKeys) : value;
|
|
2804
2971
|
const normalized = updates.map((entry) => ({
|
|
2805
2972
|
...entry,
|
|
2806
|
-
baseUrl:
|
|
2973
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
2807
2974
|
balance: entry.balance ?? 0,
|
|
2808
2975
|
lastUsed: entry.lastUsed ?? null
|
|
2809
2976
|
}));
|
|
@@ -2815,7 +2982,7 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2815
2982
|
set((state) => {
|
|
2816
2983
|
const updates = typeof value === "function" ? value(state.childKeys) : value;
|
|
2817
2984
|
const normalized = updates.map((entry) => ({
|
|
2818
|
-
parentBaseUrl:
|
|
2985
|
+
parentBaseUrl: normalizeBaseUrl5(entry.parentBaseUrl),
|
|
2819
2986
|
childKey: entry.childKey,
|
|
2820
2987
|
balance: entry.balance ?? 0,
|
|
2821
2988
|
balanceLimit: entry.balanceLimit,
|
|
@@ -2826,6 +2993,30 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2826
2993
|
return { childKeys: normalized };
|
|
2827
2994
|
});
|
|
2828
2995
|
},
|
|
2996
|
+
setXcashuTokens: (value) => {
|
|
2997
|
+
const normalized = {};
|
|
2998
|
+
for (const [baseUrl, tokens] of Object.entries(value)) {
|
|
2999
|
+
normalized[normalizeBaseUrl5(baseUrl)] = tokens.map((entry) => ({
|
|
3000
|
+
...entry,
|
|
3001
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3002
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
3003
|
+
tryCount: entry.tryCount ?? 0
|
|
3004
|
+
}));
|
|
3005
|
+
}
|
|
3006
|
+
void driver.setItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, normalized);
|
|
3007
|
+
set({ xcashuTokens: normalized });
|
|
3008
|
+
},
|
|
3009
|
+
updateXcashuTokenTryCount: (token, tryCount) => {
|
|
3010
|
+
const currentTokens = get().xcashuTokens;
|
|
3011
|
+
const updatedTokens = {};
|
|
3012
|
+
for (const [baseUrl, tokens] of Object.entries(currentTokens)) {
|
|
3013
|
+
updatedTokens[baseUrl] = tokens.map(
|
|
3014
|
+
(entry) => entry.token === token ? { ...entry, tryCount } : entry
|
|
3015
|
+
);
|
|
3016
|
+
}
|
|
3017
|
+
void driver.setItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, updatedTokens);
|
|
3018
|
+
set({ xcashuTokens: updatedTokens });
|
|
3019
|
+
},
|
|
2829
3020
|
setRoutstr21Models: (value) => {
|
|
2830
3021
|
void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, value);
|
|
2831
3022
|
set({ routstr21Models: value });
|
|
@@ -2855,6 +3046,71 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2855
3046
|
void driver.setItem(SDK_STORAGE_KEYS.CLIENT_IDS, normalized);
|
|
2856
3047
|
return { clientIds: normalized };
|
|
2857
3048
|
});
|
|
3049
|
+
},
|
|
3050
|
+
// ========== Failure Tracking ==========
|
|
3051
|
+
setFailedProviders: (value) => {
|
|
3052
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
3053
|
+
void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, normalized);
|
|
3054
|
+
set({ failedProviders: normalized });
|
|
3055
|
+
},
|
|
3056
|
+
addFailedProvider: (baseUrl) => {
|
|
3057
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3058
|
+
const current = get().failedProviders;
|
|
3059
|
+
if (!current.includes(normalized)) {
|
|
3060
|
+
const updated = [...current, normalized];
|
|
3061
|
+
void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, updated);
|
|
3062
|
+
set({ failedProviders: updated });
|
|
3063
|
+
}
|
|
3064
|
+
},
|
|
3065
|
+
removeFailedProvider: (baseUrl) => {
|
|
3066
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3067
|
+
const current = get().failedProviders;
|
|
3068
|
+
const updated = current.filter((url) => url !== normalized);
|
|
3069
|
+
void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, updated);
|
|
3070
|
+
set({ failedProviders: updated });
|
|
3071
|
+
},
|
|
3072
|
+
setLastFailed: (value) => {
|
|
3073
|
+
const normalized = {};
|
|
3074
|
+
for (const [baseUrl, timestamp] of Object.entries(value)) {
|
|
3075
|
+
normalized[normalizeBaseUrl5(baseUrl)] = timestamp;
|
|
3076
|
+
}
|
|
3077
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_FAILED, normalized);
|
|
3078
|
+
set({ lastFailed: normalized });
|
|
3079
|
+
},
|
|
3080
|
+
setLastFailedTimestamp: (baseUrl, timestamp) => {
|
|
3081
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3082
|
+
const current = get().lastFailed;
|
|
3083
|
+
const updated = { ...current, [normalized]: timestamp };
|
|
3084
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_FAILED, updated);
|
|
3085
|
+
set({ lastFailed: updated });
|
|
3086
|
+
},
|
|
3087
|
+
setProvidersOnCooldown: (value) => {
|
|
3088
|
+
const normalized = value.map((entry) => ({
|
|
3089
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3090
|
+
timestamp: entry.timestamp
|
|
3091
|
+
}));
|
|
3092
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, normalized);
|
|
3093
|
+
set({ providersOnCooldown: normalized });
|
|
3094
|
+
},
|
|
3095
|
+
addProviderOnCooldown: (baseUrl, timestamp) => {
|
|
3096
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3097
|
+
const current = get().providersOnCooldown;
|
|
3098
|
+
if (!current.some((entry) => entry.baseUrl === normalized)) {
|
|
3099
|
+
const updated = [...current, { baseUrl: normalized, timestamp }];
|
|
3100
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, updated);
|
|
3101
|
+
set({ providersOnCooldown: updated });
|
|
3102
|
+
}
|
|
3103
|
+
},
|
|
3104
|
+
removeProviderFromCooldown: (baseUrl) => {
|
|
3105
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3106
|
+
const current = get().providersOnCooldown;
|
|
3107
|
+
const updated = current.filter((entry) => entry.baseUrl !== normalized);
|
|
3108
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, updated);
|
|
3109
|
+
set({ providersOnCooldown: updated });
|
|
3110
|
+
},
|
|
3111
|
+
clearProvidersOnCooldown: () => {
|
|
3112
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, []);
|
|
3113
|
+
set({ providersOnCooldown: [] });
|
|
2858
3114
|
}
|
|
2859
3115
|
}));
|
|
2860
3116
|
var hydrateStoreFromDriver = async (store, driver) => {
|
|
@@ -2867,13 +3123,16 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2867
3123
|
rawMints,
|
|
2868
3124
|
rawInfo,
|
|
2869
3125
|
rawLastModelsUpdate,
|
|
2870
|
-
rawCachedTokens,
|
|
2871
3126
|
rawApiKeys,
|
|
2872
3127
|
rawChildKeys,
|
|
3128
|
+
rawXcashuTokens,
|
|
2873
3129
|
rawRoutstr21Models,
|
|
2874
3130
|
rawLastRoutstr21ModelsUpdate,
|
|
2875
3131
|
rawCachedReceiveTokens,
|
|
2876
|
-
rawClientIds
|
|
3132
|
+
rawClientIds,
|
|
3133
|
+
rawFailedProviders,
|
|
3134
|
+
rawLastFailed,
|
|
3135
|
+
rawProvidersOnCooldown
|
|
2877
3136
|
] = await Promise.all([
|
|
2878
3137
|
driver.getItem(
|
|
2879
3138
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -2895,65 +3154,73 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2895
3154
|
SDK_STORAGE_KEYS.LAST_MODELS_UPDATE,
|
|
2896
3155
|
{}
|
|
2897
3156
|
),
|
|
2898
|
-
driver.getItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, []),
|
|
2899
3157
|
driver.getItem(SDK_STORAGE_KEYS.API_KEYS, []),
|
|
2900
3158
|
driver.getItem(SDK_STORAGE_KEYS.CHILD_KEYS, []),
|
|
3159
|
+
driver.getItem(SDK_STORAGE_KEYS.XCASHU_TOKENS, {}),
|
|
2901
3160
|
driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
|
|
2902
3161
|
driver.getItem(
|
|
2903
3162
|
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
2904
3163
|
null
|
|
2905
3164
|
),
|
|
2906
3165
|
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
|
|
2907
|
-
driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, [])
|
|
3166
|
+
driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, []),
|
|
3167
|
+
driver.getItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, []),
|
|
3168
|
+
driver.getItem(SDK_STORAGE_KEYS.LAST_FAILED, {}),
|
|
3169
|
+
driver.getItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, [])
|
|
2908
3170
|
]);
|
|
2909
3171
|
const modelsFromAllProviders = Object.fromEntries(
|
|
2910
3172
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
2911
|
-
|
|
3173
|
+
normalizeBaseUrl5(baseUrl),
|
|
2912
3174
|
models
|
|
2913
3175
|
])
|
|
2914
3176
|
);
|
|
2915
|
-
const baseUrlsList = rawBaseUrls.map((url) =>
|
|
3177
|
+
const baseUrlsList = rawBaseUrls.map((url) => normalizeBaseUrl5(url));
|
|
2916
3178
|
const disabledProviders = rawDisabledProviders.map(
|
|
2917
|
-
(url) =>
|
|
3179
|
+
(url) => normalizeBaseUrl5(url)
|
|
2918
3180
|
);
|
|
2919
3181
|
const mintsFromAllProviders = Object.fromEntries(
|
|
2920
3182
|
Object.entries(rawMints).map(([baseUrl, mints]) => [
|
|
2921
|
-
|
|
3183
|
+
normalizeBaseUrl5(baseUrl),
|
|
2922
3184
|
mints.map((mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint)
|
|
2923
3185
|
])
|
|
2924
3186
|
);
|
|
2925
3187
|
const infoFromAllProviders = Object.fromEntries(
|
|
2926
3188
|
Object.entries(rawInfo).map(([baseUrl, info]) => [
|
|
2927
|
-
|
|
3189
|
+
normalizeBaseUrl5(baseUrl),
|
|
2928
3190
|
info
|
|
2929
3191
|
])
|
|
2930
3192
|
);
|
|
2931
3193
|
const lastModelsUpdate = Object.fromEntries(
|
|
2932
3194
|
Object.entries(rawLastModelsUpdate).map(([baseUrl, timestamp]) => [
|
|
2933
|
-
|
|
3195
|
+
normalizeBaseUrl5(baseUrl),
|
|
2934
3196
|
timestamp
|
|
2935
3197
|
])
|
|
2936
3198
|
);
|
|
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
3199
|
const apiKeys = rawApiKeys.map((entry) => ({
|
|
2944
3200
|
...entry,
|
|
2945
|
-
baseUrl:
|
|
3201
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
2946
3202
|
balance: entry.balance ?? 0,
|
|
2947
3203
|
lastUsed: entry.lastUsed ?? null
|
|
2948
3204
|
}));
|
|
2949
3205
|
const childKeys = rawChildKeys.map((entry) => ({
|
|
2950
|
-
parentBaseUrl:
|
|
3206
|
+
parentBaseUrl: normalizeBaseUrl5(entry.parentBaseUrl),
|
|
2951
3207
|
childKey: entry.childKey,
|
|
2952
3208
|
balance: entry.balance ?? 0,
|
|
2953
3209
|
balanceLimit: entry.balanceLimit,
|
|
2954
3210
|
validityDate: entry.validityDate,
|
|
2955
3211
|
createdAt: entry.createdAt ?? Date.now()
|
|
2956
3212
|
}));
|
|
3213
|
+
const xcashuTokens = Object.fromEntries(
|
|
3214
|
+
Object.entries(rawXcashuTokens).map(([baseUrl, tokens]) => [
|
|
3215
|
+
normalizeBaseUrl5(baseUrl),
|
|
3216
|
+
tokens.map((entry) => ({
|
|
3217
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3218
|
+
token: entry.token,
|
|
3219
|
+
createdAt: entry.createdAt ?? Date.now(),
|
|
3220
|
+
tryCount: entry.tryCount ?? 0
|
|
3221
|
+
}))
|
|
3222
|
+
])
|
|
3223
|
+
);
|
|
2957
3224
|
const routstr21Models = rawRoutstr21Models;
|
|
2958
3225
|
const lastRoutstr21ModelsUpdate = rawLastRoutstr21ModelsUpdate;
|
|
2959
3226
|
const cachedReceiveTokens = rawCachedReceiveTokens?.map((entry) => ({
|
|
@@ -2967,6 +3234,17 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2967
3234
|
createdAt: entry.createdAt ?? Date.now(),
|
|
2968
3235
|
lastUsed: entry.lastUsed ?? null
|
|
2969
3236
|
}));
|
|
3237
|
+
const failedProviders = rawFailedProviders.map((url) => normalizeBaseUrl5(url));
|
|
3238
|
+
const lastFailed = Object.fromEntries(
|
|
3239
|
+
Object.entries(rawLastFailed).map(([baseUrl, timestamp]) => [
|
|
3240
|
+
normalizeBaseUrl5(baseUrl),
|
|
3241
|
+
timestamp
|
|
3242
|
+
])
|
|
3243
|
+
);
|
|
3244
|
+
const providersOnCooldown = rawProvidersOnCooldown.map((entry) => ({
|
|
3245
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3246
|
+
timestamp: entry.timestamp
|
|
3247
|
+
}));
|
|
2970
3248
|
store.setState({
|
|
2971
3249
|
modelsFromAllProviders,
|
|
2972
3250
|
lastUsedModel,
|
|
@@ -2976,13 +3254,16 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2976
3254
|
mintsFromAllProviders,
|
|
2977
3255
|
infoFromAllProviders,
|
|
2978
3256
|
lastModelsUpdate,
|
|
2979
|
-
cachedTokens,
|
|
2980
3257
|
apiKeys,
|
|
2981
3258
|
childKeys,
|
|
3259
|
+
xcashuTokens,
|
|
2982
3260
|
routstr21Models,
|
|
2983
3261
|
lastRoutstr21ModelsUpdate,
|
|
2984
3262
|
cachedReceiveTokens,
|
|
2985
|
-
clientIds
|
|
3263
|
+
clientIds,
|
|
3264
|
+
failedProviders,
|
|
3265
|
+
lastFailed,
|
|
3266
|
+
providersOnCooldown
|
|
2986
3267
|
});
|
|
2987
3268
|
};
|
|
2988
3269
|
var createSdkStore = ({
|
|
@@ -3049,7 +3330,7 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
3049
3330
|
return defaultUsageTrackingDriver;
|
|
3050
3331
|
}
|
|
3051
3332
|
if (isBun3()) {
|
|
3052
|
-
defaultUsageTrackingDriver =
|
|
3333
|
+
defaultUsageTrackingDriver = createBunSqliteUsageTrackingDriver();
|
|
3053
3334
|
return defaultUsageTrackingDriver;
|
|
3054
3335
|
}
|
|
3055
3336
|
if (isNode()) {
|
|
@@ -3063,16 +3344,20 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
3063
3344
|
};
|
|
3064
3345
|
function createSSEParserTransform(onUsage, onResponseId) {
|
|
3065
3346
|
let buffer = "";
|
|
3347
|
+
let usageCaptured = false;
|
|
3348
|
+
let responseIdCaptured = false;
|
|
3066
3349
|
const maybeCaptureUsageFromJson = (jsonText) => {
|
|
3067
3350
|
try {
|
|
3068
3351
|
const data = JSON.parse(jsonText);
|
|
3069
3352
|
const responseId = data.id;
|
|
3070
3353
|
if (typeof responseId === "string" && responseId.trim().length > 0) {
|
|
3071
3354
|
onResponseId?.(responseId.trim());
|
|
3355
|
+
responseIdCaptured = true;
|
|
3072
3356
|
}
|
|
3073
3357
|
const usage = extractUsageFromSSEJson(data);
|
|
3074
3358
|
if (usage) {
|
|
3075
3359
|
onUsage(usage);
|
|
3360
|
+
usageCaptured = true;
|
|
3076
3361
|
}
|
|
3077
3362
|
} catch {
|
|
3078
3363
|
}
|
|
@@ -3128,7 +3413,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
3128
3413
|
}
|
|
3129
3414
|
var TOPUP_MARGIN = 1.2;
|
|
3130
3415
|
var RoutstrClient = class {
|
|
3131
|
-
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu") {
|
|
3416
|
+
constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
|
|
3132
3417
|
this.walletAdapter = walletAdapter;
|
|
3133
3418
|
this.storageAdapter = storageAdapter;
|
|
3134
3419
|
this.providerRegistry = providerRegistry;
|
|
@@ -3144,15 +3429,11 @@ var RoutstrClient = class {
|
|
|
3144
3429
|
this.balanceManager
|
|
3145
3430
|
);
|
|
3146
3431
|
this.streamProcessor = new StreamProcessor();
|
|
3147
|
-
this.providerManager = new ProviderManager(providerRegistry);
|
|
3148
3432
|
this.alertLevel = alertLevel;
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
} else {
|
|
3154
|
-
this.mode = mode;
|
|
3155
|
-
}
|
|
3433
|
+
this.mode = mode;
|
|
3434
|
+
this.usageTrackingDriver = options.usageTrackingDriver;
|
|
3435
|
+
this.sdkStore = options.sdkStore;
|
|
3436
|
+
this.providerManager = options.providerManager ?? new ProviderManager(providerRegistry, this.sdkStore);
|
|
3156
3437
|
}
|
|
3157
3438
|
cashuSpender;
|
|
3158
3439
|
balanceManager;
|
|
@@ -3161,6 +3442,8 @@ var RoutstrClient = class {
|
|
|
3161
3442
|
alertLevel;
|
|
3162
3443
|
mode;
|
|
3163
3444
|
debugLevel = "WARN";
|
|
3445
|
+
usageTrackingDriver;
|
|
3446
|
+
sdkStore;
|
|
3164
3447
|
/**
|
|
3165
3448
|
* Get the current client mode
|
|
3166
3449
|
*/
|
|
@@ -3230,11 +3513,13 @@ var RoutstrClient = class {
|
|
|
3230
3513
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3231
3514
|
token: prepared.tokenUsed,
|
|
3232
3515
|
baseUrl: prepared.baseUrlUsed,
|
|
3516
|
+
mintUrl: params.mintUrl,
|
|
3233
3517
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3234
3518
|
response: prepared.response,
|
|
3235
3519
|
modelId: prepared.modelId,
|
|
3236
3520
|
usage: prepared.capturedUsage,
|
|
3237
|
-
requestId: prepared.capturedResponseId
|
|
3521
|
+
requestId: prepared.capturedResponseId,
|
|
3522
|
+
clientApiKey: prepared.clientApiKey
|
|
3238
3523
|
});
|
|
3239
3524
|
prepared.response.satsSpent = satsSpent;
|
|
3240
3525
|
prepared.response.usage = prepared.capturedUsage;
|
|
@@ -3253,11 +3538,13 @@ var RoutstrClient = class {
|
|
|
3253
3538
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3254
3539
|
token: prepared.tokenUsed,
|
|
3255
3540
|
baseUrl: prepared.baseUrlUsed,
|
|
3541
|
+
mintUrl: params.mintUrl,
|
|
3256
3542
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3257
3543
|
response: prepared.response,
|
|
3258
3544
|
modelId: prepared.modelId,
|
|
3259
3545
|
usage: prepared.capturedUsage,
|
|
3260
|
-
requestId: prepared.capturedResponseId
|
|
3546
|
+
requestId: prepared.capturedResponseId,
|
|
3547
|
+
clientApiKey: prepared.clientApiKey
|
|
3261
3548
|
});
|
|
3262
3549
|
prepared.response.satsSpent = satsSpent;
|
|
3263
3550
|
res.end();
|
|
@@ -3273,11 +3560,13 @@ var RoutstrClient = class {
|
|
|
3273
3560
|
const satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3274
3561
|
token: prepared.tokenUsed,
|
|
3275
3562
|
baseUrl: prepared.baseUrlUsed,
|
|
3563
|
+
mintUrl: params.mintUrl,
|
|
3276
3564
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
3277
3565
|
response: prepared.response,
|
|
3278
3566
|
modelId: prepared.modelId,
|
|
3279
3567
|
usage: prepared.capturedUsage,
|
|
3280
|
-
requestId: prepared.capturedResponseId
|
|
3568
|
+
requestId: prepared.capturedResponseId,
|
|
3569
|
+
clientApiKey: prepared.clientApiKey
|
|
3281
3570
|
});
|
|
3282
3571
|
prepared.response.satsSpent = satsSpent;
|
|
3283
3572
|
prepared.response.usage = prepared.capturedUsage;
|
|
@@ -3307,8 +3596,10 @@ var RoutstrClient = class {
|
|
|
3307
3596
|
headers = {},
|
|
3308
3597
|
baseUrl,
|
|
3309
3598
|
mintUrl,
|
|
3310
|
-
modelId
|
|
3599
|
+
modelId,
|
|
3600
|
+
clientApiKey: providedClientApiKey
|
|
3311
3601
|
} = params;
|
|
3602
|
+
const clientApiKey = providedClientApiKey ?? this._extractClientApiKey(headers);
|
|
3312
3603
|
await this._checkBalance();
|
|
3313
3604
|
let requiredSats = 1;
|
|
3314
3605
|
let selectedModel;
|
|
@@ -3330,7 +3621,6 @@ var RoutstrClient = class {
|
|
|
3330
3621
|
amount: requiredSats,
|
|
3331
3622
|
baseUrl
|
|
3332
3623
|
});
|
|
3333
|
-
this._log("DEBUG", token, baseUrl);
|
|
3334
3624
|
let requestBody = body;
|
|
3335
3625
|
if (body && typeof body === "object") {
|
|
3336
3626
|
const bodyObj = body;
|
|
@@ -3338,7 +3628,7 @@ var RoutstrClient = class {
|
|
|
3338
3628
|
requestBody = { ...bodyObj, stream: false };
|
|
3339
3629
|
}
|
|
3340
3630
|
}
|
|
3341
|
-
const baseHeaders = this._buildBaseHeaders(
|
|
3631
|
+
const baseHeaders = this._buildBaseHeaders();
|
|
3342
3632
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
3343
3633
|
const response = await this._makeRequest({
|
|
3344
3634
|
path,
|
|
@@ -3390,9 +3680,21 @@ var RoutstrClient = class {
|
|
|
3390
3680
|
tokenBalanceInSats,
|
|
3391
3681
|
modelId,
|
|
3392
3682
|
capturedUsage,
|
|
3393
|
-
capturedResponseId
|
|
3683
|
+
capturedResponseId,
|
|
3684
|
+
clientApiKey
|
|
3394
3685
|
};
|
|
3395
3686
|
}
|
|
3687
|
+
/**
|
|
3688
|
+
* Extract clientApiKey from Authorization Bearer token if present
|
|
3689
|
+
*/
|
|
3690
|
+
_extractClientApiKey(headers) {
|
|
3691
|
+
const authHeader = headers["Authorization"] || headers["authorization"];
|
|
3692
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
3693
|
+
const extractedKey = authHeader.slice(7);
|
|
3694
|
+
return extractedKey;
|
|
3695
|
+
}
|
|
3696
|
+
return void 0;
|
|
3697
|
+
}
|
|
3396
3698
|
/**
|
|
3397
3699
|
* Fetch AI response with streaming
|
|
3398
3700
|
*/
|
|
@@ -3496,6 +3798,7 @@ var RoutstrClient = class {
|
|
|
3496
3798
|
let satsSpent = await this._handlePostResponseBalanceUpdate({
|
|
3497
3799
|
token,
|
|
3498
3800
|
baseUrl: baseUrlUsed,
|
|
3801
|
+
mintUrl,
|
|
3499
3802
|
initialTokenBalance: tokenBalanceInSats,
|
|
3500
3803
|
fallbackSatsSpent: isApikeysEstimate ? this._getEstimatedCosts(selectedModel, streamingResult) : void 0,
|
|
3501
3804
|
response,
|
|
@@ -3534,7 +3837,6 @@ var RoutstrClient = class {
|
|
|
3534
3837
|
try {
|
|
3535
3838
|
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
3536
3839
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
3537
|
-
this._log("DEBUG", "HEADERS,", headers);
|
|
3538
3840
|
const response = await fetch(url, {
|
|
3539
3841
|
method,
|
|
3540
3842
|
headers,
|
|
@@ -3603,8 +3905,6 @@ var RoutstrClient = class {
|
|
|
3603
3905
|
`[RoutstrClient] _handleErrorResponse: Token restored successfully, amount=${tryReceiveTokenResult.amount}`
|
|
3604
3906
|
);
|
|
3605
3907
|
tryNextProvider = true;
|
|
3606
|
-
if (this.mode === "lazyrefund")
|
|
3607
|
-
this.storageAdapter.removeToken(baseUrl);
|
|
3608
3908
|
} else {
|
|
3609
3909
|
this._log(
|
|
3610
3910
|
"DEBUG",
|
|
@@ -3652,24 +3952,21 @@ var RoutstrClient = class {
|
|
|
3652
3952
|
);
|
|
3653
3953
|
}
|
|
3654
3954
|
}
|
|
3655
|
-
if (status === 402 && !tryNextProvider &&
|
|
3955
|
+
if (status === 402 && !tryNextProvider && this.mode === "apikeys") {
|
|
3656
3956
|
this.storageAdapter.getApiKey(baseUrl);
|
|
3657
3957
|
let topupAmount = params.requiredSats;
|
|
3658
3958
|
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
|
-
}
|
|
3959
|
+
const currentBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
3960
|
+
params.token,
|
|
3961
|
+
baseUrl
|
|
3962
|
+
);
|
|
3963
|
+
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
3671
3964
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
3672
3965
|
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
3966
|
+
this._log(
|
|
3967
|
+
"DEBUG",
|
|
3968
|
+
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
|
|
3969
|
+
);
|
|
3673
3970
|
} catch (e) {
|
|
3674
3971
|
this._log(
|
|
3675
3972
|
"WARN",
|
|
@@ -3799,61 +4096,49 @@ var RoutstrClient = class {
|
|
|
3799
4096
|
tryNextProvider = true;
|
|
3800
4097
|
}
|
|
3801
4098
|
}
|
|
4099
|
+
if (status === 401 && this.mode === "apikeys") {
|
|
4100
|
+
this._log(
|
|
4101
|
+
"DEBUG",
|
|
4102
|
+
`[RoutstrClient] _handleErrorResponse: Checking balance for ${baseUrl}, key preview=${token}`
|
|
4103
|
+
);
|
|
4104
|
+
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
4105
|
+
token,
|
|
4106
|
+
baseUrl
|
|
4107
|
+
);
|
|
4108
|
+
if (latestBalanceInfo.isInvalidApiKey) {
|
|
4109
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
4110
|
+
tryNextProvider = true;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
3802
4113
|
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
3803
4114
|
this._log(
|
|
3804
4115
|
"DEBUG",
|
|
3805
4116
|
`[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`
|
|
3806
4117
|
);
|
|
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") {
|
|
4118
|
+
if (this.mode === "apikeys") {
|
|
3835
4119
|
this._log(
|
|
3836
4120
|
"DEBUG",
|
|
3837
4121
|
`[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
|
|
3838
4122
|
);
|
|
3839
|
-
const
|
|
4123
|
+
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
3840
4124
|
token,
|
|
3841
4125
|
baseUrl
|
|
3842
4126
|
);
|
|
3843
4127
|
this._log(
|
|
3844
4128
|
"DEBUG",
|
|
3845
|
-
`[RoutstrClient] _handleErrorResponse: Initial API key balance: ${
|
|
4129
|
+
`[RoutstrClient] _handleErrorResponse: Initial API key balance: ${latestBalanceInfo.amount}`
|
|
3846
4130
|
);
|
|
3847
4131
|
const refundResult = await this.balanceManager.refundApiKey({
|
|
3848
4132
|
mintUrl,
|
|
3849
4133
|
baseUrl,
|
|
3850
|
-
apiKey: token
|
|
4134
|
+
apiKey: token,
|
|
4135
|
+
forceRefund: true
|
|
3851
4136
|
});
|
|
3852
4137
|
this._log(
|
|
3853
4138
|
"DEBUG",
|
|
3854
4139
|
`[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
|
|
3855
4140
|
);
|
|
3856
|
-
if (!refundResult.success &&
|
|
4141
|
+
if (!refundResult.success && latestBalanceInfo.amount > 0) {
|
|
3857
4142
|
throw new ProviderError(
|
|
3858
4143
|
baseUrl,
|
|
3859
4144
|
status,
|
|
@@ -3929,12 +4214,14 @@ var RoutstrClient = class {
|
|
|
3929
4214
|
const {
|
|
3930
4215
|
token,
|
|
3931
4216
|
baseUrl,
|
|
4217
|
+
mintUrl,
|
|
3932
4218
|
initialTokenBalance,
|
|
3933
4219
|
fallbackSatsSpent,
|
|
3934
4220
|
response,
|
|
3935
4221
|
modelId,
|
|
3936
4222
|
usage,
|
|
3937
|
-
requestId
|
|
4223
|
+
requestId,
|
|
4224
|
+
clientApiKey
|
|
3938
4225
|
} = params;
|
|
3939
4226
|
let satsSpent = initialTokenBalance;
|
|
3940
4227
|
if (this.mode === "xcashu" && response) {
|
|
@@ -3942,19 +4229,14 @@ var RoutstrClient = class {
|
|
|
3942
4229
|
if (refundToken) {
|
|
3943
4230
|
try {
|
|
3944
4231
|
const receiveResult = await this.cashuSpender.receiveToken(refundToken);
|
|
3945
|
-
|
|
4232
|
+
if (receiveResult.success) {
|
|
4233
|
+
this.storageAdapter.removeXcashuToken(baseUrl, token);
|
|
4234
|
+
satsSpent = initialTokenBalance - receiveResult.amount * (receiveResult.unit == "sat" ? 1 : 1e3);
|
|
4235
|
+
}
|
|
3946
4236
|
} catch (error) {
|
|
3947
4237
|
this._log("ERROR", "[xcashu] Failed to receive refund token:", error);
|
|
3948
4238
|
}
|
|
3949
4239
|
}
|
|
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
4240
|
} else if (this.mode === "apikeys") {
|
|
3959
4241
|
try {
|
|
3960
4242
|
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
@@ -3989,8 +4271,17 @@ var RoutstrClient = class {
|
|
|
3989
4271
|
modelId,
|
|
3990
4272
|
satsSpent,
|
|
3991
4273
|
usage,
|
|
3992
|
-
requestId
|
|
4274
|
+
requestId,
|
|
4275
|
+
clientApiKey
|
|
3993
4276
|
});
|
|
4277
|
+
(async () => {
|
|
4278
|
+
try {
|
|
4279
|
+
const xcashuResults = await this.cashuSpender.refundXcashuTokens(mintUrl);
|
|
4280
|
+
this._log("DEBUG", "Refund xcashu tokens results:", xcashuResults);
|
|
4281
|
+
} catch (error) {
|
|
4282
|
+
this._log("ERROR", "Failed to refund providers:", error);
|
|
4283
|
+
}
|
|
4284
|
+
})();
|
|
3994
4285
|
return satsSpent;
|
|
3995
4286
|
}
|
|
3996
4287
|
async _trackResponseUsage(params) {
|
|
@@ -4001,7 +4292,8 @@ var RoutstrClient = class {
|
|
|
4001
4292
|
modelId,
|
|
4002
4293
|
satsSpent,
|
|
4003
4294
|
usage: providedUsage,
|
|
4004
|
-
requestId: providedRequestId
|
|
4295
|
+
requestId: providedRequestId,
|
|
4296
|
+
clientApiKey
|
|
4005
4297
|
} = params;
|
|
4006
4298
|
if (!response || !modelId) {
|
|
4007
4299
|
return;
|
|
@@ -4028,13 +4320,14 @@ var RoutstrClient = class {
|
|
|
4028
4320
|
return;
|
|
4029
4321
|
}
|
|
4030
4322
|
const finalRequestId = requestId || "unknown";
|
|
4031
|
-
const store = await getDefaultSdkStore();
|
|
4323
|
+
const store = this.sdkStore ?? await getDefaultSdkStore();
|
|
4032
4324
|
const state = store.getState();
|
|
4325
|
+
const matchKey = clientApiKey ?? token;
|
|
4033
4326
|
const matchingClient = state.clientIds.find(
|
|
4034
|
-
(client) => client.apiKey ===
|
|
4327
|
+
(client) => client.apiKey === matchKey
|
|
4035
4328
|
);
|
|
4036
4329
|
const entryId = finalRequestId === "unknown" ? `req-${Date.now()}-${modelId}` : finalRequestId;
|
|
4037
|
-
const usageTracking = getDefaultUsageTrackingDriver();
|
|
4330
|
+
const usageTracking = this.usageTrackingDriver ?? getDefaultUsageTrackingDriver();
|
|
4038
4331
|
const entry = {
|
|
4039
4332
|
id: entryId,
|
|
4040
4333
|
timestamp: Date.now(),
|
|
@@ -4109,11 +4402,11 @@ var RoutstrClient = class {
|
|
|
4109
4402
|
return estimatedCosts;
|
|
4110
4403
|
}
|
|
4111
4404
|
/**
|
|
4112
|
-
* Get pending
|
|
4405
|
+
* Get pending API key amount
|
|
4113
4406
|
*/
|
|
4114
4407
|
_getPendingCashuTokenAmount() {
|
|
4115
|
-
const
|
|
4116
|
-
return
|
|
4408
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
4409
|
+
return apiKeyDistribution.reduce((total, item) => total + item.amount, 0);
|
|
4117
4410
|
}
|
|
4118
4411
|
/**
|
|
4119
4412
|
* Handle errors and notify callbacks
|
|
@@ -4260,8 +4553,8 @@ var RoutstrClient = class {
|
|
|
4260
4553
|
const spendResult = await this.cashuSpender.spend({
|
|
4261
4554
|
mintUrl,
|
|
4262
4555
|
amount,
|
|
4263
|
-
baseUrl:
|
|
4264
|
-
reuseToken:
|
|
4556
|
+
baseUrl: "",
|
|
4557
|
+
reuseToken: false
|
|
4265
4558
|
});
|
|
4266
4559
|
if (!spendResult.token) {
|
|
4267
4560
|
this._log(
|
|
@@ -4274,6 +4567,7 @@ var RoutstrClient = class {
|
|
|
4274
4567
|
"DEBUG",
|
|
4275
4568
|
`[RoutstrClient] _spendToken: Cashu token created, token preview: ${spendResult.token}, balance: ${spendResult.balance} ${spendResult.unit ?? "sat"}`
|
|
4276
4569
|
);
|
|
4570
|
+
this.storageAdapter.addXcashuToken(baseUrl, spendResult.token);
|
|
4277
4571
|
}
|
|
4278
4572
|
return {
|
|
4279
4573
|
token: spendResult.token,
|