@routstr/sdk 0.2.12 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3,10 +3,8 @@ import { EventStore } from 'applesauce-core';
3
3
  import { tap } from 'rxjs';
4
4
  import { getDecodedToken } from '@cashu/cashu-ts';
5
5
  import { createStore } from 'zustand/vanilla';
6
- import { Transform, Readable } from 'stream';
7
- import * as fs from 'fs';
8
- import * as path from 'path';
9
- import * as os from 'os';
6
+ import { Transform } from 'stream';
7
+ import { StringDecoder } from 'string_decoder';
10
8
 
11
9
  // core/errors.ts
12
10
  var InsufficientBalanceError = class extends Error {
@@ -691,10 +689,10 @@ var AuditLogger = class _AuditLogger {
691
689
  const logLine = JSON.stringify(fullEntry) + "\n";
692
690
  if (typeof window === "undefined") {
693
691
  try {
694
- const fs2 = await import('fs');
695
- const path2 = await import('path');
696
- const logPath = path2.join(process.cwd(), "audit.log");
697
- fs2.appendFileSync(logPath, logLine);
692
+ const fs = await import('fs');
693
+ const path = await import('path');
694
+ const logPath = path.join(process.cwd(), "audit.log");
695
+ fs.appendFileSync(logPath, logLine);
698
696
  } catch (error) {
699
697
  console.error("[AuditLogger] Failed to write to file:", error);
700
698
  }
@@ -1273,7 +1271,7 @@ var CashuSpender = class {
1273
1271
  };
1274
1272
 
1275
1273
  // wallet/BalanceManager.ts
1276
- var BalanceManager = class {
1274
+ var BalanceManager = class _BalanceManager {
1277
1275
  constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
1278
1276
  this.walletAdapter = walletAdapter;
1279
1277
  this.storageAdapter = storageAdapter;
@@ -1290,6 +1288,47 @@ var BalanceManager = class {
1290
1288
  }
1291
1289
  }
1292
1290
  cashuSpender;
1291
+ /** In-memory guard for per-provider wallet mutations (topup / refund) */
1292
+ providerWalletOps = /* @__PURE__ */ new Map();
1293
+ /** Cooldown (ms) between opposite operations on the same provider */
1294
+ static PROVIDER_WALLET_COOLDOWN_MS = 1e4;
1295
+ /**
1296
+ * Check whether a wallet operation (topup/refund) may run for a provider.
1297
+ * Returns the reason when blocked.
1298
+ */
1299
+ _canRunProviderWalletOperation(baseUrl, type) {
1300
+ const existing = this.providerWalletOps.get(baseUrl);
1301
+ if (!existing) {
1302
+ return { allowed: true };
1303
+ }
1304
+ if (existing.type === type) {
1305
+ return { allowed: true };
1306
+ }
1307
+ if (!existing.endTime) {
1308
+ return {
1309
+ allowed: false,
1310
+ reason: `Provider wallet operation locked; ${existing.type} in progress`
1311
+ };
1312
+ }
1313
+ const elapsed = Date.now() - existing.endTime;
1314
+ if (elapsed < _BalanceManager.PROVIDER_WALLET_COOLDOWN_MS) {
1315
+ return {
1316
+ allowed: false,
1317
+ reason: `Provider wallet operation locked; recent ${existing.type} completed ${Math.round(elapsed / 1e3)}s ago`
1318
+ };
1319
+ }
1320
+ this.providerWalletOps.delete(baseUrl);
1321
+ return { allowed: true };
1322
+ }
1323
+ _beginProviderWalletOperation(baseUrl, type) {
1324
+ this.providerWalletOps.set(baseUrl, { type, startTime: Date.now() });
1325
+ }
1326
+ _endProviderWalletOperation(baseUrl, type) {
1327
+ const existing = this.providerWalletOps.get(baseUrl);
1328
+ if (existing && existing.type === type) {
1329
+ existing.endTime = Date.now();
1330
+ }
1331
+ }
1293
1332
  async getBalanceState() {
1294
1333
  const mintBalances = await this.walletAdapter.getBalances();
1295
1334
  const units = this.walletAdapter.getMintUnits();
@@ -1324,6 +1363,20 @@ var BalanceManager = class {
1324
1363
  * @returns Refund result
1325
1364
  */
1326
1365
  async refundApiKey(options) {
1366
+ const { mintUrl, baseUrl, apiKey, forceRefund } = options;
1367
+ const guard = this._canRunProviderWalletOperation(baseUrl, "refund");
1368
+ if (!guard.allowed) {
1369
+ console.log(`[BalanceManager] Skipping refund for ${baseUrl} - ${guard.reason}`);
1370
+ return { success: false, message: guard.reason };
1371
+ }
1372
+ this._beginProviderWalletOperation(baseUrl, "refund");
1373
+ try {
1374
+ return await this._refundApiKeyImpl({ mintUrl, baseUrl, apiKey, forceRefund });
1375
+ } finally {
1376
+ this._endProviderWalletOperation(baseUrl, "refund");
1377
+ }
1378
+ }
1379
+ async _refundApiKeyImpl(options) {
1327
1380
  const { mintUrl, baseUrl, apiKey, forceRefund } = options;
1328
1381
  if (!apiKey) {
1329
1382
  return { success: false, message: "No API key to refund" };
@@ -1452,6 +1505,20 @@ var BalanceManager = class {
1452
1505
  * Top up API key balance with a cashu token
1453
1506
  */
1454
1507
  async topUp(options) {
1508
+ const { mintUrl, baseUrl, amount, token: providedToken } = options;
1509
+ const guard = this._canRunProviderWalletOperation(baseUrl, "topup");
1510
+ if (!guard.allowed) {
1511
+ console.log(`[BalanceManager] Skipping topup for ${baseUrl} - ${guard.reason}`);
1512
+ return { success: false, message: guard.reason };
1513
+ }
1514
+ this._beginProviderWalletOperation(baseUrl, "topup");
1515
+ try {
1516
+ return await this._topUpImpl({ mintUrl, baseUrl, amount, token: providedToken });
1517
+ } finally {
1518
+ this._endProviderWalletOperation(baseUrl, "topup");
1519
+ }
1520
+ }
1521
+ async _topUpImpl(options) {
1455
1522
  const { mintUrl, baseUrl, amount, token: providedToken } = options;
1456
1523
  if (!amount || amount <= 0) {
1457
1524
  return { success: false, message: "Invalid top up amount" };
@@ -1612,7 +1679,7 @@ var BalanceManager = class {
1612
1679
  p2pkPubkey
1613
1680
  );
1614
1681
  console.log(
1615
- `[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
1682
+ `[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}, all mint balances: ${JSON.stringify(Object.fromEntries(Object.entries(balances).map(([mint, balance]) => [mint, getBalanceInSats(balance, units[mint])])))}`
1616
1683
  );
1617
1684
  return {
1618
1685
  success: true,
@@ -2807,7 +2874,9 @@ var ProviderManager = class _ProviderManager {
2807
2874
  const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
2808
2875
  const totalInputTokens = approximateTokens + imageTokens;
2809
2876
  const sp = model?.sats_pricing;
2810
- if (!sp) return 0;
2877
+ if (!sp) {
2878
+ return 0;
2879
+ }
2811
2880
  if (!sp.max_completion_cost) {
2812
2881
  return sp.max_cost ?? 50;
2813
2882
  }
@@ -4516,26 +4585,117 @@ var setDefaultUsageTrackingDriver = (driver) => {
4516
4585
  var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
4517
4586
  var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
4518
4587
  var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
4519
- function createSSEParserTransform(onUsage, onResponseId) {
4588
+ function mergeUsage(previous, next) {
4589
+ if (!previous) return next;
4590
+ return {
4591
+ promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
4592
+ completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
4593
+ totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
4594
+ cost: next.cost > 0 ? next.cost : previous.cost,
4595
+ satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
4596
+ };
4597
+ }
4598
+ function hasUsageChanged(previous, next) {
4599
+ if (!previous) return true;
4600
+ return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
4601
+ }
4602
+ async function inspectSSEWebStream(stream, onUsage, onResponseId) {
4603
+ const reader = stream.getReader();
4604
+ const decoder = new TextDecoder("utf-8");
4520
4605
  let buffer = "";
4521
4606
  let capturedUsage = null;
4607
+ let capturedResponseId;
4522
4608
  let responseIdCaptured = false;
4523
- const mergeUsage = (previous, next) => {
4524
- if (!previous) return next;
4525
- return {
4526
- promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
4527
- completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
4528
- totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
4529
- cost: next.cost > 0 ? next.cost : previous.cost,
4530
- satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
4531
- };
4609
+ const inspectDataPayload = (jsonText) => {
4610
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4611
+ return;
4612
+ }
4613
+ const trimmed = jsonText.trim();
4614
+ if (!trimmed || trimmed === "[DONE]") return;
4615
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
4616
+ try {
4617
+ const data = JSON.parse(trimmed);
4618
+ if (!responseIdCaptured) {
4619
+ const responseId = data?.id;
4620
+ if (typeof responseId === "string" && responseId.trim().length > 0) {
4621
+ capturedResponseId = responseId.trim();
4622
+ onResponseId?.(capturedResponseId);
4623
+ responseIdCaptured = true;
4624
+ }
4625
+ }
4626
+ const usage = extractUsageFromSSEJson(data);
4627
+ if (usage) {
4628
+ const merged = mergeUsage(capturedUsage, usage);
4629
+ if (hasUsageChanged(capturedUsage, merged)) {
4630
+ capturedUsage = merged;
4631
+ onUsage(merged);
4632
+ }
4633
+ }
4634
+ } catch {
4635
+ }
4532
4636
  };
4533
- const hasUsageChanged = (previous, next) => {
4534
- if (!previous) return true;
4535
- return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
4637
+ const inspectEventBlock = (eventBlock) => {
4638
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4639
+ return;
4640
+ }
4641
+ const lines = eventBlock.split(/\r?\n/);
4642
+ const dataParts = [];
4643
+ for (const line of lines) {
4644
+ if (!line || line.startsWith(":")) continue;
4645
+ if (line.startsWith("data:")) {
4646
+ const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
4647
+ dataParts.push(value);
4648
+ }
4649
+ }
4650
+ if (dataParts.length === 0) return;
4651
+ inspectDataPayload(dataParts.join("\n"));
4652
+ };
4653
+ const drainBufferedEvents = () => {
4654
+ const terminator = /\r?\n\r?\n/g;
4655
+ let lastIndex = 0;
4656
+ let match;
4657
+ while ((match = terminator.exec(buffer)) !== null) {
4658
+ const block = buffer.slice(lastIndex, match.index);
4659
+ lastIndex = match.index + match[0].length;
4660
+ if (block.length > 0) inspectEventBlock(block);
4661
+ }
4662
+ if (lastIndex > 0) buffer = buffer.slice(lastIndex);
4536
4663
  };
4664
+ try {
4665
+ while (true) {
4666
+ const { value, done } = await reader.read();
4667
+ if (done) break;
4668
+ if (value && value.byteLength > 0) {
4669
+ buffer += decoder.decode(value, { stream: true });
4670
+ drainBufferedEvents();
4671
+ }
4672
+ }
4673
+ buffer += decoder.decode();
4674
+ drainBufferedEvents();
4675
+ if (buffer.length > 0) {
4676
+ const tail = buffer.replace(/\r?\n+$/, "");
4677
+ if (tail.length > 0) inspectEventBlock(tail);
4678
+ buffer = "";
4679
+ }
4680
+ } catch {
4681
+ } finally {
4682
+ try {
4683
+ reader.releaseLock();
4684
+ } catch {
4685
+ }
4686
+ }
4687
+ return {
4688
+ capturedUsage: capturedUsage ?? void 0,
4689
+ capturedResponseId
4690
+ };
4691
+ }
4692
+ function createSSEParserTransform(onUsage, onResponseId) {
4693
+ let buffer = "";
4694
+ const decoder = new StringDecoder("utf8");
4695
+ let capturedUsage = null;
4696
+ let responseIdCaptured = false;
4537
4697
  const inspectDataPayload = (jsonText) => {
4538
- if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
4698
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4539
4699
  return;
4540
4700
  }
4541
4701
  const trimmed = jsonText.trim();
@@ -4562,7 +4722,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
4562
4722
  }
4563
4723
  };
4564
4724
  const inspectEventBlock = (eventBlock) => {
4565
- if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
4725
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4566
4726
  return;
4567
4727
  }
4568
4728
  const lines = eventBlock.split(/\r?\n/);
@@ -4578,32 +4738,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
4578
4738
  const payload = dataParts.join("\n");
4579
4739
  inspectDataPayload(payload);
4580
4740
  };
4581
- const emitEventBlock = (self, eventBlock) => {
4582
- if (eventBlock.length === 0) return;
4583
- inspectEventBlock(eventBlock);
4584
- self.push(eventBlock + "\n\n");
4741
+ const processBufferedEvents = () => {
4742
+ const terminator = /\r?\n\r?\n/g;
4743
+ let lastIndex = 0;
4744
+ let match;
4745
+ while ((match = terminator.exec(buffer)) !== null) {
4746
+ const block = buffer.slice(lastIndex, match.index);
4747
+ lastIndex = match.index + match[0].length;
4748
+ if (block.length > 0) {
4749
+ inspectEventBlock(block);
4750
+ }
4751
+ }
4752
+ if (lastIndex > 0) {
4753
+ buffer = buffer.slice(lastIndex);
4754
+ }
4585
4755
  };
4586
4756
  return new Transform({
4587
4757
  transform(chunk, _encoding, callback) {
4588
- buffer += chunk.toString();
4589
- const terminator = /\r?\n\r?\n/g;
4590
- let lastIndex = 0;
4591
- let match;
4592
- while ((match = terminator.exec(buffer)) !== null) {
4593
- const block = buffer.slice(lastIndex, match.index);
4594
- lastIndex = match.index + match[0].length;
4595
- emitEventBlock(this, block);
4596
- }
4597
- if (lastIndex > 0) {
4598
- buffer = buffer.slice(lastIndex);
4599
- }
4758
+ this.push(chunk);
4759
+ buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
4760
+ processBufferedEvents();
4600
4761
  callback();
4601
4762
  },
4602
4763
  flush(callback) {
4764
+ buffer += decoder.end();
4765
+ processBufferedEvents();
4603
4766
  if (buffer.length > 0) {
4604
4767
  const tail = buffer.replace(/\r?\n+$/, "");
4605
4768
  if (tail.length > 0) {
4606
- emitEventBlock(this, tail);
4769
+ inspectEventBlock(tail);
4607
4770
  }
4608
4771
  buffer = "";
4609
4772
  }
@@ -4611,6 +4774,8 @@ function createSSEParserTransform(onUsage, onResponseId) {
4611
4774
  }
4612
4775
  });
4613
4776
  }
4777
+
4778
+ // client/RoutstrClient.ts
4614
4779
  var TOPUP_MARGIN = 1.2;
4615
4780
  var RoutstrClient = class {
4616
4781
  constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
@@ -4710,31 +4875,12 @@ var RoutstrClient = class {
4710
4875
  */
4711
4876
  async routeRequest(params) {
4712
4877
  const prepared = await this._prepareRoutedRequest(params);
4713
- const satsSpent = await this._handlePostResponseBalanceUpdate({
4714
- token: prepared.tokenUsed,
4715
- baseUrl: prepared.baseUrlUsed,
4716
- mintUrl: params.mintUrl,
4717
- initialTokenBalance: prepared.tokenBalanceInSats,
4718
- response: prepared.response,
4719
- modelId: prepared.modelId,
4720
- usage: prepared.capturedUsage,
4721
- requestId: prepared.capturedResponseId,
4722
- clientApiKey: prepared.clientApiKey
4723
- });
4724
- prepared.response.satsSpent = satsSpent;
4725
- prepared.response.usage = prepared.capturedUsage;
4726
- prepared.response.requestId = prepared.capturedResponseId;
4727
- return prepared.response;
4728
- }
4729
- async routeRequestToNodeResponse(params) {
4730
- const { res } = params;
4731
- const prepared = await this._prepareRoutedRequest(params);
4732
- res.statusCode = prepared.response.status;
4733
- prepared.response.headers.forEach((value, key) => {
4734
- res.setHeader(key, value);
4735
- });
4736
- const body = prepared.response.body;
4737
- if (!body) {
4878
+ const contentType = prepared.response.headers.get("content-type") || "";
4879
+ const isSSE = contentType.includes("text/event-stream");
4880
+ const runFinalize = async () => {
4881
+ const { capturedUsage, capturedResponseId } = await prepared.usagePromise;
4882
+ const usage = capturedUsage ?? prepared.capturedUsage;
4883
+ const requestId = capturedResponseId ?? prepared.capturedResponseId;
4738
4884
  const satsSpent = await this._handlePostResponseBalanceUpdate({
4739
4885
  token: prepared.tokenUsed,
4740
4886
  baseUrl: prepared.baseUrlUsed,
@@ -4742,51 +4888,25 @@ var RoutstrClient = class {
4742
4888
  initialTokenBalance: prepared.tokenBalanceInSats,
4743
4889
  response: prepared.response,
4744
4890
  modelId: prepared.modelId,
4745
- usage: prepared.capturedUsage,
4746
- requestId: prepared.capturedResponseId,
4891
+ usage,
4892
+ requestId,
4747
4893
  clientApiKey: prepared.clientApiKey
4748
4894
  });
4749
4895
  prepared.response.satsSpent = satsSpent;
4750
- res.end();
4751
- return;
4896
+ prepared.response.usage = usage;
4897
+ prepared.response.requestId = requestId;
4898
+ return satsSpent;
4899
+ };
4900
+ if (isSSE) {
4901
+ const finalizePromise = runFinalize().catch((error) => {
4902
+ this._log("ERROR", "[RoutstrClient] SSE finalize failed:", error);
4903
+ return 0;
4904
+ });
4905
+ prepared.response.finalize = () => finalizePromise;
4906
+ return prepared.response;
4752
4907
  }
4753
- const nodeReadable = Readable.fromWeb(body);
4754
- await new Promise((resolve, reject) => {
4755
- let settled = false;
4756
- const finish = async () => {
4757
- if (settled) return;
4758
- settled = true;
4759
- try {
4760
- const satsSpent = await this._handlePostResponseBalanceUpdate({
4761
- token: prepared.tokenUsed,
4762
- baseUrl: prepared.baseUrlUsed,
4763
- mintUrl: params.mintUrl,
4764
- initialTokenBalance: prepared.tokenBalanceInSats,
4765
- response: prepared.response,
4766
- modelId: prepared.modelId,
4767
- usage: prepared.capturedUsage,
4768
- requestId: prepared.capturedResponseId,
4769
- clientApiKey: prepared.clientApiKey
4770
- });
4771
- prepared.response.satsSpent = satsSpent;
4772
- prepared.response.usage = prepared.capturedUsage;
4773
- prepared.response.requestId = prepared.capturedResponseId;
4774
- resolve();
4775
- } catch (error) {
4776
- reject(error);
4777
- }
4778
- };
4779
- const fail = (error) => {
4780
- if (settled) return;
4781
- settled = true;
4782
- reject(error);
4783
- };
4784
- res.once("finish", finish);
4785
- res.once("close", finish);
4786
- res.once("error", fail);
4787
- nodeReadable.once("error", fail);
4788
- nodeReadable.pipe(res);
4789
- });
4908
+ await runFinalize();
4909
+ return prepared.response;
4790
4910
  }
4791
4911
  async _prepareRoutedRequest(params) {
4792
4912
  const {
@@ -4810,9 +4930,23 @@ var RoutstrClient = class {
4810
4930
  );
4811
4931
  selectedModel = providerModel ?? void 0;
4812
4932
  if (selectedModel) {
4933
+ const requestMessages = Array.isArray(
4934
+ body?.messages
4935
+ ) ? body.messages : [];
4936
+ const requestMaxTokens = typeof body?.max_tokens === "number" ? body.max_tokens : void 0;
4937
+ this._log(
4938
+ "DEBUG",
4939
+ "[RoutstrClient] generic request pricing input",
4940
+ {
4941
+ modelId: selectedModel.id,
4942
+ messageCount: requestMessages.length,
4943
+ maxTokens: requestMaxTokens
4944
+ }
4945
+ );
4813
4946
  requiredSats = this.providerManager.getRequiredSatsForModel(
4814
4947
  selectedModel,
4815
- []
4948
+ requestMessages,
4949
+ requestMaxTokens
4816
4950
  );
4817
4951
  }
4818
4952
  }
@@ -4849,22 +4983,18 @@ var RoutstrClient = class {
4849
4983
  let processedResponse = response;
4850
4984
  let capturedUsage;
4851
4985
  let capturedResponseId;
4986
+ let usagePromise = Promise.resolve({});
4852
4987
  if (contentType.includes("text/event-stream") && response.body) {
4853
- const logDir = path.join(os.homedir(), ".routstrd", "stream-response");
4854
- if (!fs.existsSync(logDir)) {
4855
- fs.mkdirSync(logDir, { recursive: true });
4856
- }
4857
- const logFile = path.join(logDir, `${Date.now()}.jsonl`);
4858
- const logStream = fs.createWriteStream(logFile);
4859
- const nodeReadable = Readable.fromWeb(response.body);
4860
- const loggingTransform = new Transform({
4861
- transform(chunk, encoding, callback) {
4862
- const raw = chunk.toString();
4863
- logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + "\n");
4864
- callback(null, chunk);
4865
- }
4988
+ const [clientStream, inspectStream] = response.body.tee();
4989
+ processedResponse = new Response(clientStream, {
4990
+ status: response.status,
4991
+ statusText: response.statusText,
4992
+ headers: response.headers
4866
4993
  });
4867
- const sseParser = createSSEParserTransform(
4994
+ processedResponse.baseUrl = response.baseUrl;
4995
+ processedResponse.token = response.token;
4996
+ usagePromise = inspectSSEWebStream(
4997
+ inspectStream,
4868
4998
  (usage) => {
4869
4999
  capturedUsage = usage;
4870
5000
  processedResponse.usage = usage;
@@ -4874,17 +5004,7 @@ var RoutstrClient = class {
4874
5004
  processedResponse.requestId = responseId;
4875
5005
  }
4876
5006
  );
4877
- const transformed = nodeReadable.pipe(loggingTransform).pipe(sseParser, { end: true });
4878
- const webStream = Readable.toWeb(
4879
- transformed
4880
- );
4881
- processedResponse = new Response(webStream, {
4882
- status: response.status,
4883
- statusText: response.statusText,
4884
- headers: response.headers
4885
- });
4886
- processedResponse.baseUrl = response.baseUrl;
4887
- processedResponse.token = response.token;
5007
+ processedResponse.usagePromise = usagePromise;
4888
5008
  }
4889
5009
  return {
4890
5010
  response: processedResponse,
@@ -4894,7 +5014,8 @@ var RoutstrClient = class {
4894
5014
  modelId,
4895
5015
  capturedUsage,
4896
5016
  capturedResponseId,
4897
- clientApiKey
5017
+ clientApiKey,
5018
+ usagePromise
4898
5019
  };
4899
5020
  }
4900
5021
  /**
@@ -5045,9 +5166,9 @@ var RoutstrClient = class {
5045
5166
  * Make the API request with failover support
5046
5167
  */
5047
5168
  async _makeRequest(params) {
5048
- const { path: path2, method, body, baseUrl, token, headers } = params;
5169
+ const { path, method, body, baseUrl, token, headers } = params;
5049
5170
  try {
5050
- const url = `${baseUrl.replace(/\/$/, "")}${path2}`;
5171
+ const url = `${baseUrl.replace(/\/$/, "")}${path}`;
5051
5172
  if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
5052
5173
  const response = await fetch(url, {
5053
5174
  method,
@@ -5097,7 +5218,7 @@ var RoutstrClient = class {
5097
5218
  */
5098
5219
  async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
5099
5220
  const MAX_RETRIES_PER_PROVIDER = 2;
5100
- const { path: path2, method, body, selectedModel, baseUrl, mintUrl } = params;
5221
+ const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
5101
5222
  let tryNextProvider = false;
5102
5223
  const errorMessage = responseBody;
5103
5224
  this._log(
@@ -5170,7 +5291,7 @@ var RoutstrClient = class {
5170
5291
  );
5171
5292
  const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
5172
5293
  const shortfall = Math.max(0, params.requiredSats - currentBalance);
5173
- topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
5294
+ topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
5174
5295
  this._log(
5175
5296
  "DEBUG",
5176
5297
  `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
@@ -5399,7 +5520,7 @@ var RoutstrClient = class {
5399
5520
  });
5400
5521
  return this._makeRequest({
5401
5522
  ...params,
5402
- path: path2,
5523
+ path,
5403
5524
  method,
5404
5525
  body,
5405
5526
  baseUrl: nextProvider,
@@ -5807,7 +5928,7 @@ async function resolveRouteRequestContext(options) {
5807
5928
  const {
5808
5929
  modelId,
5809
5930
  requestBody,
5810
- path: path2 = "/v1/chat/completions",
5931
+ path = "/v1/chat/completions",
5811
5932
  headers = {},
5812
5933
  forcedProvider,
5813
5934
  walletAdapter,
@@ -5906,17 +6027,17 @@ async function resolveRouteRequestContext(options) {
5906
6027
  client,
5907
6028
  baseUrl,
5908
6029
  mintUrl,
5909
- path: path2,
6030
+ path,
5910
6031
  headers,
5911
6032
  modelId,
5912
6033
  proxiedBody
5913
6034
  };
5914
6035
  }
5915
6036
  async function routeRequests(options) {
5916
- const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
6037
+ const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
5917
6038
  try {
5918
6039
  const response = await client.routeRequest({
5919
- path: path2,
6040
+ path,
5920
6041
  method: "POST",
5921
6042
  body: proxiedBody,
5922
6043
  headers,
@@ -5935,27 +6056,6 @@ async function routeRequests(options) {
5935
6056
  throw error;
5936
6057
  }
5937
6058
  }
5938
- async function routeRequestsToNodeResponse(options) {
5939
- const { res } = options;
5940
- const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
5941
- try {
5942
- await client.routeRequestToNodeResponse({
5943
- path: path2,
5944
- method: "POST",
5945
- body: proxiedBody,
5946
- headers,
5947
- baseUrl,
5948
- mintUrl,
5949
- modelId,
5950
- res
5951
- });
5952
- } catch (error) {
5953
- if (error instanceof Error && (error.message.includes("401") || error.message.includes("402") || error.message.includes("403"))) {
5954
- throw new Error(`Authentication failed: ${error.message}`);
5955
- }
5956
- throw error;
5957
- }
5958
- }
5959
6059
  function extractMaxTokens(requestBody) {
5960
6060
  if (!requestBody || typeof requestBody !== "object") {
5961
6061
  return void 0;
@@ -5973,6 +6073,6 @@ function extractStream(requestBody) {
5973
6073
  return typeof stream === "boolean" ? stream : void 0;
5974
6074
  }
5975
6075
 
5976
- export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, createBunSqliteDriver, createBunSqliteUsageTrackingDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createSqliteDriver, createSqliteUsageTrackingDriver, createStorageAdapterFromStore, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, isOnionUrl, isTorContext, localStorageDriver, normalizeProviderUrl, routeRequests, routeRequestsToNodeResponse, setDefaultUsageTrackingDriver };
6076
+ export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, createBunSqliteDriver, createBunSqliteUsageTrackingDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createSqliteDriver, createSqliteUsageTrackingDriver, createStorageAdapterFromStore, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, inspectSSEWebStream, isOnionUrl, isTorContext, localStorageDriver, normalizeProviderUrl, routeRequests, setDefaultUsageTrackingDriver };
5977
6077
  //# sourceMappingURL=index.mjs.map
5978
6078
  //# sourceMappingURL=index.mjs.map