@routstr/sdk 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +44 -7
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +44 -7
- package/dist/client/index.mjs.map +1 -1
- package/dist/discovery/index.d.mts +18 -1
- package/dist/discovery/index.d.ts +18 -1
- package/dist/discovery/index.js +125 -6
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +125 -6
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +172 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +172 -16
- package/dist/index.mjs.map +1 -1
- package/dist/{interfaces-Cxi8R4TT.d.mts → interfaces-Cqkt41QR.d.mts} +6 -0
- package/dist/{interfaces-CIfd_phZ.d.ts → interfaces-D9qI1ym6.d.ts} +6 -0
- package/dist/storage/index.d.mts +3 -3
- package/dist/storage/index.d.ts +3 -3
- package/dist/storage/index.js +9 -5
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +9 -5
- package/dist/storage/index.mjs.map +1 -1
- package/dist/{store-BD5zF9Hp.d.ts → store-BFUGGr_v.d.ts} +1 -1
- package/dist/{store-CBSyK2qg.d.mts → store-C4FyyOnO.d.mts} +1 -1
- package/dist/wallet/index.js +37 -5
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +37 -5
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -128,13 +128,16 @@ var ModelManager = class _ModelManager {
|
|
|
128
128
|
this.cacheTTL = config.cacheTTL || 210 * 60 * 1e3;
|
|
129
129
|
this.includeProviderUrls = config.includeProviderUrls || [];
|
|
130
130
|
this.excludeProviderUrls = config.excludeProviderUrls || [];
|
|
131
|
+
this.routstrPubkey = config.routstrPubkey || "4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8";
|
|
131
132
|
this.logger = (config.logger ?? consoleLogger).child("ModelManager");
|
|
132
133
|
}
|
|
133
134
|
cacheTTL;
|
|
134
135
|
providerDirectoryUrl;
|
|
135
136
|
includeProviderUrls;
|
|
136
137
|
excludeProviderUrls;
|
|
138
|
+
routstrPubkey;
|
|
137
139
|
logger;
|
|
140
|
+
providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
|
|
138
141
|
/**
|
|
139
142
|
* Get the list of bootstrapped provider base URLs
|
|
140
143
|
* @returns Array of provider base URLs
|
|
@@ -165,8 +168,13 @@ var ModelManager = class _ModelManager {
|
|
|
165
168
|
const lastUpdate = this.adapter.getBaseUrlsLastUpdate();
|
|
166
169
|
const cacheValid = lastUpdate && Date.now() - lastUpdate <= this.cacheTTL;
|
|
167
170
|
if (cacheValid) {
|
|
171
|
+
const filteredCachedUrls = this.filterBaseUrlsForTor(
|
|
172
|
+
cachedUrls,
|
|
173
|
+
torMode
|
|
174
|
+
);
|
|
168
175
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
169
|
-
|
|
176
|
+
await this.syncReviewedProvidersFromNostr(filteredCachedUrls);
|
|
177
|
+
return filteredCachedUrls;
|
|
170
178
|
}
|
|
171
179
|
}
|
|
172
180
|
}
|
|
@@ -177,6 +185,7 @@ var ModelManager = class _ModelManager {
|
|
|
177
185
|
this.adapter.setBaseUrlsList(filtered);
|
|
178
186
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
179
187
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
188
|
+
await this.syncReviewedProvidersFromNostr(filtered);
|
|
180
189
|
return filtered;
|
|
181
190
|
}
|
|
182
191
|
} catch (e) {
|
|
@@ -219,6 +228,7 @@ var ModelManager = class _ModelManager {
|
|
|
219
228
|
});
|
|
220
229
|
const timeline = localEventStore.getTimeline({ kinds: [kind] });
|
|
221
230
|
const bases = /* @__PURE__ */ new Set();
|
|
231
|
+
this.providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
|
|
222
232
|
for (const event of timeline) {
|
|
223
233
|
const eventUrls = [];
|
|
224
234
|
for (const tag of event.tags) {
|
|
@@ -231,6 +241,11 @@ var ModelManager = class _ModelManager {
|
|
|
231
241
|
const normalized = this.normalizeUrl(url);
|
|
232
242
|
if (!torMode || normalized.includes(".onion")) {
|
|
233
243
|
bases.add(normalized);
|
|
244
|
+
this.addProviderNode(
|
|
245
|
+
this.providerNodePubkeysByUrl,
|
|
246
|
+
normalized,
|
|
247
|
+
event.pubkey
|
|
248
|
+
);
|
|
234
249
|
}
|
|
235
250
|
}
|
|
236
251
|
continue;
|
|
@@ -242,6 +257,11 @@ var ModelManager = class _ModelManager {
|
|
|
242
257
|
const endpoints = this.getProviderEndpoints(p, torMode);
|
|
243
258
|
for (const endpoint of endpoints) {
|
|
244
259
|
bases.add(endpoint);
|
|
260
|
+
this.addProviderNode(
|
|
261
|
+
this.providerNodePubkeysByUrl,
|
|
262
|
+
endpoint,
|
|
263
|
+
p?.pubkey || event.pubkey
|
|
264
|
+
);
|
|
245
265
|
}
|
|
246
266
|
}
|
|
247
267
|
} catch {
|
|
@@ -252,11 +272,19 @@ var ModelManager = class _ModelManager {
|
|
|
252
272
|
const endpoints = this.getProviderEndpoints(p, torMode);
|
|
253
273
|
for (const endpoint of endpoints) {
|
|
254
274
|
bases.add(endpoint);
|
|
275
|
+
this.addProviderNode(
|
|
276
|
+
this.providerNodePubkeysByUrl,
|
|
277
|
+
endpoint,
|
|
278
|
+
p?.pubkey || event.pubkey
|
|
279
|
+
);
|
|
255
280
|
}
|
|
256
281
|
}
|
|
257
282
|
}
|
|
258
283
|
} catch {
|
|
259
|
-
this.logger.warn(
|
|
284
|
+
this.logger.warn(
|
|
285
|
+
"NostrBootstrap: failed to parse event content:",
|
|
286
|
+
event.id
|
|
287
|
+
);
|
|
260
288
|
}
|
|
261
289
|
}
|
|
262
290
|
}
|
|
@@ -287,10 +315,12 @@ var ModelManager = class _ModelManager {
|
|
|
287
315
|
const data = await res.json();
|
|
288
316
|
const providers = Array.isArray(data?.providers) ? data.providers : [];
|
|
289
317
|
const bases = /* @__PURE__ */ new Set();
|
|
318
|
+
this.providerNodePubkeysByUrl = /* @__PURE__ */ new Map();
|
|
290
319
|
for (const p of providers) {
|
|
291
320
|
const endpoints = this.getProviderEndpoints(p, torMode);
|
|
292
321
|
for (const endpoint of endpoints) {
|
|
293
322
|
bases.add(endpoint);
|
|
323
|
+
this.addProviderNode(this.providerNodePubkeysByUrl, endpoint, p?.pubkey);
|
|
294
324
|
}
|
|
295
325
|
}
|
|
296
326
|
for (const url of this.includeProviderUrls) {
|
|
@@ -307,6 +337,7 @@ var ModelManager = class _ModelManager {
|
|
|
307
337
|
this.adapter.setBaseUrlsList(list);
|
|
308
338
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
309
339
|
await this.fetchRoutstr21Models(forceRefresh);
|
|
340
|
+
await this.syncReviewedProvidersFromNostr(list);
|
|
310
341
|
}
|
|
311
342
|
return list;
|
|
312
343
|
} catch (e) {
|
|
@@ -314,6 +345,93 @@ var ModelManager = class _ModelManager {
|
|
|
314
345
|
throw new ProviderBootstrapError([], `Provider bootstrap failed: ${e}`);
|
|
315
346
|
}
|
|
316
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* Fetch Routstr review events from Nostr (kind 38425) and disable providers
|
|
350
|
+
* whose 38421 node pubkey does not have at least one review tagged `t=lgtm`.
|
|
351
|
+
*
|
|
352
|
+
* Review events are expected to have:
|
|
353
|
+
* - `node`: the reviewed 38421 provider event pubkey
|
|
354
|
+
* - `t`: review label, where `lgtm` means the node looks good
|
|
355
|
+
*
|
|
356
|
+
* @param baseUrls Current provider base URLs to evaluate
|
|
357
|
+
* @returns Array of provider base URLs disabled by the review set
|
|
358
|
+
*/
|
|
359
|
+
async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl) {
|
|
360
|
+
if (baseUrls.length === 0) return [];
|
|
361
|
+
if (!this.adapter.setDisabledProviders) {
|
|
362
|
+
this.logger.warn(
|
|
363
|
+
"NostrReviews: adapter does not support setDisabledProviders; skipping provider disable sync"
|
|
364
|
+
);
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
const LGTM_RELAYS = [
|
|
368
|
+
"wss://relay.primal.net",
|
|
369
|
+
"wss://nos.lol",
|
|
370
|
+
"wss://relay.damus.io",
|
|
371
|
+
"wss://relay.routstr.com"
|
|
372
|
+
];
|
|
373
|
+
const reviewedNodePubkeys = /* @__PURE__ */ new Set();
|
|
374
|
+
{
|
|
375
|
+
const pool = new applesauceRelay.RelayPool();
|
|
376
|
+
const store = new applesauceCore.EventStore();
|
|
377
|
+
const timeoutMs = 5e3;
|
|
378
|
+
await new Promise((resolve) => {
|
|
379
|
+
pool.req(LGTM_RELAYS, {
|
|
380
|
+
kinds: [38425],
|
|
381
|
+
"#t": ["lgtm"],
|
|
382
|
+
limit: 500,
|
|
383
|
+
authors: [this.routstrPubkey]
|
|
384
|
+
}).pipe(
|
|
385
|
+
applesauceRelay.onlyEvents(),
|
|
386
|
+
rxjs.tap((event) => store.add(event))
|
|
387
|
+
).subscribe({ complete: () => resolve() });
|
|
388
|
+
setTimeout(() => resolve(), timeoutMs);
|
|
389
|
+
});
|
|
390
|
+
for (const event of store.getTimeline({ kinds: [38425] })) {
|
|
391
|
+
const hasLgtmTag = event.tags.some(
|
|
392
|
+
(tag) => tag[0] === "t" && tag[1]?.toLowerCase() === "lgtm"
|
|
393
|
+
);
|
|
394
|
+
if (!hasLgtmTag) continue;
|
|
395
|
+
for (const tag of event.tags) {
|
|
396
|
+
if (tag[0] === "node" && typeof tag[1] === "string" && tag[1]) {
|
|
397
|
+
reviewedNodePubkeys.add(tag[1]);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (reviewedNodePubkeys.size === 0) {
|
|
403
|
+
this.logger.warn(
|
|
404
|
+
"NostrReviews: no kind 38425 lgtm reviews found; keeping disabled providers unchanged"
|
|
405
|
+
);
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
if (providerNodes.size === 0) {
|
|
409
|
+
this.logger.warn(
|
|
410
|
+
"NostrReviews: no kind 38421 provider node metadata found; keeping disabled providers unchanged"
|
|
411
|
+
);
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
const disabledByReview = [];
|
|
415
|
+
for (const url of baseUrls) {
|
|
416
|
+
const normalized = this.normalizeUrl(url);
|
|
417
|
+
const nodePubkeys = providerNodes.get(normalized) || /* @__PURE__ */ new Set();
|
|
418
|
+
const hasLgtmReview = Array.from(nodePubkeys).some(
|
|
419
|
+
(pubkey) => reviewedNodePubkeys.has(pubkey)
|
|
420
|
+
);
|
|
421
|
+
if (!hasLgtmReview) {
|
|
422
|
+
disabledByReview.push(normalized);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
this.adapter.setDisabledProviders(Array.from(new Set(disabledByReview)));
|
|
426
|
+
return disabledByReview;
|
|
427
|
+
}
|
|
428
|
+
addProviderNode(map, url, pubkey) {
|
|
429
|
+
if (!pubkey) return;
|
|
430
|
+
const normalized = this.normalizeUrl(url);
|
|
431
|
+
const existing = map.get(normalized) || /* @__PURE__ */ new Set();
|
|
432
|
+
existing.add(pubkey);
|
|
433
|
+
map.set(normalized, existing);
|
|
434
|
+
}
|
|
317
435
|
/**
|
|
318
436
|
* Fetch models from all providers and select best-priced options
|
|
319
437
|
* Uses cache if available and not expired
|
|
@@ -506,9 +624,7 @@ var ModelManager = class _ModelManager {
|
|
|
506
624
|
kinds: [38423],
|
|
507
625
|
"#d": ["routstr-21-models"],
|
|
508
626
|
limit: 1,
|
|
509
|
-
authors: [
|
|
510
|
-
"4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8"
|
|
511
|
-
]
|
|
627
|
+
authors: [this.routstrPubkey]
|
|
512
628
|
}).pipe(
|
|
513
629
|
applesauceRelay.onlyEvents(),
|
|
514
630
|
rxjs.tap((event2) => {
|
|
@@ -535,7 +651,10 @@ var ModelManager = class _ModelManager {
|
|
|
535
651
|
this.adapter.setRoutstr21ModelsLastUpdate(Date.now());
|
|
536
652
|
return models;
|
|
537
653
|
} catch {
|
|
538
|
-
this.logger.warn(
|
|
654
|
+
this.logger.warn(
|
|
655
|
+
"Routstr21Models: failed to parse Nostr event content:",
|
|
656
|
+
event.id
|
|
657
|
+
);
|
|
539
658
|
return cachedModels.length > 0 ? cachedModels : [];
|
|
540
659
|
}
|
|
541
660
|
}
|
|
@@ -1221,19 +1340,50 @@ var CashuSpender = class {
|
|
|
1221
1340
|
apiKeyEntry.baseUrl
|
|
1222
1341
|
);
|
|
1223
1342
|
if (apiKeyEntryFull && this.balanceManager) {
|
|
1343
|
+
try {
|
|
1344
|
+
const balanceResult = await this.balanceManager.getTokenBalance(
|
|
1345
|
+
apiKeyEntryFull.key,
|
|
1346
|
+
apiKeyEntry.baseUrl
|
|
1347
|
+
);
|
|
1348
|
+
if (balanceResult.isInvalidApiKey) {
|
|
1349
|
+
this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
|
|
1350
|
+
results.push({
|
|
1351
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
1352
|
+
success: true
|
|
1353
|
+
});
|
|
1354
|
+
continue;
|
|
1355
|
+
}
|
|
1356
|
+
if (balanceResult.amount >= 0) {
|
|
1357
|
+
const balanceSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.amount / 1e3) : balanceResult.amount;
|
|
1358
|
+
this.storageAdapter.updateApiKeyBalance(
|
|
1359
|
+
apiKeyEntry.baseUrl,
|
|
1360
|
+
balanceSat
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
} catch {
|
|
1364
|
+
}
|
|
1365
|
+
const refreshedEntry = this.storageAdapter.getApiKey(
|
|
1366
|
+
apiKeyEntry.baseUrl
|
|
1367
|
+
);
|
|
1368
|
+
if (!refreshedEntry) continue;
|
|
1224
1369
|
const refundResult = await this.balanceManager.refundApiKey({
|
|
1225
1370
|
mintUrl,
|
|
1226
1371
|
baseUrl: apiKeyEntry.baseUrl,
|
|
1227
|
-
apiKey:
|
|
1372
|
+
apiKey: refreshedEntry.key,
|
|
1228
1373
|
forceRefund
|
|
1229
1374
|
});
|
|
1230
1375
|
if (refundResult.success) {
|
|
1231
1376
|
this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
|
|
1232
1377
|
} else {
|
|
1233
|
-
this.storageAdapter.
|
|
1234
|
-
apiKeyEntry.baseUrl
|
|
1235
|
-
apiKeyEntry.amount
|
|
1378
|
+
const currentEntry = this.storageAdapter.getApiKey(
|
|
1379
|
+
apiKeyEntry.baseUrl
|
|
1236
1380
|
);
|
|
1381
|
+
if (currentEntry) {
|
|
1382
|
+
this.storageAdapter.updateApiKeyBalance(
|
|
1383
|
+
apiKeyEntry.baseUrl,
|
|
1384
|
+
currentEntry.balance
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1237
1387
|
}
|
|
1238
1388
|
results.push({
|
|
1239
1389
|
baseUrl: apiKeyEntry.baseUrl,
|
|
@@ -1442,7 +1592,8 @@ var BalanceManager = class _BalanceManager {
|
|
|
1442
1592
|
};
|
|
1443
1593
|
}
|
|
1444
1594
|
if (fetchResult.error === "No balance to refund") {
|
|
1445
|
-
|
|
1595
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
1596
|
+
return { success: true, message: "No balance to refund, key cleaned up" };
|
|
1446
1597
|
}
|
|
1447
1598
|
const receiveResult = await this.cashuSpender.receiveToken(
|
|
1448
1599
|
fetchResult.token
|
|
@@ -4110,7 +4261,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
4110
4261
|
driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, []),
|
|
4111
4262
|
driver.getItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, []),
|
|
4112
4263
|
driver.getItem(SDK_STORAGE_KEYS.LAST_FAILED, {}),
|
|
4113
|
-
driver.getItem(
|
|
4264
|
+
driver.getItem(
|
|
4265
|
+
SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN,
|
|
4266
|
+
[]
|
|
4267
|
+
)
|
|
4114
4268
|
]);
|
|
4115
4269
|
const modelsFromAllProviders = Object.fromEntries(
|
|
4116
4270
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
@@ -4178,7 +4332,9 @@ var hydrateStoreFromDriver = async (store, driver) => {
|
|
|
4178
4332
|
createdAt: entry.createdAt ?? Date.now(),
|
|
4179
4333
|
lastUsed: entry.lastUsed ?? null
|
|
4180
4334
|
}));
|
|
4181
|
-
const failedProviders = rawFailedProviders.map(
|
|
4335
|
+
const failedProviders = rawFailedProviders.map(
|
|
4336
|
+
(url) => normalizeBaseUrl5(url)
|
|
4337
|
+
);
|
|
4182
4338
|
const lastFailed = Object.fromEntries(
|
|
4183
4339
|
Object.entries(rawLastFailed).map(([baseUrl, timestamp]) => [
|
|
4184
4340
|
normalizeBaseUrl5(baseUrl),
|
|
@@ -4240,6 +4396,7 @@ var createDiscoveryAdapterFromStore = (store) => ({
|
|
|
4240
4396
|
getLastUsedModel: () => store.getState().lastUsedModel,
|
|
4241
4397
|
setLastUsedModel: (modelId) => store.getState().setLastUsedModel(modelId),
|
|
4242
4398
|
getDisabledProviders: () => store.getState().disabledProviders,
|
|
4399
|
+
setDisabledProviders: (urls) => store.getState().setDisabledProviders(urls),
|
|
4243
4400
|
getBaseUrlsList: () => store.getState().baseUrlsList,
|
|
4244
4401
|
setBaseUrlsList: (urls) => store.getState().setBaseUrlsList(urls),
|
|
4245
4402
|
getBaseUrlsLastUpdate: () => store.getState().lastBaseUrlsUpdate,
|
|
@@ -4255,9 +4412,7 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
4255
4412
|
const distributionMap = {};
|
|
4256
4413
|
for (const entry of apiKeys) {
|
|
4257
4414
|
const sum = entry.balance || 0;
|
|
4258
|
-
|
|
4259
|
-
distributionMap[entry.baseUrl] = (distributionMap[entry.baseUrl] || 0) + sum;
|
|
4260
|
-
}
|
|
4415
|
+
distributionMap[entry.baseUrl] = (distributionMap[entry.baseUrl] || 0) + sum;
|
|
4261
4416
|
}
|
|
4262
4417
|
return Object.entries(distributionMap).map(([baseUrl, amt]) => ({ baseUrl, amount: amt })).sort((a, b) => b.amount - a.amount);
|
|
4263
4418
|
},
|
|
@@ -5914,6 +6069,7 @@ async function resolveRouteRequestContext(options) {
|
|
|
5914
6069
|
} else {
|
|
5915
6070
|
modelManager = new ModelManager(discoveryAdapter, {
|
|
5916
6071
|
includeProviderUrls: forcedProvider ? [forcedProvider, ...includeProviderUrls] : includeProviderUrls,
|
|
6072
|
+
routstrPubkey: options.routstrPubkey,
|
|
5917
6073
|
logger
|
|
5918
6074
|
});
|
|
5919
6075
|
providers = await modelManager.bootstrapProviders(torMode);
|