@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.
@@ -3,31 +3,7 @@
3
3
  var cashuTs = require('@cashu/cashu-ts');
4
4
  var vanilla = require('zustand/vanilla');
5
5
  var stream = require('stream');
6
- var fs = require('fs');
7
- var path = require('path');
8
- var os = require('os');
9
-
10
- function _interopNamespace(e) {
11
- if (e && e.__esModule) return e;
12
- var n = Object.create(null);
13
- if (e) {
14
- Object.keys(e).forEach(function (k) {
15
- if (k !== 'default') {
16
- var d = Object.getOwnPropertyDescriptor(e, k);
17
- Object.defineProperty(n, k, d.get ? d : {
18
- enumerable: true,
19
- get: function () { return e[k]; }
20
- });
21
- }
22
- });
23
- }
24
- n.default = e;
25
- return Object.freeze(n);
26
- }
27
-
28
- var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
29
- var path__namespace = /*#__PURE__*/_interopNamespace(path);
30
- var os__namespace = /*#__PURE__*/_interopNamespace(os);
6
+ var string_decoder = require('string_decoder');
31
7
 
32
8
  // core/errors.ts
33
9
  var InsufficientBalanceError = class extends Error {
@@ -81,10 +57,10 @@ var AuditLogger = class _AuditLogger {
81
57
  const logLine = JSON.stringify(fullEntry) + "\n";
82
58
  if (typeof window === "undefined") {
83
59
  try {
84
- const fs2 = await import('fs');
85
- const path2 = await import('path');
86
- const logPath = path2.join(process.cwd(), "audit.log");
87
- fs2.appendFileSync(logPath, logLine);
60
+ const fs = await import('fs');
61
+ const path = await import('path');
62
+ const logPath = path.join(process.cwd(), "audit.log");
63
+ fs.appendFileSync(logPath, logLine);
88
64
  } catch (error) {
89
65
  console.error("[AuditLogger] Failed to write to file:", error);
90
66
  }
@@ -663,7 +639,7 @@ var CashuSpender = class {
663
639
  };
664
640
 
665
641
  // wallet/BalanceManager.ts
666
- var BalanceManager = class {
642
+ var BalanceManager = class _BalanceManager {
667
643
  constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
668
644
  this.walletAdapter = walletAdapter;
669
645
  this.storageAdapter = storageAdapter;
@@ -680,6 +656,47 @@ var BalanceManager = class {
680
656
  }
681
657
  }
682
658
  cashuSpender;
659
+ /** In-memory guard for per-provider wallet mutations (topup / refund) */
660
+ providerWalletOps = /* @__PURE__ */ new Map();
661
+ /** Cooldown (ms) between opposite operations on the same provider */
662
+ static PROVIDER_WALLET_COOLDOWN_MS = 1e4;
663
+ /**
664
+ * Check whether a wallet operation (topup/refund) may run for a provider.
665
+ * Returns the reason when blocked.
666
+ */
667
+ _canRunProviderWalletOperation(baseUrl, type) {
668
+ const existing = this.providerWalletOps.get(baseUrl);
669
+ if (!existing) {
670
+ return { allowed: true };
671
+ }
672
+ if (existing.type === type) {
673
+ return { allowed: true };
674
+ }
675
+ if (!existing.endTime) {
676
+ return {
677
+ allowed: false,
678
+ reason: `Provider wallet operation locked; ${existing.type} in progress`
679
+ };
680
+ }
681
+ const elapsed = Date.now() - existing.endTime;
682
+ if (elapsed < _BalanceManager.PROVIDER_WALLET_COOLDOWN_MS) {
683
+ return {
684
+ allowed: false,
685
+ reason: `Provider wallet operation locked; recent ${existing.type} completed ${Math.round(elapsed / 1e3)}s ago`
686
+ };
687
+ }
688
+ this.providerWalletOps.delete(baseUrl);
689
+ return { allowed: true };
690
+ }
691
+ _beginProviderWalletOperation(baseUrl, type) {
692
+ this.providerWalletOps.set(baseUrl, { type, startTime: Date.now() });
693
+ }
694
+ _endProviderWalletOperation(baseUrl, type) {
695
+ const existing = this.providerWalletOps.get(baseUrl);
696
+ if (existing && existing.type === type) {
697
+ existing.endTime = Date.now();
698
+ }
699
+ }
683
700
  async getBalanceState() {
684
701
  const mintBalances = await this.walletAdapter.getBalances();
685
702
  const units = this.walletAdapter.getMintUnits();
@@ -714,6 +731,20 @@ var BalanceManager = class {
714
731
  * @returns Refund result
715
732
  */
716
733
  async refundApiKey(options) {
734
+ const { mintUrl, baseUrl, apiKey, forceRefund } = options;
735
+ const guard = this._canRunProviderWalletOperation(baseUrl, "refund");
736
+ if (!guard.allowed) {
737
+ console.log(`[BalanceManager] Skipping refund for ${baseUrl} - ${guard.reason}`);
738
+ return { success: false, message: guard.reason };
739
+ }
740
+ this._beginProviderWalletOperation(baseUrl, "refund");
741
+ try {
742
+ return await this._refundApiKeyImpl({ mintUrl, baseUrl, apiKey, forceRefund });
743
+ } finally {
744
+ this._endProviderWalletOperation(baseUrl, "refund");
745
+ }
746
+ }
747
+ async _refundApiKeyImpl(options) {
717
748
  const { mintUrl, baseUrl, apiKey, forceRefund } = options;
718
749
  if (!apiKey) {
719
750
  return { success: false, message: "No API key to refund" };
@@ -842,6 +873,20 @@ var BalanceManager = class {
842
873
  * Top up API key balance with a cashu token
843
874
  */
844
875
  async topUp(options) {
876
+ const { mintUrl, baseUrl, amount, token: providedToken } = options;
877
+ const guard = this._canRunProviderWalletOperation(baseUrl, "topup");
878
+ if (!guard.allowed) {
879
+ console.log(`[BalanceManager] Skipping topup for ${baseUrl} - ${guard.reason}`);
880
+ return { success: false, message: guard.reason };
881
+ }
882
+ this._beginProviderWalletOperation(baseUrl, "topup");
883
+ try {
884
+ return await this._topUpImpl({ mintUrl, baseUrl, amount, token: providedToken });
885
+ } finally {
886
+ this._endProviderWalletOperation(baseUrl, "topup");
887
+ }
888
+ }
889
+ async _topUpImpl(options) {
845
890
  const { mintUrl, baseUrl, amount, token: providedToken } = options;
846
891
  if (!amount || amount <= 0) {
847
892
  return { success: false, message: "Invalid top up amount" };
@@ -1002,7 +1047,7 @@ var BalanceManager = class {
1002
1047
  p2pkPubkey
1003
1048
  );
1004
1049
  console.log(
1005
- `[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
1050
+ `[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])])))}`
1006
1051
  );
1007
1052
  return {
1008
1053
  success: true,
@@ -2141,7 +2186,9 @@ var ProviderManager = class _ProviderManager {
2141
2186
  const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
2142
2187
  const totalInputTokens = approximateTokens + imageTokens;
2143
2188
  const sp = model?.sats_pricing;
2144
- if (!sp) return 0;
2189
+ if (!sp) {
2190
+ return 0;
2191
+ }
2145
2192
  if (!sp.max_completion_cost) {
2146
2193
  return sp.max_cost ?? 50;
2147
2194
  }
@@ -3453,26 +3500,117 @@ var getDefaultUsageTrackingDriver = () => {
3453
3500
  defaultUsageTrackingDriver = createMemoryUsageTrackingDriver();
3454
3501
  return defaultUsageTrackingDriver;
3455
3502
  };
3456
- function createSSEParserTransform(onUsage, onResponseId) {
3503
+ function mergeUsage(previous, next) {
3504
+ if (!previous) return next;
3505
+ return {
3506
+ promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
3507
+ completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
3508
+ totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
3509
+ cost: next.cost > 0 ? next.cost : previous.cost,
3510
+ satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
3511
+ };
3512
+ }
3513
+ function hasUsageChanged(previous, next) {
3514
+ if (!previous) return true;
3515
+ return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
3516
+ }
3517
+ async function inspectSSEWebStream(stream, onUsage, onResponseId) {
3518
+ const reader = stream.getReader();
3519
+ const decoder = new TextDecoder("utf-8");
3457
3520
  let buffer = "";
3458
3521
  let capturedUsage = null;
3522
+ let capturedResponseId;
3459
3523
  let responseIdCaptured = false;
3460
- const mergeUsage = (previous, next) => {
3461
- if (!previous) return next;
3462
- return {
3463
- promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
3464
- completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
3465
- totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
3466
- cost: next.cost > 0 ? next.cost : previous.cost,
3467
- satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
3468
- };
3524
+ const inspectDataPayload = (jsonText) => {
3525
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
3526
+ return;
3527
+ }
3528
+ const trimmed = jsonText.trim();
3529
+ if (!trimmed || trimmed === "[DONE]") return;
3530
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
3531
+ try {
3532
+ const data = JSON.parse(trimmed);
3533
+ if (!responseIdCaptured) {
3534
+ const responseId = data?.id;
3535
+ if (typeof responseId === "string" && responseId.trim().length > 0) {
3536
+ capturedResponseId = responseId.trim();
3537
+ onResponseId?.(capturedResponseId);
3538
+ responseIdCaptured = true;
3539
+ }
3540
+ }
3541
+ const usage = extractUsageFromSSEJson(data);
3542
+ if (usage) {
3543
+ const merged = mergeUsage(capturedUsage, usage);
3544
+ if (hasUsageChanged(capturedUsage, merged)) {
3545
+ capturedUsage = merged;
3546
+ onUsage(merged);
3547
+ }
3548
+ }
3549
+ } catch {
3550
+ }
3551
+ };
3552
+ const inspectEventBlock = (eventBlock) => {
3553
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
3554
+ return;
3555
+ }
3556
+ const lines = eventBlock.split(/\r?\n/);
3557
+ const dataParts = [];
3558
+ for (const line of lines) {
3559
+ if (!line || line.startsWith(":")) continue;
3560
+ if (line.startsWith("data:")) {
3561
+ const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
3562
+ dataParts.push(value);
3563
+ }
3564
+ }
3565
+ if (dataParts.length === 0) return;
3566
+ inspectDataPayload(dataParts.join("\n"));
3469
3567
  };
3470
- const hasUsageChanged = (previous, next) => {
3471
- if (!previous) return true;
3472
- return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
3568
+ const drainBufferedEvents = () => {
3569
+ const terminator = /\r?\n\r?\n/g;
3570
+ let lastIndex = 0;
3571
+ let match;
3572
+ while ((match = terminator.exec(buffer)) !== null) {
3573
+ const block = buffer.slice(lastIndex, match.index);
3574
+ lastIndex = match.index + match[0].length;
3575
+ if (block.length > 0) inspectEventBlock(block);
3576
+ }
3577
+ if (lastIndex > 0) buffer = buffer.slice(lastIndex);
3578
+ };
3579
+ try {
3580
+ while (true) {
3581
+ const { value, done } = await reader.read();
3582
+ if (done) break;
3583
+ if (value && value.byteLength > 0) {
3584
+ buffer += decoder.decode(value, { stream: true });
3585
+ drainBufferedEvents();
3586
+ }
3587
+ }
3588
+ buffer += decoder.decode();
3589
+ drainBufferedEvents();
3590
+ if (buffer.length > 0) {
3591
+ const tail = buffer.replace(/\r?\n+$/, "");
3592
+ if (tail.length > 0) inspectEventBlock(tail);
3593
+ buffer = "";
3594
+ }
3595
+ } catch {
3596
+ } finally {
3597
+ try {
3598
+ reader.releaseLock();
3599
+ } catch {
3600
+ }
3601
+ }
3602
+ return {
3603
+ capturedUsage: capturedUsage ?? void 0,
3604
+ capturedResponseId
3473
3605
  };
3606
+ }
3607
+ function createSSEParserTransform(onUsage, onResponseId) {
3608
+ let buffer = "";
3609
+ const decoder = new string_decoder.StringDecoder("utf8");
3610
+ let capturedUsage = null;
3611
+ let responseIdCaptured = false;
3474
3612
  const inspectDataPayload = (jsonText) => {
3475
- if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
3613
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
3476
3614
  return;
3477
3615
  }
3478
3616
  const trimmed = jsonText.trim();
@@ -3499,7 +3637,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
3499
3637
  }
3500
3638
  };
3501
3639
  const inspectEventBlock = (eventBlock) => {
3502
- if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
3640
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
3503
3641
  return;
3504
3642
  }
3505
3643
  const lines = eventBlock.split(/\r?\n/);
@@ -3515,32 +3653,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
3515
3653
  const payload = dataParts.join("\n");
3516
3654
  inspectDataPayload(payload);
3517
3655
  };
3518
- const emitEventBlock = (self, eventBlock) => {
3519
- if (eventBlock.length === 0) return;
3520
- inspectEventBlock(eventBlock);
3521
- self.push(eventBlock + "\n\n");
3656
+ const processBufferedEvents = () => {
3657
+ const terminator = /\r?\n\r?\n/g;
3658
+ let lastIndex = 0;
3659
+ let match;
3660
+ while ((match = terminator.exec(buffer)) !== null) {
3661
+ const block = buffer.slice(lastIndex, match.index);
3662
+ lastIndex = match.index + match[0].length;
3663
+ if (block.length > 0) {
3664
+ inspectEventBlock(block);
3665
+ }
3666
+ }
3667
+ if (lastIndex > 0) {
3668
+ buffer = buffer.slice(lastIndex);
3669
+ }
3522
3670
  };
3523
3671
  return new stream.Transform({
3524
3672
  transform(chunk, _encoding, callback) {
3525
- buffer += chunk.toString();
3526
- const terminator = /\r?\n\r?\n/g;
3527
- let lastIndex = 0;
3528
- let match;
3529
- while ((match = terminator.exec(buffer)) !== null) {
3530
- const block = buffer.slice(lastIndex, match.index);
3531
- lastIndex = match.index + match[0].length;
3532
- emitEventBlock(this, block);
3533
- }
3534
- if (lastIndex > 0) {
3535
- buffer = buffer.slice(lastIndex);
3536
- }
3673
+ this.push(chunk);
3674
+ buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
3675
+ processBufferedEvents();
3537
3676
  callback();
3538
3677
  },
3539
3678
  flush(callback) {
3679
+ buffer += decoder.end();
3680
+ processBufferedEvents();
3540
3681
  if (buffer.length > 0) {
3541
3682
  const tail = buffer.replace(/\r?\n+$/, "");
3542
3683
  if (tail.length > 0) {
3543
- emitEventBlock(this, tail);
3684
+ inspectEventBlock(tail);
3544
3685
  }
3545
3686
  buffer = "";
3546
3687
  }
@@ -3548,6 +3689,8 @@ function createSSEParserTransform(onUsage, onResponseId) {
3548
3689
  }
3549
3690
  });
3550
3691
  }
3692
+
3693
+ // client/RoutstrClient.ts
3551
3694
  var TOPUP_MARGIN = 1.2;
3552
3695
  var RoutstrClient = class {
3553
3696
  constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
@@ -3647,31 +3790,12 @@ var RoutstrClient = class {
3647
3790
  */
3648
3791
  async routeRequest(params) {
3649
3792
  const prepared = await this._prepareRoutedRequest(params);
3650
- const satsSpent = await this._handlePostResponseBalanceUpdate({
3651
- token: prepared.tokenUsed,
3652
- baseUrl: prepared.baseUrlUsed,
3653
- mintUrl: params.mintUrl,
3654
- initialTokenBalance: prepared.tokenBalanceInSats,
3655
- response: prepared.response,
3656
- modelId: prepared.modelId,
3657
- usage: prepared.capturedUsage,
3658
- requestId: prepared.capturedResponseId,
3659
- clientApiKey: prepared.clientApiKey
3660
- });
3661
- prepared.response.satsSpent = satsSpent;
3662
- prepared.response.usage = prepared.capturedUsage;
3663
- prepared.response.requestId = prepared.capturedResponseId;
3664
- return prepared.response;
3665
- }
3666
- async routeRequestToNodeResponse(params) {
3667
- const { res } = params;
3668
- const prepared = await this._prepareRoutedRequest(params);
3669
- res.statusCode = prepared.response.status;
3670
- prepared.response.headers.forEach((value, key) => {
3671
- res.setHeader(key, value);
3672
- });
3673
- const body = prepared.response.body;
3674
- if (!body) {
3793
+ const contentType = prepared.response.headers.get("content-type") || "";
3794
+ const isSSE = contentType.includes("text/event-stream");
3795
+ const runFinalize = async () => {
3796
+ const { capturedUsage, capturedResponseId } = await prepared.usagePromise;
3797
+ const usage = capturedUsage ?? prepared.capturedUsage;
3798
+ const requestId = capturedResponseId ?? prepared.capturedResponseId;
3675
3799
  const satsSpent = await this._handlePostResponseBalanceUpdate({
3676
3800
  token: prepared.tokenUsed,
3677
3801
  baseUrl: prepared.baseUrlUsed,
@@ -3679,51 +3803,25 @@ var RoutstrClient = class {
3679
3803
  initialTokenBalance: prepared.tokenBalanceInSats,
3680
3804
  response: prepared.response,
3681
3805
  modelId: prepared.modelId,
3682
- usage: prepared.capturedUsage,
3683
- requestId: prepared.capturedResponseId,
3806
+ usage,
3807
+ requestId,
3684
3808
  clientApiKey: prepared.clientApiKey
3685
3809
  });
3686
3810
  prepared.response.satsSpent = satsSpent;
3687
- res.end();
3688
- return;
3811
+ prepared.response.usage = usage;
3812
+ prepared.response.requestId = requestId;
3813
+ return satsSpent;
3814
+ };
3815
+ if (isSSE) {
3816
+ const finalizePromise = runFinalize().catch((error) => {
3817
+ this._log("ERROR", "[RoutstrClient] SSE finalize failed:", error);
3818
+ return 0;
3819
+ });
3820
+ prepared.response.finalize = () => finalizePromise;
3821
+ return prepared.response;
3689
3822
  }
3690
- const nodeReadable = stream.Readable.fromWeb(body);
3691
- await new Promise((resolve, reject) => {
3692
- let settled = false;
3693
- const finish = async () => {
3694
- if (settled) return;
3695
- settled = true;
3696
- try {
3697
- const satsSpent = await this._handlePostResponseBalanceUpdate({
3698
- token: prepared.tokenUsed,
3699
- baseUrl: prepared.baseUrlUsed,
3700
- mintUrl: params.mintUrl,
3701
- initialTokenBalance: prepared.tokenBalanceInSats,
3702
- response: prepared.response,
3703
- modelId: prepared.modelId,
3704
- usage: prepared.capturedUsage,
3705
- requestId: prepared.capturedResponseId,
3706
- clientApiKey: prepared.clientApiKey
3707
- });
3708
- prepared.response.satsSpent = satsSpent;
3709
- prepared.response.usage = prepared.capturedUsage;
3710
- prepared.response.requestId = prepared.capturedResponseId;
3711
- resolve();
3712
- } catch (error) {
3713
- reject(error);
3714
- }
3715
- };
3716
- const fail = (error) => {
3717
- if (settled) return;
3718
- settled = true;
3719
- reject(error);
3720
- };
3721
- res.once("finish", finish);
3722
- res.once("close", finish);
3723
- res.once("error", fail);
3724
- nodeReadable.once("error", fail);
3725
- nodeReadable.pipe(res);
3726
- });
3823
+ await runFinalize();
3824
+ return prepared.response;
3727
3825
  }
3728
3826
  async _prepareRoutedRequest(params) {
3729
3827
  const {
@@ -3747,9 +3845,23 @@ var RoutstrClient = class {
3747
3845
  );
3748
3846
  selectedModel = providerModel ?? void 0;
3749
3847
  if (selectedModel) {
3848
+ const requestMessages = Array.isArray(
3849
+ body?.messages
3850
+ ) ? body.messages : [];
3851
+ const requestMaxTokens = typeof body?.max_tokens === "number" ? body.max_tokens : void 0;
3852
+ this._log(
3853
+ "DEBUG",
3854
+ "[RoutstrClient] generic request pricing input",
3855
+ {
3856
+ modelId: selectedModel.id,
3857
+ messageCount: requestMessages.length,
3858
+ maxTokens: requestMaxTokens
3859
+ }
3860
+ );
3750
3861
  requiredSats = this.providerManager.getRequiredSatsForModel(
3751
3862
  selectedModel,
3752
- []
3863
+ requestMessages,
3864
+ requestMaxTokens
3753
3865
  );
3754
3866
  }
3755
3867
  }
@@ -3786,22 +3898,18 @@ var RoutstrClient = class {
3786
3898
  let processedResponse = response;
3787
3899
  let capturedUsage;
3788
3900
  let capturedResponseId;
3901
+ let usagePromise = Promise.resolve({});
3789
3902
  if (contentType.includes("text/event-stream") && response.body) {
3790
- const logDir = path__namespace.join(os__namespace.homedir(), ".routstrd", "stream-response");
3791
- if (!fs__namespace.existsSync(logDir)) {
3792
- fs__namespace.mkdirSync(logDir, { recursive: true });
3793
- }
3794
- const logFile = path__namespace.join(logDir, `${Date.now()}.jsonl`);
3795
- const logStream = fs__namespace.createWriteStream(logFile);
3796
- const nodeReadable = stream.Readable.fromWeb(response.body);
3797
- const loggingTransform = new stream.Transform({
3798
- transform(chunk, encoding, callback) {
3799
- const raw = chunk.toString();
3800
- logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + "\n");
3801
- callback(null, chunk);
3802
- }
3903
+ const [clientStream, inspectStream] = response.body.tee();
3904
+ processedResponse = new Response(clientStream, {
3905
+ status: response.status,
3906
+ statusText: response.statusText,
3907
+ headers: response.headers
3803
3908
  });
3804
- const sseParser = createSSEParserTransform(
3909
+ processedResponse.baseUrl = response.baseUrl;
3910
+ processedResponse.token = response.token;
3911
+ usagePromise = inspectSSEWebStream(
3912
+ inspectStream,
3805
3913
  (usage) => {
3806
3914
  capturedUsage = usage;
3807
3915
  processedResponse.usage = usage;
@@ -3811,17 +3919,7 @@ var RoutstrClient = class {
3811
3919
  processedResponse.requestId = responseId;
3812
3920
  }
3813
3921
  );
3814
- const transformed = nodeReadable.pipe(loggingTransform).pipe(sseParser, { end: true });
3815
- const webStream = stream.Readable.toWeb(
3816
- transformed
3817
- );
3818
- processedResponse = new Response(webStream, {
3819
- status: response.status,
3820
- statusText: response.statusText,
3821
- headers: response.headers
3822
- });
3823
- processedResponse.baseUrl = response.baseUrl;
3824
- processedResponse.token = response.token;
3922
+ processedResponse.usagePromise = usagePromise;
3825
3923
  }
3826
3924
  return {
3827
3925
  response: processedResponse,
@@ -3831,7 +3929,8 @@ var RoutstrClient = class {
3831
3929
  modelId,
3832
3930
  capturedUsage,
3833
3931
  capturedResponseId,
3834
- clientApiKey
3932
+ clientApiKey,
3933
+ usagePromise
3835
3934
  };
3836
3935
  }
3837
3936
  /**
@@ -3982,9 +4081,9 @@ var RoutstrClient = class {
3982
4081
  * Make the API request with failover support
3983
4082
  */
3984
4083
  async _makeRequest(params) {
3985
- const { path: path2, method, body, baseUrl, token, headers } = params;
4084
+ const { path, method, body, baseUrl, token, headers } = params;
3986
4085
  try {
3987
- const url = `${baseUrl.replace(/\/$/, "")}${path2}`;
4086
+ const url = `${baseUrl.replace(/\/$/, "")}${path}`;
3988
4087
  if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
3989
4088
  const response = await fetch(url, {
3990
4089
  method,
@@ -4034,7 +4133,7 @@ var RoutstrClient = class {
4034
4133
  */
4035
4134
  async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
4036
4135
  const MAX_RETRIES_PER_PROVIDER = 2;
4037
- const { path: path2, method, body, selectedModel, baseUrl, mintUrl } = params;
4136
+ const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
4038
4137
  let tryNextProvider = false;
4039
4138
  const errorMessage = responseBody;
4040
4139
  this._log(
@@ -4107,7 +4206,7 @@ var RoutstrClient = class {
4107
4206
  );
4108
4207
  const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
4109
4208
  const shortfall = Math.max(0, params.requiredSats - currentBalance);
4110
- topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
4209
+ topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
4111
4210
  this._log(
4112
4211
  "DEBUG",
4113
4212
  `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
@@ -4336,7 +4435,7 @@ var RoutstrClient = class {
4336
4435
  });
4337
4436
  return this._makeRequest({
4338
4437
  ...params,
4339
- path: path2,
4438
+ path,
4340
4439
  method,
4341
4440
  body,
4342
4441
  baseUrl: nextProvider,
@@ -4743,5 +4842,6 @@ exports.ProviderManager = ProviderManager;
4743
4842
  exports.RoutstrClient = RoutstrClient;
4744
4843
  exports.StreamProcessor = StreamProcessor;
4745
4844
  exports.createSSEParserTransform = createSSEParserTransform;
4845
+ exports.inspectSSEWebStream = inspectSSEWebStream;
4746
4846
  //# sourceMappingURL=index.js.map
4747
4847
  //# sourceMappingURL=index.js.map