routstrd 0.4.8 → 0.4.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.
@@ -36717,10 +36717,10 @@ function invert4(number6, modulo) {
36717
36717
  let b = modulo;
36718
36718
  let x2 = _0n19, y = _1n19, u = _1n19, v3 = _0n19;
36719
36719
  while (a !== _0n19) {
36720
- const q = b / a;
36720
+ const q2 = b / a;
36721
36721
  const r = b % a;
36722
- const m = x2 - u * q;
36723
- const n = y - v3 * q;
36722
+ const m = x2 - u * q2;
36723
+ const n = y - v3 * q2;
36724
36724
  b = a, a = r, x2 = u, y = v3, u = m, v3 = n;
36725
36725
  }
36726
36726
  const gcd4 = b;
@@ -38003,14 +38003,14 @@ function ecdsa2(Point4, hash5, ecdsaOpts = {}) {
38003
38003
  if (!Fn3.isValidNot0(k2))
38004
38004
  return;
38005
38005
  const ik = Fn3.inv(k2);
38006
- const q = Point4.BASE.multiply(k2).toAffine();
38007
- const r = Fn3.create(q.x);
38006
+ const q2 = Point4.BASE.multiply(k2).toAffine();
38007
+ const r = Fn3.create(q2.x);
38008
38008
  if (r === _0n21)
38009
38009
  return;
38010
38010
  const s = Fn3.create(ik * Fn3.create(m + r * d));
38011
38011
  if (s === _0n21)
38012
38012
  return;
38013
- let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n21);
38013
+ let recovery = (q2.x === r ? 0 : 2) | Number(q2.y & _1n21);
38014
38014
  let normS = s;
38015
38015
  if (lowS && isBiggerThanHalfOrder(s)) {
38016
38016
  normS = Fn3.neg(s);
@@ -65238,10 +65238,10 @@ function invert5(number10, modulo) {
65238
65238
  let b = modulo;
65239
65239
  let x2 = _0n25, y = _1n25, u = _1n25, v3 = _0n25;
65240
65240
  while (a !== _0n25) {
65241
- const q = b / a;
65241
+ const q2 = b / a;
65242
65242
  const r = b % a;
65243
- const m = x2 - u * q;
65244
- const n = y - v3 * q;
65243
+ const m = x2 - u * q2;
65244
+ const n = y - v3 * q2;
65245
65245
  b = a, a = r, x2 = u, y = v3, u = m, v3 = n;
65246
65246
  }
65247
65247
  const gcd4 = b;
@@ -66159,14 +66159,14 @@ function weierstrass5(curveDef) {
66159
66159
  if (!isWithinCurveOrder(k2))
66160
66160
  return;
66161
66161
  const ik = invN(k2);
66162
- const q = Point4.BASE.multiply(k2).toAffine();
66163
- const r = modN3(q.x);
66162
+ const q2 = Point4.BASE.multiply(k2).toAffine();
66163
+ const r = modN3(q2.x);
66164
66164
  if (r === _0n27)
66165
66165
  return;
66166
66166
  const s = modN3(ik * modN3(m + r * d));
66167
66167
  if (s === _0n27)
66168
66168
  return;
66169
- let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n27);
66169
+ let recovery = (q2.x === r ? 0 : 2) | Number(q2.y & _1n27);
66170
66170
  let normS = s;
66171
66171
  if (lowS && isBiggerThanHalfOrder(s)) {
66172
66172
  normS = normalizeS(s);
@@ -88410,9 +88410,22 @@ function cr(t) {
88410
88410
  };
88411
88411
  return t.d && (r.memo = t.d), r;
88412
88412
  }
88413
- function lr(e2, t) {
88414
- let n = (t ?? []).map((e3) => typeof e3 == "string" ? e3 : e3.id), r = dr(Or(e2));
88415
- return n && (r.proofs = Sr(r.proofs, n)), r;
88413
+ function ur(e2) {
88414
+ e2 = Or(e2);
88415
+ let t = dr(e2);
88416
+ return {
88417
+ unit: t.unit || "sat",
88418
+ mint: t.mint,
88419
+ amount: q(t.proofs),
88420
+ ...t.memo && { memo: t.memo },
88421
+ incompleteProofs: t.proofs.map((e3) => ({
88422
+ secret: e3.secret,
88423
+ C: e3.C,
88424
+ amount: e3.amount,
88425
+ ...e3.dleq && { dleq: e3.dleq },
88426
+ ...e3.witness && { witness: e3.witness }
88427
+ }))
88428
+ };
88416
88429
  }
88417
88430
  function dr(e2) {
88418
88431
  let t = e2.slice(0, 1), n = e2.slice(1);
@@ -88469,6 +88482,9 @@ function K2(...e2) {
88469
88482
  function gr(e2) {
88470
88483
  return e2.replace(/\/$/, "");
88471
88484
  }
88485
+ function q(e2) {
88486
+ return D.sum(e2.map((e3) => e3.amount)).toNumber();
88487
+ }
88472
88488
  var vr = class {
88473
88489
  get value() {
88474
88490
  return this._value;
@@ -88519,32 +88535,6 @@ var yr = class {
88519
88535
  return this._first = e2.next, e2.next = null, this._size--, e2.value;
88520
88536
  }
88521
88537
  };
88522
- function Sr(e2, n) {
88523
- let r = (n ?? []).map((e3) => typeof e3 == "string" ? e3 : e3.id), i4 = [];
88524
- for (let n2 of e2) {
88525
- let e3;
88526
- try {
88527
- e3 = hexToBytes7(n2.id);
88528
- } catch {
88529
- i4.push(n2);
88530
- continue;
88531
- }
88532
- if (e3[0] === 0)
88533
- i4.push(n2);
88534
- else if (e3[0] === 1) {
88535
- if (!r)
88536
- throw Error("A short keyset ID v2 was encountered, but got no keysets to map it to.");
88537
- let e4 = r.filter((e5) => n2.id === e5.slice(0, n2.id.length));
88538
- if (e4.length > 1)
88539
- throw Error(`Short keyset ID ${n2.id} is ambiguous.`);
88540
- if (e4.length === 0)
88541
- throw Error(`Couldn't map short keyset ID ${n2.id} to any known keysets of the current Mint`);
88542
- n2.id = e4[0], i4.push(n2);
88543
- } else
88544
- throw Error(`Unknown keyset ID version: ${e3[0]}`);
88545
- }
88546
- return i4;
88547
- }
88548
88538
  function Cr(e2, n) {
88549
88539
  if (e2.dleq == null)
88550
88540
  return false;
@@ -90844,6 +90834,7 @@ var ModelManager = class _ModelManager {
90844
90834
  this.logger = (config2.logger ?? consoleLogger).child("ModelManager");
90845
90835
  this.eventStoreDbPath = config2.eventStoreDbPath;
90846
90836
  this.persistentEventDatabaseFactory = config2.persistentEventDatabaseFactory;
90837
+ this.eventStore = config2.eventStore ?? null;
90847
90838
  }
90848
90839
  adapter;
90849
90840
  cacheTTL;
@@ -90857,6 +90848,8 @@ var ModelManager = class _ModelManager {
90857
90848
  providerNodePubkeysByUrl = /* @__PURE__ */ new Map;
90858
90849
  relayPool = null;
90859
90850
  eventStore = null;
90851
+ memoryEventStore = new EventStore({ verifyEvent });
90852
+ queryLastUpdate = /* @__PURE__ */ new Map;
90860
90853
  eventStoreDb = null;
90861
90854
  eventStoreInitPromise = null;
90862
90855
  eventStoreDbPath;
@@ -90865,17 +90858,16 @@ var ModelManager = class _ModelManager {
90865
90858
  return this.adapter.getBaseUrlsList();
90866
90859
  }
90867
90860
  async ensureEventStore() {
90868
- if (!this.eventStoreDbPath)
90869
- return null;
90870
90861
  if (this.eventStore)
90871
90862
  return this.eventStore;
90863
+ if (!this.eventStoreDbPath)
90864
+ return null;
90872
90865
  if (!this.eventStoreInitPromise) {
90873
90866
  this.eventStoreInitPromise = (async () => {
90874
90867
  try {
90875
90868
  const db = await this.createPersistentEventDatabase();
90876
90869
  this.eventStoreDb = db;
90877
90870
  this.eventStore = new EventStore({ database: db });
90878
- this.initializeEventStoreMetadata();
90879
90871
  this.logger.log(`Persistent event store initialized at ${this.eventStoreDbPath}`);
90880
90872
  return this.eventStore;
90881
90873
  } catch (error53) {
@@ -90904,34 +90896,40 @@ var ModelManager = class _ModelManager {
90904
90896
  this.eventStoreDb = null;
90905
90897
  this.eventStoreInitPromise = null;
90906
90898
  }
90907
- initializeEventStoreMetadata() {
90908
- this.eventStoreDb?.db?.exec(`CREATE TABLE IF NOT EXISTS routstr_event_cache_metadata (
90909
- event_id TEXT PRIMARY KEY,
90910
- fetched_at INTEGER NOT NULL
90911
- )`);
90912
- }
90913
- markEventFetched(event3, fetchedAt = Date.now()) {
90914
- const db = this.eventStoreDb?.db;
90915
- if (!db)
90916
- return;
90917
- db.prepare(`INSERT INTO routstr_event_cache_metadata (event_id, fetched_at)
90918
- VALUES (?, ?)
90919
- ON CONFLICT(event_id) DO UPDATE SET fetched_at = excluded.fetched_at`).run?.(event3.id, fetchedAt);
90920
- }
90921
- getEventFetchedAt(event3) {
90922
- const db = this.eventStoreDb?.db;
90923
- if (!db)
90924
- return;
90925
- const row = db.prepare(`SELECT fetched_at FROM routstr_event_cache_metadata WHERE event_id = ?`).get?.(event3.id);
90926
- return typeof row?.fetched_at === "number" ? row.fetched_at : undefined;
90927
- }
90928
- async getCachedNostrEvents(filter10, forceRefresh = false) {
90929
- const eventStore = await this.ensureEventStore();
90930
- if (forceRefresh)
90931
- return [];
90932
- if (!eventStore)
90933
- return [];
90934
- return eventStore.getTimeline(filter10).filter((event3) => !this.isFutureDated(event3));
90899
+ async getNostrEvents(filter10, forceRefresh = false, onEvent) {
90900
+ const eventStore = await this.ensureEventStore() ?? this.memoryEventStore;
90901
+ const query2 = JSON.stringify(filter10);
90902
+ const lastUpdate = this.queryLastUpdate.get(query2) ?? this.adapter.getNostrQueryLastUpdate?.()?.[query2];
90903
+ const now2 = Date.now();
90904
+ const cacheValid = typeof lastUpdate === "number" && lastUpdate <= now2 && now2 - lastUpdate <= this.cacheTTL;
90905
+ const received = /* @__PURE__ */ new Set;
90906
+ const cached2 = forceRefresh ? [] : eventStore.getTimeline(filter10).filter((event3) => this.isNostrEventTrustworthy(event3));
90907
+ if (forceRefresh || cached2.length === 0 || !cacheValid) {
90908
+ await this.collectNostrEvents(filter10, this.getNostrRelays(), NOSTR_QUERY_TIMEOUT_MS, (event3) => {
90909
+ const stored = eventStore.add(event3);
90910
+ if (!stored || stored.id !== event3.id)
90911
+ return;
90912
+ received.add(event3.id);
90913
+ onEvent?.(event3);
90914
+ });
90915
+ if (received.size > 0) {
90916
+ this.queryLastUpdate.set(query2, Date.now());
90917
+ this.adapter.setNostrQueryLastUpdate?.({
90918
+ ...this.adapter.getNostrQueryLastUpdate?.(),
90919
+ ...Object.fromEntries(this.queryLastUpdate)
90920
+ });
90921
+ }
90922
+ const events = eventStore.getTimeline(filter10).filter((event3) => this.isNostrEventTrustworthy(event3));
90923
+ for (const event3 of events) {
90924
+ if (!received.has(event3.id))
90925
+ onEvent?.(event3);
90926
+ }
90927
+ return events;
90928
+ }
90929
+ for (const event3 of cached2) {
90930
+ onEvent?.(event3);
90931
+ }
90932
+ return cached2;
90935
90933
  }
90936
90934
  async refreshNostrEvents() {
90937
90935
  const eventStore = await this.ensureEventStore();
@@ -90939,23 +90937,12 @@ var ModelManager = class _ModelManager {
90939
90937
  this.logger.warn("refreshNostrEvents: no event store configured, skipping");
90940
90938
  return;
90941
90939
  }
90942
- const relays2 = this.getNostrRelays();
90943
- const timeoutMs = 5000;
90944
- await this.fetchLiveIntoStore({ kinds: [38421], limit: 100 }, relays2, timeoutMs);
90945
- await this.fetchLiveIntoStore({ kinds: [38425], limit: 500, authors: [this.routstrPubkey] }, relays2, timeoutMs);
90946
- await this.fetchLiveIntoStore({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, relays2, timeoutMs);
90940
+ await this.getNostrEvents({ kinds: [38421], limit: 100 }, true);
90941
+ await this.getNostrEvents({ kinds: [38425], limit: 500, authors: [this.routstrPubkey] }, true);
90942
+ await this.getNostrEvents({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, true);
90947
90943
  this.logger.log("refreshNostrEvents: live fetch complete");
90948
90944
  await this.syncReviewedProvidersFromNostr();
90949
90945
  }
90950
- async fetchLiveIntoStore(filter10, relays2, timeoutMs) {
90951
- const eventStore = await this.ensureEventStore();
90952
- if (!eventStore)
90953
- return;
90954
- await this.collectNostrEvents(filter10, relays2, timeoutMs, (event3) => {
90955
- eventStore.add(event3);
90956
- this.markEventFetched(event3);
90957
- });
90958
- }
90959
90946
  static async init(adapter, config2 = {}, options = {}) {
90960
90947
  const manager = new _ModelManager(adapter, config2);
90961
90948
  const torMode = options.torMode ?? false;
@@ -90974,7 +90961,7 @@ var ModelManager = class _ModelManager {
90974
90961
  const filteredCachedUrls = this.filterBaseUrlsForTor(cachedUrls, torMode);
90975
90962
  await Promise.all([
90976
90963
  this.fetchRoutstr21Models(forceRefresh),
90977
- this.syncReviewedProvidersFromNostr(filteredCachedUrls, this.providerNodePubkeysByUrl, forceRefresh)
90964
+ this.syncReviewedProvidersFromNostr(filteredCachedUrls)
90978
90965
  ]);
90979
90966
  return filteredCachedUrls;
90980
90967
  }
@@ -91056,7 +91043,6 @@ var ModelManager = class _ModelManager {
91056
91043
  });
91057
91044
  }
91058
91045
  async bootstrapFromNostr(kind, torMode, forceRefresh = false, options = {}) {
91059
- const relays2 = this.getNostrRelays();
91060
91046
  const bases = /* @__PURE__ */ new Set;
91061
91047
  this.providerNodePubkeysByUrl = /* @__PURE__ */ new Map;
91062
91048
  const excluded = new Set(this.excludeProviderUrls.map((url3) => this.normalizeUrl(url3)));
@@ -91112,19 +91098,7 @@ var ModelManager = class _ModelManager {
91112
91098
  }
91113
91099
  }
91114
91100
  };
91115
- const cached2 = await this.getCachedNostrEvents({ kinds: [kind] }, forceRefresh);
91116
- if (cached2.length > 0) {
91117
- this.logger.log(`Using ${cached2.length} cached kind ${kind} events from persistent store`);
91118
- for (const event3 of cached2) {
91119
- collectFromEvent(event3);
91120
- }
91121
- } else {
91122
- await this.collectNostrEvents({ kinds: [kind], limit: 100 }, relays2, NOSTR_QUERY_TIMEOUT_MS, (event3) => {
91123
- this.eventStore?.add(event3);
91124
- this.markEventFetched(event3);
91125
- collectFromEvent(event3);
91126
- });
91127
- }
91101
+ await this.getNostrEvents({ kinds: [kind], limit: 100 }, forceRefresh, collectFromEvent);
91128
91102
  for (const url3 of this.includeProviderUrls) {
91129
91103
  const normalized = this.normalizeUrl(url3);
91130
91104
  if (!torMode || normalized.includes(".onion")) {
@@ -91173,25 +91147,25 @@ var ModelManager = class _ModelManager {
91173
91147
  throw new ProviderBootstrapError([], `Provider bootstrap failed: ${e13}`);
91174
91148
  }
91175
91149
  }
91176
- async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl, forceRefresh = false) {
91150
+ async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes, forceRefresh = false) {
91177
91151
  if (baseUrls.length === 0)
91178
91152
  return null;
91179
91153
  if (!this.adapter.setDisabledProviders) {
91180
91154
  this.logger.warn("NostrReviews: adapter does not support setDisabledProviders; skipping provider disable sync");
91181
91155
  return null;
91182
91156
  }
91183
- if (providerNodes.size === 0) {
91184
- providerNodes = await this.rebuildProviderNodesFromStore();
91157
+ if (!providerNodes || providerNodes.size === 0) {
91158
+ providerNodes = await this.rebuildProviderNodesFromStore(forceRefresh);
91159
+ if (!forceRefresh && providerNodes.size > 0 && baseUrls.some((url3) => !providerNodes.has(this.normalizeUrl(url3)))) {
91160
+ providerNodes = await this.rebuildProviderNodesFromStore(true);
91161
+ }
91185
91162
  this.providerNodePubkeysByUrl = providerNodes;
91186
91163
  }
91187
91164
  const reviewLabels = await this.fetchReviewLabels(forceRefresh);
91188
91165
  return this.applyReviewDisables(baseUrls, providerNodes, reviewLabels);
91189
91166
  }
91190
- async rebuildProviderNodesFromStore() {
91167
+ async rebuildProviderNodesFromStore(forceRefresh) {
91191
91168
  const map14 = /* @__PURE__ */ new Map;
91192
- const eventStore = await this.ensureEventStore();
91193
- if (!eventStore)
91194
- return map14;
91195
91169
  const addNode = (url3, pubkey) => {
91196
91170
  if (!pubkey)
91197
91171
  return;
@@ -91200,7 +91174,7 @@ var ModelManager = class _ModelManager {
91200
91174
  existing.add(pubkey);
91201
91175
  map14.set(normalized, existing);
91202
91176
  };
91203
- const events = eventStore.getTimeline({ kinds: [38421] });
91177
+ const events = await this.getNostrEvents({ kinds: [38421], limit: 100 }, forceRefresh);
91204
91178
  for (const event3 of events) {
91205
91179
  const eventUrls = [];
91206
91180
  for (const tag2 of event3.tags) {
@@ -91218,7 +91192,10 @@ var ModelManager = class _ModelManager {
91218
91192
  const content2 = JSON.parse(event3.content);
91219
91193
  const providers = Array.isArray(content2) ? content2 : content2.providers || [];
91220
91194
  for (const p of providers) {
91221
- const endpoints = this.getProviderEndpoints(p, false);
91195
+ const endpoints = [
91196
+ ...this.getProviderEndpoints(p, false),
91197
+ ...this.getProviderEndpoints(p, true)
91198
+ ];
91222
91199
  for (const endpoint of endpoints) {
91223
91200
  addNode(endpoint, p?.pubkey || event3.pubkey);
91224
91201
  }
@@ -91243,23 +91220,9 @@ var ModelManager = class _ModelManager {
91243
91220
  }
91244
91221
  }
91245
91222
  };
91246
- const cached2 = await this.getCachedNostrEvents({ kinds: [38425], authors: [this.routstrPubkey] }, forceRefresh);
91247
- if (cached2.length > 0) {
91248
- this.logger.log(`Using ${cached2.length} cached kind 38425 review events from persistent store`);
91249
- for (const event3 of cached2) {
91250
- collectFromEvent(event3);
91251
- }
91252
- } else {
91253
- await this.collectNostrEvents({
91254
- kinds: [38425],
91255
- limit: 500,
91256
- authors: [this.routstrPubkey]
91257
- }, this.getNostrRelays(), NOSTR_QUERY_TIMEOUT_MS, (event3) => {
91258
- this.eventStore?.add(event3);
91259
- this.markEventFetched(event3);
91260
- collectFromEvent(event3);
91261
- });
91262
- }
91223
+ const events = await this.getNostrEvents({ kinds: [38425], limit: 500, authors: [this.routstrPubkey] }, forceRefresh);
91224
+ for (const event3 of events)
91225
+ collectFromEvent(event3);
91263
91226
  const labels = /* @__PURE__ */ new Map;
91264
91227
  for (const [node, event3] of latestByNode) {
91265
91228
  const label = event3.tags.find((tag2) => tag2[0] === "t" && typeof tag2[1] === "string" && tag2[1])?.[1]?.toLowerCase();
@@ -91473,24 +91436,8 @@ var ModelManager = class _ModelManager {
91473
91436
  return cachedModels;
91474
91437
  }
91475
91438
  }
91476
- const relays2 = this.getNostrRelays();
91477
- const cached2 = await this.getCachedNostrEvents({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, forceRefresh);
91478
- let event3 = null;
91479
- if (cached2.length === 0) {
91480
- await this.collectNostrEvents({
91481
- kinds: [38423],
91482
- "#d": ["routstr-21-models"],
91483
- authors: [this.routstrModelsPubkey]
91484
- }, relays2, NOSTR_QUERY_TIMEOUT_MS, (e13) => {
91485
- this.eventStore?.add(e13);
91486
- this.markEventFetched(e13);
91487
- if (!event3 || e13.created_at > event3.created_at)
91488
- event3 = e13;
91489
- });
91490
- } else {
91491
- this.logger.log(`Using ${cached2.length} cached kind 38423 events from persistent store`);
91492
- event3 = cached2.reduce((latest, e13) => e13.created_at > latest.created_at ? e13 : latest, cached2[0]);
91493
- }
91439
+ const events = await this.getNostrEvents({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, forceRefresh);
91440
+ const event3 = events.reduce((latest, event22) => !latest || event22.created_at > latest.created_at ? event22 : latest, null);
91494
91441
  if (!event3) {
91495
91442
  return cachedModels.length > 0 ? cachedModels : [];
91496
91443
  }
@@ -91613,10 +91560,9 @@ var CashuSpender = class _CashuSpender {
91613
91560
  }
91614
91561
  _decodeTokenAmount(token) {
91615
91562
  try {
91616
- const decoded = lr(token);
91617
- const amount = decoded.proofs.reduce((acc, proof) => acc + proof.amount, 0);
91618
- const unit = decoded.unit || "sat";
91619
- return { amount, unit };
91563
+ const metadata = ur(token);
91564
+ const unit = metadata.unit || "sat";
91565
+ return { amount: metadata.amount, unit };
91620
91566
  } catch {
91621
91567
  return { amount: 0, unit: "sat" };
91622
91568
  }
@@ -92023,7 +91969,8 @@ var CashuSpender = class _CashuSpender {
92023
91969
  }
92024
91970
  if (balanceResult.amount >= 0 && !balanceResult.balanceUnknown) {
92025
91971
  const balanceSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.amount / 1000) : balanceResult.amount;
92026
- this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, balanceSat);
91972
+ const reservedSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.reserved / 1000) : balanceResult.reserved;
91973
+ this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, balanceSat, reservedSat);
92027
91974
  } else {
92028
91975
  this.logger.warn(`refundProviders: balance refresh for ${apiKeyEntry.baseUrl} returned negative amount; keeping stale local balance=${apiKeyEntryFull.balance}`);
92029
91976
  }
@@ -93628,11 +93575,7 @@ var ProviderManager = class _ProviderManager {
93628
93575
  getProviderPriceRankingForModel(modelId, options = {}) {
93629
93576
  const includeDisabled = options.includeDisabled ?? false;
93630
93577
  const torMode = options.torMode ?? false;
93631
- const disabledProviderList = this.discoveryAdapter.getDisabledProviders();
93632
- const disabledProviders = new Set(disabledProviderList);
93633
- if (disabledProviderList.length > 0) {
93634
- this.logger.log(`getProviderPriceRankingForModel: disabled providers (${disabledProviderList.length}): ${disabledProviderList.join(", ")}`);
93635
- }
93578
+ const disabledProviders = new Set(this.discoveryAdapter.getDisabledProviders());
93636
93579
  const allModels = this.discoveryAdapter.getCachedModels();
93637
93580
  const results = [];
93638
93581
  for (const [baseUrl, models] of Object.entries(allModels)) {
@@ -93842,6 +93785,7 @@ var createMemoryDriver = (seed) => {
93842
93785
  };
93843
93786
  };
93844
93787
  var SDK_STORAGE_KEYS = {
93788
+ NOSTR_QUERY_LAST_UPDATE: "nostr_query_last_update",
93845
93789
  MODELS_FROM_ALL_PROVIDERS: "modelsFromAllProviders",
93846
93790
  LAST_USED_MODEL: "lastUsedModel",
93847
93791
  BASE_URLS_LIST: "base_urls_list",
@@ -94220,6 +94164,11 @@ var createMemoryUsageTrackingDriver = (seed = []) => {
94220
94164
  };
94221
94165
  var normalizeBaseUrl5 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
94222
94166
  var createEmptyStore = (driver) => createStore((set2, get) => ({
94167
+ nostrQueryLastUpdate: {},
94168
+ setNostrQueryLastUpdate: (value) => {
94169
+ driver.setItem(SDK_STORAGE_KEYS.NOSTR_QUERY_LAST_UPDATE, value);
94170
+ set2({ nostrQueryLastUpdate: value });
94171
+ },
94223
94172
  modelsFromAllProviders: {},
94224
94173
  lastUsedModel: null,
94225
94174
  baseUrlsList: [],
@@ -94307,6 +94256,7 @@ var createEmptyStore = (driver) => createStore((set2, get) => ({
94307
94256
  ...entry,
94308
94257
  baseUrl: normalizeBaseUrl5(entry.baseUrl),
94309
94258
  balance: entry.balance ?? 0,
94259
+ reserved: entry.reserved ?? 0,
94310
94260
  lastUsed: entry.lastUsed ?? null
94311
94261
  }));
94312
94262
  driver.setItem(SDK_STORAGE_KEYS.API_KEYS, normalized);
@@ -94447,6 +94397,7 @@ var createEmptyStore = (driver) => createStore((set2, get) => ({
94447
94397
  }));
94448
94398
  var hydrateStoreFromDriver = async (store, driver) => {
94449
94399
  const [
94400
+ nostrQueryLastUpdate,
94450
94401
  rawModels,
94451
94402
  lastUsedModel,
94452
94403
  rawBaseUrls,
@@ -94468,6 +94419,7 @@ var hydrateStoreFromDriver = async (store, driver) => {
94468
94419
  rawLastFailed,
94469
94420
  rawProvidersOnCooldown
94470
94421
  ] = await Promise.all([
94422
+ driver.getItem(SDK_STORAGE_KEYS.NOSTR_QUERY_LAST_UPDATE, {}),
94471
94423
  driver.getItem(SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS, {}),
94472
94424
  driver.getItem(SDK_STORAGE_KEYS.LAST_USED_MODEL, null),
94473
94425
  driver.getItem(SDK_STORAGE_KEYS.BASE_URLS_LIST, []),
@@ -94513,6 +94465,7 @@ var hydrateStoreFromDriver = async (store, driver) => {
94513
94465
  ...entry,
94514
94466
  baseUrl: normalizeBaseUrl5(entry.baseUrl),
94515
94467
  balance: entry.balance ?? 0,
94468
+ reserved: entry.reserved ?? 0,
94516
94469
  lastUsed: entry.lastUsed ?? null
94517
94470
  }));
94518
94471
  const childKeys = rawChildKeys.map((entry) => ({
@@ -94555,6 +94508,7 @@ var hydrateStoreFromDriver = async (store, driver) => {
94555
94508
  timestamp: entry.timestamp
94556
94509
  }));
94557
94510
  store.setState({
94511
+ nostrQueryLastUpdate,
94558
94512
  modelsFromAllProviders,
94559
94513
  lastUsedModel,
94560
94514
  baseUrlsList,
@@ -94591,10 +94545,15 @@ var createStorageAdapterFromStore = (store) => ({
94591
94545
  const apiKeys = store.getState().apiKeys;
94592
94546
  const distributionMap = {};
94593
94547
  for (const entry of apiKeys) {
94594
- const sum = entry.balance || 0;
94595
- distributionMap[entry.baseUrl] = (distributionMap[entry.baseUrl] || 0) + sum;
94548
+ const snapshot = distributionMap[entry.baseUrl] ?? {
94549
+ amount: 0,
94550
+ reserved: 0
94551
+ };
94552
+ snapshot.amount += entry.balance || 0;
94553
+ snapshot.reserved += entry.reserved || 0;
94554
+ distributionMap[entry.baseUrl] = snapshot;
94596
94555
  }
94597
- return Object.entries(distributionMap).map(([baseUrl, amt]) => ({ baseUrl, amount: amt })).sort((a, b) => b.amount - a.amount);
94556
+ return Object.entries(distributionMap).map(([baseUrl, snapshot]) => ({ baseUrl, ...snapshot })).sort((a, b) => b.amount - a.amount);
94598
94557
  },
94599
94558
  saveProviderInfo: (baseUrl, info) => {
94600
94559
  const normalized = normalizeBaseUrl5(baseUrl);
@@ -94625,14 +94584,19 @@ var createStorageAdapterFromStore = (store) => ({
94625
94584
  baseUrl: normalized,
94626
94585
  key,
94627
94586
  balance: 0,
94587
+ reserved: 0,
94628
94588
  lastUsed: Date.now()
94629
94589
  });
94630
94590
  store.getState().setApiKeys(next);
94631
94591
  },
94632
- updateApiKeyBalance: (baseUrl, balance) => {
94592
+ updateApiKeyBalance: (baseUrl, balance, reserved) => {
94633
94593
  const normalized = normalizeBaseUrl5(baseUrl);
94634
94594
  const keys2 = store.getState().apiKeys;
94635
- const next = keys2.map((entry) => entry.baseUrl === normalized ? { ...entry, balance } : entry);
94595
+ const next = keys2.map((entry) => entry.baseUrl === normalized ? {
94596
+ ...entry,
94597
+ balance,
94598
+ reserved: reserved ?? entry.reserved ?? 0
94599
+ } : entry);
94636
94600
  store.getState().setApiKeys(next);
94637
94601
  },
94638
94602
  touchApiKeyLastUsed: (baseUrl) => {
@@ -94651,6 +94615,7 @@ var createStorageAdapterFromStore = (store) => ({
94651
94615
  baseUrl: entry.baseUrl,
94652
94616
  key: entry.key,
94653
94617
  balance: entry.balance,
94618
+ reserved: entry.reserved ?? 0,
94654
94619
  lastUsed: entry.lastUsed
94655
94620
  }));
94656
94621
  },
@@ -95042,7 +95007,8 @@ async function inspectSSEWebStream(stream2, onUsage, onResponseId, options) {
95042
95007
  capturedResponseId
95043
95008
  };
95044
95009
  }
95045
- var TOPUP_MARGIN = 1.2;
95010
+ var TOPUP_MARGIN = 1.4;
95011
+ var PROACTIVE_TOPUP_MIN_FRACTION = 0.21;
95046
95012
  var RoutstrClient = class {
95047
95013
  constructor(walletAdapter, storageAdapter, discoveryAdapter, alertLevel, mode = "xcashu", options = {}) {
95048
95014
  this.walletAdapter = walletAdapter;
@@ -95075,6 +95041,7 @@ var RoutstrClient = class {
95075
95041
  requestResponseLogSink;
95076
95042
  userCacheSecret;
95077
95043
  tinfoilCacheSecretPath;
95044
+ _inflightTopups = /* @__PURE__ */ new Map;
95078
95045
  getMode() {
95079
95046
  return this.mode;
95080
95047
  }
@@ -95214,10 +95181,21 @@ var RoutstrClient = class {
95214
95181
  const {
95215
95182
  token,
95216
95183
  tokenBalance,
95184
+ tokenReserved,
95217
95185
  tokenBalanceUnit,
95218
95186
  tokenBalanceUnknown,
95219
95187
  selectedMintUrl
95220
95188
  } = spendResult;
95189
+ this._spinOffTopupIfNeeded({
95190
+ token,
95191
+ baseUrl,
95192
+ mintUrl,
95193
+ requiredSats,
95194
+ tokenBalance,
95195
+ tokenReserved,
95196
+ tokenBalanceUnit,
95197
+ tokenBalanceUnknown
95198
+ });
95221
95199
  const finalHeaders = this._withAuthAndTinfoilHeaders(baseHeaders, token, tinfoilEnabled, modelId);
95222
95200
  const response = await this._makeRequest({
95223
95201
  path: requestPath,
@@ -95524,12 +95502,12 @@ var RoutstrClient = class {
95524
95502
  if (!balanceValidated) {
95525
95503
  tryNextProvider = true;
95526
95504
  } else {
95527
- const topupResult = await this.balanceManager.topUp({
95505
+ const topupResult = await this._topUpOnce(`${baseUrl}:${params.token}`, () => this.balanceManager.topUp({
95528
95506
  mintUrl,
95529
95507
  baseUrl,
95530
95508
  amount: topupAmount * TOPUP_MARGIN,
95531
95509
  token: params.token
95532
- });
95510
+ }));
95533
95511
  this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: Topup result for ${baseUrl}: success=${topupResult.success}, message=${topupResult.message}`);
95534
95512
  if (!topupResult.success) {
95535
95513
  const message = topupResult.message || "";
@@ -95574,6 +95552,7 @@ var RoutstrClient = class {
95574
95552
  tryNextProvider = true;
95575
95553
  } else {
95576
95554
  const latestTokenBalance = latestBalanceInfo.balanceUnknown ? undefined : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1000 : latestBalanceInfo.amount;
95555
+ const latestReservedBalance = latestBalanceInfo.balanceUnknown ? undefined : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.reserved / 1000 : latestBalanceInfo.reserved;
95577
95556
  if (latestBalanceInfo.apiKey) {
95578
95557
  const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
95579
95558
  if (storedApiKeyEntry?.key !== latestBalanceInfo.apiKey) {
@@ -95585,7 +95564,7 @@ var RoutstrClient = class {
95585
95564
  retryToken = latestBalanceInfo.apiKey;
95586
95565
  }
95587
95566
  if (latestTokenBalance !== undefined && latestTokenBalance >= 0) {
95588
- this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
95567
+ this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance, latestReservedBalance);
95589
95568
  this.storageAdapter.touchApiKeyLastUsed(baseUrl);
95590
95569
  }
95591
95570
  }
@@ -95839,13 +95818,14 @@ var RoutstrClient = class {
95839
95818
  const latestBalanceInfo = await this.balanceManager.getTokenBalance(token, baseUrl);
95840
95819
  this._log("DEBUG", "LATEST Balance", latestBalanceInfo.amount, latestBalanceInfo.reserved, latestBalanceInfo.apiKey, baseUrl);
95841
95820
  const latestTokenBalance = latestBalanceInfo.balanceUnknown ? undefined : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1000 : latestBalanceInfo.amount;
95821
+ const latestReservedBalance = latestBalanceInfo.balanceUnknown ? undefined : latestBalanceInfo.unit === "msat" ? latestBalanceInfo.reserved / 1000 : latestBalanceInfo.reserved;
95842
95822
  const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
95843
95823
  if (storedApiKeyEntry?.key.startsWith("cashu") && latestBalanceInfo.apiKey) {
95844
95824
  this.storageAdapter.removeApiKey(baseUrl);
95845
95825
  this.storageAdapter.setApiKey(baseUrl, latestBalanceInfo.apiKey);
95846
95826
  }
95847
95827
  if (latestTokenBalance !== undefined) {
95848
- this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
95828
+ this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance, latestReservedBalance);
95849
95829
  this.storageAdapter.touchApiKeyLastUsed(baseUrl);
95850
95830
  }
95851
95831
  satsSpent = latestTokenBalance !== undefined && !initialTokenBalanceUnknown ? Math.max(0, initialTokenBalance - latestTokenBalance) : fallbackSatsSpent ?? usage?.satsCost ?? this._headerSatsCost(response) ?? 0;
@@ -95961,6 +95941,72 @@ var RoutstrClient = class {
95961
95941
  throw new InsufficientBalanceError(1, 0);
95962
95942
  }
95963
95943
  }
95944
+ _spinOffTopupIfNeeded(snapshot) {
95945
+ if (this.mode !== "apikeys" || !snapshot.token)
95946
+ return;
95947
+ if (snapshot.tokenBalanceUnknown)
95948
+ return;
95949
+ const snapshotSats = snapshot.tokenBalanceUnit === "msat" ? snapshot.tokenBalance / 1000 : snapshot.tokenBalance;
95950
+ const tokenReserved = snapshot.tokenReserved ?? 0;
95951
+ const snapshotReservedSats = snapshot.tokenBalanceUnit === "msat" ? tokenReserved / 1000 : tokenReserved;
95952
+ const snapshotAvailableSats = snapshotSats - snapshotReservedSats;
95953
+ const targetSats = snapshot.requiredSats * TOPUP_MARGIN;
95954
+ if (snapshotAvailableSats >= targetSats)
95955
+ return;
95956
+ const key = `${snapshot.baseUrl}:${snapshot.token}`;
95957
+ if (this._inflightTopups.has(key))
95958
+ return;
95959
+ this._log("DEBUG", `[RoutstrClient] _spinOffTopupIfNeeded: snapshot total=${snapshotSats} sat, reserved=${snapshotReservedSats} sat, available=${snapshotAvailableSats} sat < target=${targetSats} sat (required=${snapshot.requiredSats} x ${TOPUP_MARGIN}) for ${snapshot.baseUrl}; spinning off background topup`);
95960
+ this._topUpOnce(key, () => this._runProactiveTopup(snapshot)).catch((e13) => {
95961
+ this._log("WARN", `[RoutstrClient] _spinOffTopupIfNeeded: background topup crashed for ${snapshot.baseUrl}`, e13);
95962
+ });
95963
+ }
95964
+ async _runProactiveTopup(snapshot) {
95965
+ try {
95966
+ const tokenReserved = snapshot.tokenReserved ?? 0;
95967
+ const snapshotSats = snapshot.tokenBalanceUnit === "msat" ? snapshot.tokenBalance / 1000 : snapshot.tokenBalance;
95968
+ const reservedSats = snapshot.tokenBalanceUnit === "msat" ? tokenReserved / 1000 : tokenReserved;
95969
+ const availableSats = snapshotSats - reservedSats;
95970
+ const targetSats = snapshot.requiredSats * TOPUP_MARGIN;
95971
+ const shortfall = Math.max(0, targetSats - availableSats);
95972
+ if (shortfall <= 0) {
95973
+ this._log("DEBUG", `[RoutstrClient] _runProactiveTopup: snapshot for ${snapshot.baseUrl} is sufficient (available=${availableSats} sat >= target=${targetSats} sat); no topup needed`);
95974
+ return {
95975
+ success: true,
95976
+ message: "proactive topup skipped: balance sufficient on snapshot"
95977
+ };
95978
+ }
95979
+ const topupAmount = Math.max(shortfall, PROACTIVE_TOPUP_MIN_FRACTION * snapshot.requiredSats);
95980
+ const result = await this.balanceManager.topUp({
95981
+ mintUrl: snapshot.mintUrl,
95982
+ baseUrl: snapshot.baseUrl,
95983
+ amount: topupAmount,
95984
+ token: snapshot.token
95985
+ });
95986
+ this._log("DEBUG", `[RoutstrClient] _runProactiveTopup: result for ${snapshot.baseUrl}: success=${result.success}, amount=${topupAmount}, message=${result.message}`);
95987
+ if (result.success) {
95988
+ const added = result.toppedUpAmount ?? topupAmount;
95989
+ this.storageAdapter.updateApiKeyBalance(snapshot.baseUrl, Math.floor(snapshotSats + added), Math.floor(reservedSats));
95990
+ }
95991
+ return result;
95992
+ } catch (e13) {
95993
+ this._log("WARN", `[RoutstrClient] _runProactiveTopup: failed for ${snapshot.baseUrl}`, e13);
95994
+ return {
95995
+ success: false,
95996
+ message: `proactive topup failed: ${e13 instanceof Error ? e13.message : String(e13)}`
95997
+ };
95998
+ }
95999
+ }
96000
+ _topUpOnce(key, start) {
96001
+ const existing = this._inflightTopups.get(key);
96002
+ if (existing)
96003
+ return existing;
96004
+ const promise2 = start().finally(() => {
96005
+ this._inflightTopups.delete(key);
96006
+ });
96007
+ this._inflightTopups.set(key, promise2);
96008
+ return promise2;
96009
+ }
95964
96010
  async _spendToken(params) {
95965
96011
  const { mintUrl, amount, baseUrl, excludeMints = [] } = params;
95966
96012
  this._log("DEBUG", `[RoutstrClient] _spendToken: mode=${this.mode}, amount=${amount}, baseUrl=${baseUrl}, mintUrl=${mintUrl}`);
@@ -96018,27 +96064,31 @@ var RoutstrClient = class {
96018
96064
  this._log("DEBUG", `[RoutstrClient] _spendToken: Using existing API key for ${baseUrl}, key preview: ${parentApiKey.key}`);
96019
96065
  }
96020
96066
  let tokenBalance = 0;
96067
+ let tokenReserved = 0;
96021
96068
  let tokenBalanceUnit = "sat";
96022
96069
  let tokenBalanceUnknown = false;
96023
96070
  const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
96024
96071
  const distributionForBaseUrl = apiKeyDistribution.find((d) => d.baseUrl === baseUrl);
96025
96072
  if (distributionForBaseUrl) {
96026
96073
  tokenBalance = distributionForBaseUrl.amount;
96074
+ tokenReserved = distributionForBaseUrl.reserved ?? 0;
96027
96075
  }
96028
96076
  if (tokenBalance === 0 && parentApiKey) {
96029
96077
  try {
96030
96078
  const balanceInfo = await this.balanceManager.getTokenBalance(parentApiKey.key, baseUrl);
96031
96079
  tokenBalance = balanceInfo.amount;
96080
+ tokenReserved = balanceInfo.reserved;
96032
96081
  tokenBalanceUnit = balanceInfo.unit;
96033
96082
  tokenBalanceUnknown = Boolean(balanceInfo.balanceUnknown);
96034
96083
  } catch (e13) {
96035
96084
  this._log("WARN", "Could not get initial API key balance:", e13);
96036
96085
  }
96037
96086
  }
96038
- this._log("DEBUG", `[RoutstrClient] _spendToken: Returning token with balance=${tokenBalance} ${tokenBalanceUnit}`);
96087
+ this._log("DEBUG", `[RoutstrClient] _spendToken: Returning token with balance=${tokenBalance}, reserved=${tokenReserved} ${tokenBalanceUnit}`);
96039
96088
  return {
96040
96089
  token: parentApiKey?.key ?? "",
96041
96090
  tokenBalance,
96091
+ tokenReserved,
96042
96092
  tokenBalanceUnit,
96043
96093
  tokenBalanceUnknown,
96044
96094
  selectedMintUrl
@@ -96061,6 +96111,7 @@ var RoutstrClient = class {
96061
96111
  return {
96062
96112
  token: spendResult.token,
96063
96113
  tokenBalance: spendResult.balance,
96114
+ tokenReserved: 0,
96064
96115
  tokenBalanceUnit: spendResult.unit ?? "sat",
96065
96116
  tokenBalanceUnknown: false,
96066
96117
  selectedMintUrl: spendResult.selectedMintUrl
@@ -96341,6 +96392,7 @@ var ModelManager2 = class _ModelManager2 {
96341
96392
  this.logger = (config2.logger ?? consoleLogger2).child("ModelManager");
96342
96393
  this.eventStoreDbPath = config2.eventStoreDbPath;
96343
96394
  this.persistentEventDatabaseFactory = config2.persistentEventDatabaseFactory;
96395
+ this.eventStore = config2.eventStore ?? null;
96344
96396
  }
96345
96397
  adapter;
96346
96398
  cacheTTL;
@@ -96354,6 +96406,8 @@ var ModelManager2 = class _ModelManager2 {
96354
96406
  providerNodePubkeysByUrl = /* @__PURE__ */ new Map;
96355
96407
  relayPool = null;
96356
96408
  eventStore = null;
96409
+ memoryEventStore = new EventStore({ verifyEvent });
96410
+ queryLastUpdate = /* @__PURE__ */ new Map;
96357
96411
  eventStoreDb = null;
96358
96412
  eventStoreInitPromise = null;
96359
96413
  eventStoreDbPath;
@@ -96362,17 +96416,16 @@ var ModelManager2 = class _ModelManager2 {
96362
96416
  return this.adapter.getBaseUrlsList();
96363
96417
  }
96364
96418
  async ensureEventStore() {
96365
- if (!this.eventStoreDbPath)
96366
- return null;
96367
96419
  if (this.eventStore)
96368
96420
  return this.eventStore;
96421
+ if (!this.eventStoreDbPath)
96422
+ return null;
96369
96423
  if (!this.eventStoreInitPromise) {
96370
96424
  this.eventStoreInitPromise = (async () => {
96371
96425
  try {
96372
96426
  const db = await this.createPersistentEventDatabase();
96373
96427
  this.eventStoreDb = db;
96374
96428
  this.eventStore = new EventStore({ database: db });
96375
- this.initializeEventStoreMetadata();
96376
96429
  this.logger.log(`Persistent event store initialized at ${this.eventStoreDbPath}`);
96377
96430
  return this.eventStore;
96378
96431
  } catch (error53) {
@@ -96401,34 +96454,40 @@ var ModelManager2 = class _ModelManager2 {
96401
96454
  this.eventStoreDb = null;
96402
96455
  this.eventStoreInitPromise = null;
96403
96456
  }
96404
- initializeEventStoreMetadata() {
96405
- this.eventStoreDb?.db?.exec(`CREATE TABLE IF NOT EXISTS routstr_event_cache_metadata (
96406
- event_id TEXT PRIMARY KEY,
96407
- fetched_at INTEGER NOT NULL
96408
- )`);
96409
- }
96410
- markEventFetched(event7, fetchedAt = Date.now()) {
96411
- const db = this.eventStoreDb?.db;
96412
- if (!db)
96413
- return;
96414
- db.prepare(`INSERT INTO routstr_event_cache_metadata (event_id, fetched_at)
96415
- VALUES (?, ?)
96416
- ON CONFLICT(event_id) DO UPDATE SET fetched_at = excluded.fetched_at`).run?.(event7.id, fetchedAt);
96417
- }
96418
- getEventFetchedAt(event7) {
96419
- const db = this.eventStoreDb?.db;
96420
- if (!db)
96421
- return;
96422
- const row = db.prepare(`SELECT fetched_at FROM routstr_event_cache_metadata WHERE event_id = ?`).get?.(event7.id);
96423
- return typeof row?.fetched_at === "number" ? row.fetched_at : undefined;
96424
- }
96425
- async getCachedNostrEvents(filter16, forceRefresh = false) {
96426
- const eventStore = await this.ensureEventStore();
96427
- if (forceRefresh)
96428
- return [];
96429
- if (!eventStore)
96430
- return [];
96431
- return eventStore.getTimeline(filter16).filter((event7) => !this.isFutureDated(event7));
96457
+ async getNostrEvents(filter16, forceRefresh = false, onEvent) {
96458
+ const eventStore = await this.ensureEventStore() ?? this.memoryEventStore;
96459
+ const query2 = JSON.stringify(filter16);
96460
+ const lastUpdate = this.queryLastUpdate.get(query2) ?? this.adapter.getNostrQueryLastUpdate?.()?.[query2];
96461
+ const now3 = Date.now();
96462
+ const cacheValid = typeof lastUpdate === "number" && lastUpdate <= now3 && now3 - lastUpdate <= this.cacheTTL;
96463
+ const received = /* @__PURE__ */ new Set;
96464
+ const cached2 = forceRefresh ? [] : eventStore.getTimeline(filter16).filter((event7) => this.isNostrEventTrustworthy(event7));
96465
+ if (forceRefresh || cached2.length === 0 || !cacheValid) {
96466
+ await this.collectNostrEvents(filter16, this.getNostrRelays(), NOSTR_QUERY_TIMEOUT_MS2, (event7) => {
96467
+ const stored = eventStore.add(event7);
96468
+ if (!stored || stored.id !== event7.id)
96469
+ return;
96470
+ received.add(event7.id);
96471
+ onEvent?.(event7);
96472
+ });
96473
+ if (received.size > 0) {
96474
+ this.queryLastUpdate.set(query2, Date.now());
96475
+ this.adapter.setNostrQueryLastUpdate?.({
96476
+ ...this.adapter.getNostrQueryLastUpdate?.(),
96477
+ ...Object.fromEntries(this.queryLastUpdate)
96478
+ });
96479
+ }
96480
+ const events = eventStore.getTimeline(filter16).filter((event7) => this.isNostrEventTrustworthy(event7));
96481
+ for (const event7 of events) {
96482
+ if (!received.has(event7.id))
96483
+ onEvent?.(event7);
96484
+ }
96485
+ return events;
96486
+ }
96487
+ for (const event7 of cached2) {
96488
+ onEvent?.(event7);
96489
+ }
96490
+ return cached2;
96432
96491
  }
96433
96492
  async refreshNostrEvents() {
96434
96493
  const eventStore = await this.ensureEventStore();
@@ -96436,23 +96495,12 @@ var ModelManager2 = class _ModelManager2 {
96436
96495
  this.logger.warn("refreshNostrEvents: no event store configured, skipping");
96437
96496
  return;
96438
96497
  }
96439
- const relays3 = this.getNostrRelays();
96440
- const timeoutMs = 5000;
96441
- await this.fetchLiveIntoStore({ kinds: [38421], limit: 100 }, relays3, timeoutMs);
96442
- await this.fetchLiveIntoStore({ kinds: [38425], limit: 500, authors: [this.routstrPubkey] }, relays3, timeoutMs);
96443
- await this.fetchLiveIntoStore({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, relays3, timeoutMs);
96498
+ await this.getNostrEvents({ kinds: [38421], limit: 100 }, true);
96499
+ await this.getNostrEvents({ kinds: [38425], limit: 500, authors: [this.routstrPubkey] }, true);
96500
+ await this.getNostrEvents({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, true);
96444
96501
  this.logger.log("refreshNostrEvents: live fetch complete");
96445
96502
  await this.syncReviewedProvidersFromNostr();
96446
96503
  }
96447
- async fetchLiveIntoStore(filter16, relays3, timeoutMs) {
96448
- const eventStore = await this.ensureEventStore();
96449
- if (!eventStore)
96450
- return;
96451
- await this.collectNostrEvents(filter16, relays3, timeoutMs, (event7) => {
96452
- eventStore.add(event7);
96453
- this.markEventFetched(event7);
96454
- });
96455
- }
96456
96504
  static async init(adapter, config2 = {}, options = {}) {
96457
96505
  const manager = new _ModelManager2(adapter, config2);
96458
96506
  const torMode = options.torMode ?? false;
@@ -96471,7 +96519,7 @@ var ModelManager2 = class _ModelManager2 {
96471
96519
  const filteredCachedUrls = this.filterBaseUrlsForTor(cachedUrls, torMode);
96472
96520
  await Promise.all([
96473
96521
  this.fetchRoutstr21Models(forceRefresh),
96474
- this.syncReviewedProvidersFromNostr(filteredCachedUrls, this.providerNodePubkeysByUrl, forceRefresh)
96522
+ this.syncReviewedProvidersFromNostr(filteredCachedUrls)
96475
96523
  ]);
96476
96524
  return filteredCachedUrls;
96477
96525
  }
@@ -96553,7 +96601,6 @@ var ModelManager2 = class _ModelManager2 {
96553
96601
  });
96554
96602
  }
96555
96603
  async bootstrapFromNostr(kind, torMode, forceRefresh = false, options = {}) {
96556
- const relays3 = this.getNostrRelays();
96557
96604
  const bases = /* @__PURE__ */ new Set;
96558
96605
  this.providerNodePubkeysByUrl = /* @__PURE__ */ new Map;
96559
96606
  const excluded = new Set(this.excludeProviderUrls.map((url4) => this.normalizeUrl(url4)));
@@ -96609,19 +96656,7 @@ var ModelManager2 = class _ModelManager2 {
96609
96656
  }
96610
96657
  }
96611
96658
  };
96612
- const cached2 = await this.getCachedNostrEvents({ kinds: [kind] }, forceRefresh);
96613
- if (cached2.length > 0) {
96614
- this.logger.log(`Using ${cached2.length} cached kind ${kind} events from persistent store`);
96615
- for (const event7 of cached2) {
96616
- collectFromEvent(event7);
96617
- }
96618
- } else {
96619
- await this.collectNostrEvents({ kinds: [kind], limit: 100 }, relays3, NOSTR_QUERY_TIMEOUT_MS2, (event7) => {
96620
- this.eventStore?.add(event7);
96621
- this.markEventFetched(event7);
96622
- collectFromEvent(event7);
96623
- });
96624
- }
96659
+ await this.getNostrEvents({ kinds: [kind], limit: 100 }, forceRefresh, collectFromEvent);
96625
96660
  for (const url4 of this.includeProviderUrls) {
96626
96661
  const normalized = this.normalizeUrl(url4);
96627
96662
  if (!torMode || normalized.includes(".onion")) {
@@ -96670,25 +96705,25 @@ var ModelManager2 = class _ModelManager2 {
96670
96705
  throw new ProviderBootstrapError2([], `Provider bootstrap failed: ${e13}`);
96671
96706
  }
96672
96707
  }
96673
- async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes = this.providerNodePubkeysByUrl, forceRefresh = false) {
96708
+ async syncReviewedProvidersFromNostr(baseUrls = this.adapter.getBaseUrlsList(), providerNodes, forceRefresh = false) {
96674
96709
  if (baseUrls.length === 0)
96675
96710
  return null;
96676
96711
  if (!this.adapter.setDisabledProviders) {
96677
96712
  this.logger.warn("NostrReviews: adapter does not support setDisabledProviders; skipping provider disable sync");
96678
96713
  return null;
96679
96714
  }
96680
- if (providerNodes.size === 0) {
96681
- providerNodes = await this.rebuildProviderNodesFromStore();
96715
+ if (!providerNodes || providerNodes.size === 0) {
96716
+ providerNodes = await this.rebuildProviderNodesFromStore(forceRefresh);
96717
+ if (!forceRefresh && providerNodes.size > 0 && baseUrls.some((url4) => !providerNodes.has(this.normalizeUrl(url4)))) {
96718
+ providerNodes = await this.rebuildProviderNodesFromStore(true);
96719
+ }
96682
96720
  this.providerNodePubkeysByUrl = providerNodes;
96683
96721
  }
96684
96722
  const reviewLabels = await this.fetchReviewLabels(forceRefresh);
96685
96723
  return this.applyReviewDisables(baseUrls, providerNodes, reviewLabels);
96686
96724
  }
96687
- async rebuildProviderNodesFromStore() {
96725
+ async rebuildProviderNodesFromStore(forceRefresh) {
96688
96726
  const map23 = /* @__PURE__ */ new Map;
96689
- const eventStore = await this.ensureEventStore();
96690
- if (!eventStore)
96691
- return map23;
96692
96727
  const addNode = (url4, pubkey2) => {
96693
96728
  if (!pubkey2)
96694
96729
  return;
@@ -96697,7 +96732,7 @@ var ModelManager2 = class _ModelManager2 {
96697
96732
  existing.add(pubkey2);
96698
96733
  map23.set(normalized, existing);
96699
96734
  };
96700
- const events = eventStore.getTimeline({ kinds: [38421] });
96735
+ const events = await this.getNostrEvents({ kinds: [38421], limit: 100 }, forceRefresh);
96701
96736
  for (const event7 of events) {
96702
96737
  const eventUrls = [];
96703
96738
  for (const tag2 of event7.tags) {
@@ -96715,7 +96750,10 @@ var ModelManager2 = class _ModelManager2 {
96715
96750
  const content3 = JSON.parse(event7.content);
96716
96751
  const providers = Array.isArray(content3) ? content3 : content3.providers || [];
96717
96752
  for (const p of providers) {
96718
- const endpoints = this.getProviderEndpoints(p, false);
96753
+ const endpoints = [
96754
+ ...this.getProviderEndpoints(p, false),
96755
+ ...this.getProviderEndpoints(p, true)
96756
+ ];
96719
96757
  for (const endpoint of endpoints) {
96720
96758
  addNode(endpoint, p?.pubkey || event7.pubkey);
96721
96759
  }
@@ -96740,23 +96778,9 @@ var ModelManager2 = class _ModelManager2 {
96740
96778
  }
96741
96779
  }
96742
96780
  };
96743
- const cached2 = await this.getCachedNostrEvents({ kinds: [38425], authors: [this.routstrPubkey] }, forceRefresh);
96744
- if (cached2.length > 0) {
96745
- this.logger.log(`Using ${cached2.length} cached kind 38425 review events from persistent store`);
96746
- for (const event7 of cached2) {
96747
- collectFromEvent(event7);
96748
- }
96749
- } else {
96750
- await this.collectNostrEvents({
96751
- kinds: [38425],
96752
- limit: 500,
96753
- authors: [this.routstrPubkey]
96754
- }, this.getNostrRelays(), NOSTR_QUERY_TIMEOUT_MS2, (event7) => {
96755
- this.eventStore?.add(event7);
96756
- this.markEventFetched(event7);
96757
- collectFromEvent(event7);
96758
- });
96759
- }
96781
+ const events = await this.getNostrEvents({ kinds: [38425], limit: 500, authors: [this.routstrPubkey] }, forceRefresh);
96782
+ for (const event7 of events)
96783
+ collectFromEvent(event7);
96760
96784
  const labels = /* @__PURE__ */ new Map;
96761
96785
  for (const [node, event7] of latestByNode) {
96762
96786
  const label = event7.tags.find((tag2) => tag2[0] === "t" && typeof tag2[1] === "string" && tag2[1])?.[1]?.toLowerCase();
@@ -96970,24 +96994,8 @@ var ModelManager2 = class _ModelManager2 {
96970
96994
  return cachedModels;
96971
96995
  }
96972
96996
  }
96973
- const relays3 = this.getNostrRelays();
96974
- const cached2 = await this.getCachedNostrEvents({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, forceRefresh);
96975
- let event7 = null;
96976
- if (cached2.length === 0) {
96977
- await this.collectNostrEvents({
96978
- kinds: [38423],
96979
- "#d": ["routstr-21-models"],
96980
- authors: [this.routstrModelsPubkey]
96981
- }, relays3, NOSTR_QUERY_TIMEOUT_MS2, (e13) => {
96982
- this.eventStore?.add(e13);
96983
- this.markEventFetched(e13);
96984
- if (!event7 || e13.created_at > event7.created_at)
96985
- event7 = e13;
96986
- });
96987
- } else {
96988
- this.logger.log(`Using ${cached2.length} cached kind 38423 events from persistent store`);
96989
- event7 = cached2.reduce((latest, e13) => e13.created_at > latest.created_at ? e13 : latest, cached2[0]);
96990
- }
96997
+ const events = await this.getNostrEvents({ kinds: [38423], "#d": ["routstr-21-models"], authors: [this.routstrModelsPubkey] }, forceRefresh);
96998
+ const event7 = events.reduce((latest, event22) => !latest || event22.created_at > latest.created_at ? event22 : latest, null);
96991
96999
  if (!event7) {
96992
97000
  return cachedModels.length > 0 ? cachedModels : [];
96993
97001
  }
@@ -97141,7 +97149,9 @@ function parseArgs(argv) {
97141
97149
  const portFlagIndex = argv.findIndex((arg) => arg === "--port");
97142
97150
  const hostFlagIndex = argv.findIndex((arg) => arg === "--host");
97143
97151
  const providerFlagIndex = argv.findIndex((arg) => arg === "--provider" || arg === "-p");
97144
- const port = portFlagIndex !== -1 ? Number.parseInt(argv[portFlagIndex + 1] || "8008", 10) : 8008;
97152
+ const portValue = portFlagIndex !== -1 ? argv[portFlagIndex + 1] : undefined;
97153
+ const parsedPort = portValue ? Number.parseInt(portValue, 10) : Number.NaN;
97154
+ const port = Number.isInteger(parsedPort) ? parsedPort : null;
97145
97155
  const hostValue = hostFlagIndex !== -1 ? argv[hostFlagIndex + 1] : undefined;
97146
97156
  const host = hostValue?.trim() || null;
97147
97157
  const providerValue = providerFlagIndex !== -1 ? argv[providerFlagIndex + 1] : undefined;
@@ -97241,6 +97251,7 @@ function saveDaemonConfig(config2) {
97241
97251
 
97242
97252
  // node_modules/@routstr/sdk/dist/storage/bun.mjs
97243
97253
  var SDK_STORAGE_KEYS2 = {
97254
+ NOSTR_QUERY_LAST_UPDATE: "nostr_query_last_update",
97244
97255
  MODELS_FROM_ALL_PROVIDERS: "modelsFromAllProviders",
97245
97256
  LAST_USED_MODEL: "lastUsedModel",
97246
97257
  BASE_URLS_LIST: "base_urls_list",
@@ -97350,6 +97361,7 @@ var createShardedDiscoveryAdapter = async (options) => {
97350
97361
  }
97351
97362
  await driver.setItem(MIGRATION_MARKER_KEY2, true);
97352
97363
  const [
97364
+ rawNostrQueryLastUpdate,
97353
97365
  rawMints,
97354
97366
  rawInfo,
97355
97367
  lastUsedModel,
@@ -97361,6 +97373,7 @@ var createShardedDiscoveryAdapter = async (options) => {
97361
97373
  rawRoutstr21Models,
97362
97374
  lastRoutstr21ModelsUpdate
97363
97375
  ] = await Promise.all([
97376
+ driver.getItem(SDK_STORAGE_KEYS2.NOSTR_QUERY_LAST_UPDATE, {}).catch(() => ({})),
97364
97377
  driver.getItem(SDK_STORAGE_KEYS2.MINTS_FROM_ALL_PROVIDERS, {}),
97365
97378
  driver.getItem(SDK_STORAGE_KEYS2.INFO_FROM_ALL_PROVIDERS, {}),
97366
97379
  driver.getItem(SDK_STORAGE_KEYS2.LAST_USED_MODEL, null),
@@ -97420,6 +97433,7 @@ var createShardedDiscoveryAdapter = async (options) => {
97420
97433
  entry
97421
97434
  ]));
97422
97435
  let _lastUsedModel = lastUsedModel;
97436
+ let nostrQueryLastUpdate = rawNostrQueryLastUpdate;
97423
97437
  let _disabledProviders = rawDisabled.map(normalizeBaseUrl42);
97424
97438
  let _manuallyDisabledProviders = rawManuallyDisabled.map(normalizeBaseUrl42);
97425
97439
  let _manuallyEnabledProviders = rawManuallyEnabled.map(normalizeBaseUrl42);
@@ -97431,6 +97445,11 @@ var createShardedDiscoveryAdapter = async (options) => {
97431
97445
  driver.setItem(PROVIDER_INDEX_KEY, [...providerIndex]);
97432
97446
  };
97433
97447
  return {
97448
+ getNostrQueryLastUpdate: () => nostrQueryLastUpdate,
97449
+ setNostrQueryLastUpdate: (timestamps) => {
97450
+ nostrQueryLastUpdate = timestamps;
97451
+ driver.setItem(SDK_STORAGE_KEYS2.NOSTR_QUERY_LAST_UPDATE, timestamps).catch((error53) => console.warn("Failed to persist discovery query times", error53));
97452
+ },
97434
97453
  getCachedModels: () => {
97435
97454
  const result = {};
97436
97455
  for (const [baseUrl, models] of modelsByBaseUrl.entries()) {
@@ -99117,7 +99136,7 @@ function Gr2(e22) {
99117
99136
  return {
99118
99137
  unit: t.unit || "sat",
99119
99138
  mint: t.mint,
99120
- amount: q(t.proofs),
99139
+ amount: q2(t.proofs),
99121
99140
  ...t.memo && { memo: t.memo },
99122
99141
  incompleteProofs: t.proofs.map((e23) => {
99123
99142
  let { id: t2, ...n } = e23;
@@ -99202,7 +99221,7 @@ function Zr2(e22) {
99202
99221
  throw new _2("Mint URL path must not contain percent-encoded characters");
99203
99222
  return t.href.replace(/\/+$/, "");
99204
99223
  }
99205
- function q(e22) {
99224
+ function q2(e22) {
99206
99225
  return k2.sum(e22.map((e23) => e23.amount));
99207
99226
  }
99208
99227
  function ri2(e22, t, r) {
@@ -101691,10 +101710,10 @@ function invert6(number13, modulo) {
101691
101710
  let b2 = modulo;
101692
101711
  let x3 = _0n30, y2 = _1n30, u = _1n30, v4 = _0n30;
101693
101712
  while (a !== _0n30) {
101694
- const q2 = b2 / a;
101713
+ const q3 = b2 / a;
101695
101714
  const r = b2 % a;
101696
- const m = x3 - u * q2;
101697
- const n = y2 - v4 * q2;
101715
+ const m = x3 - u * q3;
101716
+ const n = y2 - v4 * q3;
101698
101717
  b2 = a, a = r, x3 = u, y2 = v4, u = m, v4 = n;
101699
101718
  }
101700
101719
  const gcd5 = b2;
@@ -102651,14 +102670,14 @@ function weierstrass6(curveDef) {
102651
102670
  if (!isWithinCurveOrder(k3))
102652
102671
  return;
102653
102672
  const ik = invN(k3);
102654
- const q2 = Point5.BASE.multiply(k3).toAffine();
102655
- const r = modN4(q2.x);
102673
+ const q3 = Point5.BASE.multiply(k3).toAffine();
102674
+ const r = modN4(q3.x);
102656
102675
  if (r === _0n32)
102657
102676
  return;
102658
102677
  const s = modN4(ik * modN4(m + r * d));
102659
102678
  if (s === _0n32)
102660
102679
  return;
102661
- let recovery = (q2.x === r ? 0 : 2) | Number(q2.y & _1n32);
102680
+ let recovery = (q3.x === r ? 0 : 2) | Number(q3.y & _1n32);
102662
102681
  let normS = s;
102663
102682
  if (lowS && isBiggerThanHalfOrder(s)) {
102664
102683
  normS = normalizeS(s);
@@ -112433,7 +112452,7 @@ function createCocodClient(options = {}) {
112433
112452
  return message;
112434
112453
  },
112435
112454
  async receiveBolt11(amount, mintUrl) {
112436
- return post("/receive/bolt11", { amount, mintUrl });
112455
+ return { invoice: await post("/receive/bolt11", { amount, mintUrl }) };
112437
112456
  },
112438
112457
  async sendCashu(amount, mintUrl) {
112439
112458
  return post("/send/cashu", { amount, mintUrl });
@@ -112538,7 +112557,7 @@ function startAutoRefillLoop(cocod, getWallet, getConfig, intervalMs = 5000) {
112538
112557
  return;
112539
112558
  }
112540
112559
  logger5.log(`[auto-refill] Creating BOLT-11 invoice for ${config2.amount} sats via ${mintUrl}...`);
112541
- const invoice = await cocod.receiveBolt11(config2.amount, mintUrl);
112560
+ const { invoice } = await cocod.receiveBolt11(config2.amount, mintUrl);
112542
112561
  logger5.log(`[auto-refill] Paying invoice via NWC...`);
112543
112562
  const currentWallet = getWallet();
112544
112563
  if (!currentWallet?.service) {
@@ -112691,7 +112710,7 @@ async function createWalletAdapter(options = {}) {
112691
112710
  logger5.log("[nwc] Could not retrieve initial balance");
112692
112711
  }
112693
112712
  logger5.log(`[nwc] Creating ${amount}-sat Lightning invoice via cocod...`);
112694
- const invoice = await client4.receiveBolt11(amount, mintUrl);
112713
+ const { invoice } = await client4.receiveBolt11(amount, mintUrl);
112695
112714
  logger5.log(`[nwc] Invoice: ${invoice}`);
112696
112715
  logger5.log("[nwc] Paying invoice via NWC...");
112697
112716
  const { preimage, fees_paid } = await wallet.payInvoice(invoice);
@@ -113958,10 +113977,10 @@ var radixBaseN = (BASE, GROUP) => ({
113958
113977
  let carry = 0;
113959
113978
  for (let i8 = pos;i8 < nlimbs; i8++) {
113960
113979
  const cur = carry * 65536 + limbs[i8];
113961
- const q2 = Math.floor(cur / GROUP);
113962
- carry = cur - q2 * GROUP;
113963
- limbs[i8] = q2;
113964
- if (q2 === 0 && i8 === pos)
113980
+ const q3 = Math.floor(cur / GROUP);
113981
+ carry = cur - q3 * GROUP;
113982
+ limbs[i8] = q3;
113983
+ if (q3 === 0 && i8 === pos)
113965
113984
  pos++;
113966
113985
  }
113967
113986
  groups.push(carry);
@@ -114301,9 +114320,9 @@ function invert7(number15, modulo) {
114301
114320
  let b2 = modulo;
114302
114321
  let x3 = _0n35, u = _1n35;
114303
114322
  while (a !== _0n35) {
114304
- const q2 = b2 / a;
114305
- const r = b2 - a * q2;
114306
- const m = x3 - u * q2;
114323
+ const q3 = b2 / a;
114324
+ const r = b2 - a * q3;
114325
+ const m = x3 - u * q3;
114307
114326
  b2 = a, a = r, x3 = u, u = m;
114308
114327
  }
114309
114328
  const gcd6 = b2;
@@ -115908,8 +115927,8 @@ function ecdsa3(Point6, hash10, ecdsaOpts = {}) {
115908
115927
  const k3 = bits2int(kBytes);
115909
115928
  if (!Fn4.isValidNot0(k3))
115910
115929
  return;
115911
- const q2 = Point6.BASE.multiply(k3).toAffine();
115912
- const r = Fn4.create(q2.x);
115930
+ const q3 = Point6.BASE.multiply(k3).toAffine();
115931
+ const r = Fn4.create(q3.x);
115913
115932
  if (r === _0n38)
115914
115933
  return;
115915
115934
  let s;
@@ -115925,7 +115944,7 @@ function ecdsa3(Point6, hash10, ecdsaOpts = {}) {
115925
115944
  }
115926
115945
  if (s === _0n38)
115927
115946
  return;
115928
- let recovery = getRecoveryBit(q2.x, q2.y, r);
115947
+ let recovery = getRecoveryBit(q3.x, q3.y, r);
115929
115948
  let normS = s;
115930
115949
  if (lowS && isBiggerThanHalfOrder(s)) {
115931
115950
  normS = Fn4.neg(s);
@@ -115965,11 +115984,11 @@ function ecdsa3(Point6, hash10, ecdsaOpts = {}) {
115965
115984
  const R = Point6.BASE.mulAddUnsafe(u1, P2, u2);
115966
115985
  if (R.is0())
115967
115986
  return false;
115968
- const q2 = R.toAffine();
115969
- const v4 = Fn4.create(q2.x);
115987
+ const q3 = R.toAffine();
115988
+ const v4 = Fn4.create(q3.x);
115970
115989
  if (v4 !== r)
115971
115990
  return false;
115972
- if (format === "recovered" && sig.recovery !== getRecoveryBit(q2.x, q2.y, r))
115991
+ if (format === "recovered" && sig.recovery !== getRecoveryBit(q3.x, q3.y, r))
115973
115992
  return false;
115974
115993
  return true;
115975
115994
  } catch (e29) {
@@ -118280,17 +118299,17 @@ function cr2(t) {
118280
118299
  };
118281
118300
  return t.d && (r.memo = t.d), r;
118282
118301
  }
118283
- function lr2(e31, t) {
118302
+ function lr(e31, t) {
118284
118303
  let n = (t ?? []).map((e32) => typeof e32 == "string" ? e32 : e32.id), r = dr2(Or2(e31));
118285
- return n && (r.proofs = Sr2(r.proofs, n)), r;
118304
+ return n && (r.proofs = Sr(r.proofs, n)), r;
118286
118305
  }
118287
- function ur(e31) {
118306
+ function ur2(e31) {
118288
118307
  e31 = Or2(e31);
118289
118308
  let t = dr2(e31);
118290
118309
  return {
118291
118310
  unit: t.unit || "sat",
118292
118311
  mint: t.mint,
118293
- amount: q2(t.proofs),
118312
+ amount: q3(t.proofs),
118294
118313
  ...t.memo && { memo: t.memo },
118295
118314
  incompleteProofs: t.proofs.map((e32) => ({
118296
118315
  secret: e32.secret,
@@ -118356,7 +118375,7 @@ function K5(...e31) {
118356
118375
  function gr2(e31) {
118357
118376
  return e31.replace(/\/$/, "");
118358
118377
  }
118359
- function q2(e31) {
118378
+ function q3(e31) {
118360
118379
  return D3.sum(e31.map((e32) => e32.amount)).toNumber();
118361
118380
  }
118362
118381
  var vr2 = class {
@@ -118415,7 +118434,7 @@ function br(e31) {
118415
118434
  return delete t.dleq, t;
118416
118435
  });
118417
118436
  }
118418
- function Sr2(e31, n) {
118437
+ function Sr(e31, n) {
118419
118438
  let r = (n ?? []).map((e32) => typeof e32 == "string" ? e32 : e32.id), i8 = [];
118420
118439
  for (let n2 of e31) {
118421
118440
  let e32;
@@ -121267,7 +121286,7 @@ var Xi = class e41 {
121267
121286
  });
121268
121287
  let l = [];
121269
121288
  ({ proofs: l } = s);
121270
- let u = this.parseAmount(q2(l), "prepareSwapToReceive", true);
121289
+ let u = this.parseAmount(q3(l), "prepareSwapToReceive", true);
121271
121290
  this.failIf(u.isZero(), "Token contains no proofs", { proofs: l });
121272
121291
  let d = this.getKeyset(r);
121273
121292
  if (i8)
@@ -121289,7 +121308,7 @@ var Xi = class e41 {
121289
121308
  }
121290
121309
  sendOffline(e42, t, n) {
121291
121310
  let r = this.normalizeAmount(e42, "sendOffline"), { requireDleq: i8 = false, includeFees: a = false, exactMatch: o = true } = n || {};
121292
- i8 && (t = t.filter((e43) => e43.dleq != null)), this.failIf(q2(t) < r, "Not enough funds available to send");
121311
+ i8 && (t = t.filter((e43) => e43.dleq != null)), this.failIf(q3(t) < r, "Not enough funds available to send");
121293
121312
  let { keep: s, send: c } = this.selectProofsToSend(t, r, a, o);
121294
121313
  return {
121295
121314
  keep: s,
@@ -121313,7 +121332,7 @@ var Xi = class e41 {
121313
121332
  exactMatch: true,
121314
121333
  requireDleq: false
121315
121334
  }), l = o ? this.getFeesForProofs(c) : 0;
121316
- if (q2(c) === i8 + l)
121335
+ if (q3(c) === i8 + l)
121317
121336
  return this._logger.info("Successful exactMatch offline selection!"), {
121318
121337
  keep: s2,
121319
121338
  send: c
@@ -121334,7 +121353,7 @@ var Xi = class e41 {
121334
121353
  let c = this.getKeyset(a), l = this.configureOutputs(i8, c, r.send ?? this.defaultOutputType(), o), u = this.preparedTotal(l), { keep: d, send: f } = this.selectProofsToSend(t, u, true);
121335
121354
  if (f.length === 0)
121336
121355
  throw Error("Not enough funds available to send");
121337
- let p = D3.from(q2(f)), m = this.getFeesForProofs(f), h3 = u.add(m);
121356
+ let p = D3.from(q3(f)), m = this.getFeesForProofs(f), h3 = u.add(m);
121338
121357
  p.lessThan(h3) && this.failIf(true, "Not enough funds available for swap", {
121339
121358
  selectedSum: p.toString(),
121340
121359
  required: h3.toString()
@@ -121434,7 +121453,7 @@ var Xi = class e41 {
121434
121453
  });
121435
121454
  }
121436
121455
  decodeToken(e42) {
121437
- return lr2(e42, this._keyChain.getAllKeysetIds());
121456
+ return lr(e42, this._keyChain.getAllKeysetIds());
121438
121457
  }
121439
121458
  async batchRestore(e42 = 300, t = 100, n = 0, r) {
121440
121459
  let i8 = Math.ceil(e42 / t), a = [], o, s = 0;
@@ -121634,7 +121653,7 @@ var Xi = class e41 {
121634
121653
  }
121635
121654
  async prepareMelt(e42, t, n, r, i8) {
121636
121655
  i8 = i8 ?? this.defaultOutputType();
121637
- let { keysetId: a, onChangeOutputsCreated: o, onCountersReserved: s } = r || {}, c = this.getKeyset(a), l = q2(n), u = l - t.amount, d = [];
121656
+ let { keysetId: a, onChangeOutputsCreated: o, onCountersReserved: s } = r || {}, c = this.getKeyset(a), l = q3(n), u = l - t.amount, d = [];
121638
121657
  if (this.failIf(u < 0, "Not enough proofs to cover amount + fee reserve", {
121639
121658
  sendAmount: l,
121640
121659
  quoteAmount: t.amount
@@ -125386,7 +125405,7 @@ var TokenService = class {
125386
125405
  }
125387
125406
  try {
125388
125407
  const keysetIds = mintKeysets.map((keyset) => keyset.id);
125389
- return typeof token === "string" ? lr2(token, keysetIds) : token;
125408
+ return typeof token === "string" ? lr(token, keysetIds) : token;
125390
125409
  } catch (err) {
125391
125410
  const errMsg = err instanceof Error ? err.message : "Unknown error during token decoding";
125392
125411
  this.logger?.warn("Failed to decode token", {
@@ -127612,7 +127631,7 @@ var ReceiveOperationService = class {
127612
127631
  }
127613
127632
  extractMintUrl(token) {
127614
127633
  try {
127615
- return normalizeMintUrl(typeof token === "string" ? ur(token).mint : token.mint);
127634
+ return normalizeMintUrl(typeof token === "string" ? ur2(token).mint : token.mint);
127616
127635
  } catch (err) {
127617
127636
  this.logger?.warn("Failed to decode token for receive", { err });
127618
127637
  throw new ProofValidationError("Invalid token");
@@ -127997,10 +128016,10 @@ var PollingTransport = class {
127997
128016
  subToYs = /* @__PURE__ */ new Map;
127998
128017
  this.subToYsByMint.set(mintUrl, subToYs);
127999
128018
  }
128000
- let q3 = this.proofQueueByMint.get(mintUrl);
128001
- if (!q3) {
128002
- q3 = [];
128003
- this.proofQueueByMint.set(mintUrl, q3);
128019
+ let q4 = this.proofQueueByMint.get(mintUrl);
128020
+ if (!q4) {
128021
+ q4 = [];
128022
+ this.proofQueueByMint.set(mintUrl, q4);
128004
128023
  }
128005
128024
  let set2 = this.proofSetByMint.get(mintUrl);
128006
128025
  if (!set2) {
@@ -128022,7 +128041,7 @@ var PollingTransport = class {
128022
128041
  subs.add(subId);
128023
128042
  if (!set2.has(y2)) {
128024
128043
  set2.add(y2);
128025
- q3.push(y2);
128044
+ q4.push(y2);
128026
128045
  }
128027
128046
  }
128028
128047
  if (!scheduler.hasProofBatchTask) {
@@ -128071,7 +128090,7 @@ var PollingTransport = class {
128071
128090
  unsubscribed.add(subId);
128072
128091
  const subToYs = this.subToYsByMint.get(mintUrl);
128073
128092
  const yToSubs = this.yToSubsByMint.get(mintUrl);
128074
- const q3 = this.proofQueueByMint.get(mintUrl);
128093
+ const q4 = this.proofQueueByMint.get(mintUrl);
128075
128094
  const set2 = this.proofSetByMint.get(mintUrl);
128076
128095
  if (subToYs && yToSubs) {
128077
128096
  const ys = subToYs.get(subId);
@@ -128084,10 +128103,10 @@ var PollingTransport = class {
128084
128103
  yToSubs.delete(y2);
128085
128104
  if (set2)
128086
128105
  set2.delete(y2);
128087
- if (q3) {
128088
- const idx = q3.indexOf(y2);
128106
+ if (q4) {
128107
+ const idx = q4.indexOf(y2);
128089
128108
  if (idx >= 0)
128090
- q3.splice(idx, 1);
128109
+ q4.splice(idx, 1);
128091
128110
  }
128092
128111
  }
128093
128112
  }
@@ -130271,7 +130290,7 @@ var WalletApi = class {
130271
130290
  async decodeToken(tokenString, mintUrl) {
130272
130291
  if (mintUrl)
130273
130292
  return await this.tokenService.decodeToken(tokenString, mintUrl);
130274
- const metadata = ur(tokenString);
130293
+ const metadata = ur2(tokenString);
130275
130294
  return (await this.walletService.getWallet(metadata.mint)).decodeToken(tokenString);
130276
130295
  }
130277
130296
  encodeToken(token, opts) {
@@ -132285,10 +132304,10 @@ function invert8(number15, modulo) {
132285
132304
  let b2 = modulo;
132286
132305
  let x4 = _0n41, y2 = _1n39, u = _1n39, v4 = _0n41;
132287
132306
  while (a !== _0n41) {
132288
- const q3 = b2 / a;
132307
+ const q4 = b2 / a;
132289
132308
  const r = b2 % a;
132290
- const m = x4 - u * q3;
132291
- const n = y2 - v4 * q3;
132309
+ const m = x4 - u * q4;
132310
+ const n = y2 - v4 * q4;
132292
132311
  b2 = a, a = r, x4 = u, y2 = v4, u = m, v4 = n;
132293
132312
  }
132294
132313
  const gcd6 = b2;
@@ -133662,14 +133681,14 @@ function ecdsa4(Point7, hash10, ecdsaOpts = {}) {
133662
133681
  if (!Fn6.isValidNot0(k4))
133663
133682
  return;
133664
133683
  const ik = Fn6.inv(k4);
133665
- const q3 = Point7.BASE.multiply(k4).toAffine();
133666
- const r = Fn6.create(q3.x);
133684
+ const q4 = Point7.BASE.multiply(k4).toAffine();
133685
+ const r = Fn6.create(q4.x);
133667
133686
  if (r === _0n43)
133668
133687
  return;
133669
133688
  const s = Fn6.create(ik * Fn6.create(m + r * d));
133670
133689
  if (s === _0n43)
133671
133690
  return;
133672
- let recovery = (q3.x === r ? 0 : 2) | Number(q3.y & _1n41);
133691
+ let recovery = (q4.x === r ? 0 : 2) | Number(q4.y & _1n41);
133673
133692
  let normS = s;
133674
133693
  if (lowS && isBiggerThanHalfOrder(s)) {
133675
133694
  normS = Fn6.neg(s);
@@ -139832,8 +139851,19 @@ function createDaemonRequestHandler(deps) {
139832
139851
  const body = await readJsonBody(req);
139833
139852
  const amount = getRequiredPositiveNumberField(body, "amount");
139834
139853
  const mintUrl = optionalStringField(body, "mintUrl");
139835
- const invoice = await deps.walletClient.receiveBolt11(amount, mintUrl);
139836
- return { output: { invoice, amount, mintUrl } };
139854
+ const { invoice, operationId } = await deps.walletClient.receiveBolt11(amount, mintUrl);
139855
+ return { output: { invoice, operationId, amount, mintUrl } };
139856
+ });
139857
+ return;
139858
+ }
139859
+ const mintQuoteMatch = url5.pathname.match(/^\/wallet\/receive\/bolt11\/([^/]+)$/);
139860
+ if (req.method === "GET" && mintQuoteMatch?.[1]) {
139861
+ const operationId = decodeURIComponent(mintQuoteMatch[1]);
139862
+ await respond(res, async () => {
139863
+ const quote = await deps.walletClient.getMintQuote?.(operationId);
139864
+ if (!quote)
139865
+ throw new CocodHttpError(404, "Mint quote not found");
139866
+ return { output: quote };
139837
139867
  });
139838
139868
  return;
139839
139869
  }
@@ -140069,6 +140099,22 @@ function createDaemonRequestHandler(deps) {
140069
140099
  });
140070
140100
  return;
140071
140101
  }
140102
+ if (req.method === "POST" && url5.pathname === "/settings/auto-refresh") {
140103
+ await respond(res, async () => {
140104
+ const body = await readJsonBody(req);
140105
+ const enabled = body.enabled === true || body.enabled === "true";
140106
+ const config2 = await loadDaemonConfig();
140107
+ config2.autoRefresh = { ...config2.autoRefresh, enabled };
140108
+ saveDaemonConfig(config2);
140109
+ return {
140110
+ output: {
140111
+ message: `Automatic refresh ${enabled ? "enabled" : "disabled"}.`,
140112
+ autoRefresh: config2.autoRefresh
140113
+ }
140114
+ };
140115
+ });
140116
+ return;
140117
+ }
140072
140118
  if (req.method === "GET" && url5.pathname === "/models") {
140073
140119
  try {
140074
140120
  const forceRefresh = url5.searchParams.get("refresh")?.toLowerCase() === "true";
@@ -144748,10 +144794,10 @@ function invert9(number15, modulo) {
144748
144794
  let b2 = modulo;
144749
144795
  let x4 = _0n46, y2 = _1n43, u = _1n43, v4 = _0n46;
144750
144796
  while (a !== _0n46) {
144751
- const q3 = b2 / a;
144797
+ const q4 = b2 / a;
144752
144798
  const r = b2 % a;
144753
- const m = x4 - u * q3;
144754
- const n = y2 - v4 * q3;
144799
+ const m = x4 - u * q4;
144800
+ const n = y2 - v4 * q4;
144755
144801
  b2 = a, a = r, x4 = u, y2 = v4, u = m, v4 = n;
144756
144802
  }
144757
144803
  const gcd8 = b2;
@@ -146249,7 +146295,7 @@ class E3 {
146249
146295
  return e43.length - t.length;
146250
146296
  }
146251
146297
  }
146252
- function q3(r) {
146298
+ function q4(r) {
146253
146299
  return E3.fromBase64(r);
146254
146300
  }
146255
146301
  function $2(r) {
@@ -146539,7 +146585,7 @@ class w4 {
146539
146585
  throw new Error("unsupported pr: invalid prefix");
146540
146586
  if (e43[4] !== "A")
146541
146587
  throw new Error("unsupported pr version");
146542
- const t = e43.slice(5), n = q3(t), s = B3(n);
146588
+ const t = e43.slice(5), n = q4(t), s = B3(n);
146543
146589
  return this.fromRawRequest(s);
146544
146590
  }
146545
146591
  }
@@ -148210,17 +148256,17 @@ function cr3(t) {
148210
148256
  };
148211
148257
  return t.d && (r.memo = t.d), r;
148212
148258
  }
148213
- function lr3(e45, t) {
148259
+ function lr2(e45, t) {
148214
148260
  let n = (t ?? []).map((e46) => typeof e46 == "string" ? e46 : e46.id), r = dr3(Or3(e45));
148215
- return n && (r.proofs = Sr3(r.proofs, n)), r;
148261
+ return n && (r.proofs = Sr2(r.proofs, n)), r;
148216
148262
  }
148217
- function ur2(e45) {
148263
+ function ur3(e45) {
148218
148264
  e45 = Or3(e45);
148219
148265
  let t = dr3(e45);
148220
148266
  return {
148221
148267
  unit: t.unit || "sat",
148222
148268
  mint: t.mint,
148223
- amount: q4(t.proofs),
148269
+ amount: q5(t.proofs),
148224
148270
  ...t.memo && { memo: t.memo },
148225
148271
  incompleteProofs: t.proofs.map((e46) => ({
148226
148272
  secret: e46.secret,
@@ -148286,7 +148332,7 @@ function K7(...e45) {
148286
148332
  function gr3(e45) {
148287
148333
  return e45.replace(/\/$/, "");
148288
148334
  }
148289
- function q4(e45) {
148335
+ function q5(e45) {
148290
148336
  return D5.sum(e45.map((e46) => e46.amount)).toNumber();
148291
148337
  }
148292
148338
  var vr3 = class {
@@ -148345,7 +148391,7 @@ function br2(e45) {
148345
148391
  return delete t.dleq, t;
148346
148392
  });
148347
148393
  }
148348
- function Sr3(e45, n) {
148394
+ function Sr2(e45, n) {
148349
148395
  let r = (n ?? []).map((e46) => typeof e46 == "string" ? e46 : e46.id), i10 = [];
148350
148396
  for (let n2 of e45) {
148351
148397
  let e46;
@@ -151197,7 +151243,7 @@ var Xi2 = class e55 {
151197
151243
  });
151198
151244
  let l2 = [];
151199
151245
  ({ proofs: l2 } = s);
151200
- let u = this.parseAmount(q4(l2), "prepareSwapToReceive", true);
151246
+ let u = this.parseAmount(q5(l2), "prepareSwapToReceive", true);
151201
151247
  this.failIf(u.isZero(), "Token contains no proofs", { proofs: l2 });
151202
151248
  let d2 = this.getKeyset(r);
151203
151249
  if (i10)
@@ -151219,7 +151265,7 @@ var Xi2 = class e55 {
151219
151265
  }
151220
151266
  sendOffline(e56, t, n) {
151221
151267
  let r = this.normalizeAmount(e56, "sendOffline"), { requireDleq: i10 = false, includeFees: a = false, exactMatch: o = true } = n || {};
151222
- i10 && (t = t.filter((e57) => e57.dleq != null)), this.failIf(q4(t) < r, "Not enough funds available to send");
151268
+ i10 && (t = t.filter((e57) => e57.dleq != null)), this.failIf(q5(t) < r, "Not enough funds available to send");
151223
151269
  let { keep: s, send: c } = this.selectProofsToSend(t, r, a, o);
151224
151270
  return {
151225
151271
  keep: s,
@@ -151243,7 +151289,7 @@ var Xi2 = class e55 {
151243
151289
  exactMatch: true,
151244
151290
  requireDleq: false
151245
151291
  }), l2 = o ? this.getFeesForProofs(c) : 0;
151246
- if (q4(c) === i10 + l2)
151292
+ if (q5(c) === i10 + l2)
151247
151293
  return this._logger.info("Successful exactMatch offline selection!"), {
151248
151294
  keep: s2,
151249
151295
  send: c
@@ -151264,7 +151310,7 @@ var Xi2 = class e55 {
151264
151310
  let c = this.getKeyset(a), l2 = this.configureOutputs(i10, c, r.send ?? this.defaultOutputType(), o), u = this.preparedTotal(l2), { keep: d2, send: f2 } = this.selectProofsToSend(t, u, true);
151265
151311
  if (f2.length === 0)
151266
151312
  throw Error("Not enough funds available to send");
151267
- let p2 = D5.from(q4(f2)), m2 = this.getFeesForProofs(f2), h5 = u.add(m2);
151313
+ let p2 = D5.from(q5(f2)), m2 = this.getFeesForProofs(f2), h5 = u.add(m2);
151268
151314
  p2.lessThan(h5) && this.failIf(true, "Not enough funds available for swap", {
151269
151315
  selectedSum: p2.toString(),
151270
151316
  required: h5.toString()
@@ -151364,7 +151410,7 @@ var Xi2 = class e55 {
151364
151410
  });
151365
151411
  }
151366
151412
  decodeToken(e56) {
151367
- return lr3(e56, this._keyChain.getAllKeysetIds());
151413
+ return lr2(e56, this._keyChain.getAllKeysetIds());
151368
151414
  }
151369
151415
  async batchRestore(e56 = 300, t = 100, n = 0, r) {
151370
151416
  let i10 = Math.ceil(e56 / t), a = [], o, s = 0;
@@ -151564,7 +151610,7 @@ var Xi2 = class e55 {
151564
151610
  }
151565
151611
  async prepareMelt(e56, t, n, r, i10) {
151566
151612
  i10 = i10 ?? this.defaultOutputType();
151567
- let { keysetId: a, onChangeOutputsCreated: o, onCountersReserved: s } = r || {}, c = this.getKeyset(a), l2 = q4(n), u = l2 - t.amount, d2 = [];
151613
+ let { keysetId: a, onChangeOutputsCreated: o, onCountersReserved: s } = r || {}, c = this.getKeyset(a), l2 = q5(n), u = l2 - t.amount, d2 = [];
151568
151614
  if (this.failIf(u < 0, "Not enough proofs to cover amount + fee reserve", {
151569
151615
  sendAmount: l2,
151570
151616
  quoteAmount: t.amount
@@ -154084,7 +154130,7 @@ var TransactionService = class {
154084
154130
  async receive(token) {
154085
154131
  let mint;
154086
154132
  try {
154087
- mint = typeof token === "string" ? ur2(token).mint : token.mint;
154133
+ mint = typeof token === "string" ? ur3(token).mint : token.mint;
154088
154134
  } catch (err) {
154089
154135
  this.logger?.warn("Failed to decode token for receive", { err });
154090
154136
  throw new ProofValidationError2("Invalid token");
@@ -154095,7 +154141,7 @@ var TransactionService = class {
154095
154141
  const { keysets } = await this.mintService.ensureUpdatedMint(mint);
154096
154142
  const { wallet } = await this.walletService.getWalletWithActiveKeysetId(mint);
154097
154143
  const keysetIds = keysets.map((keyset) => keyset.id);
154098
- let proofs = typeof token === "string" ? lr3(token, keysetIds).proofs : token.proofs;
154144
+ let proofs = typeof token === "string" ? lr2(token, keysetIds).proofs : token.proofs;
154099
154145
  proofs = await this.proofService.prepareProofsForReceiving(proofs);
154100
154146
  if (!Array.isArray(proofs) || proofs.length === 0) {
154101
154147
  this.logger?.warn("Token contains no proofs", { mint });
@@ -155373,7 +155419,7 @@ var TokenService2 = class {
155373
155419
  }
155374
155420
  try {
155375
155421
  const keysetIds = mintKeysets.map((keyset) => keyset.id);
155376
- return typeof token === "string" ? lr3(token, keysetIds) : token;
155422
+ return typeof token === "string" ? lr2(token, keysetIds) : token;
155377
155423
  } catch (err) {
155378
155424
  const errMsg = err instanceof Error ? err.message : "Unknown error during token decoding";
155379
155425
  this.logger?.warn("Failed to decode token", {
@@ -157543,7 +157589,7 @@ var ReceiveOperationService2 = class {
157543
157589
  }
157544
157590
  extractMintUrl(token) {
157545
157591
  try {
157546
- return normalizeMintUrl2(typeof token === "string" ? ur2(token).mint : token.mint);
157592
+ return normalizeMintUrl2(typeof token === "string" ? ur3(token).mint : token.mint);
157547
157593
  } catch (err) {
157548
157594
  this.logger?.warn("Failed to decode token for receive", { err });
157549
157595
  throw new ProofValidationError2("Invalid token");
@@ -157928,10 +157974,10 @@ var PollingTransport2 = class {
157928
157974
  subToYs = /* @__PURE__ */ new Map;
157929
157975
  this.subToYsByMint.set(mintUrl, subToYs);
157930
157976
  }
157931
- let q5 = this.proofQueueByMint.get(mintUrl);
157932
- if (!q5) {
157933
- q5 = [];
157934
- this.proofQueueByMint.set(mintUrl, q5);
157977
+ let q6 = this.proofQueueByMint.get(mintUrl);
157978
+ if (!q6) {
157979
+ q6 = [];
157980
+ this.proofQueueByMint.set(mintUrl, q6);
157935
157981
  }
157936
157982
  let set2 = this.proofSetByMint.get(mintUrl);
157937
157983
  if (!set2) {
@@ -157953,7 +157999,7 @@ var PollingTransport2 = class {
157953
157999
  subs.add(subId);
157954
158000
  if (!set2.has(y2)) {
157955
158001
  set2.add(y2);
157956
- q5.push(y2);
158002
+ q6.push(y2);
157957
158003
  }
157958
158004
  }
157959
158005
  if (!scheduler.hasProofBatchTask) {
@@ -158002,7 +158048,7 @@ var PollingTransport2 = class {
158002
158048
  unsubscribed.add(subId);
158003
158049
  const subToYs = this.subToYsByMint.get(mintUrl);
158004
158050
  const yToSubs = this.yToSubsByMint.get(mintUrl);
158005
- const q5 = this.proofQueueByMint.get(mintUrl);
158051
+ const q6 = this.proofQueueByMint.get(mintUrl);
158006
158052
  const set2 = this.proofSetByMint.get(mintUrl);
158007
158053
  if (subToYs && yToSubs) {
158008
158054
  const ys = subToYs.get(subId);
@@ -158015,10 +158061,10 @@ var PollingTransport2 = class {
158015
158061
  yToSubs.delete(y2);
158016
158062
  if (set2)
158017
158063
  set2.delete(y2);
158018
- if (q5) {
158019
- const idx = q5.indexOf(y2);
158064
+ if (q6) {
158065
+ const idx = q6.indexOf(y2);
158020
158066
  if (idx >= 0)
158021
- q5.splice(idx, 1);
158067
+ q6.splice(idx, 1);
158022
158068
  }
158023
158069
  }
158024
158070
  }
@@ -160210,7 +160256,7 @@ var WalletApi2 = class {
160210
160256
  async decodeToken(tokenString, mintUrl) {
160211
160257
  if (mintUrl)
160212
160258
  return await this.tokenService.decodeToken(tokenString, mintUrl);
160213
- const metadata = ur2(tokenString);
160259
+ const metadata = ur3(tokenString);
160214
160260
  return (await this.walletService.getWallet(metadata.mint)).decodeToken(tokenString);
160215
160261
  }
160216
160262
  encodeToken(token, opts) {
@@ -161385,8 +161431,8 @@ var QUOTE_DEFAULTS = {
161385
161431
  function isValidQuote(quote) {
161386
161432
  if (!quote || typeof quote !== "object")
161387
161433
  return false;
161388
- const q5 = quote;
161389
- return typeof q5.quoteId === "string" && typeof q5.mintUrl === "string" && typeof q5.paidAt === "number";
161434
+ const q6 = quote;
161435
+ return typeof q6.quoteId === "string" && typeof q6.mintUrl === "string" && typeof q6.paidAt === "number";
161390
161436
  }
161391
161437
  function isValidUrl(url5) {
161392
161438
  try {
@@ -162723,6 +162769,9 @@ var SAFE_COCO_LOG_FIELDS = new Set([
162723
162769
  "operationId",
162724
162770
  "quoteId",
162725
162771
  "state",
162772
+ "code",
162773
+ "detail",
162774
+ "status",
162726
162775
  "count",
162727
162776
  "total",
162728
162777
  "filterCount",
@@ -163042,6 +163091,107 @@ async function settleExpiredMintQuotes(source, nowMs, deadlineMs = EXPIRED_MINT_
163042
163091
  }
163043
163092
  return settlement;
163044
163093
  }
163094
+ var PENDING_MINT_SWEEP_INTERVAL_MS = 15000;
163095
+ var PENDING_MINT_CHECK_TIMEOUT_MS = 1e4;
163096
+ var PENDING_MINT_SWEEP_DEADLINE_MS = 30000;
163097
+ var PENDING_MINT_STOP_DRAIN_MS = 1e4;
163098
+ async function settlePendingMintQuotes(source, nowMs, options = {}) {
163099
+ const deadlineMs = options.deadlineMs ?? PENDING_MINT_SWEEP_DEADLINE_MS;
163100
+ const checkTimeoutMs = options.checkTimeoutMs ?? PENDING_MINT_CHECK_TIMEOUT_MS;
163101
+ const state = options.state ?? { outstanding: new Map };
163102
+ let unreachable = 0;
163103
+ const pending = await source.ops.mint.listPending();
163104
+ const resumeAt = pending.findIndex((op) => op.id === state.after) + 1;
163105
+ const ordered = [...pending.slice(resumeAt), ...pending.slice(0, resumeAt)];
163106
+ const startedAt = Date.now();
163107
+ for (const op of ordered) {
163108
+ const remainingMs = deadlineMs - (Date.now() - startedAt);
163109
+ if (state.outstanding.has(op.id) || remainingMs <= 0) {
163110
+ unreachable++;
163111
+ continue;
163112
+ }
163113
+ state.after = op.id;
163114
+ const settled = source.ops.mint.refresh(op.id).then((result) => reportPendingMintRefresh(source, op, result, nowMs), (error53) => reportPendingMintRefreshError(source, op, error53)).finally(() => state.outstanding.delete(op.id));
163115
+ state.outstanding.set(op.id, settled);
163116
+ try {
163117
+ const outcome = await withTimeout(settled, Math.min(checkTimeoutMs, remainingMs));
163118
+ if (outcome === "unreachable")
163119
+ unreachable++;
163120
+ } catch {
163121
+ unreachable++;
163122
+ }
163123
+ }
163124
+ return { unreachable };
163125
+ }
163126
+ async function reportPendingMintRefresh(source, op, result, nowMs) {
163127
+ const quote = `Mint quote ${op.quoteId} at ${op.mintUrl}`;
163128
+ if (result.state === "finalized") {
163129
+ if (result.error) {
163130
+ logger5.warn(`${quote}: ${result.error}`);
163131
+ return "other";
163132
+ }
163133
+ const balance = (await source.wallet.balances.byMint())[op.mintUrl]?.spendable;
163134
+ logger5.log(`${quote}: paid, ${op.amount} sat minted` + (balance === undefined ? "" : `, balance now ${balance} sat`));
163135
+ return "other";
163136
+ }
163137
+ if (result.state === "failed") {
163138
+ logger5.warn(`${quote}: failed at the mint: ${result.error ?? "unknown reason"}`);
163139
+ return "other";
163140
+ }
163141
+ const expired = op.expiry > 0 && op.expiry * 1000 <= nowMs;
163142
+ if (expired && result.state === "pending" && result.lastObservedRemoteState === "UNPAID") {
163143
+ await source.mintOperationService.failPendingOperation({ id: op.id }, {
163144
+ reason: "Expired mint quote confirmed unpaid by mint",
163145
+ retryable: false,
163146
+ observedAt: Date.now()
163147
+ });
163148
+ }
163149
+ return "other";
163150
+ }
163151
+ async function reportPendingMintRefreshError(source, op, error53) {
163152
+ if (error53 instanceof OperationInProgressError)
163153
+ return "other";
163154
+ const current = await source.ops.mint.get(op.id);
163155
+ if (current?.state === "pending" && current.lastObservedRemoteState === "PAID") {
163156
+ if (op.lastObservedRemoteState !== "PAID" || current.error !== op.error) {
163157
+ logger5.warn(`Mint quote ${op.quoteId} at ${op.mintUrl}: paid (${op.amount} sat) but proofs not minted yet: ${current.error ?? String(error53)}; will retry`);
163158
+ }
163159
+ return "other";
163160
+ }
163161
+ return "unreachable";
163162
+ }
163163
+ function startPendingMintSweep(source) {
163164
+ let stopped = false;
163165
+ let timer6;
163166
+ let inFlight = Promise.resolve();
163167
+ let unreachableBefore = 0;
163168
+ const state = { outstanding: new Map };
163169
+ const tick = async () => {
163170
+ if (stopped)
163171
+ return;
163172
+ inFlight = settlePendingMintQuotes(source, Date.now(), { state }).then(({ unreachable }) => {
163173
+ if (unreachable > 0 && unreachableBefore === 0) {
163174
+ logger5.warn(`Could not check ${unreachable} pending mint quote(s); will keep retrying`);
163175
+ } else if (unreachable === 0 && unreachableBefore > 0) {
163176
+ logger5.log("Pending mint quote checks are reaching the mint again");
163177
+ }
163178
+ unreachableBefore = unreachable;
163179
+ }, (error53) => {
163180
+ logger5.warn(`Pending mint quote sweep failed: ${error53 instanceof Error ? error53.message : String(error53)}`);
163181
+ });
163182
+ await inFlight;
163183
+ if (!stopped)
163184
+ timer6 = setTimeout(tick, PENDING_MINT_SWEEP_INTERVAL_MS);
163185
+ };
163186
+ timer6 = setTimeout(tick, PENDING_MINT_SWEEP_INTERVAL_MS);
163187
+ return async () => {
163188
+ stopped = true;
163189
+ if (timer6 !== undefined)
163190
+ clearTimeout(timer6);
163191
+ await inFlight;
163192
+ await withTimeout(Promise.allSettled(state.outstanding.values()), PENDING_MINT_STOP_DRAIN_MS).catch(() => {});
163193
+ };
163194
+ }
163045
163195
  async function reconcileDuplicateReceiveOperations(coco, repo) {
163046
163196
  const internals = coco;
163047
163197
  const unavailableMints = new Set;
@@ -163165,6 +163315,7 @@ async function createCocoClient(options = {}) {
163165
163315
  pendingMints: 0
163166
163316
  };
163167
163317
  let recoveryResolve;
163318
+ let stopPendingMintSweep;
163168
163319
  const recoveryPromise = new Promise((resolve) => {
163169
163320
  recoveryResolve = resolve;
163170
163321
  });
@@ -163291,6 +163442,11 @@ async function createCocoClient(options = {}) {
163291
163442
  recoveryPhase = "done";
163292
163443
  recoveryResolve?.();
163293
163444
  startupProgress("Wallet recovery complete.");
163445
+ stopPendingMintSweep = startPendingMintSweep({
163446
+ ops: coco.ops,
163447
+ wallet: coco.wallet,
163448
+ mintOperationService: coco.mintOperationService
163449
+ });
163294
163450
  }).catch((error53) => {
163295
163451
  recoveryDone = true;
163296
163452
  recoveryPhase = "error";
@@ -163488,7 +163644,20 @@ async function createCocoClient(options = {}) {
163488
163644
  if (!("request" in op)) {
163489
163645
  throw new Error("mint prepare did not return a payment request");
163490
163646
  }
163491
- return op.request;
163647
+ return { invoice: op.request, operationId: op.id };
163648
+ },
163649
+ async getMintQuote(operationId) {
163650
+ const op = await coco.ops.mint.get(operationId);
163651
+ if (!op || op.state === "init")
163652
+ return null;
163653
+ return {
163654
+ operationId: op.id,
163655
+ state: op.state,
163656
+ mintState: op.lastObservedRemoteState,
163657
+ amount: op.amount,
163658
+ mintUrl: op.mintUrl,
163659
+ error: op.error
163660
+ };
163492
163661
  },
163493
163662
  async sendCashu(amount, mintUrl) {
163494
163663
  await waitForRecovery();
@@ -163550,6 +163719,7 @@ async function createCocoClient(options = {}) {
163550
163719
  disposed = true;
163551
163720
  try {
163552
163721
  await recoveryPromise;
163722
+ await stopPendingMintSweep?.();
163553
163723
  await coco.dispose();
163554
163724
  } finally {
163555
163725
  try {
@@ -164096,8 +164266,8 @@ function startupProgress2(message) {
164096
164266
  logger5.info(message);
164097
164267
  console.log(`${STARTUP_LOG_PREFIX2} ${message}`);
164098
164268
  }
164099
- installGlobalErrorHandlers();
164100
- async function main() {
164269
+ async function runDaemon(argv = process.argv) {
164270
+ installGlobalErrorHandlers();
164101
164271
  let shutdownDaemon = () => process.exit(0);
164102
164272
  const shutdownForSignal = (signal) => {
164103
164273
  logger5.log(`Received ${signal}; shutting down...`);
@@ -164106,9 +164276,9 @@ async function main() {
164106
164276
  process.once("SIGINT", shutdownForSignal);
164107
164277
  process.once("SIGTERM", shutdownForSignal);
164108
164278
  startupProgress2("Loading configuration...");
164109
- const args = parseArgs(process.argv);
164279
+ const args = parseArgs(argv);
164110
164280
  const config2 = await loadDaemonConfig();
164111
- const port = args.port;
164281
+ const port = args.port ?? config2.port ?? 8008;
164112
164282
  const host = args.host || config2.host || "127.0.0.1";
164113
164283
  const provider = args.provider || config2.provider;
164114
164284
  const requestResponseLogDir = process.env.ROUTSTRD_REQUEST_RESPONSE_LOG_DIR || (config2.requestResponseLogging?.enabled ? config2.requestResponseLogging.dir || REQUEST_RESPONSE_LOGS_DIR : undefined);
@@ -164211,31 +164381,61 @@ async function main() {
164211
164381
  unlinkSync4(SOCKET_PATH);
164212
164382
  }
164213
164383
  } catch {}
164214
- const REFRESH_INTERVAL_MS = 21 * 60 * 1000;
164215
- let refreshInterval = null;
164384
+ const DEFAULT_REFRESH_INTERVAL_MS = 21 * 60 * 1000;
164385
+ const DISABLED_REFRESH_POLL_MS = 60 * 1000;
164386
+ const readAutoRefreshSettings = () => {
164387
+ const autoRefresh = loadDaemonConfigSync().autoRefresh;
164388
+ const intervalMs = typeof autoRefresh?.intervalMs === "number" && autoRefresh.intervalMs > 0 ? autoRefresh.intervalMs : DEFAULT_REFRESH_INTERVAL_MS;
164389
+ return { enabled: autoRefresh?.enabled !== false, intervalMs };
164390
+ };
164391
+ let refreshTimer = null;
164392
+ let refreshJobActive = false;
164393
+ let disabledNoticeLogged = false;
164394
+ const runScheduledRefresh = async () => {
164395
+ logger5.log("Running scheduled Nostr event refresh...");
164396
+ try {
164397
+ await modelManager.refreshNostrEvents();
164398
+ } catch (error53) {
164399
+ logger5.error("Scheduled Nostr event refresh failed:", error53);
164400
+ }
164401
+ logger5.log("Running scheduled model refresh...");
164402
+ try {
164403
+ await refreshModelsAndIntegrations(getRoutstr21Models, updatedConfig, "Scheduled");
164404
+ } catch (error53) {
164405
+ logger5.error("Scheduled model refresh failed:", error53);
164406
+ }
164407
+ };
164408
+ const scheduleNextRefresh = () => {
164409
+ if (!refreshJobActive)
164410
+ return;
164411
+ const { enabled, intervalMs } = readAutoRefreshSettings();
164412
+ if (!enabled) {
164413
+ if (!disabledNoticeLogged) {
164414
+ logger5.log("Scheduled refresh job is disabled (autoRefresh.enabled=false). Polling for re-enable every 60s.");
164415
+ disabledNoticeLogged = true;
164416
+ }
164417
+ refreshTimer = setTimeout(scheduleNextRefresh, Math.min(intervalMs, DISABLED_REFRESH_POLL_MS));
164418
+ return;
164419
+ }
164420
+ if (disabledNoticeLogged) {
164421
+ logger5.log("Scheduled refresh job re-enabled.");
164422
+ disabledNoticeLogged = false;
164423
+ }
164424
+ refreshTimer = setTimeout(() => {
164425
+ runScheduledRefresh().catch((error53) => logger5.error("Model refresh interval escaped:", error53)).finally(() => scheduleNextRefresh());
164426
+ }, intervalMs);
164427
+ };
164216
164428
  const startModelRefreshJob = () => {
164217
- logger5.log(`Starting recurring model refresh job (every ${REFRESH_INTERVAL_MS / 1000 / 60 / 60} hours)`);
164218
- refreshInterval = setInterval(() => {
164219
- (async () => {
164220
- logger5.log("Running scheduled Nostr event refresh...");
164221
- try {
164222
- await modelManager.refreshNostrEvents();
164223
- } catch (error53) {
164224
- logger5.error("Scheduled Nostr event refresh failed:", error53);
164225
- }
164226
- logger5.log("Running scheduled model refresh...");
164227
- try {
164228
- await refreshModelsAndIntegrations(getRoutstr21Models, updatedConfig, "Scheduled");
164229
- } catch (error53) {
164230
- logger5.error("Scheduled model refresh failed:", error53);
164231
- }
164232
- })().catch((error53) => logger5.error("Model refresh interval escaped:", error53));
164233
- }, REFRESH_INTERVAL_MS);
164429
+ refreshJobActive = true;
164430
+ const { enabled, intervalMs } = readAutoRefreshSettings();
164431
+ logger5.log(enabled ? `Starting recurring model refresh job (every ${Math.round(intervalMs / 60000)} minutes)` : "Recurring model refresh job is disabled (autoRefresh.enabled=false).");
164432
+ scheduleNextRefresh();
164234
164433
  };
164235
164434
  const stopModelRefreshJob = () => {
164236
- if (refreshInterval) {
164237
- clearInterval(refreshInterval);
164238
- refreshInterval = null;
164435
+ refreshJobActive = false;
164436
+ if (refreshTimer) {
164437
+ clearTimeout(refreshTimer);
164438
+ refreshTimer = null;
164239
164439
  logger5.log("Stopped recurring model refresh job.");
164240
164440
  }
164241
164441
  };
@@ -164315,8 +164515,13 @@ async function main() {
164315
164515
  await modelManager.refreshNostrEvents();
164316
164516
  startModelRefreshJob();
164317
164517
  startRefundJob();
164318
- logger5.log("Running initial model refresh...");
164319
- await refreshModelsAndIntegrations(getRoutstr21Models, updatedConfig, "Initial");
164518
+ if (readAutoRefreshSettings().enabled) {
164519
+ logger5.log("Running initial model refresh...");
164520
+ await refreshModelsAndIntegrations(getRoutstr21Models, updatedConfig, "Initial");
164521
+ } else {
164522
+ logger5.log("Running initial model refresh (client integrations skipped)...");
164523
+ await getRoutstr21Models(true);
164524
+ }
164320
164525
  }).catch((error53) => {
164321
164526
  logger5.error("Initial model refresh failed:", error53);
164322
164527
  startModelRefreshJob();
@@ -164325,9 +164530,12 @@ async function main() {
164325
164530
  });
164326
164531
  }
164327
164532
  if (import.meta.main) {
164328
- main().catch((error53) => {
164533
+ runDaemon().catch((error53) => {
164329
164534
  logger5.error("Failed to start Routstr daemon:", error53);
164330
164535
  console.error("Failed to start Routstr daemon:", error53 instanceof Error ? error53.message : error53);
164331
164536
  process.exit(1);
164332
164537
  });
164333
164538
  }
164539
+ export {
164540
+ runDaemon
164541
+ };