@routstr/sdk 0.2.6 → 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/dist/client/index.d.mts +16 -2
- package/dist/client/index.d.ts +16 -2
- package/dist/client/index.js +276 -37
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +276 -37
- 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 +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +281 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +281 -41
- package/dist/index.mjs.map +1 -1
- package/dist/storage/index.d.mts +5 -2
- package/dist/storage/index.d.ts +5 -2
- package/dist/storage/index.js +95 -4
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +95 -4
- package/dist/storage/index.mjs.map +1 -1
- package/dist/{store-C5lnyX8k.d.mts → store-DGeLPv9E.d.mts} +21 -0
- package/dist/{store-BJlwiDX5.d.ts → store-h7m23ffq.d.ts} +21 -0
- package/dist/wallet/index.d.mts +10 -4
- package/dist/wallet/index.d.ts +10 -4
- package/dist/wallet/index.js +45 -24
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +45 -24
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/client/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { M as Model, a as Message, T as TransactionHistory, l as StreamingResult } from '../types-BYj_8c5c.mjs';
|
|
2
2
|
import { P as ProviderRegistry, W as WalletAdapter, S as StorageAdapter, a as StreamingCallbacks } from '../interfaces-C5fLD3jB.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import { S as SdkStore, U as UsageTrackingDriver } from '../store-DGeLPv9E.mjs';
|
|
4
4
|
import { ServerResponse } from 'http';
|
|
5
5
|
import { CashuSpender, BalanceManager } from '../wallet/index.mjs';
|
|
6
6
|
import { Transform } from 'stream';
|
|
@@ -35,7 +35,19 @@ declare class ProviderManager {
|
|
|
35
35
|
private providersOnCoolDown;
|
|
36
36
|
/** Cooldown duration in milliseconds (5 minutes) */
|
|
37
37
|
private static readonly COOLDOWN_DURATION_MS;
|
|
38
|
-
|
|
38
|
+
/** Optional persistent store for failure tracking */
|
|
39
|
+
private store;
|
|
40
|
+
/** Instance ID for debugging */
|
|
41
|
+
private readonly instanceId;
|
|
42
|
+
constructor(providerRegistry: ProviderRegistry, store?: SdkStore);
|
|
43
|
+
/**
|
|
44
|
+
* Hydrate in-memory state from persistent store
|
|
45
|
+
*/
|
|
46
|
+
private hydrateFromStore;
|
|
47
|
+
/**
|
|
48
|
+
* Get instance ID for debugging
|
|
49
|
+
*/
|
|
50
|
+
getInstanceId(): string;
|
|
39
51
|
/**
|
|
40
52
|
* Clean up expired cooldown entries
|
|
41
53
|
*/
|
|
@@ -184,6 +196,8 @@ interface RouteRequestToNodeResponseParams extends RouteRequestParams {
|
|
|
184
196
|
interface RoutstrClientConfig {
|
|
185
197
|
usageTrackingDriver?: UsageTrackingDriver;
|
|
186
198
|
sdkStore?: SdkStore;
|
|
199
|
+
/** Optional: shared ProviderManager instance for consistent failure tracking across requests */
|
|
200
|
+
providerManager?: ProviderManager;
|
|
187
201
|
}
|
|
188
202
|
declare class RoutstrClient {
|
|
189
203
|
private walletAdapter;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { M as Model, a as Message, T as TransactionHistory, l as StreamingResult } from '../types-BYj_8c5c.js';
|
|
2
2
|
import { P as ProviderRegistry, W as WalletAdapter, S as StorageAdapter, a as StreamingCallbacks } from '../interfaces-B62Rw-dd.js';
|
|
3
|
-
import {
|
|
3
|
+
import { S as SdkStore, U as UsageTrackingDriver } from '../store-h7m23ffq.js';
|
|
4
4
|
import { ServerResponse } from 'http';
|
|
5
5
|
import { CashuSpender, BalanceManager } from '../wallet/index.js';
|
|
6
6
|
import { Transform } from 'stream';
|
|
@@ -35,7 +35,19 @@ declare class ProviderManager {
|
|
|
35
35
|
private providersOnCoolDown;
|
|
36
36
|
/** Cooldown duration in milliseconds (5 minutes) */
|
|
37
37
|
private static readonly COOLDOWN_DURATION_MS;
|
|
38
|
-
|
|
38
|
+
/** Optional persistent store for failure tracking */
|
|
39
|
+
private store;
|
|
40
|
+
/** Instance ID for debugging */
|
|
41
|
+
private readonly instanceId;
|
|
42
|
+
constructor(providerRegistry: ProviderRegistry, store?: SdkStore);
|
|
43
|
+
/**
|
|
44
|
+
* Hydrate in-memory state from persistent store
|
|
45
|
+
*/
|
|
46
|
+
private hydrateFromStore;
|
|
47
|
+
/**
|
|
48
|
+
* Get instance ID for debugging
|
|
49
|
+
*/
|
|
50
|
+
getInstanceId(): string;
|
|
39
51
|
/**
|
|
40
52
|
* Clean up expired cooldown entries
|
|
41
53
|
*/
|
|
@@ -184,6 +196,8 @@ interface RouteRequestToNodeResponseParams extends RouteRequestParams {
|
|
|
184
196
|
interface RoutstrClientConfig {
|
|
185
197
|
usageTrackingDriver?: UsageTrackingDriver;
|
|
186
198
|
sdkStore?: SdkStore;
|
|
199
|
+
/** Optional: shared ProviderManager instance for consistent failure tracking across requests */
|
|
200
|
+
providerManager?: ProviderManager;
|
|
187
201
|
}
|
|
188
202
|
declare class RoutstrClient {
|
|
189
203
|
private walletAdapter;
|
package/dist/client/index.js
CHANGED
|
@@ -449,8 +449,9 @@ var CashuSpender = class {
|
|
|
449
449
|
return null;
|
|
450
450
|
}
|
|
451
451
|
/**
|
|
452
|
-
* Refund all xcashu tokens from storage
|
|
453
|
-
*
|
|
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.
|
|
454
455
|
* @param mintUrl - The mint URL for receiving tokens
|
|
455
456
|
* @param excludeBaseUrls - Base URLs to exclude from refund (optional)
|
|
456
457
|
* @returns Results for each xcashu token refund attempt
|
|
@@ -463,7 +464,20 @@ var CashuSpender = class {
|
|
|
463
464
|
if (excludedUrls.has(baseUrl)) continue;
|
|
464
465
|
for (const xcashuToken of tokens) {
|
|
465
466
|
try {
|
|
466
|
-
|
|
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);
|
|
467
481
|
if (receiveResult.success) {
|
|
468
482
|
this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
|
|
469
483
|
results.push({
|
|
@@ -478,7 +492,10 @@ var CashuSpender = class {
|
|
|
478
492
|
} else {
|
|
479
493
|
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
480
494
|
const newTryCount = currentTryCount + 1;
|
|
481
|
-
this.storageAdapter.updateXcashuTokenTryCount(
|
|
495
|
+
this.storageAdapter.updateXcashuTokenTryCount(
|
|
496
|
+
xcashuToken.token,
|
|
497
|
+
newTryCount
|
|
498
|
+
);
|
|
482
499
|
results.push({
|
|
483
500
|
baseUrl,
|
|
484
501
|
token: xcashuToken.token,
|
|
@@ -487,13 +504,16 @@ var CashuSpender = class {
|
|
|
487
504
|
});
|
|
488
505
|
this._log(
|
|
489
506
|
"DEBUG",
|
|
490
|
-
`[CashuSpender] refundXcashuTokens: Failed to refund
|
|
507
|
+
`[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}`
|
|
491
508
|
);
|
|
492
509
|
}
|
|
493
510
|
} catch (error) {
|
|
494
511
|
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
495
512
|
const newTryCount = currentTryCount + 1;
|
|
496
|
-
this.storageAdapter.updateXcashuTokenTryCount(
|
|
513
|
+
this.storageAdapter.updateXcashuTokenTryCount(
|
|
514
|
+
xcashuToken.token,
|
|
515
|
+
newTryCount
|
|
516
|
+
);
|
|
497
517
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
498
518
|
results.push({
|
|
499
519
|
baseUrl,
|
|
@@ -530,7 +550,10 @@ var CashuSpender = class {
|
|
|
530
550
|
if (refundResult.success) {
|
|
531
551
|
this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
|
|
532
552
|
} else {
|
|
533
|
-
this.storageAdapter.updateApiKeyBalance(
|
|
553
|
+
this.storageAdapter.updateApiKeyBalance(
|
|
554
|
+
apiKeyEntry.baseUrl,
|
|
555
|
+
apiKeyEntry.amount
|
|
556
|
+
);
|
|
534
557
|
}
|
|
535
558
|
results.push({
|
|
536
559
|
baseUrl: apiKeyEntry.baseUrl,
|
|
@@ -668,7 +691,7 @@ var BalanceManager = class {
|
|
|
668
691
|
}
|
|
669
692
|
let fetchResult;
|
|
670
693
|
try {
|
|
671
|
-
fetchResult = await this.
|
|
694
|
+
fetchResult = await this.fetchRefundToken(baseUrl, apiKey);
|
|
672
695
|
if (!fetchResult.success) {
|
|
673
696
|
return {
|
|
674
697
|
success: false,
|
|
@@ -704,9 +727,9 @@ var BalanceManager = class {
|
|
|
704
727
|
}
|
|
705
728
|
}
|
|
706
729
|
/**
|
|
707
|
-
* Fetch refund token from provider API using API key authentication
|
|
730
|
+
* Fetch refund token from provider API using API key (or xcashu token) authentication
|
|
708
731
|
*/
|
|
709
|
-
async
|
|
732
|
+
async fetchRefundToken(baseUrl, apiKeyOrToken, xCashu = false) {
|
|
710
733
|
if (!baseUrl) {
|
|
711
734
|
return {
|
|
712
735
|
success: false,
|
|
@@ -720,12 +743,17 @@ var BalanceManager = class {
|
|
|
720
743
|
controller.abort();
|
|
721
744
|
}, 6e4);
|
|
722
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
|
+
}
|
|
723
754
|
const response = await fetch(url, {
|
|
724
755
|
method: "POST",
|
|
725
|
-
headers
|
|
726
|
-
Authorization: `Bearer ${apiKey}`,
|
|
727
|
-
"Content-Type": "application/json"
|
|
728
|
-
},
|
|
756
|
+
headers,
|
|
729
757
|
signal: controller.signal
|
|
730
758
|
});
|
|
731
759
|
clearTimeout(timeoutId);
|
|
@@ -746,10 +774,7 @@ var BalanceManager = class {
|
|
|
746
774
|
};
|
|
747
775
|
} catch (error) {
|
|
748
776
|
clearTimeout(timeoutId);
|
|
749
|
-
console.error(
|
|
750
|
-
"[BalanceManager._fetchRefundTokenWithApiKey] Fetch error",
|
|
751
|
-
error
|
|
752
|
-
);
|
|
777
|
+
console.error("[BalanceManager.fetchRefundToken] Fetch error", error);
|
|
753
778
|
if (error instanceof Error) {
|
|
754
779
|
if (error.name === "AbortError") {
|
|
755
780
|
return {
|
|
@@ -796,11 +821,7 @@ var BalanceManager = class {
|
|
|
796
821
|
};
|
|
797
822
|
}
|
|
798
823
|
cashuToken = tokenResult.token;
|
|
799
|
-
const topUpResult = await this._postTopUp(
|
|
800
|
-
baseUrl,
|
|
801
|
-
apiKey,
|
|
802
|
-
cashuToken
|
|
803
|
-
);
|
|
824
|
+
const topUpResult = await this._postTopUp(baseUrl, apiKey, cashuToken);
|
|
804
825
|
requestId = topUpResult.requestId;
|
|
805
826
|
console.log(topUpResult);
|
|
806
827
|
if (!topUpResult.success) {
|
|
@@ -1166,7 +1187,7 @@ var BalanceManager = class {
|
|
|
1166
1187
|
console.log(response.status);
|
|
1167
1188
|
const data = await response.json();
|
|
1168
1189
|
console.log("FAILED ", data);
|
|
1169
|
-
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");
|
|
1170
1191
|
return {
|
|
1171
1192
|
amount: -1,
|
|
1172
1193
|
reserved: data.reserved ?? 0,
|
|
@@ -1608,8 +1629,13 @@ function isInsecureHttpUrl(url) {
|
|
|
1608
1629
|
return url.startsWith("http://");
|
|
1609
1630
|
}
|
|
1610
1631
|
var ProviderManager = class _ProviderManager {
|
|
1611
|
-
constructor(providerRegistry) {
|
|
1632
|
+
constructor(providerRegistry, store) {
|
|
1612
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
|
+
}
|
|
1613
1639
|
}
|
|
1614
1640
|
failedProviders = /* @__PURE__ */ new Set();
|
|
1615
1641
|
/** Track when each provider last failed (provider URL -> timestamp) */
|
|
@@ -1618,14 +1644,57 @@ var ProviderManager = class _ProviderManager {
|
|
|
1618
1644
|
providersOnCoolDown = [];
|
|
1619
1645
|
/** Cooldown duration in milliseconds (5 minutes) */
|
|
1620
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
|
+
}
|
|
1621
1674
|
/**
|
|
1622
1675
|
* Clean up expired cooldown entries
|
|
1623
1676
|
*/
|
|
1624
1677
|
cleanupExpiredCooldowns() {
|
|
1625
1678
|
const now = Date.now();
|
|
1679
|
+
const before = this.providersOnCoolDown.length;
|
|
1626
1680
|
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1627
|
-
([, 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
|
+
}
|
|
1628
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
|
+
}
|
|
1629
1698
|
}
|
|
1630
1699
|
/**
|
|
1631
1700
|
* Get the cooldown duration in milliseconds
|
|
@@ -1638,7 +1707,8 @@ var ProviderManager = class _ProviderManager {
|
|
|
1638
1707
|
*/
|
|
1639
1708
|
isOnCooldown(baseUrl) {
|
|
1640
1709
|
this.cleanupExpiredCooldowns();
|
|
1641
|
-
|
|
1710
|
+
const result = this.providersOnCoolDown.some(([url]) => url === baseUrl);
|
|
1711
|
+
return result;
|
|
1642
1712
|
}
|
|
1643
1713
|
/**
|
|
1644
1714
|
* Get all providers currently on cooldown
|
|
@@ -1652,6 +1722,9 @@ var ProviderManager = class _ProviderManager {
|
|
|
1652
1722
|
*/
|
|
1653
1723
|
resetFailedProviders() {
|
|
1654
1724
|
this.failedProviders.clear();
|
|
1725
|
+
if (this.store) {
|
|
1726
|
+
this.store.getState().setFailedProviders([]);
|
|
1727
|
+
}
|
|
1655
1728
|
}
|
|
1656
1729
|
/**
|
|
1657
1730
|
* Get the last failed timestamp for a provider
|
|
@@ -1672,13 +1745,62 @@ var ProviderManager = class _ProviderManager {
|
|
|
1672
1745
|
markFailed(baseUrl) {
|
|
1673
1746
|
const now = Date.now();
|
|
1674
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
|
+
}
|
|
1675
1767
|
this.lastFailed.set(baseUrl, now);
|
|
1676
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
|
+
);
|
|
1677
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
|
+
);
|
|
1678
1783
|
if (!this.isOnCooldown(baseUrl)) {
|
|
1679
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) {
|
|
1680
1798
|
console.log(
|
|
1681
|
-
`
|
|
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)`
|
|
1682
1804
|
);
|
|
1683
1805
|
}
|
|
1684
1806
|
}
|
|
@@ -1690,18 +1812,27 @@ var ProviderManager = class _ProviderManager {
|
|
|
1690
1812
|
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1691
1813
|
([url]) => url !== baseUrl
|
|
1692
1814
|
);
|
|
1815
|
+
if (this.store) {
|
|
1816
|
+
this.store.getState().removeProviderFromCooldown(baseUrl);
|
|
1817
|
+
}
|
|
1693
1818
|
}
|
|
1694
1819
|
/**
|
|
1695
1820
|
* Clear all cooldown tracking
|
|
1696
1821
|
*/
|
|
1697
1822
|
clearCooldowns() {
|
|
1698
1823
|
this.providersOnCoolDown = [];
|
|
1824
|
+
if (this.store) {
|
|
1825
|
+
this.store.getState().clearProvidersOnCooldown();
|
|
1826
|
+
}
|
|
1699
1827
|
}
|
|
1700
1828
|
/**
|
|
1701
1829
|
* Clear all failure tracking (lastFailed timestamps)
|
|
1702
1830
|
*/
|
|
1703
1831
|
clearFailureHistory() {
|
|
1704
1832
|
this.lastFailed.clear();
|
|
1833
|
+
if (this.store) {
|
|
1834
|
+
this.store.getState().setLastFailed({});
|
|
1835
|
+
}
|
|
1705
1836
|
}
|
|
1706
1837
|
/**
|
|
1707
1838
|
* Check if a provider has failed
|
|
@@ -2123,7 +2254,10 @@ var SDK_STORAGE_KEYS = {
|
|
|
2123
2254
|
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
2124
2255
|
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
2125
2256
|
USAGE_TRACKING: "usage_tracking",
|
|
2126
|
-
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"
|
|
2127
2261
|
};
|
|
2128
2262
|
|
|
2129
2263
|
// storage/usageTracking/indexedDB.ts
|
|
@@ -2770,6 +2904,9 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2770
2904
|
lastRoutstr21ModelsUpdate: null,
|
|
2771
2905
|
cachedReceiveTokens: [],
|
|
2772
2906
|
clientIds: [],
|
|
2907
|
+
failedProviders: [],
|
|
2908
|
+
lastFailed: {},
|
|
2909
|
+
providersOnCooldown: [],
|
|
2773
2910
|
setModelsFromAllProviders: (value) => {
|
|
2774
2911
|
const normalized = {};
|
|
2775
2912
|
for (const [baseUrl, models] of Object.entries(value)) {
|
|
@@ -2909,6 +3046,71 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
2909
3046
|
void driver.setItem(SDK_STORAGE_KEYS.CLIENT_IDS, normalized);
|
|
2910
3047
|
return { clientIds: normalized };
|
|
2911
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: [] });
|
|
2912
3114
|
}
|
|
2913
3115
|
}));
|
|
2914
3116
|
var hydrateStoreFromDriver = async (store, driver) => {
|
|
@@ -2927,7 +3129,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2927
3129
|
rawRoutstr21Models,
|
|
2928
3130
|
rawLastRoutstr21ModelsUpdate,
|
|
2929
3131
|
rawCachedReceiveTokens,
|
|
2930
|
-
rawClientIds
|
|
3132
|
+
rawClientIds,
|
|
3133
|
+
rawFailedProviders,
|
|
3134
|
+
rawLastFailed,
|
|
3135
|
+
rawProvidersOnCooldown
|
|
2931
3136
|
] = await Promise.all([
|
|
2932
3137
|
driver.getItem(
|
|
2933
3138
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -2958,7 +3163,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
2958
3163
|
null
|
|
2959
3164
|
),
|
|
2960
3165
|
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
|
|
2961
|
-
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, [])
|
|
2962
3170
|
]);
|
|
2963
3171
|
const modelsFromAllProviders = Object.fromEntries(
|
|
2964
3172
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
@@ -3026,6 +3234,17 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3026
3234
|
createdAt: entry.createdAt ?? Date.now(),
|
|
3027
3235
|
lastUsed: entry.lastUsed ?? null
|
|
3028
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
|
+
}));
|
|
3029
3248
|
store.setState({
|
|
3030
3249
|
modelsFromAllProviders,
|
|
3031
3250
|
lastUsedModel,
|
|
@@ -3041,7 +3260,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3041
3260
|
routstr21Models,
|
|
3042
3261
|
lastRoutstr21ModelsUpdate,
|
|
3043
3262
|
cachedReceiveTokens,
|
|
3044
|
-
clientIds
|
|
3263
|
+
clientIds,
|
|
3264
|
+
failedProviders,
|
|
3265
|
+
lastFailed,
|
|
3266
|
+
providersOnCooldown
|
|
3045
3267
|
});
|
|
3046
3268
|
};
|
|
3047
3269
|
var createSdkStore = ({
|
|
@@ -3207,11 +3429,11 @@ var RoutstrClient = class {
|
|
|
3207
3429
|
this.balanceManager
|
|
3208
3430
|
);
|
|
3209
3431
|
this.streamProcessor = new StreamProcessor();
|
|
3210
|
-
this.providerManager = new ProviderManager(providerRegistry);
|
|
3211
3432
|
this.alertLevel = alertLevel;
|
|
3212
3433
|
this.mode = mode;
|
|
3213
3434
|
this.usageTrackingDriver = options.usageTrackingDriver;
|
|
3214
3435
|
this.sdkStore = options.sdkStore;
|
|
3436
|
+
this.providerManager = options.providerManager ?? new ProviderManager(providerRegistry, this.sdkStore);
|
|
3215
3437
|
}
|
|
3216
3438
|
cashuSpender;
|
|
3217
3439
|
balanceManager;
|
|
@@ -3741,6 +3963,10 @@ var RoutstrClient = class {
|
|
|
3741
3963
|
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
3742
3964
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
3743
3965
|
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
3966
|
+
this._log(
|
|
3967
|
+
"DEBUG",
|
|
3968
|
+
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
|
|
3969
|
+
);
|
|
3744
3970
|
} catch (e) {
|
|
3745
3971
|
this._log(
|
|
3746
3972
|
"WARN",
|
|
@@ -3870,6 +4096,20 @@ var RoutstrClient = class {
|
|
|
3870
4096
|
tryNextProvider = true;
|
|
3871
4097
|
}
|
|
3872
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
|
+
}
|
|
3873
4113
|
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
3874
4114
|
this._log(
|
|
3875
4115
|
"DEBUG",
|
|
@@ -3880,13 +4120,13 @@ var RoutstrClient = class {
|
|
|
3880
4120
|
"DEBUG",
|
|
3881
4121
|
`[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
|
|
3882
4122
|
);
|
|
3883
|
-
const
|
|
4123
|
+
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
3884
4124
|
token,
|
|
3885
4125
|
baseUrl
|
|
3886
4126
|
);
|
|
3887
4127
|
this._log(
|
|
3888
4128
|
"DEBUG",
|
|
3889
|
-
`[RoutstrClient] _handleErrorResponse: Initial API key balance: ${
|
|
4129
|
+
`[RoutstrClient] _handleErrorResponse: Initial API key balance: ${latestBalanceInfo.amount}`
|
|
3890
4130
|
);
|
|
3891
4131
|
const refundResult = await this.balanceManager.refundApiKey({
|
|
3892
4132
|
mintUrl,
|
|
@@ -3898,7 +4138,7 @@ var RoutstrClient = class {
|
|
|
3898
4138
|
"DEBUG",
|
|
3899
4139
|
`[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
|
|
3900
4140
|
);
|
|
3901
|
-
if (!refundResult.success &&
|
|
4141
|
+
if (!refundResult.success && latestBalanceInfo.amount > 0) {
|
|
3902
4142
|
throw new ProviderError(
|
|
3903
4143
|
baseUrl,
|
|
3904
4144
|
status,
|
|
@@ -4038,7 +4278,6 @@ var RoutstrClient = class {
|
|
|
4038
4278
|
try {
|
|
4039
4279
|
const xcashuResults = await this.cashuSpender.refundXcashuTokens(mintUrl);
|
|
4040
4280
|
this._log("DEBUG", "Refund xcashu tokens results:", xcashuResults);
|
|
4041
|
-
const results = await this.cashuSpender.refundProviders(mintUrl);
|
|
4042
4281
|
} catch (error) {
|
|
4043
4282
|
this._log("ERROR", "Failed to refund providers:", error);
|
|
4044
4283
|
}
|