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