@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.js CHANGED
@@ -6,31 +6,7 @@ var rxjs = require('rxjs');
6
6
  var cashuTs = require('@cashu/cashu-ts');
7
7
  var vanilla = require('zustand/vanilla');
8
8
  var stream = require('stream');
9
- var fs = require('fs');
10
- var path = require('path');
11
- var os = require('os');
12
-
13
- function _interopNamespace(e) {
14
- if (e && e.__esModule) return e;
15
- var n = Object.create(null);
16
- if (e) {
17
- Object.keys(e).forEach(function (k) {
18
- if (k !== 'default') {
19
- var d = Object.getOwnPropertyDescriptor(e, k);
20
- Object.defineProperty(n, k, d.get ? d : {
21
- enumerable: true,
22
- get: function () { return e[k]; }
23
- });
24
- }
25
- });
26
- }
27
- n.default = e;
28
- return Object.freeze(n);
29
- }
30
-
31
- var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
32
- var path__namespace = /*#__PURE__*/_interopNamespace(path);
33
- var os__namespace = /*#__PURE__*/_interopNamespace(os);
9
+ var string_decoder = require('string_decoder');
34
10
 
35
11
  // core/errors.ts
36
12
  var InsufficientBalanceError = class extends Error {
@@ -715,10 +691,10 @@ var AuditLogger = class _AuditLogger {
715
691
  const logLine = JSON.stringify(fullEntry) + "\n";
716
692
  if (typeof window === "undefined") {
717
693
  try {
718
- const fs2 = await import('fs');
719
- const path2 = await import('path');
720
- const logPath = path2.join(process.cwd(), "audit.log");
721
- fs2.appendFileSync(logPath, logLine);
694
+ const fs = await import('fs');
695
+ const path = await import('path');
696
+ const logPath = path.join(process.cwd(), "audit.log");
697
+ fs.appendFileSync(logPath, logLine);
722
698
  } catch (error) {
723
699
  console.error("[AuditLogger] Failed to write to file:", error);
724
700
  }
@@ -1297,7 +1273,7 @@ var CashuSpender = class {
1297
1273
  };
1298
1274
 
1299
1275
  // wallet/BalanceManager.ts
1300
- var BalanceManager = class {
1276
+ var BalanceManager = class _BalanceManager {
1301
1277
  constructor(walletAdapter, storageAdapter, providerRegistry, cashuSpender) {
1302
1278
  this.walletAdapter = walletAdapter;
1303
1279
  this.storageAdapter = storageAdapter;
@@ -1314,6 +1290,47 @@ var BalanceManager = class {
1314
1290
  }
1315
1291
  }
1316
1292
  cashuSpender;
1293
+ /** In-memory guard for per-provider wallet mutations (topup / refund) */
1294
+ providerWalletOps = /* @__PURE__ */ new Map();
1295
+ /** Cooldown (ms) between opposite operations on the same provider */
1296
+ static PROVIDER_WALLET_COOLDOWN_MS = 1e4;
1297
+ /**
1298
+ * Check whether a wallet operation (topup/refund) may run for a provider.
1299
+ * Returns the reason when blocked.
1300
+ */
1301
+ _canRunProviderWalletOperation(baseUrl, type) {
1302
+ const existing = this.providerWalletOps.get(baseUrl);
1303
+ if (!existing) {
1304
+ return { allowed: true };
1305
+ }
1306
+ if (existing.type === type) {
1307
+ return { allowed: true };
1308
+ }
1309
+ if (!existing.endTime) {
1310
+ return {
1311
+ allowed: false,
1312
+ reason: `Provider wallet operation locked; ${existing.type} in progress`
1313
+ };
1314
+ }
1315
+ const elapsed = Date.now() - existing.endTime;
1316
+ if (elapsed < _BalanceManager.PROVIDER_WALLET_COOLDOWN_MS) {
1317
+ return {
1318
+ allowed: false,
1319
+ reason: `Provider wallet operation locked; recent ${existing.type} completed ${Math.round(elapsed / 1e3)}s ago`
1320
+ };
1321
+ }
1322
+ this.providerWalletOps.delete(baseUrl);
1323
+ return { allowed: true };
1324
+ }
1325
+ _beginProviderWalletOperation(baseUrl, type) {
1326
+ this.providerWalletOps.set(baseUrl, { type, startTime: Date.now() });
1327
+ }
1328
+ _endProviderWalletOperation(baseUrl, type) {
1329
+ const existing = this.providerWalletOps.get(baseUrl);
1330
+ if (existing && existing.type === type) {
1331
+ existing.endTime = Date.now();
1332
+ }
1333
+ }
1317
1334
  async getBalanceState() {
1318
1335
  const mintBalances = await this.walletAdapter.getBalances();
1319
1336
  const units = this.walletAdapter.getMintUnits();
@@ -1348,6 +1365,20 @@ var BalanceManager = class {
1348
1365
  * @returns Refund result
1349
1366
  */
1350
1367
  async refundApiKey(options) {
1368
+ const { mintUrl, baseUrl, apiKey, forceRefund } = options;
1369
+ const guard = this._canRunProviderWalletOperation(baseUrl, "refund");
1370
+ if (!guard.allowed) {
1371
+ console.log(`[BalanceManager] Skipping refund for ${baseUrl} - ${guard.reason}`);
1372
+ return { success: false, message: guard.reason };
1373
+ }
1374
+ this._beginProviderWalletOperation(baseUrl, "refund");
1375
+ try {
1376
+ return await this._refundApiKeyImpl({ mintUrl, baseUrl, apiKey, forceRefund });
1377
+ } finally {
1378
+ this._endProviderWalletOperation(baseUrl, "refund");
1379
+ }
1380
+ }
1381
+ async _refundApiKeyImpl(options) {
1351
1382
  const { mintUrl, baseUrl, apiKey, forceRefund } = options;
1352
1383
  if (!apiKey) {
1353
1384
  return { success: false, message: "No API key to refund" };
@@ -1476,6 +1507,20 @@ var BalanceManager = class {
1476
1507
  * Top up API key balance with a cashu token
1477
1508
  */
1478
1509
  async topUp(options) {
1510
+ const { mintUrl, baseUrl, amount, token: providedToken } = options;
1511
+ const guard = this._canRunProviderWalletOperation(baseUrl, "topup");
1512
+ if (!guard.allowed) {
1513
+ console.log(`[BalanceManager] Skipping topup for ${baseUrl} - ${guard.reason}`);
1514
+ return { success: false, message: guard.reason };
1515
+ }
1516
+ this._beginProviderWalletOperation(baseUrl, "topup");
1517
+ try {
1518
+ return await this._topUpImpl({ mintUrl, baseUrl, amount, token: providedToken });
1519
+ } finally {
1520
+ this._endProviderWalletOperation(baseUrl, "topup");
1521
+ }
1522
+ }
1523
+ async _topUpImpl(options) {
1479
1524
  const { mintUrl, baseUrl, amount, token: providedToken } = options;
1480
1525
  if (!amount || amount <= 0) {
1481
1526
  return { success: false, message: "Invalid top up amount" };
@@ -1636,7 +1681,7 @@ var BalanceManager = class {
1636
1681
  p2pkPubkey
1637
1682
  );
1638
1683
  console.log(
1639
- `[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
1684
+ `[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])])))}`
1640
1685
  );
1641
1686
  return {
1642
1687
  success: true,
@@ -2831,7 +2876,9 @@ var ProviderManager = class _ProviderManager {
2831
2876
  const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
2832
2877
  const totalInputTokens = approximateTokens + imageTokens;
2833
2878
  const sp = model?.sats_pricing;
2834
- if (!sp) return 0;
2879
+ if (!sp) {
2880
+ return 0;
2881
+ }
2835
2882
  if (!sp.max_completion_cost) {
2836
2883
  return sp.max_cost ?? 50;
2837
2884
  }
@@ -4540,26 +4587,117 @@ var setDefaultUsageTrackingDriver = (driver) => {
4540
4587
  var getDefaultDiscoveryAdapter = async () => createDiscoveryAdapterFromStore(await getDefaultSdkStore());
4541
4588
  var getDefaultStorageAdapter = async () => createStorageAdapterFromStore(await getDefaultSdkStore());
4542
4589
  var getDefaultProviderRegistry = async () => createProviderRegistryFromStore(await getDefaultSdkStore());
4543
- function createSSEParserTransform(onUsage, onResponseId) {
4590
+ function mergeUsage(previous, next) {
4591
+ if (!previous) return next;
4592
+ return {
4593
+ promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
4594
+ completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
4595
+ totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
4596
+ cost: next.cost > 0 ? next.cost : previous.cost,
4597
+ satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
4598
+ };
4599
+ }
4600
+ function hasUsageChanged(previous, next) {
4601
+ if (!previous) return true;
4602
+ return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
4603
+ }
4604
+ async function inspectSSEWebStream(stream, onUsage, onResponseId) {
4605
+ const reader = stream.getReader();
4606
+ const decoder = new TextDecoder("utf-8");
4544
4607
  let buffer = "";
4545
4608
  let capturedUsage = null;
4609
+ let capturedResponseId;
4546
4610
  let responseIdCaptured = false;
4547
- const mergeUsage = (previous, next) => {
4548
- if (!previous) return next;
4549
- return {
4550
- promptTokens: next.promptTokens > 0 ? next.promptTokens : previous.promptTokens,
4551
- completionTokens: next.completionTokens > 0 ? next.completionTokens : previous.completionTokens,
4552
- totalTokens: next.totalTokens > 0 ? next.totalTokens : previous.totalTokens,
4553
- cost: next.cost > 0 ? next.cost : previous.cost,
4554
- satsCost: next.satsCost > 0 ? next.satsCost : previous.satsCost
4555
- };
4611
+ const inspectDataPayload = (jsonText) => {
4612
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4613
+ return;
4614
+ }
4615
+ const trimmed = jsonText.trim();
4616
+ if (!trimmed || trimmed === "[DONE]") return;
4617
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return;
4618
+ try {
4619
+ const data = JSON.parse(trimmed);
4620
+ if (!responseIdCaptured) {
4621
+ const responseId = data?.id;
4622
+ if (typeof responseId === "string" && responseId.trim().length > 0) {
4623
+ capturedResponseId = responseId.trim();
4624
+ onResponseId?.(capturedResponseId);
4625
+ responseIdCaptured = true;
4626
+ }
4627
+ }
4628
+ const usage = extractUsageFromSSEJson(data);
4629
+ if (usage) {
4630
+ const merged = mergeUsage(capturedUsage, usage);
4631
+ if (hasUsageChanged(capturedUsage, merged)) {
4632
+ capturedUsage = merged;
4633
+ onUsage(merged);
4634
+ }
4635
+ }
4636
+ } catch {
4637
+ }
4638
+ };
4639
+ const inspectEventBlock = (eventBlock) => {
4640
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4641
+ return;
4642
+ }
4643
+ const lines = eventBlock.split(/\r?\n/);
4644
+ const dataParts = [];
4645
+ for (const line of lines) {
4646
+ if (!line || line.startsWith(":")) continue;
4647
+ if (line.startsWith("data:")) {
4648
+ const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
4649
+ dataParts.push(value);
4650
+ }
4651
+ }
4652
+ if (dataParts.length === 0) return;
4653
+ inspectDataPayload(dataParts.join("\n"));
4556
4654
  };
4557
- const hasUsageChanged = (previous, next) => {
4558
- if (!previous) return true;
4559
- return previous.promptTokens !== next.promptTokens || previous.completionTokens !== next.completionTokens || previous.totalTokens !== next.totalTokens || previous.cost !== next.cost || previous.satsCost !== next.satsCost;
4655
+ const drainBufferedEvents = () => {
4656
+ const terminator = /\r?\n\r?\n/g;
4657
+ let lastIndex = 0;
4658
+ let match;
4659
+ while ((match = terminator.exec(buffer)) !== null) {
4660
+ const block = buffer.slice(lastIndex, match.index);
4661
+ lastIndex = match.index + match[0].length;
4662
+ if (block.length > 0) inspectEventBlock(block);
4663
+ }
4664
+ if (lastIndex > 0) buffer = buffer.slice(lastIndex);
4560
4665
  };
4666
+ try {
4667
+ while (true) {
4668
+ const { value, done } = await reader.read();
4669
+ if (done) break;
4670
+ if (value && value.byteLength > 0) {
4671
+ buffer += decoder.decode(value, { stream: true });
4672
+ drainBufferedEvents();
4673
+ }
4674
+ }
4675
+ buffer += decoder.decode();
4676
+ drainBufferedEvents();
4677
+ if (buffer.length > 0) {
4678
+ const tail = buffer.replace(/\r?\n+$/, "");
4679
+ if (tail.length > 0) inspectEventBlock(tail);
4680
+ buffer = "";
4681
+ }
4682
+ } catch {
4683
+ } finally {
4684
+ try {
4685
+ reader.releaseLock();
4686
+ } catch {
4687
+ }
4688
+ }
4689
+ return {
4690
+ capturedUsage: capturedUsage ?? void 0,
4691
+ capturedResponseId
4692
+ };
4693
+ }
4694
+ function createSSEParserTransform(onUsage, onResponseId) {
4695
+ let buffer = "";
4696
+ const decoder = new string_decoder.StringDecoder("utf8");
4697
+ let capturedUsage = null;
4698
+ let responseIdCaptured = false;
4561
4699
  const inspectDataPayload = (jsonText) => {
4562
- if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
4700
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4563
4701
  return;
4564
4702
  }
4565
4703
  const trimmed = jsonText.trim();
@@ -4586,7 +4724,7 @@ function createSSEParserTransform(onUsage, onResponseId) {
4586
4724
  }
4587
4725
  };
4588
4726
  const inspectEventBlock = (eventBlock) => {
4589
- if (responseIdCaptured && capturedUsage?.satsCost && capturedUsage.totalTokens) {
4727
+ if (responseIdCaptured && capturedUsage && capturedUsage.totalTokens > 0) {
4590
4728
  return;
4591
4729
  }
4592
4730
  const lines = eventBlock.split(/\r?\n/);
@@ -4602,32 +4740,35 @@ function createSSEParserTransform(onUsage, onResponseId) {
4602
4740
  const payload = dataParts.join("\n");
4603
4741
  inspectDataPayload(payload);
4604
4742
  };
4605
- const emitEventBlock = (self, eventBlock) => {
4606
- if (eventBlock.length === 0) return;
4607
- inspectEventBlock(eventBlock);
4608
- self.push(eventBlock + "\n\n");
4743
+ const processBufferedEvents = () => {
4744
+ const terminator = /\r?\n\r?\n/g;
4745
+ let lastIndex = 0;
4746
+ let match;
4747
+ while ((match = terminator.exec(buffer)) !== null) {
4748
+ const block = buffer.slice(lastIndex, match.index);
4749
+ lastIndex = match.index + match[0].length;
4750
+ if (block.length > 0) {
4751
+ inspectEventBlock(block);
4752
+ }
4753
+ }
4754
+ if (lastIndex > 0) {
4755
+ buffer = buffer.slice(lastIndex);
4756
+ }
4609
4757
  };
4610
4758
  return new stream.Transform({
4611
4759
  transform(chunk, _encoding, callback) {
4612
- buffer += chunk.toString();
4613
- const terminator = /\r?\n\r?\n/g;
4614
- let lastIndex = 0;
4615
- let match;
4616
- while ((match = terminator.exec(buffer)) !== null) {
4617
- const block = buffer.slice(lastIndex, match.index);
4618
- lastIndex = match.index + match[0].length;
4619
- emitEventBlock(this, block);
4620
- }
4621
- if (lastIndex > 0) {
4622
- buffer = buffer.slice(lastIndex);
4623
- }
4760
+ this.push(chunk);
4761
+ buffer += decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
4762
+ processBufferedEvents();
4624
4763
  callback();
4625
4764
  },
4626
4765
  flush(callback) {
4766
+ buffer += decoder.end();
4767
+ processBufferedEvents();
4627
4768
  if (buffer.length > 0) {
4628
4769
  const tail = buffer.replace(/\r?\n+$/, "");
4629
4770
  if (tail.length > 0) {
4630
- emitEventBlock(this, tail);
4771
+ inspectEventBlock(tail);
4631
4772
  }
4632
4773
  buffer = "";
4633
4774
  }
@@ -4635,6 +4776,8 @@ function createSSEParserTransform(onUsage, onResponseId) {
4635
4776
  }
4636
4777
  });
4637
4778
  }
4779
+
4780
+ // client/RoutstrClient.ts
4638
4781
  var TOPUP_MARGIN = 1.2;
4639
4782
  var RoutstrClient = class {
4640
4783
  constructor(walletAdapter, storageAdapter, providerRegistry, alertLevel, mode = "xcashu", options = {}) {
@@ -4734,31 +4877,12 @@ var RoutstrClient = class {
4734
4877
  */
4735
4878
  async routeRequest(params) {
4736
4879
  const prepared = await this._prepareRoutedRequest(params);
4737
- const satsSpent = await this._handlePostResponseBalanceUpdate({
4738
- token: prepared.tokenUsed,
4739
- baseUrl: prepared.baseUrlUsed,
4740
- mintUrl: params.mintUrl,
4741
- initialTokenBalance: prepared.tokenBalanceInSats,
4742
- response: prepared.response,
4743
- modelId: prepared.modelId,
4744
- usage: prepared.capturedUsage,
4745
- requestId: prepared.capturedResponseId,
4746
- clientApiKey: prepared.clientApiKey
4747
- });
4748
- prepared.response.satsSpent = satsSpent;
4749
- prepared.response.usage = prepared.capturedUsage;
4750
- prepared.response.requestId = prepared.capturedResponseId;
4751
- return prepared.response;
4752
- }
4753
- async routeRequestToNodeResponse(params) {
4754
- const { res } = params;
4755
- const prepared = await this._prepareRoutedRequest(params);
4756
- res.statusCode = prepared.response.status;
4757
- prepared.response.headers.forEach((value, key) => {
4758
- res.setHeader(key, value);
4759
- });
4760
- const body = prepared.response.body;
4761
- if (!body) {
4880
+ const contentType = prepared.response.headers.get("content-type") || "";
4881
+ const isSSE = contentType.includes("text/event-stream");
4882
+ const runFinalize = async () => {
4883
+ const { capturedUsage, capturedResponseId } = await prepared.usagePromise;
4884
+ const usage = capturedUsage ?? prepared.capturedUsage;
4885
+ const requestId = capturedResponseId ?? prepared.capturedResponseId;
4762
4886
  const satsSpent = await this._handlePostResponseBalanceUpdate({
4763
4887
  token: prepared.tokenUsed,
4764
4888
  baseUrl: prepared.baseUrlUsed,
@@ -4766,51 +4890,25 @@ var RoutstrClient = class {
4766
4890
  initialTokenBalance: prepared.tokenBalanceInSats,
4767
4891
  response: prepared.response,
4768
4892
  modelId: prepared.modelId,
4769
- usage: prepared.capturedUsage,
4770
- requestId: prepared.capturedResponseId,
4893
+ usage,
4894
+ requestId,
4771
4895
  clientApiKey: prepared.clientApiKey
4772
4896
  });
4773
4897
  prepared.response.satsSpent = satsSpent;
4774
- res.end();
4775
- return;
4898
+ prepared.response.usage = usage;
4899
+ prepared.response.requestId = requestId;
4900
+ return satsSpent;
4901
+ };
4902
+ if (isSSE) {
4903
+ const finalizePromise = runFinalize().catch((error) => {
4904
+ this._log("ERROR", "[RoutstrClient] SSE finalize failed:", error);
4905
+ return 0;
4906
+ });
4907
+ prepared.response.finalize = () => finalizePromise;
4908
+ return prepared.response;
4776
4909
  }
4777
- const nodeReadable = stream.Readable.fromWeb(body);
4778
- await new Promise((resolve, reject) => {
4779
- let settled = false;
4780
- const finish = async () => {
4781
- if (settled) return;
4782
- settled = true;
4783
- try {
4784
- const satsSpent = await this._handlePostResponseBalanceUpdate({
4785
- token: prepared.tokenUsed,
4786
- baseUrl: prepared.baseUrlUsed,
4787
- mintUrl: params.mintUrl,
4788
- initialTokenBalance: prepared.tokenBalanceInSats,
4789
- response: prepared.response,
4790
- modelId: prepared.modelId,
4791
- usage: prepared.capturedUsage,
4792
- requestId: prepared.capturedResponseId,
4793
- clientApiKey: prepared.clientApiKey
4794
- });
4795
- prepared.response.satsSpent = satsSpent;
4796
- prepared.response.usage = prepared.capturedUsage;
4797
- prepared.response.requestId = prepared.capturedResponseId;
4798
- resolve();
4799
- } catch (error) {
4800
- reject(error);
4801
- }
4802
- };
4803
- const fail = (error) => {
4804
- if (settled) return;
4805
- settled = true;
4806
- reject(error);
4807
- };
4808
- res.once("finish", finish);
4809
- res.once("close", finish);
4810
- res.once("error", fail);
4811
- nodeReadable.once("error", fail);
4812
- nodeReadable.pipe(res);
4813
- });
4910
+ await runFinalize();
4911
+ return prepared.response;
4814
4912
  }
4815
4913
  async _prepareRoutedRequest(params) {
4816
4914
  const {
@@ -4834,9 +4932,23 @@ var RoutstrClient = class {
4834
4932
  );
4835
4933
  selectedModel = providerModel ?? void 0;
4836
4934
  if (selectedModel) {
4935
+ const requestMessages = Array.isArray(
4936
+ body?.messages
4937
+ ) ? body.messages : [];
4938
+ const requestMaxTokens = typeof body?.max_tokens === "number" ? body.max_tokens : void 0;
4939
+ this._log(
4940
+ "DEBUG",
4941
+ "[RoutstrClient] generic request pricing input",
4942
+ {
4943
+ modelId: selectedModel.id,
4944
+ messageCount: requestMessages.length,
4945
+ maxTokens: requestMaxTokens
4946
+ }
4947
+ );
4837
4948
  requiredSats = this.providerManager.getRequiredSatsForModel(
4838
4949
  selectedModel,
4839
- []
4950
+ requestMessages,
4951
+ requestMaxTokens
4840
4952
  );
4841
4953
  }
4842
4954
  }
@@ -4873,22 +4985,18 @@ var RoutstrClient = class {
4873
4985
  let processedResponse = response;
4874
4986
  let capturedUsage;
4875
4987
  let capturedResponseId;
4988
+ let usagePromise = Promise.resolve({});
4876
4989
  if (contentType.includes("text/event-stream") && response.body) {
4877
- const logDir = path__namespace.join(os__namespace.homedir(), ".routstrd", "stream-response");
4878
- if (!fs__namespace.existsSync(logDir)) {
4879
- fs__namespace.mkdirSync(logDir, { recursive: true });
4880
- }
4881
- const logFile = path__namespace.join(logDir, `${Date.now()}.jsonl`);
4882
- const logStream = fs__namespace.createWriteStream(logFile);
4883
- const nodeReadable = stream.Readable.fromWeb(response.body);
4884
- const loggingTransform = new stream.Transform({
4885
- transform(chunk, encoding, callback) {
4886
- const raw = chunk.toString();
4887
- logStream.write(JSON.stringify({ raw, timestamp: Date.now() }) + "\n");
4888
- callback(null, chunk);
4889
- }
4990
+ const [clientStream, inspectStream] = response.body.tee();
4991
+ processedResponse = new Response(clientStream, {
4992
+ status: response.status,
4993
+ statusText: response.statusText,
4994
+ headers: response.headers
4890
4995
  });
4891
- const sseParser = createSSEParserTransform(
4996
+ processedResponse.baseUrl = response.baseUrl;
4997
+ processedResponse.token = response.token;
4998
+ usagePromise = inspectSSEWebStream(
4999
+ inspectStream,
4892
5000
  (usage) => {
4893
5001
  capturedUsage = usage;
4894
5002
  processedResponse.usage = usage;
@@ -4898,17 +5006,7 @@ var RoutstrClient = class {
4898
5006
  processedResponse.requestId = responseId;
4899
5007
  }
4900
5008
  );
4901
- const transformed = nodeReadable.pipe(loggingTransform).pipe(sseParser, { end: true });
4902
- const webStream = stream.Readable.toWeb(
4903
- transformed
4904
- );
4905
- processedResponse = new Response(webStream, {
4906
- status: response.status,
4907
- statusText: response.statusText,
4908
- headers: response.headers
4909
- });
4910
- processedResponse.baseUrl = response.baseUrl;
4911
- processedResponse.token = response.token;
5009
+ processedResponse.usagePromise = usagePromise;
4912
5010
  }
4913
5011
  return {
4914
5012
  response: processedResponse,
@@ -4918,7 +5016,8 @@ var RoutstrClient = class {
4918
5016
  modelId,
4919
5017
  capturedUsage,
4920
5018
  capturedResponseId,
4921
- clientApiKey
5019
+ clientApiKey,
5020
+ usagePromise
4922
5021
  };
4923
5022
  }
4924
5023
  /**
@@ -5069,9 +5168,9 @@ var RoutstrClient = class {
5069
5168
  * Make the API request with failover support
5070
5169
  */
5071
5170
  async _makeRequest(params) {
5072
- const { path: path2, method, body, baseUrl, token, headers } = params;
5171
+ const { path, method, body, baseUrl, token, headers } = params;
5073
5172
  try {
5074
- const url = `${baseUrl.replace(/\/$/, "")}${path2}`;
5173
+ const url = `${baseUrl.replace(/\/$/, "")}${path}`;
5075
5174
  if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
5076
5175
  const response = await fetch(url, {
5077
5176
  method,
@@ -5121,7 +5220,7 @@ var RoutstrClient = class {
5121
5220
  */
5122
5221
  async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
5123
5222
  const MAX_RETRIES_PER_PROVIDER = 2;
5124
- const { path: path2, method, body, selectedModel, baseUrl, mintUrl } = params;
5223
+ const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
5125
5224
  let tryNextProvider = false;
5126
5225
  const errorMessage = responseBody;
5127
5226
  this._log(
@@ -5194,7 +5293,7 @@ var RoutstrClient = class {
5194
5293
  );
5195
5294
  const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
5196
5295
  const shortfall = Math.max(0, params.requiredSats - currentBalance);
5197
- topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
5296
+ topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
5198
5297
  this._log(
5199
5298
  "DEBUG",
5200
5299
  `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
@@ -5423,7 +5522,7 @@ var RoutstrClient = class {
5423
5522
  });
5424
5523
  return this._makeRequest({
5425
5524
  ...params,
5426
- path: path2,
5525
+ path,
5427
5526
  method,
5428
5527
  body,
5429
5528
  baseUrl: nextProvider,
@@ -5831,7 +5930,7 @@ async function resolveRouteRequestContext(options) {
5831
5930
  const {
5832
5931
  modelId,
5833
5932
  requestBody,
5834
- path: path2 = "/v1/chat/completions",
5933
+ path = "/v1/chat/completions",
5835
5934
  headers = {},
5836
5935
  forcedProvider,
5837
5936
  walletAdapter,
@@ -5930,17 +6029,17 @@ async function resolveRouteRequestContext(options) {
5930
6029
  client,
5931
6030
  baseUrl,
5932
6031
  mintUrl,
5933
- path: path2,
6032
+ path,
5934
6033
  headers,
5935
6034
  modelId,
5936
6035
  proxiedBody
5937
6036
  };
5938
6037
  }
5939
6038
  async function routeRequests(options) {
5940
- const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
6039
+ const { client, baseUrl, mintUrl, path, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
5941
6040
  try {
5942
6041
  const response = await client.routeRequest({
5943
- path: path2,
6042
+ path,
5944
6043
  method: "POST",
5945
6044
  body: proxiedBody,
5946
6045
  headers,
@@ -5959,27 +6058,6 @@ async function routeRequests(options) {
5959
6058
  throw error;
5960
6059
  }
5961
6060
  }
5962
- async function routeRequestsToNodeResponse(options) {
5963
- const { res } = options;
5964
- const { client, baseUrl, mintUrl, path: path2, headers, modelId, proxiedBody } = await resolveRouteRequestContext(options);
5965
- try {
5966
- await client.routeRequestToNodeResponse({
5967
- path: path2,
5968
- method: "POST",
5969
- body: proxiedBody,
5970
- headers,
5971
- baseUrl,
5972
- mintUrl,
5973
- modelId,
5974
- res
5975
- });
5976
- } catch (error) {
5977
- if (error instanceof Error && (error.message.includes("401") || error.message.includes("402") || error.message.includes("403"))) {
5978
- throw new Error(`Authentication failed: ${error.message}`);
5979
- }
5980
- throw error;
5981
- }
5982
- }
5983
6061
  function extractMaxTokens(requestBody) {
5984
6062
  if (!requestBody || typeof requestBody !== "object") {
5985
6063
  return void 0;
@@ -6036,12 +6114,12 @@ exports.getDefaultSdkStore = getDefaultSdkStore;
6036
6114
  exports.getDefaultStorageAdapter = getDefaultStorageAdapter;
6037
6115
  exports.getDefaultUsageTrackingDriver = getDefaultUsageTrackingDriver;
6038
6116
  exports.getProviderEndpoints = getProviderEndpoints;
6117
+ exports.inspectSSEWebStream = inspectSSEWebStream;
6039
6118
  exports.isOnionUrl = isOnionUrl;
6040
6119
  exports.isTorContext = isTorContext;
6041
6120
  exports.localStorageDriver = localStorageDriver;
6042
6121
  exports.normalizeProviderUrl = normalizeProviderUrl;
6043
6122
  exports.routeRequests = routeRequests;
6044
- exports.routeRequestsToNodeResponse = routeRequestsToNodeResponse;
6045
6123
  exports.setDefaultUsageTrackingDriver = setDefaultUsageTrackingDriver;
6046
6124
  //# sourceMappingURL=index.js.map
6047
6125
  //# sourceMappingURL=index.js.map