@routstr/sdk 0.3.8 → 0.3.9
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.js +80 -25
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +80 -25
- package/dist/client/index.mjs.map +1 -1
- package/dist/discovery/index.d.mts +37 -3
- package/dist/discovery/index.d.ts +37 -3
- package/dist/discovery/index.js +231 -75
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +231 -75
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +592 -102
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +591 -103
- package/dist/index.mjs.map +1 -1
- package/dist/storage/index.d.mts +27 -1
- package/dist/storage/index.d.ts +27 -1
- package/dist/storage/index.js +281 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +280 -3
- package/dist/storage/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +4 -0
- package/dist/wallet/index.d.ts +4 -0
- package/dist/wallet/index.js +11 -4
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +11 -4
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -139,6 +139,9 @@ var MintDiscoveryError = class extends Error {
|
|
|
139
139
|
}
|
|
140
140
|
baseUrl;
|
|
141
141
|
};
|
|
142
|
+
function isBunRuntime() {
|
|
143
|
+
return typeof Bun !== "undefined";
|
|
144
|
+
}
|
|
142
145
|
var ModelManager = class _ModelManager {
|
|
143
146
|
constructor(adapter, config = {}) {
|
|
144
147
|
this.adapter = adapter;
|
|
@@ -148,6 +151,7 @@ var ModelManager = class _ModelManager {
|
|
|
148
151
|
this.excludeProviderUrls = config.excludeProviderUrls || [];
|
|
149
152
|
this.routstrPubkey = config.routstrPubkey || "4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8";
|
|
150
153
|
this.logger = (config.logger ?? consoleLogger).child("ModelManager");
|
|
154
|
+
this.eventStoreDbPath = config.eventStoreDbPath;
|
|
151
155
|
}
|
|
152
156
|
adapter;
|
|
153
157
|
cacheTTL;
|
|
@@ -157,6 +161,11 @@ var ModelManager = class _ModelManager {
|
|
|
157
161
|
routstrPubkey;
|
|
158
162
|
logger;
|
|
159
163
|
providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
|
|
164
|
+
/** Persistent event store for relay-fetched events (null if not configured/initialized) */
|
|
165
|
+
eventStore = null;
|
|
166
|
+
eventStoreDb = null;
|
|
167
|
+
eventStoreInitPromise = null;
|
|
168
|
+
eventStoreDbPath;
|
|
160
169
|
/**
|
|
161
170
|
* Get the list of bootstrapped provider base URLs
|
|
162
171
|
* @returns Array of provider base URLs
|
|
@@ -164,6 +173,104 @@ var ModelManager = class _ModelManager {
|
|
|
164
173
|
getBaseUrls() {
|
|
165
174
|
return this.adapter.getBaseUrlsList();
|
|
166
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Lazily initialize the persistent event store.
|
|
178
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
179
|
+
*/
|
|
180
|
+
async ensureEventStore() {
|
|
181
|
+
if (!this.eventStoreDbPath) return null;
|
|
182
|
+
if (this.eventStore) return this.eventStore;
|
|
183
|
+
if (!this.eventStoreInitPromise) {
|
|
184
|
+
this.eventStoreInitPromise = (async () => {
|
|
185
|
+
try {
|
|
186
|
+
const db = await this.createPersistentEventDatabase();
|
|
187
|
+
this.eventStoreDb = db;
|
|
188
|
+
this.eventStore = new applesauceCore.EventStore({ database: db });
|
|
189
|
+
this.initializeEventStoreMetadata();
|
|
190
|
+
this.logger.log(
|
|
191
|
+
`Persistent event store initialized at ${this.eventStoreDbPath}`
|
|
192
|
+
);
|
|
193
|
+
return this.eventStore;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this.eventStoreInitPromise = null;
|
|
196
|
+
throw new Error(
|
|
197
|
+
`applesauce-sqlite with a supported SQLite driver is required for persistent Nostr event storage. Bun uses bun:sqlite; Node.js uses better-sqlite3. Install optional dependencies or omit eventStoreDbPath. (${error})`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
}
|
|
202
|
+
return this.eventStoreInitPromise;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get the persistent event store, initializing it if configured.
|
|
206
|
+
* Returns null if no eventStoreDbPath was provided.
|
|
207
|
+
*/
|
|
208
|
+
async getEventStore() {
|
|
209
|
+
return this.ensureEventStore();
|
|
210
|
+
}
|
|
211
|
+
async createPersistentEventDatabase() {
|
|
212
|
+
if (isBunRuntime()) {
|
|
213
|
+
const { BunSqliteEventDatabase } = await import('applesauce-sqlite/bun');
|
|
214
|
+
return new BunSqliteEventDatabase(
|
|
215
|
+
this.eventStoreDbPath
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
const { BetterSqlite3EventDatabase } = await import('applesauce-sqlite/better-sqlite3');
|
|
219
|
+
return new BetterSqlite3EventDatabase(
|
|
220
|
+
this.eventStoreDbPath
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
/** Close the persistent event store database handle, if configured. */
|
|
224
|
+
closeEventStore() {
|
|
225
|
+
this.eventStoreDb?.close?.();
|
|
226
|
+
this.eventStore = null;
|
|
227
|
+
this.eventStoreDb = null;
|
|
228
|
+
this.eventStoreInitPromise = null;
|
|
229
|
+
}
|
|
230
|
+
initializeEventStoreMetadata() {
|
|
231
|
+
this.eventStoreDb?.db?.exec(
|
|
232
|
+
`CREATE TABLE IF NOT EXISTS routstr_event_cache_metadata (
|
|
233
|
+
event_id TEXT PRIMARY KEY,
|
|
234
|
+
fetched_at INTEGER NOT NULL
|
|
235
|
+
)`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
markEventFetched(event, fetchedAt = Date.now()) {
|
|
239
|
+
const db = this.eventStoreDb?.db;
|
|
240
|
+
if (!db) return;
|
|
241
|
+
db.prepare(
|
|
242
|
+
`INSERT INTO routstr_event_cache_metadata (event_id, fetched_at)
|
|
243
|
+
VALUES (?, ?)
|
|
244
|
+
ON CONFLICT(event_id) DO UPDATE SET fetched_at = excluded.fetched_at`
|
|
245
|
+
).run?.(event.id, fetchedAt);
|
|
246
|
+
}
|
|
247
|
+
getEventFetchedAt(event) {
|
|
248
|
+
const db = this.eventStoreDb?.db;
|
|
249
|
+
if (!db) return void 0;
|
|
250
|
+
const row = db.prepare(
|
|
251
|
+
`SELECT fetched_at FROM routstr_event_cache_metadata WHERE event_id = ?`
|
|
252
|
+
).get?.(event.id);
|
|
253
|
+
return typeof row?.fetched_at === "number" ? row.fetched_at : void 0;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Check the persistent event store for fresh cached events.
|
|
257
|
+
* Returns events from SQLite if they were fetched within `maxAge`, otherwise
|
|
258
|
+
* returns empty array (caller should hit relays). Events without local fetch
|
|
259
|
+
* metadata fall back to Nostr created_at for backwards compatibility.
|
|
260
|
+
*/
|
|
261
|
+
async getCachedNostrEvents(filter, maxAge, forceRefresh = false) {
|
|
262
|
+
const eventStore = await this.ensureEventStore();
|
|
263
|
+
if (forceRefresh) return [];
|
|
264
|
+
if (!eventStore) return [];
|
|
265
|
+
const timeline = eventStore.getTimeline(filter);
|
|
266
|
+
if (timeline.length === 0) return [];
|
|
267
|
+
const cutoff = Date.now() - maxAge;
|
|
268
|
+
const freshest = Math.max(
|
|
269
|
+
...timeline.map((e) => this.getEventFetchedAt(e) ?? e.created_at * 1e3)
|
|
270
|
+
);
|
|
271
|
+
if (freshest < cutoff) return [];
|
|
272
|
+
return timeline;
|
|
273
|
+
}
|
|
167
274
|
static async init(adapter, config = {}, options = {}) {
|
|
168
275
|
const manager = new _ModelManager(adapter, config);
|
|
169
276
|
const torMode = options.torMode ?? false;
|
|
@@ -192,19 +299,31 @@ var ModelManager = class _ModelManager {
|
|
|
192
299
|
torMode
|
|
193
300
|
);
|
|
194
301
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
195
|
-
await this.syncReviewedProvidersFromNostr(
|
|
302
|
+
await this.syncReviewedProvidersFromNostr(
|
|
303
|
+
filteredCachedUrls,
|
|
304
|
+
this.providerNodePubkeysByUrl,
|
|
305
|
+
forceRefresh
|
|
306
|
+
);
|
|
196
307
|
return filteredCachedUrls;
|
|
197
308
|
}
|
|
198
309
|
}
|
|
199
310
|
}
|
|
200
311
|
try {
|
|
201
|
-
const nostrProviders = await this.bootstrapFromNostr(
|
|
312
|
+
const nostrProviders = await this.bootstrapFromNostr(
|
|
313
|
+
38421,
|
|
314
|
+
torMode,
|
|
315
|
+
forceRefresh
|
|
316
|
+
);
|
|
202
317
|
if (nostrProviders.length > 0) {
|
|
203
318
|
const filtered = this.filterBaseUrlsForTor(nostrProviders, torMode);
|
|
204
319
|
this.adapter.setBaseUrlsList(filtered);
|
|
205
320
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
206
321
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
207
|
-
await this.syncReviewedProvidersFromNostr(
|
|
322
|
+
await this.syncReviewedProvidersFromNostr(
|
|
323
|
+
filtered,
|
|
324
|
+
this.providerNodePubkeysByUrl,
|
|
325
|
+
forceRefresh
|
|
326
|
+
);
|
|
208
327
|
return filtered;
|
|
209
328
|
}
|
|
210
329
|
} catch (e) {
|
|
@@ -213,42 +332,52 @@ var ModelManager = class _ModelManager {
|
|
|
213
332
|
return this.bootstrapFromHttp(torMode, forceRefresh);
|
|
214
333
|
}
|
|
215
334
|
/**
|
|
216
|
-
* Bootstrap providers from Nostr network (kind
|
|
335
|
+
* Bootstrap providers from Nostr network (kind 38421)
|
|
217
336
|
* @param kind The Nostr kind to fetch
|
|
218
337
|
* @param torMode Whether running in Tor context
|
|
219
338
|
* @returns Array of provider base URLs
|
|
220
339
|
*/
|
|
221
|
-
async bootstrapFromNostr(kind, torMode) {
|
|
340
|
+
async bootstrapFromNostr(kind, torMode, forceRefresh = false) {
|
|
222
341
|
const DEFAULT_RELAYS = [
|
|
223
342
|
"wss://relay.primal.net",
|
|
224
343
|
"wss://nos.lol",
|
|
225
344
|
"wss://relay.damus.io"
|
|
226
345
|
];
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
346
|
+
const cached = await this.getCachedNostrEvents(
|
|
347
|
+
{ kinds: [kind] },
|
|
348
|
+
this.cacheTTL,
|
|
349
|
+
forceRefresh
|
|
350
|
+
);
|
|
351
|
+
let sessionEvents = cached;
|
|
352
|
+
if (cached.length === 0) {
|
|
353
|
+
const pool = new applesauceRelay.RelayPool();
|
|
354
|
+
const timeoutMs = 5e3;
|
|
355
|
+
await new Promise((resolve) => {
|
|
356
|
+
pool.req(DEFAULT_RELAYS, {
|
|
357
|
+
kinds: [kind],
|
|
358
|
+
limit: 100
|
|
359
|
+
}).pipe(
|
|
360
|
+
applesauceRelay.onlyEvents(),
|
|
361
|
+
rxjs.tap((event) => {
|
|
362
|
+
sessionEvents.push(event);
|
|
363
|
+
this.eventStore?.add(event);
|
|
364
|
+
this.markEventFetched(event);
|
|
365
|
+
})
|
|
366
|
+
).subscribe({
|
|
367
|
+
complete: () => {
|
|
368
|
+
resolve();
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
setTimeout(() => {
|
|
241
372
|
resolve();
|
|
242
|
-
}
|
|
373
|
+
}, timeoutMs);
|
|
243
374
|
});
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
});
|
|
248
|
-
const timeline = localEventStore.getTimeline({ kinds: [kind] });
|
|
375
|
+
} else {
|
|
376
|
+
this.logger.log(`Using ${cached.length} cached kind ${kind} events from persistent store`);
|
|
377
|
+
}
|
|
249
378
|
const bases = /* @__PURE__ */ new Set();
|
|
250
379
|
this.providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
|
|
251
|
-
for (const event of
|
|
380
|
+
for (const event of sessionEvents) {
|
|
252
381
|
const eventUrls = [];
|
|
253
382
|
for (const tag of event.tags) {
|
|
254
383
|
if (tag[0] === "u" && typeof tag[1] === "string") {
|
|
@@ -356,7 +485,11 @@ var ModelManager = class _ModelManager {
|
|
|
356
485
|
this.adapter.setBaseUrlsList(list);
|
|
357
486
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
358
487
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
359
|
-
await this.syncReviewedProvidersFromNostr(
|
|
488
|
+
await this.syncReviewedProvidersFromNostr(
|
|
489
|
+
list,
|
|
490
|
+
this.providerNodePubkeysByUrl,
|
|
491
|
+
forceRefresh
|
|
492
|
+
);
|
|
360
493
|
}
|
|
361
494
|
return list;
|
|
362
495
|
} catch (e) {
|
|
@@ -375,7 +508,7 @@ var ModelManager = class _ModelManager {
|
|
|
375
508
|
* @param baseUrls Current provider base URLs to evaluate
|
|
376
509
|
* @returns Array of provider base URLs disabled by the review set
|
|
377
510
|
*/
|
|
378
|
-
async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl) {
|
|
511
|
+
async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl, forceRefresh = false) {
|
|
379
512
|
if (baseUrls.length === 0) return [];
|
|
380
513
|
if (!this.adapter.setDisabledProviders) {
|
|
381
514
|
this.logger.warn(
|
|
@@ -383,30 +516,43 @@ var ModelManager = class _ModelManager {
|
|
|
383
516
|
);
|
|
384
517
|
return [];
|
|
385
518
|
}
|
|
386
|
-
const LGTM_RELAYS = [
|
|
387
|
-
"wss://relay.primal.net",
|
|
388
|
-
"wss://nos.lol",
|
|
389
|
-
"wss://relay.damus.io",
|
|
390
|
-
"wss://relay.routstr.com"
|
|
391
|
-
];
|
|
392
519
|
const reviewedNodePubkeys = /* @__PURE__ */ new Set();
|
|
393
520
|
{
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
521
|
+
const cached = await this.getCachedNostrEvents(
|
|
522
|
+
{ kinds: [38425], "#t": ["lgtm"], authors: [this.routstrPubkey] },
|
|
523
|
+
this.cacheTTL,
|
|
524
|
+
forceRefresh
|
|
525
|
+
);
|
|
526
|
+
let sessionEvents = cached;
|
|
527
|
+
if (cached.length === 0) {
|
|
528
|
+
const LGTM_RELAYS = [
|
|
529
|
+
"wss://relay.primal.net",
|
|
530
|
+
"wss://nos.lol",
|
|
531
|
+
"wss://relay.damus.io",
|
|
532
|
+
"wss://relay.routstr.com"
|
|
533
|
+
];
|
|
534
|
+
const pool = new applesauceRelay.RelayPool();
|
|
535
|
+
const timeoutMs = 5e3;
|
|
536
|
+
await new Promise((resolve) => {
|
|
537
|
+
pool.req(LGTM_RELAYS, {
|
|
538
|
+
kinds: [38425],
|
|
539
|
+
"#t": ["lgtm"],
|
|
540
|
+
limit: 500,
|
|
541
|
+
authors: [this.routstrPubkey]
|
|
542
|
+
}).pipe(
|
|
543
|
+
applesauceRelay.onlyEvents(),
|
|
544
|
+
rxjs.tap((event) => {
|
|
545
|
+
sessionEvents.push(event);
|
|
546
|
+
this.eventStore?.add(event);
|
|
547
|
+
this.markEventFetched(event);
|
|
548
|
+
})
|
|
549
|
+
).subscribe({ complete: () => resolve() });
|
|
550
|
+
setTimeout(() => resolve(), timeoutMs);
|
|
551
|
+
});
|
|
552
|
+
} else {
|
|
553
|
+
this.logger.log(`Using ${cached.length} cached kind 38425 events from persistent store`);
|
|
554
|
+
}
|
|
555
|
+
for (const event of sessionEvents) {
|
|
410
556
|
const hasLgtmTag = event.tags.some(
|
|
411
557
|
(tag) => tag[0] === "t" && tag[1]?.toLowerCase() === "lgtm"
|
|
412
558
|
);
|
|
@@ -515,7 +661,7 @@ var ModelManager = class _ModelManager {
|
|
|
515
661
|
if (this.isProviderDownError(error)) {
|
|
516
662
|
this.logger.warn(`Provider ${base} is down right now.`);
|
|
517
663
|
} else {
|
|
518
|
-
this.logger.warn(`
|
|
664
|
+
this.logger.warn(`Provider ${base} unreachable: ${error.message}`);
|
|
519
665
|
}
|
|
520
666
|
this.adapter.setProviderLastUpdate(base, Date.now());
|
|
521
667
|
return { success: false, base };
|
|
@@ -635,34 +781,44 @@ var ModelManager = class _ModelManager {
|
|
|
635
781
|
"wss://nos.lol",
|
|
636
782
|
"wss://relay.routstr.com"
|
|
637
783
|
];
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
784
|
+
const cached = await this.getCachedNostrEvents(
|
|
785
|
+
{ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrPubkey] },
|
|
786
|
+
this.cacheTTL,
|
|
787
|
+
forceRefresh
|
|
788
|
+
);
|
|
789
|
+
let sessionEvents = cached;
|
|
790
|
+
if (cached.length === 0) {
|
|
791
|
+
const pool = new applesauceRelay.RelayPool();
|
|
792
|
+
const timeoutMs = 5e3;
|
|
793
|
+
await new Promise((resolve) => {
|
|
794
|
+
pool.req(DEFAULT_RELAYS, {
|
|
795
|
+
kinds: [38423],
|
|
796
|
+
"#d": ["routstr-21-models"],
|
|
797
|
+
limit: 1,
|
|
798
|
+
authors: [this.routstrPubkey]
|
|
799
|
+
}).pipe(
|
|
800
|
+
applesauceRelay.onlyEvents(),
|
|
801
|
+
rxjs.tap((event2) => {
|
|
802
|
+
sessionEvents.push(event2);
|
|
803
|
+
this.eventStore?.add(event2);
|
|
804
|
+
this.markEventFetched(event2);
|
|
805
|
+
})
|
|
806
|
+
).subscribe({
|
|
807
|
+
complete: () => {
|
|
808
|
+
resolve();
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
setTimeout(() => {
|
|
654
812
|
resolve();
|
|
655
|
-
}
|
|
813
|
+
}, timeoutMs);
|
|
656
814
|
});
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
const timeline = localEventStore.getTimeline({ kinds: [38423] });
|
|
662
|
-
if (timeline.length === 0) {
|
|
815
|
+
} else {
|
|
816
|
+
this.logger.log(`Using ${cached.length} cached kind 38423 events from persistent store`);
|
|
817
|
+
}
|
|
818
|
+
if (sessionEvents.length === 0) {
|
|
663
819
|
return cachedModels.length > 0 ? cachedModels : [];
|
|
664
820
|
}
|
|
665
|
-
const event =
|
|
821
|
+
const event = sessionEvents[0];
|
|
666
822
|
try {
|
|
667
823
|
const content = JSON.parse(event.content);
|
|
668
824
|
const models = Array.isArray(content?.models) ? content.models : [];
|
|
@@ -1380,7 +1536,7 @@ var CashuSpender = class {
|
|
|
1380
1536
|
});
|
|
1381
1537
|
continue;
|
|
1382
1538
|
}
|
|
1383
|
-
if (balanceResult.amount >= 0) {
|
|
1539
|
+
if (balanceResult.amount >= 0 && !balanceResult.balanceUnknown) {
|
|
1384
1540
|
const balanceSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.amount / 1e3) : balanceResult.amount;
|
|
1385
1541
|
this.storageAdapter.updateApiKeyBalance(
|
|
1386
1542
|
apiKeyEntry.baseUrl,
|
|
@@ -2129,17 +2285,24 @@ var BalanceManager = class _BalanceManager {
|
|
|
2129
2285
|
this.logger.warn("getTokenBalance: FAILED", data);
|
|
2130
2286
|
const isInvalidApiKey = response.status === 401 && data?.detail?.error?.code === "invalid_api_key" && data?.detail?.error?.message?.includes("proofs already spent");
|
|
2131
2287
|
return {
|
|
2132
|
-
amount:
|
|
2288
|
+
amount: 0,
|
|
2133
2289
|
reserved: data.reserved ?? 0,
|
|
2134
2290
|
unit: "msat",
|
|
2135
2291
|
apiKey: data.api_key,
|
|
2136
|
-
isInvalidApiKey
|
|
2292
|
+
isInvalidApiKey,
|
|
2293
|
+
balanceUnknown: true
|
|
2137
2294
|
};
|
|
2138
2295
|
}
|
|
2139
2296
|
} catch (error) {
|
|
2140
2297
|
this.logger.error("getTokenBalance error", error);
|
|
2141
2298
|
}
|
|
2142
|
-
return {
|
|
2299
|
+
return {
|
|
2300
|
+
amount: 0,
|
|
2301
|
+
reserved: 0,
|
|
2302
|
+
unit: "sat",
|
|
2303
|
+
apiKey: "",
|
|
2304
|
+
balanceUnknown: true
|
|
2305
|
+
};
|
|
2143
2306
|
}
|
|
2144
2307
|
/**
|
|
2145
2308
|
* Handle topup errors with specific error types
|
|
@@ -4686,6 +4849,277 @@ var createProviderRegistryFromStore = (store, logger) => {
|
|
|
4686
4849
|
};
|
|
4687
4850
|
};
|
|
4688
4851
|
|
|
4852
|
+
// storage/shardedDiscoveryAdapter.ts
|
|
4853
|
+
var MODEL_KEY_PREFIX = "models:provider:";
|
|
4854
|
+
var MODEL_TS_KEY_PREFIX = "models:provider_timestamp:";
|
|
4855
|
+
var PROVIDER_INDEX_KEY = "models:provider_index";
|
|
4856
|
+
var MIGRATION_MARKER_KEY4 = "models_sharded_migration_v1";
|
|
4857
|
+
var encodeBaseUrl = (baseUrl) => encodeURIComponent(baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
4858
|
+
var modelKey = (baseUrl) => `${MODEL_KEY_PREFIX}${encodeBaseUrl(baseUrl)}`;
|
|
4859
|
+
var modelTsKey = (baseUrl) => `${MODEL_TS_KEY_PREFIX}${encodeBaseUrl(baseUrl)}`;
|
|
4860
|
+
var normalizeBaseUrl6 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
4861
|
+
var createShardedDiscoveryAdapter = async (options) => {
|
|
4862
|
+
const { driver } = options;
|
|
4863
|
+
const legacyModels = await driver.getItem(SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS, {});
|
|
4864
|
+
const legacyTimestamps = await driver.getItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE, {});
|
|
4865
|
+
if (Object.keys(legacyModels).length > 0) {
|
|
4866
|
+
const migratedProviders = [];
|
|
4867
|
+
for (const [baseUrl, models] of Object.entries(legacyModels)) {
|
|
4868
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
4869
|
+
await driver.setItem(modelKey(normalized), models);
|
|
4870
|
+
const ts = legacyTimestamps[normalized] ?? Date.now();
|
|
4871
|
+
await driver.setItem(modelTsKey(normalized), ts);
|
|
4872
|
+
migratedProviders.push(normalized);
|
|
4873
|
+
}
|
|
4874
|
+
const existingIndex = await driver.getItem(
|
|
4875
|
+
PROVIDER_INDEX_KEY,
|
|
4876
|
+
[]
|
|
4877
|
+
);
|
|
4878
|
+
const merged = [.../* @__PURE__ */ new Set([...existingIndex, ...migratedProviders])];
|
|
4879
|
+
await driver.setItem(PROVIDER_INDEX_KEY, merged);
|
|
4880
|
+
await driver.removeItem(SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS);
|
|
4881
|
+
await driver.removeItem(SDK_STORAGE_KEYS.LAST_MODELS_UPDATE);
|
|
4882
|
+
}
|
|
4883
|
+
await driver.setItem(MIGRATION_MARKER_KEY4, true);
|
|
4884
|
+
const [
|
|
4885
|
+
rawMints,
|
|
4886
|
+
rawInfo,
|
|
4887
|
+
lastUsedModel,
|
|
4888
|
+
rawDisabled,
|
|
4889
|
+
rawBaseUrls,
|
|
4890
|
+
lastBaseUrlsUpdate,
|
|
4891
|
+
rawRoutstr21Models,
|
|
4892
|
+
lastRoutstr21ModelsUpdate
|
|
4893
|
+
] = await Promise.all([
|
|
4894
|
+
driver.getItem(
|
|
4895
|
+
SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS,
|
|
4896
|
+
{}
|
|
4897
|
+
),
|
|
4898
|
+
driver.getItem(
|
|
4899
|
+
SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS,
|
|
4900
|
+
{}
|
|
4901
|
+
),
|
|
4902
|
+
driver.getItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, null),
|
|
4903
|
+
driver.getItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, []),
|
|
4904
|
+
driver.getItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, []),
|
|
4905
|
+
driver.getItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, null),
|
|
4906
|
+
driver.getItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, []),
|
|
4907
|
+
driver.getItem(
|
|
4908
|
+
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
4909
|
+
null
|
|
4910
|
+
)
|
|
4911
|
+
]);
|
|
4912
|
+
const modelsByBaseUrl = /* @__PURE__ */ new Map();
|
|
4913
|
+
const timestampsByBaseUrl = /* @__PURE__ */ new Map();
|
|
4914
|
+
const providerIndex = /* @__PURE__ */ new Set();
|
|
4915
|
+
const knownProviders = /* @__PURE__ */ new Set();
|
|
4916
|
+
for (const baseUrl of Object.keys(rawInfo)) {
|
|
4917
|
+
knownProviders.add(normalizeBaseUrl6(baseUrl));
|
|
4918
|
+
}
|
|
4919
|
+
for (const baseUrl of Object.keys(rawMints)) {
|
|
4920
|
+
knownProviders.add(normalizeBaseUrl6(baseUrl));
|
|
4921
|
+
}
|
|
4922
|
+
for (const baseUrl of rawBaseUrls) {
|
|
4923
|
+
knownProviders.add(normalizeBaseUrl6(baseUrl));
|
|
4924
|
+
}
|
|
4925
|
+
for (const baseUrl of rawDisabled) {
|
|
4926
|
+
knownProviders.add(normalizeBaseUrl6(baseUrl));
|
|
4927
|
+
}
|
|
4928
|
+
for (const baseUrl of Object.keys(legacyModels)) {
|
|
4929
|
+
knownProviders.add(normalizeBaseUrl6(baseUrl));
|
|
4930
|
+
}
|
|
4931
|
+
const indexProviders = await driver.getItem(
|
|
4932
|
+
PROVIDER_INDEX_KEY,
|
|
4933
|
+
[]
|
|
4934
|
+
);
|
|
4935
|
+
for (const baseUrl of indexProviders) {
|
|
4936
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
4937
|
+
providerIndex.add(normalized);
|
|
4938
|
+
knownProviders.add(normalized);
|
|
4939
|
+
}
|
|
4940
|
+
for (const baseUrl of knownProviders) {
|
|
4941
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
4942
|
+
const models = await driver.getItem(
|
|
4943
|
+
modelKey(normalized),
|
|
4944
|
+
null
|
|
4945
|
+
);
|
|
4946
|
+
const ts = await driver.getItem(
|
|
4947
|
+
modelTsKey(normalized),
|
|
4948
|
+
null
|
|
4949
|
+
);
|
|
4950
|
+
if (models !== null) {
|
|
4951
|
+
modelsByBaseUrl.set(normalized, models);
|
|
4952
|
+
}
|
|
4953
|
+
if (ts !== null) {
|
|
4954
|
+
timestampsByBaseUrl.set(normalized, ts);
|
|
4955
|
+
}
|
|
4956
|
+
if (models !== null || ts !== null) {
|
|
4957
|
+
providerIndex.add(normalized);
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
let mints = Object.fromEntries(
|
|
4961
|
+
Object.entries(rawMints).map(([baseUrl, mintList]) => [
|
|
4962
|
+
normalizeBaseUrl6(baseUrl),
|
|
4963
|
+
mintList.map((mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint)
|
|
4964
|
+
])
|
|
4965
|
+
);
|
|
4966
|
+
let info = Object.fromEntries(
|
|
4967
|
+
Object.entries(rawInfo).map(([baseUrl, entry]) => [
|
|
4968
|
+
normalizeBaseUrl6(baseUrl),
|
|
4969
|
+
entry
|
|
4970
|
+
])
|
|
4971
|
+
);
|
|
4972
|
+
let _lastUsedModel = lastUsedModel;
|
|
4973
|
+
let _disabledProviders = rawDisabled.map(normalizeBaseUrl6);
|
|
4974
|
+
let _baseUrlsList = rawBaseUrls.map(normalizeBaseUrl6);
|
|
4975
|
+
let _lastBaseUrlsUpdate = lastBaseUrlsUpdate;
|
|
4976
|
+
let _routstr21Models = rawRoutstr21Models;
|
|
4977
|
+
let _lastRoutstr21ModelsUpdate = lastRoutstr21ModelsUpdate;
|
|
4978
|
+
const persistProviderIndex = () => {
|
|
4979
|
+
void driver.setItem(PROVIDER_INDEX_KEY, [...providerIndex]);
|
|
4980
|
+
};
|
|
4981
|
+
return {
|
|
4982
|
+
// -- Models (sharded kv) --
|
|
4983
|
+
getCachedModels: () => {
|
|
4984
|
+
const result = {};
|
|
4985
|
+
for (const [baseUrl, models] of modelsByBaseUrl.entries()) {
|
|
4986
|
+
result[baseUrl] = models;
|
|
4987
|
+
}
|
|
4988
|
+
return result;
|
|
4989
|
+
},
|
|
4990
|
+
setCachedModels: (models) => {
|
|
4991
|
+
const nextKeys = new Set(
|
|
4992
|
+
Object.keys(models).map((baseUrl) => normalizeBaseUrl6(baseUrl))
|
|
4993
|
+
);
|
|
4994
|
+
for (const baseUrl of [...modelsByBaseUrl.keys()]) {
|
|
4995
|
+
if (!nextKeys.has(normalizeBaseUrl6(baseUrl))) {
|
|
4996
|
+
providerIndex.delete(baseUrl);
|
|
4997
|
+
modelsByBaseUrl.delete(baseUrl);
|
|
4998
|
+
timestampsByBaseUrl.delete(baseUrl);
|
|
4999
|
+
void driver.removeItem(modelKey(baseUrl));
|
|
5000
|
+
void driver.removeItem(modelTsKey(baseUrl));
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
for (const [baseUrl, modelList] of Object.entries(models)) {
|
|
5004
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
5005
|
+
providerIndex.add(normalized);
|
|
5006
|
+
modelsByBaseUrl.set(normalized, modelList);
|
|
5007
|
+
const ts = timestampsByBaseUrl.get(normalized) ?? Date.now();
|
|
5008
|
+
timestampsByBaseUrl.set(normalized, ts);
|
|
5009
|
+
void driver.setItem(modelKey(normalized), modelList);
|
|
5010
|
+
void driver.setItem(modelTsKey(normalized), ts);
|
|
5011
|
+
}
|
|
5012
|
+
persistProviderIndex();
|
|
5013
|
+
},
|
|
5014
|
+
getProviderLastUpdate: (baseUrl) => {
|
|
5015
|
+
return timestampsByBaseUrl.get(normalizeBaseUrl6(baseUrl)) ?? null;
|
|
5016
|
+
},
|
|
5017
|
+
setProviderLastUpdate: (baseUrl, timestamp) => {
|
|
5018
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
5019
|
+
providerIndex.add(normalized);
|
|
5020
|
+
timestampsByBaseUrl.set(normalized, timestamp);
|
|
5021
|
+
void driver.setItem(modelTsKey(normalized), timestamp);
|
|
5022
|
+
persistProviderIndex();
|
|
5023
|
+
},
|
|
5024
|
+
// -- Mints (kv) --
|
|
5025
|
+
getCachedMints: () => mints,
|
|
5026
|
+
setCachedMints: (value) => {
|
|
5027
|
+
const normalized = {};
|
|
5028
|
+
for (const [baseUrl, mintList] of Object.entries(value)) {
|
|
5029
|
+
normalized[normalizeBaseUrl6(baseUrl)] = mintList.map(
|
|
5030
|
+
(mint) => mint.endsWith("/") ? mint.slice(0, -1) : mint
|
|
5031
|
+
);
|
|
5032
|
+
}
|
|
5033
|
+
mints = normalized;
|
|
5034
|
+
void driver.setItem(SDK_STORAGE_KEYS.MINTS_FROM_ALL_PROVIDERS, normalized);
|
|
5035
|
+
},
|
|
5036
|
+
// -- Provider info (kv) --
|
|
5037
|
+
getCachedProviderInfo: () => info,
|
|
5038
|
+
setCachedProviderInfo: (value) => {
|
|
5039
|
+
const normalized = {};
|
|
5040
|
+
for (const [baseUrl, entry] of Object.entries(value)) {
|
|
5041
|
+
normalized[normalizeBaseUrl6(baseUrl)] = entry;
|
|
5042
|
+
}
|
|
5043
|
+
info = normalized;
|
|
5044
|
+
void driver.setItem(SDK_STORAGE_KEYS.INFO_FROM_ALL_PROVIDERS, normalized);
|
|
5045
|
+
},
|
|
5046
|
+
// -- Last used model (kv) --
|
|
5047
|
+
getLastUsedModel: () => _lastUsedModel,
|
|
5048
|
+
setLastUsedModel: (modelId) => {
|
|
5049
|
+
_lastUsedModel = modelId;
|
|
5050
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, modelId);
|
|
5051
|
+
},
|
|
5052
|
+
// -- Disabled providers (kv) --
|
|
5053
|
+
getDisabledProviders: () => _disabledProviders,
|
|
5054
|
+
setDisabledProviders: (urls) => {
|
|
5055
|
+
const normalized = urls.map(normalizeBaseUrl6);
|
|
5056
|
+
_disabledProviders = normalized;
|
|
5057
|
+
void driver.setItem(SDK_STORAGE_KEYS.DISABLED_PROVIDERS, normalized);
|
|
5058
|
+
},
|
|
5059
|
+
// -- Base URLs (kv) --
|
|
5060
|
+
getBaseUrlsList: () => _baseUrlsList,
|
|
5061
|
+
getBaseUrlsLastUpdate: () => _lastBaseUrlsUpdate,
|
|
5062
|
+
setBaseUrlsList: (urls) => {
|
|
5063
|
+
const normalized = urls.map(normalizeBaseUrl6);
|
|
5064
|
+
_baseUrlsList = normalized;
|
|
5065
|
+
void driver.setItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, normalized);
|
|
5066
|
+
},
|
|
5067
|
+
setBaseUrlsLastUpdate: (timestamp) => {
|
|
5068
|
+
_lastBaseUrlsUpdate = timestamp;
|
|
5069
|
+
void driver.setItem(SDK_STORAGE_KEYS.LAST_BASE_URLS_UPDATE, timestamp);
|
|
5070
|
+
},
|
|
5071
|
+
// -- Routstr21 models (kv) --
|
|
5072
|
+
getRoutstr21Models: () => _routstr21Models,
|
|
5073
|
+
setRoutstr21Models: (models) => {
|
|
5074
|
+
_routstr21Models = models;
|
|
5075
|
+
void driver.setItem(SDK_STORAGE_KEYS.ROUTSTR21_MODELS, models);
|
|
5076
|
+
},
|
|
5077
|
+
getRoutstr21ModelsLastUpdate: () => _lastRoutstr21ModelsUpdate,
|
|
5078
|
+
setRoutstr21ModelsLastUpdate: (timestamp) => {
|
|
5079
|
+
_lastRoutstr21ModelsUpdate = timestamp;
|
|
5080
|
+
void driver.setItem(
|
|
5081
|
+
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
5082
|
+
timestamp
|
|
5083
|
+
);
|
|
5084
|
+
}
|
|
5085
|
+
};
|
|
5086
|
+
};
|
|
5087
|
+
var createProviderRegistryFromDiscoveryAdapter = (adapter, logger) => {
|
|
5088
|
+
const log = (logger ?? consoleLogger).child("ProviderRegistry");
|
|
5089
|
+
return {
|
|
5090
|
+
getModelsForProvider: (baseUrl) => {
|
|
5091
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
5092
|
+
return adapter.getCachedModels()[normalized] || [];
|
|
5093
|
+
},
|
|
5094
|
+
getDisabledProviders: () => adapter.getDisabledProviders(),
|
|
5095
|
+
getProviderMints: (baseUrl) => {
|
|
5096
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
5097
|
+
return adapter.getCachedMints()[normalized] || [];
|
|
5098
|
+
},
|
|
5099
|
+
getProviderInfo: async (baseUrl) => {
|
|
5100
|
+
const normalized = normalizeBaseUrl6(baseUrl);
|
|
5101
|
+
const cached = adapter.getCachedProviderInfo()[normalized];
|
|
5102
|
+
if (cached) return cached;
|
|
5103
|
+
try {
|
|
5104
|
+
const response = await fetch(`${normalized}v1/info`);
|
|
5105
|
+
if (!response.ok) {
|
|
5106
|
+
throw new Error(`Failed ${response.status}`);
|
|
5107
|
+
}
|
|
5108
|
+
const info = await response.json();
|
|
5109
|
+
adapter.setCachedProviderInfo({
|
|
5110
|
+
...adapter.getCachedProviderInfo(),
|
|
5111
|
+
[normalized]: info
|
|
5112
|
+
});
|
|
5113
|
+
return info;
|
|
5114
|
+
} catch (error) {
|
|
5115
|
+
log.warn(`Failed to fetch provider info from ${normalized}:`, error);
|
|
5116
|
+
return null;
|
|
5117
|
+
}
|
|
5118
|
+
},
|
|
5119
|
+
getAllProvidersModels: () => adapter.getCachedModels()
|
|
5120
|
+
};
|
|
5121
|
+
};
|
|
5122
|
+
|
|
4689
5123
|
// storage/index.ts
|
|
4690
5124
|
var isBrowser3 = () => {
|
|
4691
5125
|
try {
|
|
@@ -4755,9 +5189,15 @@ var getDefaultUsageTrackingDriver = () => {
|
|
|
4755
5189
|
var setDefaultUsageTrackingDriver = (driver) => {
|
|
4756
5190
|
defaultUsageTrackingDriver = driver;
|
|
4757
5191
|
};
|
|
4758
|
-
var
|
|
5192
|
+
var defaultDiscoveryAdapter = null;
|
|
5193
|
+
var getDefaultDiscoveryAdapter = async () => {
|
|
5194
|
+
if (defaultDiscoveryAdapter) return defaultDiscoveryAdapter;
|
|
5195
|
+
const driver = getDefaultSdkDriver();
|
|
5196
|
+
defaultDiscoveryAdapter = await createShardedDiscoveryAdapter({ driver });
|
|
5197
|
+
return defaultDiscoveryAdapter;
|
|
5198
|
+
};
|
|
4759
5199
|
var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
|
|
4760
|
-
var getDefaultProviderRegistry = async () =>
|
|
5200
|
+
var getDefaultProviderRegistry = async () => createProviderRegistryFromDiscoveryAdapter(await getDefaultDiscoveryAdapter());
|
|
4761
5201
|
function mergeUsage(previous, next) {
|
|
4762
5202
|
if (!previous) return next;
|
|
4763
5203
|
return {
|
|
@@ -5067,6 +5507,8 @@ var RoutstrClient = class {
|
|
|
5067
5507
|
baseUrl: prepared.baseUrlUsed,
|
|
5068
5508
|
mintUrl: params.mintUrl,
|
|
5069
5509
|
initialTokenBalance: prepared.tokenBalanceInSats,
|
|
5510
|
+
initialTokenBalanceUnknown: prepared.tokenBalanceUnknown,
|
|
5511
|
+
fallbackSatsSpent: usage?.satsCost,
|
|
5070
5512
|
response: prepared.response,
|
|
5071
5513
|
modelId: prepared.modelId,
|
|
5072
5514
|
usage,
|
|
@@ -5131,7 +5573,7 @@ var RoutstrClient = class {
|
|
|
5131
5573
|
);
|
|
5132
5574
|
}
|
|
5133
5575
|
}
|
|
5134
|
-
const { token, tokenBalance, tokenBalanceUnit } = await this._spendToken({
|
|
5576
|
+
const { token, tokenBalance, tokenBalanceUnit, tokenBalanceUnknown } = await this._spendToken({
|
|
5135
5577
|
mintUrl,
|
|
5136
5578
|
amount: requiredSats,
|
|
5137
5579
|
baseUrl
|
|
@@ -5157,9 +5599,20 @@ var RoutstrClient = class {
|
|
|
5157
5599
|
baseHeaders,
|
|
5158
5600
|
selectedModel
|
|
5159
5601
|
});
|
|
5160
|
-
|
|
5602
|
+
let tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
|
|
5603
|
+
let initialTokenBalanceUnknown = tokenBalanceUnknown;
|
|
5161
5604
|
const baseUrlUsed = response.baseUrl || baseUrl;
|
|
5162
5605
|
const tokenUsed = response.token || token;
|
|
5606
|
+
if (baseUrlUsed !== baseUrl || tokenUsed !== token) {
|
|
5607
|
+
if (typeof response.initialTokenBalanceInSats === "number") {
|
|
5608
|
+
tokenBalanceInSats = response.initialTokenBalanceInSats;
|
|
5609
|
+
initialTokenBalanceUnknown = Boolean(
|
|
5610
|
+
response.initialTokenBalanceUnknown
|
|
5611
|
+
);
|
|
5612
|
+
} else {
|
|
5613
|
+
initialTokenBalanceUnknown = true;
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5163
5616
|
const contentType = response.headers.get("content-type") || "";
|
|
5164
5617
|
let processedResponse = response;
|
|
5165
5618
|
let capturedUsage;
|
|
@@ -5192,6 +5645,7 @@ var RoutstrClient = class {
|
|
|
5192
5645
|
tokenUsed,
|
|
5193
5646
|
baseUrlUsed,
|
|
5194
5647
|
tokenBalanceInSats,
|
|
5648
|
+
tokenBalanceUnknown: initialTokenBalanceUnknown,
|
|
5195
5649
|
modelId,
|
|
5196
5650
|
capturedUsage,
|
|
5197
5651
|
capturedResponseId,
|
|
@@ -5241,7 +5695,8 @@ var RoutstrClient = class {
|
|
|
5241
5695
|
let token = spendResult.token;
|
|
5242
5696
|
let tokenBalance = spendResult.tokenBalance;
|
|
5243
5697
|
let tokenBalanceUnit = spendResult.tokenBalanceUnit;
|
|
5244
|
-
|
|
5698
|
+
let tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
|
|
5699
|
+
let initialTokenBalanceUnknown = spendResult.tokenBalanceUnknown;
|
|
5245
5700
|
callbacks.onTokenCreated?.(this._getPendingCashuTokenAmount());
|
|
5246
5701
|
const baseHeaders = this._buildBaseHeaders(headers);
|
|
5247
5702
|
const requestHeaders = this._withAuthHeader(baseHeaders, token);
|
|
@@ -5284,6 +5739,18 @@ var RoutstrClient = class {
|
|
|
5284
5739
|
}
|
|
5285
5740
|
if (response.status === 200) {
|
|
5286
5741
|
const baseUrlUsed = response.baseUrl || baseUrl;
|
|
5742
|
+
const responseToken = response.token || token;
|
|
5743
|
+
if (baseUrlUsed !== baseUrl || responseToken !== token) {
|
|
5744
|
+
token = responseToken;
|
|
5745
|
+
if (typeof response.initialTokenBalanceInSats === "number") {
|
|
5746
|
+
tokenBalanceInSats = response.initialTokenBalanceInSats;
|
|
5747
|
+
initialTokenBalanceUnknown = Boolean(
|
|
5748
|
+
response.initialTokenBalanceUnknown
|
|
5749
|
+
);
|
|
5750
|
+
} else {
|
|
5751
|
+
initialTokenBalanceUnknown = true;
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5287
5754
|
const streamingResult = await this.streamProcessor.process(
|
|
5288
5755
|
response,
|
|
5289
5756
|
{
|
|
@@ -5314,6 +5781,7 @@ var RoutstrClient = class {
|
|
|
5314
5781
|
baseUrl: baseUrlUsed,
|
|
5315
5782
|
mintUrl,
|
|
5316
5783
|
initialTokenBalance: tokenBalanceInSats,
|
|
5784
|
+
initialTokenBalanceUnknown,
|
|
5317
5785
|
fallbackSatsSpent: isApikeysEstimate ? this._getEstimatedCosts(selectedModel, streamingResult) : void 0,
|
|
5318
5786
|
response,
|
|
5319
5787
|
modelId: selectedModel.id,
|
|
@@ -5470,14 +5938,24 @@ var RoutstrClient = class {
|
|
|
5470
5938
|
params.token,
|
|
5471
5939
|
baseUrl
|
|
5472
5940
|
);
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5941
|
+
if (currentBalanceInfo.balanceUnknown) {
|
|
5942
|
+
this._log(
|
|
5943
|
+
"DEBUG",
|
|
5944
|
+
`[RoutstrClient] _handleErrorResponse: Current balance unknown for ${baseUrl}; using default topup amount=${topupAmount}`
|
|
5945
|
+
);
|
|
5946
|
+
} else {
|
|
5947
|
+
const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
5948
|
+
const reservedBalance = currentBalanceInfo.unit === "msat" ? (currentBalanceInfo.reserved ?? 0) / 1e3 : currentBalanceInfo.reserved ?? 0;
|
|
5949
|
+
const shortfall = Math.max(
|
|
5950
|
+
0,
|
|
5951
|
+
params.requiredSats - currentBalance + reservedBalance
|
|
5952
|
+
);
|
|
5953
|
+
topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
|
|
5954
|
+
this._log(
|
|
5955
|
+
"DEBUG",
|
|
5956
|
+
`The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance}. Reserved Balance: ${reservedBalance}. Available Balance: ${currentBalance - reservedBalance}`
|
|
5957
|
+
);
|
|
5958
|
+
}
|
|
5481
5959
|
} catch (e) {
|
|
5482
5960
|
this._log(
|
|
5483
5961
|
"WARN",
|
|
@@ -5563,7 +6041,7 @@ var RoutstrClient = class {
|
|
|
5563
6041
|
this.storageAdapter.removeApiKey(baseUrl);
|
|
5564
6042
|
tryNextProvider = true;
|
|
5565
6043
|
} else {
|
|
5566
|
-
const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
6044
|
+
const latestTokenBalance = latestBalanceInfo.balanceUnknown ? void 0 : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
5567
6045
|
if (latestBalanceInfo.apiKey) {
|
|
5568
6046
|
const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
|
|
5569
6047
|
if (storedApiKeyEntry?.key !== latestBalanceInfo.apiKey) {
|
|
@@ -5574,7 +6052,7 @@ var RoutstrClient = class {
|
|
|
5574
6052
|
}
|
|
5575
6053
|
retryToken = latestBalanceInfo.apiKey;
|
|
5576
6054
|
}
|
|
5577
|
-
if (latestTokenBalance >= 0) {
|
|
6055
|
+
if (latestTokenBalance !== void 0 && latestTokenBalance >= 0) {
|
|
5578
6056
|
this.storageAdapter.updateApiKeyBalance(
|
|
5579
6057
|
baseUrl,
|
|
5580
6058
|
latestTokenBalance
|
|
@@ -5649,7 +6127,7 @@ var RoutstrClient = class {
|
|
|
5649
6127
|
"DEBUG",
|
|
5650
6128
|
`[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
|
|
5651
6129
|
);
|
|
5652
|
-
if (!refundResult.success && latestBalanceInfo.amount > 0) {
|
|
6130
|
+
if (!refundResult.success && latestBalanceInfo.amount > 0 && !latestBalanceInfo.balanceUnknown) {
|
|
5653
6131
|
throw new ProviderError(
|
|
5654
6132
|
baseUrl,
|
|
5655
6133
|
status,
|
|
@@ -5700,7 +6178,7 @@ var RoutstrClient = class {
|
|
|
5700
6178
|
amount: newRequiredSats,
|
|
5701
6179
|
baseUrl: nextProvider
|
|
5702
6180
|
});
|
|
5703
|
-
|
|
6181
|
+
const retryResponse = await this._makeRequest({
|
|
5704
6182
|
...params,
|
|
5705
6183
|
path,
|
|
5706
6184
|
method,
|
|
@@ -5712,6 +6190,9 @@ var RoutstrClient = class {
|
|
|
5712
6190
|
headers: this._withAuthHeader(params.baseHeaders, spendResult.token),
|
|
5713
6191
|
retryCount: 0
|
|
5714
6192
|
});
|
|
6193
|
+
retryResponse.initialTokenBalanceInSats = spendResult.tokenBalanceUnit === "msat" ? spendResult.tokenBalance / 1e3 : spendResult.tokenBalance;
|
|
6194
|
+
retryResponse.initialTokenBalanceUnknown = spendResult.tokenBalanceUnknown;
|
|
6195
|
+
return retryResponse;
|
|
5715
6196
|
}
|
|
5716
6197
|
throw new FailoverError(
|
|
5717
6198
|
baseUrl,
|
|
@@ -5727,6 +6208,7 @@ var RoutstrClient = class {
|
|
|
5727
6208
|
baseUrl,
|
|
5728
6209
|
mintUrl,
|
|
5729
6210
|
initialTokenBalance,
|
|
6211
|
+
initialTokenBalanceUnknown,
|
|
5730
6212
|
fallbackSatsSpent,
|
|
5731
6213
|
response,
|
|
5732
6214
|
modelId,
|
|
@@ -5763,17 +6245,19 @@ var RoutstrClient = class {
|
|
|
5763
6245
|
latestBalanceInfo.apiKey,
|
|
5764
6246
|
baseUrl
|
|
5765
6247
|
);
|
|
5766
|
-
const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
6248
|
+
const latestTokenBalance = latestBalanceInfo.balanceUnknown ? void 0 : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
5767
6249
|
const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
|
|
5768
6250
|
if (storedApiKeyEntry?.key.startsWith("cashu") && latestBalanceInfo.apiKey) {
|
|
5769
6251
|
this.storageAdapter.removeApiKey(baseUrl);
|
|
5770
6252
|
this.storageAdapter.setApiKey(baseUrl, latestBalanceInfo.apiKey);
|
|
5771
6253
|
}
|
|
5772
|
-
|
|
5773
|
-
|
|
6254
|
+
if (latestTokenBalance !== void 0) {
|
|
6255
|
+
this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
|
|
6256
|
+
}
|
|
6257
|
+
satsSpent = latestTokenBalance !== void 0 && !initialTokenBalanceUnknown ? Math.max(0, initialTokenBalance - latestTokenBalance) : fallbackSatsSpent ?? usage?.satsCost ?? 0;
|
|
5774
6258
|
} catch (e) {
|
|
5775
6259
|
this._log("WARN", "Could not get updated API key balance:", e);
|
|
5776
|
-
satsSpent = fallbackSatsSpent ??
|
|
6260
|
+
satsSpent = fallbackSatsSpent ?? usage?.satsCost ?? 0;
|
|
5777
6261
|
}
|
|
5778
6262
|
}
|
|
5779
6263
|
await this._trackResponseUsage({
|
|
@@ -6023,6 +6507,7 @@ var RoutstrClient = class {
|
|
|
6023
6507
|
}
|
|
6024
6508
|
let tokenBalance = 0;
|
|
6025
6509
|
let tokenBalanceUnit = "sat";
|
|
6510
|
+
let tokenBalanceUnknown = false;
|
|
6026
6511
|
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
6027
6512
|
const distributionForBaseUrl = apiKeyDistribution.find(
|
|
6028
6513
|
(d) => d.baseUrl === baseUrl
|
|
@@ -6038,6 +6523,7 @@ var RoutstrClient = class {
|
|
|
6038
6523
|
);
|
|
6039
6524
|
tokenBalance = balanceInfo.amount;
|
|
6040
6525
|
tokenBalanceUnit = balanceInfo.unit;
|
|
6526
|
+
tokenBalanceUnknown = Boolean(balanceInfo.balanceUnknown);
|
|
6041
6527
|
} catch (e) {
|
|
6042
6528
|
this._log("WARN", "Could not get initial API key balance:", e);
|
|
6043
6529
|
}
|
|
@@ -6049,7 +6535,8 @@ var RoutstrClient = class {
|
|
|
6049
6535
|
return {
|
|
6050
6536
|
token: parentApiKey?.key ?? "",
|
|
6051
6537
|
tokenBalance,
|
|
6052
|
-
tokenBalanceUnit
|
|
6538
|
+
tokenBalanceUnit,
|
|
6539
|
+
tokenBalanceUnknown
|
|
6053
6540
|
};
|
|
6054
6541
|
}
|
|
6055
6542
|
this._log(
|
|
@@ -6078,7 +6565,8 @@ var RoutstrClient = class {
|
|
|
6078
6565
|
return {
|
|
6079
6566
|
token: spendResult.token,
|
|
6080
6567
|
tokenBalance: spendResult.balance,
|
|
6081
|
-
tokenBalanceUnit: spendResult.unit ?? "sat"
|
|
6568
|
+
tokenBalanceUnit: spendResult.unit ?? "sat",
|
|
6569
|
+
tokenBalanceUnknown: false
|
|
6082
6570
|
};
|
|
6083
6571
|
}
|
|
6084
6572
|
/**
|
|
@@ -6281,9 +6769,11 @@ exports.createIndexedDBDriver = createIndexedDBDriver;
|
|
|
6281
6769
|
exports.createIndexedDBUsageTrackingDriver = createIndexedDBUsageTrackingDriver;
|
|
6282
6770
|
exports.createMemoryDriver = createMemoryDriver;
|
|
6283
6771
|
exports.createMemoryUsageTrackingDriver = createMemoryUsageTrackingDriver;
|
|
6772
|
+
exports.createProviderRegistryFromDiscoveryAdapter = createProviderRegistryFromDiscoveryAdapter;
|
|
6284
6773
|
exports.createProviderRegistryFromStore = createProviderRegistryFromStore;
|
|
6285
6774
|
exports.createSSEParserTransform = createSSEParserTransform;
|
|
6286
6775
|
exports.createSdkStore = createSdkStore;
|
|
6776
|
+
exports.createShardedDiscoveryAdapter = createShardedDiscoveryAdapter;
|
|
6287
6777
|
exports.createSqliteDriver = createSqliteDriver;
|
|
6288
6778
|
exports.createSqliteUsageTrackingDriver = createSqliteUsageTrackingDriver;
|
|
6289
6779
|
exports.createStorageAdapterFromStore = createStorageAdapterFromStore;
|