@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/index.js
CHANGED
|
@@ -468,7 +468,7 @@ var ModelManager = class _ModelManager {
|
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
470
|
const DEFAULT_RELAYS = [
|
|
471
|
-
"wss://relay.
|
|
471
|
+
"wss://relay.damus.io",
|
|
472
472
|
"wss://nos.lol",
|
|
473
473
|
"wss://relay.routstr.com"
|
|
474
474
|
];
|
|
@@ -1083,8 +1083,9 @@ var CashuSpender = class {
|
|
|
1083
1083
|
return null;
|
|
1084
1084
|
}
|
|
1085
1085
|
/**
|
|
1086
|
-
* Refund all xcashu tokens from storage
|
|
1087
|
-
*
|
|
1086
|
+
* Refund all xcashu tokens from storage by calling the provider's refund endpoint.
|
|
1087
|
+
* The xcashu token acts as an API key to claim the refund, and the response contains
|
|
1088
|
+
* the actual refunded Cashu token which is then received into the wallet.
|
|
1088
1089
|
* @param mintUrl - The mint URL for receiving tokens
|
|
1089
1090
|
* @param excludeBaseUrls - Base URLs to exclude from refund (optional)
|
|
1090
1091
|
* @returns Results for each xcashu token refund attempt
|
|
@@ -1097,7 +1098,20 @@ var CashuSpender = class {
|
|
|
1097
1098
|
if (excludedUrls.has(baseUrl)) continue;
|
|
1098
1099
|
for (const xcashuToken of tokens) {
|
|
1099
1100
|
try {
|
|
1100
|
-
|
|
1101
|
+
if (!this.balanceManager) {
|
|
1102
|
+
throw new Error("BalanceManager not available for xcashu refund");
|
|
1103
|
+
}
|
|
1104
|
+
const fetchResult = await this.balanceManager.fetchRefundToken(
|
|
1105
|
+
baseUrl,
|
|
1106
|
+
xcashuToken.token,
|
|
1107
|
+
true
|
|
1108
|
+
);
|
|
1109
|
+
if (!fetchResult.success || !fetchResult.token) {
|
|
1110
|
+
throw new Error(
|
|
1111
|
+
fetchResult.error || "Failed to fetch refund token from provider"
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
const receiveResult = await this.receiveToken(fetchResult.token);
|
|
1101
1115
|
if (receiveResult.success) {
|
|
1102
1116
|
this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
|
|
1103
1117
|
results.push({
|
|
@@ -1112,7 +1126,10 @@ var CashuSpender = class {
|
|
|
1112
1126
|
} else {
|
|
1113
1127
|
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
1114
1128
|
const newTryCount = currentTryCount + 1;
|
|
1115
|
-
this.storageAdapter.updateXcashuTokenTryCount(
|
|
1129
|
+
this.storageAdapter.updateXcashuTokenTryCount(
|
|
1130
|
+
xcashuToken.token,
|
|
1131
|
+
newTryCount
|
|
1132
|
+
);
|
|
1116
1133
|
results.push({
|
|
1117
1134
|
baseUrl,
|
|
1118
1135
|
token: xcashuToken.token,
|
|
@@ -1121,13 +1138,16 @@ var CashuSpender = class {
|
|
|
1121
1138
|
});
|
|
1122
1139
|
this._log(
|
|
1123
1140
|
"DEBUG",
|
|
1124
|
-
`[CashuSpender] refundXcashuTokens: Failed to refund
|
|
1141
|
+
`[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}`
|
|
1125
1142
|
);
|
|
1126
1143
|
}
|
|
1127
1144
|
} catch (error) {
|
|
1128
1145
|
const currentTryCount = xcashuToken.tryCount ?? 0;
|
|
1129
1146
|
const newTryCount = currentTryCount + 1;
|
|
1130
|
-
this.storageAdapter.updateXcashuTokenTryCount(
|
|
1147
|
+
this.storageAdapter.updateXcashuTokenTryCount(
|
|
1148
|
+
xcashuToken.token,
|
|
1149
|
+
newTryCount
|
|
1150
|
+
);
|
|
1131
1151
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1132
1152
|
results.push({
|
|
1133
1153
|
baseUrl,
|
|
@@ -1164,7 +1184,10 @@ var CashuSpender = class {
|
|
|
1164
1184
|
if (refundResult.success) {
|
|
1165
1185
|
this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
|
|
1166
1186
|
} else {
|
|
1167
|
-
this.storageAdapter.updateApiKeyBalance(
|
|
1187
|
+
this.storageAdapter.updateApiKeyBalance(
|
|
1188
|
+
apiKeyEntry.baseUrl,
|
|
1189
|
+
apiKeyEntry.amount
|
|
1190
|
+
);
|
|
1168
1191
|
}
|
|
1169
1192
|
results.push({
|
|
1170
1193
|
baseUrl: apiKeyEntry.baseUrl,
|
|
@@ -1302,7 +1325,7 @@ var BalanceManager = class {
|
|
|
1302
1325
|
}
|
|
1303
1326
|
let fetchResult;
|
|
1304
1327
|
try {
|
|
1305
|
-
fetchResult = await this.
|
|
1328
|
+
fetchResult = await this.fetchRefundToken(baseUrl, apiKey);
|
|
1306
1329
|
if (!fetchResult.success) {
|
|
1307
1330
|
return {
|
|
1308
1331
|
success: false,
|
|
@@ -1338,9 +1361,9 @@ var BalanceManager = class {
|
|
|
1338
1361
|
}
|
|
1339
1362
|
}
|
|
1340
1363
|
/**
|
|
1341
|
-
* Fetch refund token from provider API using API key authentication
|
|
1364
|
+
* Fetch refund token from provider API using API key (or xcashu token) authentication
|
|
1342
1365
|
*/
|
|
1343
|
-
async
|
|
1366
|
+
async fetchRefundToken(baseUrl, apiKeyOrToken, xCashu = false) {
|
|
1344
1367
|
if (!baseUrl) {
|
|
1345
1368
|
return {
|
|
1346
1369
|
success: false,
|
|
@@ -1354,12 +1377,17 @@ var BalanceManager = class {
|
|
|
1354
1377
|
controller.abort();
|
|
1355
1378
|
}, 6e4);
|
|
1356
1379
|
try {
|
|
1380
|
+
const headers = {
|
|
1381
|
+
"Content-Type": "application/json"
|
|
1382
|
+
};
|
|
1383
|
+
if (xCashu) {
|
|
1384
|
+
headers["X-Cashu"] = apiKeyOrToken;
|
|
1385
|
+
} else {
|
|
1386
|
+
headers["Authorization"] = `Bearer ${apiKeyOrToken}`;
|
|
1387
|
+
}
|
|
1357
1388
|
const response = await fetch(url, {
|
|
1358
1389
|
method: "POST",
|
|
1359
|
-
headers
|
|
1360
|
-
Authorization: `Bearer ${apiKey}`,
|
|
1361
|
-
"Content-Type": "application/json"
|
|
1362
|
-
},
|
|
1390
|
+
headers,
|
|
1363
1391
|
signal: controller.signal
|
|
1364
1392
|
});
|
|
1365
1393
|
clearTimeout(timeoutId);
|
|
@@ -1380,10 +1408,7 @@ var BalanceManager = class {
|
|
|
1380
1408
|
};
|
|
1381
1409
|
} catch (error) {
|
|
1382
1410
|
clearTimeout(timeoutId);
|
|
1383
|
-
console.error(
|
|
1384
|
-
"[BalanceManager._fetchRefundTokenWithApiKey] Fetch error",
|
|
1385
|
-
error
|
|
1386
|
-
);
|
|
1411
|
+
console.error("[BalanceManager.fetchRefundToken] Fetch error", error);
|
|
1387
1412
|
if (error instanceof Error) {
|
|
1388
1413
|
if (error.name === "AbortError") {
|
|
1389
1414
|
return {
|
|
@@ -1430,11 +1455,7 @@ var BalanceManager = class {
|
|
|
1430
1455
|
};
|
|
1431
1456
|
}
|
|
1432
1457
|
cashuToken = tokenResult.token;
|
|
1433
|
-
const topUpResult = await this._postTopUp(
|
|
1434
|
-
baseUrl,
|
|
1435
|
-
apiKey,
|
|
1436
|
-
cashuToken
|
|
1437
|
-
);
|
|
1458
|
+
const topUpResult = await this._postTopUp(baseUrl, apiKey, cashuToken);
|
|
1438
1459
|
requestId = topUpResult.requestId;
|
|
1439
1460
|
console.log(topUpResult);
|
|
1440
1461
|
if (!topUpResult.success) {
|
|
@@ -1800,7 +1821,7 @@ var BalanceManager = class {
|
|
|
1800
1821
|
console.log(response.status);
|
|
1801
1822
|
const data = await response.json();
|
|
1802
1823
|
console.log("FAILED ", data);
|
|
1803
|
-
const isInvalidApiKey = response.status === 401 && data?.code === "invalid_api_key" && data?.message?.includes("proofs already spent");
|
|
1824
|
+
const isInvalidApiKey = response.status === 401 && data?.detail?.error?.code === "invalid_api_key" && data?.detail?.error?.message?.includes("proofs already spent");
|
|
1804
1825
|
return {
|
|
1805
1826
|
amount: -1,
|
|
1806
1827
|
reserved: data.reserved ?? 0,
|
|
@@ -2298,8 +2319,13 @@ function isInsecureHttpUrl(url) {
|
|
|
2298
2319
|
return url.startsWith("http://");
|
|
2299
2320
|
}
|
|
2300
2321
|
var ProviderManager = class _ProviderManager {
|
|
2301
|
-
constructor(providerRegistry) {
|
|
2322
|
+
constructor(providerRegistry, store) {
|
|
2302
2323
|
this.providerRegistry = providerRegistry;
|
|
2324
|
+
this.instanceId = `pm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
2325
|
+
if (store) {
|
|
2326
|
+
this.store = store;
|
|
2327
|
+
this.hydrateFromStore();
|
|
2328
|
+
}
|
|
2303
2329
|
}
|
|
2304
2330
|
failedProviders = /* @__PURE__ */ new Set();
|
|
2305
2331
|
/** Track when each provider last failed (provider URL -> timestamp) */
|
|
@@ -2308,14 +2334,57 @@ var ProviderManager = class _ProviderManager {
|
|
|
2308
2334
|
providersOnCoolDown = [];
|
|
2309
2335
|
/** Cooldown duration in milliseconds (5 minutes) */
|
|
2310
2336
|
static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
|
|
2337
|
+
/** Optional persistent store for failure tracking */
|
|
2338
|
+
store = null;
|
|
2339
|
+
/** Instance ID for debugging */
|
|
2340
|
+
instanceId;
|
|
2341
|
+
/**
|
|
2342
|
+
* Hydrate in-memory state from persistent store
|
|
2343
|
+
*/
|
|
2344
|
+
hydrateFromStore() {
|
|
2345
|
+
if (!this.store) return;
|
|
2346
|
+
const state = this.store.getState();
|
|
2347
|
+
this.failedProviders = new Set(state.failedProviders);
|
|
2348
|
+
this.lastFailed = new Map(Object.entries(state.lastFailed));
|
|
2349
|
+
const now = Date.now();
|
|
2350
|
+
this.providersOnCoolDown = state.providersOnCooldown.filter(
|
|
2351
|
+
(entry) => now - entry.timestamp < _ProviderManager.COOLDOWN_DURATION_MS
|
|
2352
|
+
).map((entry) => [entry.baseUrl, entry.timestamp]);
|
|
2353
|
+
console.log(`[ProviderManager:${this.instanceId}] Hydrated from store:`);
|
|
2354
|
+
console.log(` failedProviders: ${this.failedProviders.size}`);
|
|
2355
|
+
console.log(` lastFailed: ${this.lastFailed.size}`);
|
|
2356
|
+
console.log(` providersOnCooldown: ${this.providersOnCoolDown.length}`);
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Get instance ID for debugging
|
|
2360
|
+
*/
|
|
2361
|
+
getInstanceId() {
|
|
2362
|
+
return this.instanceId;
|
|
2363
|
+
}
|
|
2311
2364
|
/**
|
|
2312
2365
|
* Clean up expired cooldown entries
|
|
2313
2366
|
*/
|
|
2314
2367
|
cleanupExpiredCooldowns() {
|
|
2315
2368
|
const now = Date.now();
|
|
2369
|
+
const before = this.providersOnCoolDown.length;
|
|
2316
2370
|
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
2317
|
-
([, timestamp]) =>
|
|
2371
|
+
([url, timestamp]) => {
|
|
2372
|
+
const age = now - timestamp;
|
|
2373
|
+
const isExpired = age >= _ProviderManager.COOLDOWN_DURATION_MS;
|
|
2374
|
+
if (isExpired) {
|
|
2375
|
+
console.log(
|
|
2376
|
+
`[cleanupExpiredCooldowns:${this.instanceId}] Removing expired cooldown for ${url} (age: ${age}ms, cooldown: ${_ProviderManager.COOLDOWN_DURATION_MS}ms)`
|
|
2377
|
+
);
|
|
2378
|
+
}
|
|
2379
|
+
return !isExpired;
|
|
2380
|
+
}
|
|
2318
2381
|
);
|
|
2382
|
+
const after = this.providersOnCoolDown.length;
|
|
2383
|
+
if (before !== after) {
|
|
2384
|
+
console.log(
|
|
2385
|
+
`[cleanupExpiredCooldowns:${this.instanceId}] Cleaned up ${before - after} expired cooldown(s), ${after} remaining`
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2319
2388
|
}
|
|
2320
2389
|
/**
|
|
2321
2390
|
* Get the cooldown duration in milliseconds
|
|
@@ -2328,7 +2397,8 @@ var ProviderManager = class _ProviderManager {
|
|
|
2328
2397
|
*/
|
|
2329
2398
|
isOnCooldown(baseUrl) {
|
|
2330
2399
|
this.cleanupExpiredCooldowns();
|
|
2331
|
-
|
|
2400
|
+
const result = this.providersOnCoolDown.some(([url]) => url === baseUrl);
|
|
2401
|
+
return result;
|
|
2332
2402
|
}
|
|
2333
2403
|
/**
|
|
2334
2404
|
* Get all providers currently on cooldown
|
|
@@ -2342,6 +2412,9 @@ var ProviderManager = class _ProviderManager {
|
|
|
2342
2412
|
*/
|
|
2343
2413
|
resetFailedProviders() {
|
|
2344
2414
|
this.failedProviders.clear();
|
|
2415
|
+
if (this.store) {
|
|
2416
|
+
this.store.getState().setFailedProviders([]);
|
|
2417
|
+
}
|
|
2345
2418
|
}
|
|
2346
2419
|
/**
|
|
2347
2420
|
* Get the last failed timestamp for a provider
|
|
@@ -2362,13 +2435,62 @@ var ProviderManager = class _ProviderManager {
|
|
|
2362
2435
|
markFailed(baseUrl) {
|
|
2363
2436
|
const now = Date.now();
|
|
2364
2437
|
const lastFailure = this.lastFailed.get(baseUrl);
|
|
2438
|
+
console.log(`[markFailed:${this.instanceId}] baseUrl: ${baseUrl}`);
|
|
2439
|
+
console.log(
|
|
2440
|
+
`[markFailed:${this.instanceId}] lastFailure from map: ${lastFailure}`
|
|
2441
|
+
);
|
|
2442
|
+
console.log(
|
|
2443
|
+
`[markFailed:${this.instanceId}] current timestamp (now): ${now}`
|
|
2444
|
+
);
|
|
2445
|
+
console.log(
|
|
2446
|
+
`[markFailed:${this.instanceId}] COOLDOWN_DURATION_MS: ${_ProviderManager.COOLDOWN_DURATION_MS}`
|
|
2447
|
+
);
|
|
2448
|
+
if (lastFailure !== void 0) {
|
|
2449
|
+
const timeSinceLastFailure = now - lastFailure;
|
|
2450
|
+
console.log(
|
|
2451
|
+
`[markFailed:${this.instanceId}] timeSinceLastFailure: ${timeSinceLastFailure}ms`
|
|
2452
|
+
);
|
|
2453
|
+
console.log(
|
|
2454
|
+
`[markFailed:${this.instanceId}] isWithinCooldownWindow: ${timeSinceLastFailure < _ProviderManager.COOLDOWN_DURATION_MS}`
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2365
2457
|
this.lastFailed.set(baseUrl, now);
|
|
2366
2458
|
this.failedProviders.add(baseUrl);
|
|
2459
|
+
if (this.store) {
|
|
2460
|
+
this.store.getState().setLastFailedTimestamp(baseUrl, now);
|
|
2461
|
+
this.store.getState().addFailedProvider(baseUrl);
|
|
2462
|
+
}
|
|
2463
|
+
console.log(
|
|
2464
|
+
`[markFailed:${this.instanceId}] Updated lastFailed map for ${baseUrl} to ${now}`
|
|
2465
|
+
);
|
|
2466
|
+
console.log(
|
|
2467
|
+
`[markFailed:${this.instanceId}] failedProviders set size: ${this.failedProviders.size}`
|
|
2468
|
+
);
|
|
2367
2469
|
if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
|
|
2470
|
+
console.log(
|
|
2471
|
+
`[markFailed:${this.instanceId}] Second failure detected within cooldown window for ${baseUrl}`
|
|
2472
|
+
);
|
|
2368
2473
|
if (!this.isOnCooldown(baseUrl)) {
|
|
2369
2474
|
this.providersOnCoolDown.push([baseUrl, now]);
|
|
2475
|
+
if (this.store) {
|
|
2476
|
+
this.store.getState().addProviderOnCooldown(baseUrl, now);
|
|
2477
|
+
}
|
|
2370
2478
|
console.log(
|
|
2371
|
-
`Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
|
|
2479
|
+
`[markFailed:${this.instanceId}] Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
|
|
2480
|
+
);
|
|
2481
|
+
} else {
|
|
2482
|
+
console.log(
|
|
2483
|
+
`[markFailed:${this.instanceId}] Provider ${baseUrl} is already on cooldown`
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
} else {
|
|
2487
|
+
if (lastFailure === void 0) {
|
|
2488
|
+
console.log(
|
|
2489
|
+
`[markFailed:${this.instanceId}] First failure for ${baseUrl} - not adding to cooldown yet`
|
|
2490
|
+
);
|
|
2491
|
+
} else {
|
|
2492
|
+
console.log(
|
|
2493
|
+
`[markFailed:${this.instanceId}] Failure outside cooldown window for ${baseUrl} (timeSinceLastFailure: ${now - lastFailure}ms)`
|
|
2372
2494
|
);
|
|
2373
2495
|
}
|
|
2374
2496
|
}
|
|
@@ -2380,18 +2502,27 @@ var ProviderManager = class _ProviderManager {
|
|
|
2380
2502
|
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
2381
2503
|
([url]) => url !== baseUrl
|
|
2382
2504
|
);
|
|
2505
|
+
if (this.store) {
|
|
2506
|
+
this.store.getState().removeProviderFromCooldown(baseUrl);
|
|
2507
|
+
}
|
|
2383
2508
|
}
|
|
2384
2509
|
/**
|
|
2385
2510
|
* Clear all cooldown tracking
|
|
2386
2511
|
*/
|
|
2387
2512
|
clearCooldowns() {
|
|
2388
2513
|
this.providersOnCoolDown = [];
|
|
2514
|
+
if (this.store) {
|
|
2515
|
+
this.store.getState().clearProvidersOnCooldown();
|
|
2516
|
+
}
|
|
2389
2517
|
}
|
|
2390
2518
|
/**
|
|
2391
2519
|
* Clear all failure tracking (lastFailed timestamps)
|
|
2392
2520
|
*/
|
|
2393
2521
|
clearFailureHistory() {
|
|
2394
2522
|
this.lastFailed.clear();
|
|
2523
|
+
if (this.store) {
|
|
2524
|
+
this.store.getState().setLastFailed({});
|
|
2525
|
+
}
|
|
2395
2526
|
}
|
|
2396
2527
|
/**
|
|
2397
2528
|
* Check if a provider has failed
|
|
@@ -2960,7 +3091,10 @@ var SDK_STORAGE_KEYS = {
|
|
|
2960
3091
|
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
2961
3092
|
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
2962
3093
|
USAGE_TRACKING: "usage_tracking",
|
|
2963
|
-
CLIENT_IDS: "client_ids"
|
|
3094
|
+
CLIENT_IDS: "client_ids",
|
|
3095
|
+
FAILED_PROVIDERS: "failed_providers",
|
|
3096
|
+
LAST_FAILED: "last_failed",
|
|
3097
|
+
PROVIDERS_ON_COOLDOWN: "providers_on_cooldown"
|
|
2964
3098
|
};
|
|
2965
3099
|
|
|
2966
3100
|
// storage/usageTracking/indexedDB.ts
|
|
@@ -3607,6 +3741,9 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3607
3741
|
lastRoutstr21ModelsUpdate: null,
|
|
3608
3742
|
cachedReceiveTokens: [],
|
|
3609
3743
|
clientIds: [],
|
|
3744
|
+
failedProviders: [],
|
|
3745
|
+
lastFailed: {},
|
|
3746
|
+
providersOnCooldown: [],
|
|
3610
3747
|
setModelsFromAllProviders: (value) => {
|
|
3611
3748
|
const normalized = {};
|
|
3612
3749
|
for (const [baseUrl, models] of Object.entries(value)) {
|
|
@@ -3746,6 +3883,71 @@ var createEmptyStore = (driver) => vanilla.createStore((set, get) => ({
|
|
|
3746
3883
|
void driver.setItem(SDK_STORAGE_KEYS.CLIENT_IDS, normalized);
|
|
3747
3884
|
return { clientIds: normalized };
|
|
3748
3885
|
});
|
|
3886
|
+
},
|
|
3887
|
+
// ========== Failure Tracking ==========
|
|
3888
|
+
setFailedProviders: (value) => {
|
|
3889
|
+
const normalized = value.map((url) => normalizeBaseUrl5(url));
|
|
3890
|
+
void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, normalized);
|
|
3891
|
+
set({ failedProviders: normalized });
|
|
3892
|
+
},
|
|
3893
|
+
addFailedProvider: (baseUrl) => {
|
|
3894
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3895
|
+
const current = get().failedProviders;
|
|
3896
|
+
if (!current.includes(normalized)) {
|
|
3897
|
+
const updated = [...current, normalized];
|
|
3898
|
+
void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, updated);
|
|
3899
|
+
set({ failedProviders: updated });
|
|
3900
|
+
}
|
|
3901
|
+
},
|
|
3902
|
+
removeFailedProvider: (baseUrl) => {
|
|
3903
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3904
|
+
const current = get().failedProviders;
|
|
3905
|
+
const updated = current.filter((url) => url !== normalized);
|
|
3906
|
+
void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, updated);
|
|
3907
|
+
set({ failedProviders: updated });
|
|
3908
|
+
},
|
|
3909
|
+
setLastFailed: (value) => {
|
|
3910
|
+
const normalized = {};
|
|
3911
|
+
for (const [baseUrl, timestamp] of Object.entries(value)) {
|
|
3912
|
+
normalized[normalizeBaseUrl5(baseUrl)] = timestamp;
|
|
3913
|
+
}
|
|
3914
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_FAILED, normalized);
|
|
3915
|
+
set({ lastFailed: normalized });
|
|
3916
|
+
},
|
|
3917
|
+
setLastFailedTimestamp: (baseUrl, timestamp) => {
|
|
3918
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3919
|
+
const current = get().lastFailed;
|
|
3920
|
+
const updated = { ...current, [normalized]: timestamp };
|
|
3921
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_FAILED, updated);
|
|
3922
|
+
set({ lastFailed: updated });
|
|
3923
|
+
},
|
|
3924
|
+
setProvidersOnCooldown: (value) => {
|
|
3925
|
+
const normalized = value.map((entry) => ({
|
|
3926
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
3927
|
+
timestamp: entry.timestamp
|
|
3928
|
+
}));
|
|
3929
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, normalized);
|
|
3930
|
+
set({ providersOnCooldown: normalized });
|
|
3931
|
+
},
|
|
3932
|
+
addProviderOnCooldown: (baseUrl, timestamp) => {
|
|
3933
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3934
|
+
const current = get().providersOnCooldown;
|
|
3935
|
+
if (!current.some((entry) => entry.baseUrl === normalized)) {
|
|
3936
|
+
const updated = [...current, { baseUrl: normalized, timestamp }];
|
|
3937
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, updated);
|
|
3938
|
+
set({ providersOnCooldown: updated });
|
|
3939
|
+
}
|
|
3940
|
+
},
|
|
3941
|
+
removeProviderFromCooldown: (baseUrl) => {
|
|
3942
|
+
const normalized = normalizeBaseUrl5(baseUrl);
|
|
3943
|
+
const current = get().providersOnCooldown;
|
|
3944
|
+
const updated = current.filter((entry) => entry.baseUrl !== normalized);
|
|
3945
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, updated);
|
|
3946
|
+
set({ providersOnCooldown: updated });
|
|
3947
|
+
},
|
|
3948
|
+
clearProvidersOnCooldown: () => {
|
|
3949
|
+
void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, []);
|
|
3950
|
+
set({ providersOnCooldown: [] });
|
|
3749
3951
|
}
|
|
3750
3952
|
}));
|
|
3751
3953
|
var hydrateStoreFromDriver = async (store, driver) => {
|
|
@@ -3764,7 +3966,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3764
3966
|
rawRoutstr21Models,
|
|
3765
3967
|
rawLastRoutstr21ModelsUpdate,
|
|
3766
3968
|
rawCachedReceiveTokens,
|
|
3767
|
-
rawClientIds
|
|
3969
|
+
rawClientIds,
|
|
3970
|
+
rawFailedProviders,
|
|
3971
|
+
rawLastFailed,
|
|
3972
|
+
rawProvidersOnCooldown
|
|
3768
3973
|
] = await Promise.all([
|
|
3769
3974
|
driver.getItem(
|
|
3770
3975
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -3795,7 +4000,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3795
4000
|
null
|
|
3796
4001
|
),
|
|
3797
4002
|
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
|
|
3798
|
-
driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, [])
|
|
4003
|
+
driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, []),
|
|
4004
|
+
driver.getItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, []),
|
|
4005
|
+
driver.getItem(SDK_STORAGE_KEYS.LAST_FAILED, {}),
|
|
4006
|
+
driver.getItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, [])
|
|
3799
4007
|
]);
|
|
3800
4008
|
const modelsFromAllProviders = Object.fromEntries(
|
|
3801
4009
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
@@ -3863,6 +4071,17 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3863
4071
|
createdAt: entry.createdAt ?? Date.now(),
|
|
3864
4072
|
lastUsed: entry.lastUsed ?? null
|
|
3865
4073
|
}));
|
|
4074
|
+
const failedProviders = rawFailedProviders.map((url) => normalizeBaseUrl5(url));
|
|
4075
|
+
const lastFailed = Object.fromEntries(
|
|
4076
|
+
Object.entries(rawLastFailed).map(([baseUrl, timestamp]) => [
|
|
4077
|
+
normalizeBaseUrl5(baseUrl),
|
|
4078
|
+
timestamp
|
|
4079
|
+
])
|
|
4080
|
+
);
|
|
4081
|
+
const providersOnCooldown = rawProvidersOnCooldown.map((entry) => ({
|
|
4082
|
+
baseUrl: normalizeBaseUrl5(entry.baseUrl),
|
|
4083
|
+
timestamp: entry.timestamp
|
|
4084
|
+
}));
|
|
3866
4085
|
store.setState({
|
|
3867
4086
|
modelsFromAllProviders,
|
|
3868
4087
|
lastUsedModel,
|
|
@@ -3878,7 +4097,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
3878
4097
|
routstr21Models,
|
|
3879
4098
|
lastRoutstr21ModelsUpdate,
|
|
3880
4099
|
cachedReceiveTokens,
|
|
3881
|
-
clientIds
|
|
4100
|
+
clientIds,
|
|
4101
|
+
failedProviders,
|
|
4102
|
+
lastFailed,
|
|
4103
|
+
providersOnCooldown
|
|
3882
4104
|
});
|
|
3883
4105
|
};
|
|
3884
4106
|
var createSdkStore = ({
|
|
@@ -4294,11 +4516,11 @@ var RoutstrClient = class {
|
|
|
4294
4516
|
this.balanceManager
|
|
4295
4517
|
);
|
|
4296
4518
|
this.streamProcessor = new StreamProcessor();
|
|
4297
|
-
this.providerManager = new ProviderManager(providerRegistry);
|
|
4298
4519
|
this.alertLevel = alertLevel;
|
|
4299
4520
|
this.mode = mode;
|
|
4300
4521
|
this.usageTrackingDriver = options.usageTrackingDriver;
|
|
4301
4522
|
this.sdkStore = options.sdkStore;
|
|
4523
|
+
this.providerManager = options.providerManager ?? new ProviderManager(providerRegistry, this.sdkStore);
|
|
4302
4524
|
}
|
|
4303
4525
|
cashuSpender;
|
|
4304
4526
|
balanceManager;
|
|
@@ -4828,6 +5050,10 @@ var RoutstrClient = class {
|
|
|
4828
5050
|
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
4829
5051
|
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
4830
5052
|
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
5053
|
+
this._log(
|
|
5054
|
+
"DEBUG",
|
|
5055
|
+
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
|
|
5056
|
+
);
|
|
4831
5057
|
} catch (e) {
|
|
4832
5058
|
this._log(
|
|
4833
5059
|
"WARN",
|
|
@@ -4957,6 +5183,20 @@ var RoutstrClient = class {
|
|
|
4957
5183
|
tryNextProvider = true;
|
|
4958
5184
|
}
|
|
4959
5185
|
}
|
|
5186
|
+
if (status === 401 && this.mode === "apikeys") {
|
|
5187
|
+
this._log(
|
|
5188
|
+
"DEBUG",
|
|
5189
|
+
`[RoutstrClient] _handleErrorResponse: Checking balance for ${baseUrl}, key preview=${token}`
|
|
5190
|
+
);
|
|
5191
|
+
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
5192
|
+
token,
|
|
5193
|
+
baseUrl
|
|
5194
|
+
);
|
|
5195
|
+
if (latestBalanceInfo.isInvalidApiKey) {
|
|
5196
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
5197
|
+
tryNextProvider = true;
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
4960
5200
|
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
4961
5201
|
this._log(
|
|
4962
5202
|
"DEBUG",
|
|
@@ -4967,13 +5207,13 @@ var RoutstrClient = class {
|
|
|
4967
5207
|
"DEBUG",
|
|
4968
5208
|
`[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
|
|
4969
5209
|
);
|
|
4970
|
-
const
|
|
5210
|
+
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
4971
5211
|
token,
|
|
4972
5212
|
baseUrl
|
|
4973
5213
|
);
|
|
4974
5214
|
this._log(
|
|
4975
5215
|
"DEBUG",
|
|
4976
|
-
`[RoutstrClient] _handleErrorResponse: Initial API key balance: ${
|
|
5216
|
+
`[RoutstrClient] _handleErrorResponse: Initial API key balance: ${latestBalanceInfo.amount}`
|
|
4977
5217
|
);
|
|
4978
5218
|
const refundResult = await this.balanceManager.refundApiKey({
|
|
4979
5219
|
mintUrl,
|
|
@@ -4985,7 +5225,7 @@ var RoutstrClient = class {
|
|
|
4985
5225
|
"DEBUG",
|
|
4986
5226
|
`[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
|
|
4987
5227
|
);
|
|
4988
|
-
if (!refundResult.success &&
|
|
5228
|
+
if (!refundResult.success && latestBalanceInfo.amount > 0) {
|
|
4989
5229
|
throw new ProviderError(
|
|
4990
5230
|
baseUrl,
|
|
4991
5231
|
status,
|
|
@@ -5125,7 +5365,6 @@ var RoutstrClient = class {
|
|
|
5125
5365
|
try {
|
|
5126
5366
|
const xcashuResults = await this.cashuSpender.refundXcashuTokens(mintUrl);
|
|
5127
5367
|
this._log("DEBUG", "Refund xcashu tokens results:", xcashuResults);
|
|
5128
|
-
const results = await this.cashuSpender.refundProviders(mintUrl);
|
|
5129
5368
|
} catch (error) {
|
|
5130
5369
|
this._log("ERROR", "Failed to refund providers:", error);
|
|
5131
5370
|
}
|
|
@@ -5466,7 +5705,8 @@ async function resolveRouteRequestContext(options) {
|
|
|
5466
5705
|
debugLevel,
|
|
5467
5706
|
mode = "apikeys",
|
|
5468
5707
|
usageTrackingDriver,
|
|
5469
|
-
sdkStore
|
|
5708
|
+
sdkStore,
|
|
5709
|
+
providerManager: providedProviderManager
|
|
5470
5710
|
} = options;
|
|
5471
5711
|
let modelManager;
|
|
5472
5712
|
let providers;
|
|
@@ -5486,7 +5726,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5486
5726
|
}
|
|
5487
5727
|
await modelManager.fetchModels(providers, forceRefresh);
|
|
5488
5728
|
}
|
|
5489
|
-
const providerManager = new ProviderManager(providerRegistry);
|
|
5729
|
+
const providerManager = providedProviderManager ?? new ProviderManager(providerRegistry, sdkStore);
|
|
5490
5730
|
let baseUrl;
|
|
5491
5731
|
let selectedModel;
|
|
5492
5732
|
if (forcedProvider) {
|
|
@@ -5531,7 +5771,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5531
5771
|
providerRegistry,
|
|
5532
5772
|
"min",
|
|
5533
5773
|
mode,
|
|
5534
|
-
{ usageTrackingDriver, sdkStore }
|
|
5774
|
+
{ usageTrackingDriver, sdkStore, providerManager }
|
|
5535
5775
|
);
|
|
5536
5776
|
if (debugLevel) {
|
|
5537
5777
|
client.setDebugLevel(debugLevel);
|