routstrd 0.4.10 → 0.4.11

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
@@ -47459,6 +47459,47 @@ function legacyCocodPidPath() {
47459
47459
  }
47460
47460
  var init_paths = () => {};
47461
47461
 
47462
+ // src/daemon/wallet/trusted-mints.ts
47463
+ function safeNormalizeMintUrl(mintUrl) {
47464
+ try {
47465
+ return normalizeMintUrl(mintUrl);
47466
+ } catch {
47467
+ return mintUrl;
47468
+ }
47469
+ }
47470
+ async function seedTrustedMints(wallet, defaultMintUrl, options = {}) {
47471
+ const seeds = options.seeds ?? DEFAULT_TRUSTED_MINT_URLS;
47472
+ const target = safeNormalizeMintUrl(defaultMintUrl);
47473
+ const trusted = new Set(wallet.trustedMints.map(safeNormalizeMintUrl));
47474
+ const attempted = new Set;
47475
+ for (const seed of [defaultMintUrl, ...seeds]) {
47476
+ const mintUrl = safeNormalizeMintUrl(seed);
47477
+ if (attempted.has(mintUrl))
47478
+ continue;
47479
+ attempted.add(mintUrl);
47480
+ if (trusted.has(mintUrl))
47481
+ continue;
47482
+ const isDefault = mintUrl === target;
47483
+ try {
47484
+ options.onProgress?.(`Adding ${isDefault ? "default" : "trusted"} mint: ${mintUrl}`);
47485
+ await wallet.addMint(mintUrl);
47486
+ trusted.add(mintUrl);
47487
+ } catch (error) {
47488
+ if (isDefault)
47489
+ throw error;
47490
+ options.onError?.(`Could not add trusted mint ${mintUrl}`, error);
47491
+ }
47492
+ }
47493
+ }
47494
+ var DEFAULT_MINT_URL = "https://mint.cubabitcoin.org", DEFAULT_TRUSTED_MINT_URLS;
47495
+ var init_trusted_mints = __esm(() => {
47496
+ init_dist();
47497
+ DEFAULT_TRUSTED_MINT_URLS = [
47498
+ DEFAULT_MINT_URL,
47499
+ "https://mint.minibits.cash/Bitcoin"
47500
+ ];
47501
+ });
47502
+
47462
47503
  // src/daemon/wallet/coco-client.ts
47463
47504
  import { Database } from "bun:sqlite";
47464
47505
  import {
@@ -48100,10 +48141,15 @@ async function createCocoClient(options = {}) {
48100
48141
  const trustedMints = await coco.mint.getAllTrustedMints();
48101
48142
  const configuredDefault = walletConfig.defaultMintUrl;
48102
48143
  const defaultMintUrl = normalizeMintUrl(configuredDefault || trustedMints[0]?.mintUrl || DEFAULT_MINT_URL);
48103
- if (!trustedMints.some((mint) => mint.mintUrl === defaultMintUrl)) {
48104
- startupProgress(`Adding default mint: ${defaultMintUrl}`);
48105
- await coco.mint.addMint(defaultMintUrl, { trusted: true });
48106
- }
48144
+ await seedTrustedMints({
48145
+ trustedMints: trustedMints.map((mint) => mint.mintUrl),
48146
+ addMint: (mintUrl) => coco.mint.addMint(mintUrl, { trusted: true })
48147
+ }, defaultMintUrl, {
48148
+ onProgress: startupProgress,
48149
+ onError: (message, error) => logger.warn(message, {
48150
+ error: error instanceof Error ? error.message : String(error)
48151
+ })
48152
+ });
48107
48153
  walletConfig.defaultMintUrl = defaultMintUrl;
48108
48154
  if (configuredDefault !== defaultMintUrl) {
48109
48155
  saveConfig(walletConfig, configFile);
@@ -48518,7 +48564,7 @@ async function createCocoClient(options = {}) {
48518
48564
  }
48519
48565
  };
48520
48566
  }
48521
- var NPC_DEFAULT_BASE_URL = "https://npubx.cash", STALE_SOCKET_ERROR_CODES, STARTUP_LOG_PREFIX2 = "[routstrd:start]", DEFAULT_MINT_URL = "https://mint.cubabitcoin.org", surfacingRecoveryProgress = false, RECOVERY_STALL_MARKERS, SAFE_COCO_LOG_FIELDS, EXPIRED_MINT_OBSERVATION_DEADLINE_MS = 15000, PENDING_MINT_SWEEP_INTERVAL_MS = 15000, PENDING_MINT_CHECK_TIMEOUT_MS = 1e4, PENDING_MINT_SWEEP_DEADLINE_MS = 30000, PENDING_MINT_STOP_DRAIN_MS = 1e4;
48567
+ var NPC_DEFAULT_BASE_URL = "https://npubx.cash", STALE_SOCKET_ERROR_CODES, STARTUP_LOG_PREFIX2 = "[routstrd:start]", surfacingRecoveryProgress = false, RECOVERY_STALL_MARKERS, SAFE_COCO_LOG_FIELDS, EXPIRED_MINT_OBSERVATION_DEADLINE_MS = 15000, PENDING_MINT_SWEEP_INTERVAL_MS = 15000, PENDING_MINT_CHECK_TIMEOUT_MS = 1e4, PENDING_MINT_SWEEP_DEADLINE_MS = 30000, PENDING_MINT_STOP_DRAIN_MS = 1e4;
48522
48568
  var init_coco_client = __esm(() => {
48523
48569
  init_dist();
48524
48570
  init_dist2();
@@ -48529,6 +48575,8 @@ var init_coco_client = __esm(() => {
48529
48575
  init_receive_dedup();
48530
48576
  init_logger();
48531
48577
  init_paths();
48578
+ init_trusted_mints();
48579
+ init_trusted_mints();
48532
48580
  STALE_SOCKET_ERROR_CODES = new Set([
48533
48581
  "ECONNREFUSED",
48534
48582
  "ENOENT",
@@ -48623,6 +48671,56 @@ var init_opencode = __esm(() => {
48623
48671
  import { existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
48624
48672
  import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
48625
48673
  import { dirname as dirname4 } from "path";
48674
+ function deriveThinkingFields(model) {
48675
+ const reasoning = model.reasoning;
48676
+ if (!reasoning)
48677
+ return null;
48678
+ const supported = (reasoning.supported_efforts ?? []).filter((effort) => typeof effort === "string").map((effort) => effort.trim().toLowerCase()).filter(Boolean);
48679
+ if (supported.length === 0)
48680
+ return null;
48681
+ const allowed = new Set(supported);
48682
+ if (reasoning.mandatory === true)
48683
+ allowed.delete("none");
48684
+ const thinkingLevelMap = {};
48685
+ for (const level of PI_THINKING_LEVELS) {
48686
+ const value = THINKING_LEVEL_VALUES[level];
48687
+ thinkingLevelMap[level] = allowed.has(value) ? value : null;
48688
+ }
48689
+ return { reasoning: true, thinkingLevelMap };
48690
+ }
48691
+ function buildPiModelEntry(model, previous) {
48692
+ const entry = { id: model.id };
48693
+ if (model.context_length !== undefined && model.context_length > 0) {
48694
+ entry.contextWindow = model.context_length;
48695
+ }
48696
+ if (model.name) {
48697
+ entry.name = model.name;
48698
+ }
48699
+ const mods = model.architecture?.input_modalities ?? [];
48700
+ const input = [];
48701
+ if (mods.includes("text"))
48702
+ input.push("text");
48703
+ if (mods.includes("image"))
48704
+ input.push("image");
48705
+ entry.input = input;
48706
+ const derived = deriveThinkingFields(model);
48707
+ if (derived) {
48708
+ entry.reasoning = derived.reasoning;
48709
+ entry.thinkingLevelMap = derived.thinkingLevelMap;
48710
+ } else {
48711
+ if (previous?.reasoning !== undefined)
48712
+ entry.reasoning = previous.reasoning;
48713
+ if (previous?.thinkingLevelMap !== undefined) {
48714
+ entry.thinkingLevelMap = previous.thinkingLevelMap;
48715
+ }
48716
+ }
48717
+ if (isDeepSeekModel(model.id)) {
48718
+ entry.compat = { ...previous?.compat ?? {}, supportsDeveloperRole: false };
48719
+ } else if (previous?.compat !== undefined) {
48720
+ entry.compat = previous.compat;
48721
+ }
48722
+ return entry;
48723
+ }
48626
48724
  async function installPiIntegration(config, apiKey, integrationConfig) {
48627
48725
  const { name, configPath } = integrationConfig;
48628
48726
  console.log(`
@@ -48651,30 +48749,7 @@ Installing routstr models in pi models.json...`);
48651
48749
  return;
48652
48750
  }
48653
48751
  const existingModels = new Map((piConfig.providers["routstr"]?.models ?? []).map((m2) => [m2.id, m2]));
48654
- const providerModels = models.map((model) => {
48655
- const previous = existingModels.get(model.id);
48656
- const entry = { id: model.id };
48657
- if (model.context_length !== undefined && model.context_length > 0) {
48658
- entry.contextWindow = model.context_length;
48659
- }
48660
- if (model.name) {
48661
- entry.name = model.name;
48662
- }
48663
- const mods = model.architecture?.input_modalities ?? [];
48664
- const input = [];
48665
- if (mods.includes("text"))
48666
- input.push("text");
48667
- if (mods.includes("image"))
48668
- input.push("image");
48669
- entry.input = input;
48670
- if (previous?.reasoning !== undefined)
48671
- entry.reasoning = previous.reasoning;
48672
- if (previous?.thinkingLevelMap !== undefined)
48673
- entry.thinkingLevelMap = previous.thinkingLevelMap;
48674
- if (previous?.compat !== undefined)
48675
- entry.compat = previous.compat;
48676
- return entry;
48677
- });
48752
+ const providerModels = models.map((model) => buildPiModelEntry(model, existingModels.get(model.id)));
48678
48753
  piConfig.providers["routstr"] = {
48679
48754
  baseUrl,
48680
48755
  api: "openai-completions",
@@ -48687,8 +48762,27 @@ Installing routstr models in pi models.json...`);
48687
48762
  console.error("Failed to install models in pi models.json:", error);
48688
48763
  }
48689
48764
  }
48765
+ var PI_THINKING_LEVELS, THINKING_LEVEL_VALUES, isDeepSeekModel = (id) => id.startsWith("deepseek");
48690
48766
  var init_pi = __esm(() => {
48691
48767
  init_daemon_client();
48768
+ PI_THINKING_LEVELS = [
48769
+ "off",
48770
+ "minimal",
48771
+ "low",
48772
+ "medium",
48773
+ "high",
48774
+ "xhigh",
48775
+ "max"
48776
+ ];
48777
+ THINKING_LEVEL_VALUES = {
48778
+ off: "none",
48779
+ minimal: "minimal",
48780
+ low: "low",
48781
+ medium: "medium",
48782
+ high: "high",
48783
+ xhigh: "xhigh",
48784
+ max: "max"
48785
+ };
48692
48786
  });
48693
48787
 
48694
48788
  // src/integrations/openclaw.ts
@@ -61014,7 +61108,7 @@ var package_default;
61014
61108
  var init_package = __esm(() => {
61015
61109
  package_default = {
61016
61110
  name: "routstrd",
61017
- version: "0.4.10",
61111
+ version: "0.4.11",
61018
61112
  module: "src/index.ts",
61019
61113
  type: "module",
61020
61114
  private: false,
@@ -61037,6 +61131,7 @@ var init_package = __esm(() => {
61037
61131
  monitor: "bun src/index.ts monitor",
61038
61132
  lint: "tsc --noEmit",
61039
61133
  test: "bun test",
61134
+ smoke: "scripts/smoke/chat-completions.sh",
61040
61135
  build: "bun build src/index.ts --target=bun --outfile=dist/index.js --external better-sqlite3 && bun build src/daemon/index.ts --target=bun --outfile=dist/daemon/index.js --external better-sqlite3",
61041
61136
  "build:binary": "bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig src/index.ts --outfile=dist/routstrd",
61042
61137
  prepublishOnly: "bun run build"
@@ -61052,7 +61147,7 @@ var init_package = __esm(() => {
61052
61147
  "@cashu/cashu-ts": "^4.3.0",
61053
61148
  "@cashu/coco-core": "^1.0.1",
61054
61149
  "@cashu/coco-sqlite-bun": "^1.0.1",
61055
- "@routstr/sdk": "^0.4.5",
61150
+ "@routstr/sdk": "^0.4.6",
61056
61151
  "@scure/bip39": "^2.2.0",
61057
61152
  "applesauce-core": "^5.1.0",
61058
61153
  "applesauce-relay": "^5.1.0",
@@ -99690,7 +99785,7 @@ var init_hpke_noble = __esm(() => {
99690
99785
  priv2 = Symbol();
99691
99786
  });
99692
99787
 
99693
- // node_modules/ehbp/dist/esm/protocol.js
99788
+ // node_modules/tinfoil/node_modules/ehbp/dist/esm/protocol.js
99694
99789
  var PROTOCOL, HPKE_CONFIG;
99695
99790
  var init_protocol = __esm(() => {
99696
99791
  PROTOCOL = {
@@ -99708,7 +99803,7 @@ var init_protocol = __esm(() => {
99708
99803
  };
99709
99804
  });
99710
99805
 
99711
- // node_modules/ehbp/dist/esm/derive.js
99806
+ // node_modules/tinfoil/node_modules/ehbp/dist/esm/derive.js
99712
99807
  async function deriveResponseKeys(exportedSecret, requestEnc, responseNonce) {
99713
99808
  if (exportedSecret.length !== EXPORT_LENGTH) {
99714
99809
  throw new Error(`exported secret must be ${EXPORT_LENGTH} bytes, got ${exportedSecret.length}`);
@@ -99744,11 +99839,6 @@ function computeNonce(nonceBase, seq) {
99744
99839
  }
99745
99840
  return nonce;
99746
99841
  }
99747
- async function encryptChunk(km, seq, plaintext) {
99748
- const nonce = computeNonce(km.nonceBase, seq);
99749
- const ciphertext = await aead.Seal(km.keyBytes, nonce, new Uint8Array(0), plaintext);
99750
- return ciphertext;
99751
- }
99752
99842
  async function decryptChunk(km, seq, ciphertext) {
99753
99843
  const nonce = computeNonce(km.nonceBase, seq);
99754
99844
  const plaintext = await aead.Open(km.keyBytes, nonce, new Uint8Array(0), ciphertext);
@@ -99779,7 +99869,7 @@ var init_derive = __esm(() => {
99779
99869
  RESPONSE_NONCE_LABEL = new TextEncoder().encode("nonce");
99780
99870
  });
99781
99871
 
99782
- // node_modules/ehbp/dist/esm/errors.js
99872
+ // node_modules/tinfoil/node_modules/ehbp/dist/esm/errors.js
99783
99873
  var EhbpError, KeyConfigMismatchError, ProtocolError, DecryptionError;
99784
99874
  var init_errors = __esm(() => {
99785
99875
  EhbpError = class EhbpError extends Error {
@@ -99812,7 +99902,7 @@ var init_errors = __esm(() => {
99812
99902
  };
99813
99903
  });
99814
99904
 
99815
- // node_modules/ehbp/dist/esm/request-options.js
99905
+ // node_modules/tinfoil/node_modules/ehbp/dist/esm/request-options.js
99816
99906
  function forwardedRequestInit(source) {
99817
99907
  const forwarded = {};
99818
99908
  if (!source) {
@@ -99846,7 +99936,7 @@ var init_request_options = __esm(() => {
99846
99936
  ];
99847
99937
  });
99848
99938
 
99849
- // node_modules/ehbp/dist/esm/identity.js
99939
+ // node_modules/tinfoil/node_modules/ehbp/dist/esm/identity.js
99850
99940
  function createSuite() {
99851
99941
  return new CipherSuite(KEM_DHKEM_X25519_HKDF_SHA256, KDF_HKDF_SHA256, AEAD_AES_256_GCM);
99852
99942
  }
@@ -100132,7 +100222,7 @@ var init_identity = __esm(() => {
100132
100222
  MAX_RESPONSE_CHUNK_BYTES = 64 * 1024 * 1024;
100133
100223
  });
100134
100224
 
100135
- // node_modules/ehbp/dist/esm/client.js
100225
+ // node_modules/tinfoil/node_modules/ehbp/dist/esm/client.js
100136
100226
  class Transport {
100137
100227
  serverIdentity;
100138
100228
  serverHost;
@@ -100283,9 +100373,6 @@ class Transport {
100283
100373
  return this.request(url2, { ...init, method: "DELETE" });
100284
100374
  }
100285
100375
  }
100286
- async function createTransport(serverURL, init) {
100287
- return Transport.create(serverURL, init);
100288
- }
100289
100376
  var MAX_PROBLEM_DETAILS_BYTES;
100290
100377
  var init_client3 = __esm(() => {
100291
100378
  init_identity();
@@ -100296,35 +100383,7 @@ var init_client3 = __esm(() => {
100296
100383
  MAX_PROBLEM_DETAILS_BYTES = 64 * 1024;
100297
100384
  });
100298
100385
 
100299
- // node_modules/ehbp/dist/esm/index.js
100300
- var exports_esm = {};
100301
- __export(exports_esm, {
100302
- serializeSessionRecoveryToken: () => serializeSessionRecoveryToken,
100303
- hexToBytes: () => hexToBytes11,
100304
- extractSessionRecoveryToken: () => extractSessionRecoveryToken,
100305
- encryptChunk: () => encryptChunk,
100306
- deserializeSessionRecoveryToken: () => deserializeSessionRecoveryToken,
100307
- deriveResponseKeys: () => deriveResponseKeys,
100308
- decryptResponseWithToken: () => decryptResponseWithToken,
100309
- decryptChunk: () => decryptChunk,
100310
- createTransport: () => createTransport,
100311
- computeNonce: () => computeNonce,
100312
- bytesToHex: () => bytesToHex11,
100313
- Transport: () => Transport,
100314
- RESPONSE_NONCE_LENGTH: () => RESPONSE_NONCE_LENGTH,
100315
- ProtocolError: () => ProtocolError,
100316
- PROTOCOL: () => PROTOCOL,
100317
- KeyConfigMismatchError: () => KeyConfigMismatchError,
100318
- Identity: () => Identity,
100319
- HPKE_REQUEST_INFO: () => HPKE_REQUEST_INFO,
100320
- HPKE_CONFIG: () => HPKE_CONFIG,
100321
- EhbpError: () => EhbpError,
100322
- EXPORT_LENGTH: () => EXPORT_LENGTH,
100323
- EXPORT_LABEL: () => EXPORT_LABEL,
100324
- DecryptionError: () => DecryptionError,
100325
- AES_GCM_NONCE_LENGTH: () => AES_GCM_NONCE_LENGTH,
100326
- AES256_KEY_LENGTH: () => AES256_KEY_LENGTH
100327
- });
100386
+ // node_modules/tinfoil/node_modules/ehbp/dist/esm/index.js
100328
100387
  var init_esm5 = __esm(() => {
100329
100388
  init_identity();
100330
100389
  init_identity();
@@ -130139,6 +130198,650 @@ var init_dist16 = __esm(() => {
130139
130198
  init_openai();
130140
130199
  });
130141
130200
 
130201
+ // node_modules/ehbp/dist/esm/protocol.js
130202
+ var PROTOCOL2, HPKE_CONFIG2;
130203
+ var init_protocol2 = __esm(() => {
130204
+ PROTOCOL2 = {
130205
+ ENCAPSULATED_KEY_HEADER: "Ehbp-Encapsulated-Key",
130206
+ RESPONSE_NONCE_HEADER: "Ehbp-Response-Nonce",
130207
+ KEYS_MEDIA_TYPE: "application/ohttp-keys",
130208
+ KEYS_PATH: "/.well-known/hpke-keys",
130209
+ PROBLEM_JSON_MEDIA_TYPE: "application/problem+json",
130210
+ KEY_CONFIG_PROBLEM_TYPE: "urn:ietf:params:ehbp:error:key-config"
130211
+ };
130212
+ HPKE_CONFIG2 = {
130213
+ KEM: 32,
130214
+ KDF: 1,
130215
+ AEAD: 2
130216
+ };
130217
+ });
130218
+
130219
+ // node_modules/ehbp/dist/esm/derive.js
130220
+ async function deriveResponseKeys2(exportedSecret, requestEnc, responseNonce) {
130221
+ if (exportedSecret.length !== EXPORT_LENGTH2) {
130222
+ throw new Error(`exported secret must be ${EXPORT_LENGTH2} bytes, got ${exportedSecret.length}`);
130223
+ }
130224
+ if (requestEnc.length !== REQUEST_ENC_LENGTH2) {
130225
+ throw new Error(`request enc must be ${REQUEST_ENC_LENGTH2} bytes, got ${requestEnc.length}`);
130226
+ }
130227
+ if (responseNonce.length !== RESPONSE_NONCE_LENGTH2) {
130228
+ throw new Error(`response nonce must be ${RESPONSE_NONCE_LENGTH2} bytes, got ${responseNonce.length}`);
130229
+ }
130230
+ const salt = new Uint8Array(requestEnc.length + responseNonce.length);
130231
+ salt.set(requestEnc, 0);
130232
+ salt.set(responseNonce, requestEnc.length);
130233
+ const prk = await kdf2.Extract(salt, exportedSecret);
130234
+ const keyBytes = await kdf2.Expand(prk, RESPONSE_KEY_LABEL2, AES256_KEY_LENGTH2);
130235
+ const nonceBase = await kdf2.Expand(prk, RESPONSE_NONCE_LABEL2, AES_GCM_NONCE_LENGTH2);
130236
+ return { keyBytes, nonceBase };
130237
+ }
130238
+ function computeNonce2(nonceBase, seq) {
130239
+ if (nonceBase.length !== AES_GCM_NONCE_LENGTH2) {
130240
+ throw new Error(`nonce base must be ${AES_GCM_NONCE_LENGTH2} bytes`);
130241
+ }
130242
+ if (!Number.isInteger(seq) || seq < 0 || seq >= 4294967296) {
130243
+ throw new Error(`sequence number must be an integer in range [0, 2^32): got ${seq}`);
130244
+ }
130245
+ const nonce = new Uint8Array(AES_GCM_NONCE_LENGTH2);
130246
+ nonce.set(nonceBase);
130247
+ for (let i6 = 0;i6 < 8; i6++) {
130248
+ const shift = i6 * 8;
130249
+ if (shift < 32) {
130250
+ nonce[AES_GCM_NONCE_LENGTH2 - 1 - i6] ^= seq >>> shift & 255;
130251
+ }
130252
+ }
130253
+ return nonce;
130254
+ }
130255
+ async function encryptChunk2(km, seq, plaintext) {
130256
+ const nonce = computeNonce2(km.nonceBase, seq);
130257
+ const ciphertext = await aead2.Seal(km.keyBytes, nonce, new Uint8Array(0), plaintext);
130258
+ return ciphertext;
130259
+ }
130260
+ async function decryptChunk2(km, seq, ciphertext) {
130261
+ const nonce = computeNonce2(km.nonceBase, seq);
130262
+ const plaintext = await aead2.Open(km.keyBytes, nonce, new Uint8Array(0), ciphertext);
130263
+ return plaintext;
130264
+ }
130265
+ function hexToBytes13(hex7) {
130266
+ if (hex7.length % 2 !== 0) {
130267
+ throw new Error("Hex string must have even length");
130268
+ }
130269
+ if (!/^[0-9a-fA-F]*$/.test(hex7)) {
130270
+ throw new Error("Invalid hex character");
130271
+ }
130272
+ const bytes6 = new Uint8Array(hex7.length / 2);
130273
+ for (let i6 = 0;i6 < bytes6.length; i6++) {
130274
+ bytes6[i6] = parseInt(hex7.substring(i6 * 2, i6 * 2 + 2), 16);
130275
+ }
130276
+ return bytes6;
130277
+ }
130278
+ function bytesToHex15(bytes6) {
130279
+ return Array.from(bytes6).map((b2) => b2.toString(16).padStart(2, "0")).join("");
130280
+ }
130281
+ var kdf2, aead2, HPKE_REQUEST_INFO2 = "ehbp request", EXPORT_LABEL2 = "ehbp response", EXPORT_LENGTH2 = 32, RESPONSE_NONCE_LENGTH2 = 32, AES256_KEY_LENGTH2 = 32, AES_GCM_NONCE_LENGTH2 = 12, REQUEST_ENC_LENGTH2 = 32, RESPONSE_KEY_LABEL2, RESPONSE_NONCE_LABEL2;
130282
+ var init_derive2 = __esm(() => {
130283
+ init_hpke_noble();
130284
+ kdf2 = KDF_HKDF_SHA256();
130285
+ aead2 = AEAD_AES_256_GCM();
130286
+ RESPONSE_KEY_LABEL2 = new TextEncoder().encode("key");
130287
+ RESPONSE_NONCE_LABEL2 = new TextEncoder().encode("nonce");
130288
+ });
130289
+
130290
+ // node_modules/ehbp/dist/esm/errors.js
130291
+ var EhbpError2, KeyConfigMismatchError2, ProtocolError2, DecryptionError2;
130292
+ var init_errors6 = __esm(() => {
130293
+ EhbpError2 = class EhbpError2 extends Error {
130294
+ constructor(message, options) {
130295
+ super(message);
130296
+ this.name = "EhbpError";
130297
+ if (options?.cause)
130298
+ this.cause = options.cause;
130299
+ }
130300
+ };
130301
+ KeyConfigMismatchError2 = class KeyConfigMismatchError2 extends EhbpError2 {
130302
+ title;
130303
+ constructor(title) {
130304
+ super(title || "Server key configuration mismatch");
130305
+ this.name = "KeyConfigMismatchError";
130306
+ this.title = title || "";
130307
+ }
130308
+ };
130309
+ ProtocolError2 = class ProtocolError2 extends EhbpError2 {
130310
+ constructor(message, options) {
130311
+ super(message, options);
130312
+ this.name = "ProtocolError";
130313
+ }
130314
+ };
130315
+ DecryptionError2 = class DecryptionError2 extends EhbpError2 {
130316
+ constructor(message, options) {
130317
+ super(message, options);
130318
+ this.name = "DecryptionError";
130319
+ }
130320
+ };
130321
+ });
130322
+
130323
+ // node_modules/ehbp/dist/esm/request-options.js
130324
+ function forwardedRequestInit2(source) {
130325
+ const forwarded = {};
130326
+ if (!source) {
130327
+ return forwarded;
130328
+ }
130329
+ const record2 = source;
130330
+ const target = forwarded;
130331
+ for (const key of FORWARDED_INIT_KEYS2) {
130332
+ const value = record2[key];
130333
+ if (value !== undefined) {
130334
+ target[key] = value;
130335
+ }
130336
+ }
130337
+ if (forwarded.mode === "navigate") {
130338
+ delete forwarded.mode;
130339
+ }
130340
+ return forwarded;
130341
+ }
130342
+ var FORWARDED_INIT_KEYS2;
130343
+ var init_request_options2 = __esm(() => {
130344
+ FORWARDED_INIT_KEYS2 = [
130345
+ "cache",
130346
+ "credentials",
130347
+ "integrity",
130348
+ "keepalive",
130349
+ "mode",
130350
+ "redirect",
130351
+ "referrer",
130352
+ "referrerPolicy",
130353
+ "signal"
130354
+ ];
130355
+ });
130356
+
130357
+ // node_modules/ehbp/dist/esm/identity.js
130358
+ function createSuite2() {
130359
+ return new CipherSuite(KEM_DHKEM_X25519_HKDF_SHA256, KDF_HKDF_SHA256, AEAD_AES_256_GCM);
130360
+ }
130361
+
130362
+ class Identity3 {
130363
+ suite;
130364
+ publicKey;
130365
+ privateKey;
130366
+ constructor(suite, publicKey, privateKey) {
130367
+ this.suite = suite;
130368
+ this.publicKey = publicKey;
130369
+ this.privateKey = privateKey;
130370
+ }
130371
+ static async generate() {
130372
+ const suite = createSuite2();
130373
+ const { publicKey, privateKey } = await suite.GenerateKeyPair(true);
130374
+ return new Identity3(suite, publicKey, privateKey);
130375
+ }
130376
+ static async fromJSON(json3) {
130377
+ const data = JSON.parse(json3);
130378
+ const suite = createSuite2();
130379
+ const publicKey = await suite.DeserializePublicKey(new Uint8Array(data.publicKey));
130380
+ const privateKey = await suite.DeserializePrivateKey(new Uint8Array(data.privateKey), true);
130381
+ return new Identity3(suite, publicKey, privateKey);
130382
+ }
130383
+ async toJSON() {
130384
+ const publicKeyBytes = await this.suite.SerializePublicKey(this.publicKey);
130385
+ const privateKeyBytes = await this.suite.SerializePrivateKey(this.privateKey);
130386
+ return JSON.stringify({
130387
+ publicKey: Array.from(publicKeyBytes),
130388
+ privateKey: Array.from(privateKeyBytes)
130389
+ });
130390
+ }
130391
+ getPublicKey() {
130392
+ return this.publicKey;
130393
+ }
130394
+ async getPublicKeyHex() {
130395
+ const exported = await this.suite.SerializePublicKey(this.publicKey);
130396
+ return bytesToHex15(exported);
130397
+ }
130398
+ getPrivateKey() {
130399
+ return this.privateKey;
130400
+ }
130401
+ async marshalConfig() {
130402
+ const kemId = HPKE_CONFIG2.KEM;
130403
+ const kdfId = HPKE_CONFIG2.KDF;
130404
+ const aeadId = HPKE_CONFIG2.AEAD;
130405
+ const publicKeyBytes = await this.suite.SerializePublicKey(this.publicKey);
130406
+ const keyId = 0;
130407
+ const publicKeySize = publicKeyBytes.length;
130408
+ const cipherSuitesSize = 2 + 2;
130409
+ const buffer = new Uint8Array(1 + 2 + publicKeySize + 2 + cipherSuitesSize);
130410
+ let offset = 0;
130411
+ buffer[offset++] = keyId;
130412
+ buffer[offset++] = kemId >> 8 & 255;
130413
+ buffer[offset++] = kemId & 255;
130414
+ buffer.set(publicKeyBytes, offset);
130415
+ offset += publicKeySize;
130416
+ buffer[offset++] = cipherSuitesSize >> 8 & 255;
130417
+ buffer[offset++] = cipherSuitesSize & 255;
130418
+ buffer[offset++] = kdfId >> 8 & 255;
130419
+ buffer[offset++] = kdfId & 255;
130420
+ buffer[offset++] = aeadId >> 8 & 255;
130421
+ buffer[offset++] = aeadId & 255;
130422
+ return buffer;
130423
+ }
130424
+ static async unmarshalPublicConfig(data) {
130425
+ let offset = 0;
130426
+ const keyId = data[offset++];
130427
+ const kemId = data[offset++] << 8 | data[offset++];
130428
+ const publicKeySize = 32;
130429
+ const publicKeyBytes = data.slice(offset, offset + publicKeySize);
130430
+ offset += publicKeySize;
130431
+ const cipherSuitesLength = data[offset++] << 8 | data[offset++];
130432
+ const suites = [];
130433
+ const cipherSuitesEnd = offset + cipherSuitesLength;
130434
+ while (offset < cipherSuitesEnd) {
130435
+ const kdfId = data[offset++] << 8 | data[offset++];
130436
+ const aeadId = data[offset++] << 8 | data[offset++];
130437
+ suites.push({ kdfId, aeadId });
130438
+ }
130439
+ if (suites.length === 0) {
130440
+ throw new ProtocolError2("No cipher suites found in config");
130441
+ }
130442
+ const firstSuite = suites[0];
130443
+ if (firstSuite.kdfId !== HPKE_CONFIG2.KDF || firstSuite.aeadId !== HPKE_CONFIG2.AEAD) {
130444
+ throw new ProtocolError2(`Unsupported cipher suite: KDF=0x${firstSuite.kdfId.toString(16)}, AEAD=0x${firstSuite.aeadId.toString(16)}`);
130445
+ }
130446
+ return Identity3.fromPublicKeyBytes(publicKeyBytes);
130447
+ }
130448
+ static async fromPublicKeyHex(publicKeyHex) {
130449
+ const publicKeyBytes = hexToBytes13(publicKeyHex);
130450
+ if (publicKeyBytes.length !== 32) {
130451
+ throw new ProtocolError2(`Invalid public key length: expected 32, got ${publicKeyBytes.length}`);
130452
+ }
130453
+ return Identity3.fromPublicKeyBytes(publicKeyBytes);
130454
+ }
130455
+ static async fromPublicKeyBytes(publicKeyBytes) {
130456
+ const suite = createSuite2();
130457
+ const publicKey = await suite.DeserializePublicKey(publicKeyBytes);
130458
+ const placeholderPrivateKey = await suite.DeserializePrivateKey(new Uint8Array(32), false);
130459
+ return new Identity3(suite, publicKey, placeholderPrivateKey);
130460
+ }
130461
+ async encryptRequestWithContext(request) {
130462
+ const body = await request.arrayBuffer();
130463
+ if (body.byteLength === 0) {
130464
+ return {
130465
+ request: new Request(request.url, {
130466
+ ...forwardedRequestInit2(request),
130467
+ method: request.method,
130468
+ headers: request.headers,
130469
+ body: null
130470
+ }),
130471
+ context: null
130472
+ };
130473
+ }
130474
+ const infoBytes = new TextEncoder().encode(HPKE_REQUEST_INFO2);
130475
+ const { encapsulatedSecret, ctx } = await this.suite.SetupSender(this.publicKey, {
130476
+ info: infoBytes
130477
+ });
130478
+ const context = {
130479
+ senderContext: ctx,
130480
+ requestEnc: encapsulatedSecret
130481
+ };
130482
+ const headers = new Headers(request.headers);
130483
+ headers.set(PROTOCOL2.ENCAPSULATED_KEY_HEADER, bytesToHex15(context.requestEnc));
130484
+ const encrypted = await ctx.Seal(new Uint8Array(body));
130485
+ const chunkLength = new Uint8Array(4);
130486
+ new DataView(chunkLength.buffer).setUint32(0, encrypted.byteLength, false);
130487
+ const chunkedData = new Uint8Array(4 + encrypted.byteLength);
130488
+ chunkedData.set(chunkLength, 0);
130489
+ chunkedData.set(encrypted, 4);
130490
+ return {
130491
+ request: new Request(request.url, {
130492
+ ...forwardedRequestInit2(request),
130493
+ method: request.method,
130494
+ headers,
130495
+ body: chunkedData,
130496
+ duplex: "half"
130497
+ }),
130498
+ context
130499
+ };
130500
+ }
130501
+ async decryptResponseWithContext(response, context) {
130502
+ const token = await extractSessionRecoveryToken2(context);
130503
+ return decryptResponseWithToken2(response, token);
130504
+ }
130505
+ }
130506
+ async function extractSessionRecoveryToken2(context) {
130507
+ const exportLabelBytes = new TextEncoder().encode(EXPORT_LABEL2);
130508
+ const exportedSecret = new Uint8Array(await context.senderContext.Export(exportLabelBytes, EXPORT_LENGTH2));
130509
+ return {
130510
+ exportedSecret,
130511
+ requestEnc: new Uint8Array(context.requestEnc)
130512
+ };
130513
+ }
130514
+ function serializeSessionRecoveryToken2(token) {
130515
+ return JSON.stringify({
130516
+ exportedSecret: bytesToHex15(token.exportedSecret),
130517
+ requestEnc: bytesToHex15(token.requestEnc)
130518
+ });
130519
+ }
130520
+ function deserializeSessionRecoveryToken2(json3) {
130521
+ const parsed = JSON.parse(json3);
130522
+ return {
130523
+ exportedSecret: hexToBytes13(parsed.exportedSecret),
130524
+ requestEnc: hexToBytes13(parsed.requestEnc)
130525
+ };
130526
+ }
130527
+ async function decryptResponseWithToken2(response, token, onStreamError, onStreamComplete) {
130528
+ const responseNonceHex = response.headers.get(PROTOCOL2.RESPONSE_NONCE_HEADER);
130529
+ if (!response.body && !responseNonceHex) {
130530
+ return response;
130531
+ }
130532
+ if (!responseNonceHex) {
130533
+ throw new ProtocolError2(`Missing ${PROTOCOL2.RESPONSE_NONCE_HEADER} header`);
130534
+ }
130535
+ const responseNonce = hexToBytes13(responseNonceHex);
130536
+ if (responseNonce.length !== RESPONSE_NONCE_LENGTH2) {
130537
+ throw new ProtocolError2(`Invalid response nonce length`);
130538
+ }
130539
+ if (!response.body) {
130540
+ onStreamComplete?.();
130541
+ return response;
130542
+ }
130543
+ const km = await deriveResponseKeys2(token.exportedSecret, token.requestEnc, responseNonce);
130544
+ const decryptedStream = createDecryptStream2(response.body, km, onStreamError, onStreamComplete);
130545
+ const headers = new Headers(response.headers);
130546
+ headers.delete("content-length");
130547
+ headers.delete("transfer-encoding");
130548
+ return new Response(decryptedStream, {
130549
+ status: response.status,
130550
+ statusText: response.statusText,
130551
+ headers
130552
+ });
130553
+ }
130554
+ function createDecryptStream2(body, km, onStreamError, onStreamComplete) {
130555
+ let buffer = new Uint8Array(0);
130556
+ let seq = 0;
130557
+ const reader = body.getReader();
130558
+ let consumerCancelled = false;
130559
+ return new ReadableStream({
130560
+ async pull(controller) {
130561
+ const fail = (error53) => {
130562
+ try {
130563
+ onStreamError?.();
130564
+ } catch {}
130565
+ try {
130566
+ controller.error(error53);
130567
+ } finally {
130568
+ reader.cancel(error53).catch(() => {});
130569
+ }
130570
+ };
130571
+ while (true) {
130572
+ if (buffer.length >= 4) {
130573
+ const chunkLength = (buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]) >>> 0;
130574
+ if (chunkLength === 0) {
130575
+ buffer = buffer.slice(4);
130576
+ continue;
130577
+ }
130578
+ if (chunkLength > MAX_RESPONSE_CHUNK_BYTES2) {
130579
+ fail(new ProtocolError2("response chunk exceeds maximum allowed size"));
130580
+ return;
130581
+ }
130582
+ if (buffer.length >= 4 + chunkLength) {
130583
+ const ciphertext = buffer.slice(4, 4 + chunkLength);
130584
+ buffer = buffer.slice(4 + chunkLength);
130585
+ try {
130586
+ const plaintext = await decryptChunk2(km, seq++, ciphertext);
130587
+ controller.enqueue(plaintext);
130588
+ return;
130589
+ } catch (error53) {
130590
+ fail(new DecryptionError2(`Decryption failed at chunk ${seq - 1}`, { cause: error53 }));
130591
+ return;
130592
+ }
130593
+ }
130594
+ }
130595
+ let result;
130596
+ try {
130597
+ result = await reader.read();
130598
+ } catch (error53) {
130599
+ if (consumerCancelled) {
130600
+ return;
130601
+ }
130602
+ fail(error53 instanceof Error ? error53 : new Error("Encrypted response read failed", {
130603
+ cause: error53
130604
+ }));
130605
+ return;
130606
+ }
130607
+ const { done, value } = result;
130608
+ if (done) {
130609
+ if (consumerCancelled) {
130610
+ return;
130611
+ }
130612
+ if (buffer.length !== 0) {
130613
+ fail(new ProtocolError2("truncated encrypted response chunk"));
130614
+ } else {
130615
+ onStreamComplete?.();
130616
+ controller.close();
130617
+ }
130618
+ return;
130619
+ }
130620
+ const newBuffer = new Uint8Array(buffer.length + value.length);
130621
+ newBuffer.set(buffer);
130622
+ newBuffer.set(value, buffer.length);
130623
+ buffer = newBuffer;
130624
+ }
130625
+ },
130626
+ cancel(reason) {
130627
+ consumerCancelled = true;
130628
+ return reader.cancel(reason);
130629
+ }
130630
+ });
130631
+ }
130632
+ var MAX_RESPONSE_CHUNK_BYTES2;
130633
+ var init_identity2 = __esm(() => {
130634
+ init_hpke();
130635
+ init_hpke_noble();
130636
+ init_protocol2();
130637
+ init_derive2();
130638
+ init_errors6();
130639
+ init_request_options2();
130640
+ MAX_RESPONSE_CHUNK_BYTES2 = 64 * 1024 * 1024;
130641
+ });
130642
+
130643
+ // node_modules/ehbp/dist/esm/client.js
130644
+ class Transport2 {
130645
+ serverIdentity;
130646
+ serverHost;
130647
+ _lastSessionRecoveryToken;
130648
+ requestGeneration = 0;
130649
+ constructor(serverIdentity, serverHost) {
130650
+ this.serverIdentity = serverIdentity;
130651
+ this.serverHost = serverHost;
130652
+ }
130653
+ getSessionRecoveryToken() {
130654
+ if (!this._lastSessionRecoveryToken) {
130655
+ throw new Error("No session recovery token available \u2014 no request has been made yet");
130656
+ }
130657
+ return this._lastSessionRecoveryToken;
130658
+ }
130659
+ static async create(serverURL, init) {
130660
+ const url3 = new URL(serverURL);
130661
+ const serverHost = url3.host;
130662
+ const keysURL = new URL(PROTOCOL2.KEYS_PATH, serverURL);
130663
+ const response = await fetch(keysURL.toString(), init);
130664
+ if (!response.ok) {
130665
+ throw new Error(`Failed to get server public key: ${response.status}`);
130666
+ }
130667
+ const contentType = response.headers.get("content-type");
130668
+ if (contentType !== PROTOCOL2.KEYS_MEDIA_TYPE) {
130669
+ throw new Error(`Invalid content type: ${contentType}`);
130670
+ }
130671
+ const keysData = new Uint8Array(await response.arrayBuffer());
130672
+ const serverIdentity = await Identity3.unmarshalPublicConfig(keysData);
130673
+ return new Transport2(serverIdentity, serverHost);
130674
+ }
130675
+ static isProblemJSONContentType(contentType) {
130676
+ if (!contentType) {
130677
+ return false;
130678
+ }
130679
+ const mediaType = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
130680
+ return mediaType === PROTOCOL2.PROBLEM_JSON_MEDIA_TYPE;
130681
+ }
130682
+ static async checkKeyConfigMismatch(response) {
130683
+ if (response.status !== 422)
130684
+ return;
130685
+ if (!Transport2.isProblemJSONContentType(response.headers.get("content-type")))
130686
+ return;
130687
+ let problem;
130688
+ try {
130689
+ const clone2 = response.clone();
130690
+ if (!clone2.body)
130691
+ return;
130692
+ const reader = clone2.body.getReader();
130693
+ const chunks = [];
130694
+ let length2 = 0;
130695
+ while (true) {
130696
+ const { done, value } = await reader.read();
130697
+ if (done)
130698
+ break;
130699
+ length2 += value.byteLength;
130700
+ if (length2 > MAX_PROBLEM_DETAILS_BYTES2) {
130701
+ reader.cancel().catch(() => {});
130702
+ return;
130703
+ }
130704
+ chunks.push(value);
130705
+ }
130706
+ const body = new Uint8Array(length2);
130707
+ let offset = 0;
130708
+ for (const chunk of chunks) {
130709
+ body.set(chunk, offset);
130710
+ offset += chunk.byteLength;
130711
+ }
130712
+ problem = JSON.parse(new TextDecoder().decode(body));
130713
+ } catch {
130714
+ return;
130715
+ }
130716
+ if (problem?.type === PROTOCOL2.KEY_CONFIG_PROBLEM_TYPE) {
130717
+ throw new KeyConfigMismatchError2(typeof problem.title === "string" ? problem.title : undefined);
130718
+ }
130719
+ }
130720
+ static async shouldDecryptResponse(response) {
130721
+ if (response.headers.has(PROTOCOL2.RESPONSE_NONCE_HEADER)) {
130722
+ return true;
130723
+ }
130724
+ await Transport2.checkKeyConfigMismatch(response);
130725
+ if (!response.ok) {
130726
+ return false;
130727
+ }
130728
+ throw new ProtocolError2(`Missing ${PROTOCOL2.RESPONSE_NONCE_HEADER} header`);
130729
+ }
130730
+ getServerIdentity() {
130731
+ return this.serverIdentity;
130732
+ }
130733
+ getServerPublicKey() {
130734
+ return this.serverIdentity.getPublicKey();
130735
+ }
130736
+ async getServerPublicKeyHex() {
130737
+ return this.serverIdentity.getPublicKeyHex();
130738
+ }
130739
+ async request(input, init) {
130740
+ const generation = ++this.requestGeneration;
130741
+ this._lastSessionRecoveryToken = undefined;
130742
+ const inputUrl = input instanceof Request ? input.url : String(input);
130743
+ if (inputUrl.startsWith("data:") || inputUrl.startsWith("blob:")) {
130744
+ return fetch(input, init);
130745
+ }
130746
+ const normalizedRequest = new Request(input, init);
130747
+ const requestBodyBytes = await normalizedRequest.arrayBuffer();
130748
+ const requestBody = requestBodyBytes.byteLength > 0 ? requestBodyBytes : null;
130749
+ const url3 = new URL(normalizedRequest.url);
130750
+ url3.host = this.serverHost;
130751
+ const request = new Request(url3.toString(), {
130752
+ ...forwardedRequestInit2(normalizedRequest),
130753
+ method: normalizedRequest.method,
130754
+ headers: normalizedRequest.headers,
130755
+ body: requestBody,
130756
+ duplex: "half"
130757
+ });
130758
+ const { request: encryptedRequest, context } = await this.serverIdentity.encryptRequestWithContext(request);
130759
+ const token = context ? await extractSessionRecoveryToken2(context) : undefined;
130760
+ const response = await fetch(encryptedRequest);
130761
+ if (!token) {
130762
+ return response;
130763
+ }
130764
+ const shouldDecrypt = await Transport2.shouldDecryptResponse(response);
130765
+ if (!shouldDecrypt) {
130766
+ return response;
130767
+ }
130768
+ let streamTerminated = false;
130769
+ const clearToken = () => {
130770
+ streamTerminated = true;
130771
+ if (this.requestGeneration === generation) {
130772
+ this._lastSessionRecoveryToken = undefined;
130773
+ }
130774
+ };
130775
+ const decryptedResponse = await decryptResponseWithToken2(response, token, clearToken, clearToken);
130776
+ if (this.requestGeneration === generation) {
130777
+ this._lastSessionRecoveryToken = streamTerminated ? undefined : token;
130778
+ }
130779
+ return decryptedResponse;
130780
+ }
130781
+ async get(url3, init) {
130782
+ return this.request(url3, { ...init, method: "GET" });
130783
+ }
130784
+ async post(url3, body, init) {
130785
+ return this.request(url3, { ...init, method: "POST", body });
130786
+ }
130787
+ async put(url3, body, init) {
130788
+ return this.request(url3, { ...init, method: "PUT", body });
130789
+ }
130790
+ async delete(url3, init) {
130791
+ return this.request(url3, { ...init, method: "DELETE" });
130792
+ }
130793
+ }
130794
+ async function createTransport2(serverURL, init) {
130795
+ return Transport2.create(serverURL, init);
130796
+ }
130797
+ var MAX_PROBLEM_DETAILS_BYTES2;
130798
+ var init_client5 = __esm(() => {
130799
+ init_identity2();
130800
+ init_identity2();
130801
+ init_protocol2();
130802
+ init_request_options2();
130803
+ init_errors6();
130804
+ MAX_PROBLEM_DETAILS_BYTES2 = 64 * 1024;
130805
+ });
130806
+
130807
+ // node_modules/ehbp/dist/esm/index.js
130808
+ var exports_esm = {};
130809
+ __export(exports_esm, {
130810
+ serializeSessionRecoveryToken: () => serializeSessionRecoveryToken2,
130811
+ hexToBytes: () => hexToBytes13,
130812
+ extractSessionRecoveryToken: () => extractSessionRecoveryToken2,
130813
+ encryptChunk: () => encryptChunk2,
130814
+ deserializeSessionRecoveryToken: () => deserializeSessionRecoveryToken2,
130815
+ deriveResponseKeys: () => deriveResponseKeys2,
130816
+ decryptResponseWithToken: () => decryptResponseWithToken2,
130817
+ decryptChunk: () => decryptChunk2,
130818
+ createTransport: () => createTransport2,
130819
+ computeNonce: () => computeNonce2,
130820
+ bytesToHex: () => bytesToHex15,
130821
+ Transport: () => Transport2,
130822
+ RESPONSE_NONCE_LENGTH: () => RESPONSE_NONCE_LENGTH2,
130823
+ ProtocolError: () => ProtocolError2,
130824
+ PROTOCOL: () => PROTOCOL2,
130825
+ KeyConfigMismatchError: () => KeyConfigMismatchError2,
130826
+ Identity: () => Identity3,
130827
+ HPKE_REQUEST_INFO: () => HPKE_REQUEST_INFO2,
130828
+ HPKE_CONFIG: () => HPKE_CONFIG2,
130829
+ EhbpError: () => EhbpError2,
130830
+ EXPORT_LENGTH: () => EXPORT_LENGTH2,
130831
+ EXPORT_LABEL: () => EXPORT_LABEL2,
130832
+ DecryptionError: () => DecryptionError2,
130833
+ AES_GCM_NONCE_LENGTH: () => AES_GCM_NONCE_LENGTH2,
130834
+ AES256_KEY_LENGTH: () => AES256_KEY_LENGTH2
130835
+ });
130836
+ var init_esm6 = __esm(() => {
130837
+ init_identity2();
130838
+ init_identity2();
130839
+ init_client5();
130840
+ init_protocol2();
130841
+ init_errors6();
130842
+ init_derive2();
130843
+ });
130844
+
130142
130845
  // node_modules/@routstr/sdk/dist/index.mjs
130143
130846
  function makeConsoleLogger(prefix) {
130144
130847
  const fmt = (args) => prefix ? [prefix, ...args] : args;
@@ -130256,6 +130959,10 @@ function selectMintWithBalance(balances, units, amount, excludeMints = []) {
130256
130959
  function isKeyNotFoundResponse(status, body) {
130257
130960
  return status === 401 && typeof body === "string" && body.includes("Key not found");
130258
130961
  }
130962
+ function isOpenAiJsonBodyPath(pathname) {
130963
+ const path2 = (pathname.split("?")[0] ?? "").replace(/\/+$/, "");
130964
+ return OPENAI_JSON_BODY_PATHS.some((suffix) => path2.endsWith(suffix));
130965
+ }
130259
130966
  function newUserCacheSecret2() {
130260
130967
  try {
130261
130968
  const bytes6 = new Uint8Array(32);
@@ -130624,7 +131331,7 @@ async function isEhbpKeyConfigMismatchResponse(response, protocol) {
130624
131331
  }
130625
131332
  }
130626
131333
  async function fetchTinfoilEhbpOnce(context, options, normalized, ehbp, userCacheSecret) {
130627
- const { Identity: Identity3, PROTOCOL: PROTOCOL2, decryptResponseWithToken: decryptResponseWithToken2, extractSessionRecoveryToken: extractSessionRecoveryToken2 } = ehbp;
131334
+ const { Identity: Identity4, PROTOCOL: PROTOCOL3, decryptResponseWithToken: decryptResponseWithToken3, extractSessionRecoveryToken: extractSessionRecoveryToken3 } = ehbp;
130628
131335
  const resolved = resolveOptions(options);
130629
131336
  const baseURL = context.client.getBaseURL() ?? resolved.baseUrl;
130630
131337
  const enclaveURL = context.client.getEnclaveURL();
@@ -130652,7 +131359,7 @@ async function fetchTinfoilEhbpOnce(context, options, normalized, ehbp, userCach
130652
131359
  }
130653
131360
  }
130654
131361
  }
130655
- const serverIdentity = await Identity3.fromPublicKeyHex(context.verification.hpkePublicKey);
131362
+ const serverIdentity = await Identity4.fromPublicKeyHex(context.verification.hpkePublicKey);
130656
131363
  const request = new Request(targetUrl.toString(), {
130657
131364
  method,
130658
131365
  headers,
@@ -130667,18 +131374,18 @@ async function fetchTinfoilEhbpOnce(context, options, normalized, ehbp, userCach
130667
131374
  if (!requestContext) {
130668
131375
  return response;
130669
131376
  }
130670
- if (await isEhbpKeyConfigMismatchResponse(response, PROTOCOL2)) {
131377
+ if (await isEhbpKeyConfigMismatchResponse(response, PROTOCOL3)) {
130671
131378
  throw new ehbp.KeyConfigMismatchError("EHBP key configuration mismatch");
130672
131379
  }
130673
- if (!response.headers.get(PROTOCOL2.RESPONSE_NONCE_HEADER)) {
131380
+ if (!response.headers.get(PROTOCOL3.RESPONSE_NONCE_HEADER)) {
130674
131381
  return response;
130675
131382
  }
130676
- const token = await extractSessionRecoveryToken2(requestContext);
130677
- return await decryptResponseWithToken2(response, token);
131383
+ const token = await extractSessionRecoveryToken3(requestContext);
131384
+ return await decryptResponseWithToken3(response, token);
130678
131385
  }
130679
131386
  async function fetchTinfoilPreservingPlaintextErrors(options, input, init) {
130680
131387
  const context = await prepareTinfoilClient(options);
130681
- const ehbp = await Promise.resolve().then(() => (init_esm5(), exports_esm));
131388
+ const ehbp = await Promise.resolve().then(() => (init_esm6(), exports_esm));
130682
131389
  const normalized = normalizeFetchArgs(input, init);
130683
131390
  const userCacheSecret = await resolveUserCacheSecret2({
130684
131391
  explicit: options.userCacheSecret,
@@ -130771,6 +131478,10 @@ function getImageResolutionFromDataUrl(dataUrl) {
130771
131478
  function calculateImageTokens(width, height, detail = "auto") {
130772
131479
  if (detail === "low")
130773
131480
  return 85;
131481
+ if (detail === "original") {
131482
+ const patches = Math.ceil(width / IMAGE_PATCH_PX) * Math.ceil(height / IMAGE_PATCH_PX);
131483
+ return Math.ceil(Math.min(patches, MAX_IMAGE_PATCHES) * 1.2);
131484
+ }
130774
131485
  let w5 = width;
130775
131486
  let h5 = height;
130776
131487
  if (w5 > 2048 || h5 > 2048) {
@@ -130798,6 +131509,233 @@ function calculateImageTokens(width, height, detail = "auto") {
130798
131509
  const numTiles = tilesWidth * tilesHeight;
130799
131510
  return 85 + 170 * numTiles;
130800
131511
  }
131512
+ function worstCaseImageTokensForDetail(detail) {
131513
+ if (detail === "low")
131514
+ return 85;
131515
+ if (detail === "original")
131516
+ return MAX_ORIGINAL_IMAGE_TOKENS;
131517
+ return MAX_TILED_IMAGE_TOKENS;
131518
+ }
131519
+ function estimateImageTokensForUrl(url3, detail) {
131520
+ if (!url3 || typeof url3 !== "string")
131521
+ return 0;
131522
+ if (url3.startsWith("data:")) {
131523
+ const res = getImageResolutionFromDataUrl(url3);
131524
+ if (res) {
131525
+ return calculateImageTokens(res.width, res.height, detail);
131526
+ }
131527
+ return worstCaseImageTokensForDetail(detail);
131528
+ }
131529
+ return worstCaseImageTokensForDetail(detail);
131530
+ }
131531
+ function estimateInputImageTokens(item) {
131532
+ const detail = typeof item?.detail === "string" && item?.detail ? item.detail : "auto";
131533
+ const url3 = typeof item?.image_url === "string" ? item.image_url : item?.image_url?.url;
131534
+ const fromUrl = estimateImageTokensForUrl(url3, detail);
131535
+ if (fromUrl > 0 || !item?.file_id)
131536
+ return fromUrl;
131537
+ return worstCaseImageTokensForDetail(detail);
131538
+ }
131539
+ function countImageTokensInMessages(apiMessages) {
131540
+ let total = 0;
131541
+ for (const msg of apiMessages ?? []) {
131542
+ const content2 = msg?.content;
131543
+ if (!Array.isArray(content2))
131544
+ continue;
131545
+ for (const part of content2) {
131546
+ if (!part || typeof part !== "object")
131547
+ continue;
131548
+ if (part.type === "image_url") {
131549
+ const detail = typeof part.image_url?.detail === "string" && part.image_url?.detail ? part.image_url.detail : "auto";
131550
+ const url3 = typeof part.image_url === "string" ? part.image_url : part.image_url?.url;
131551
+ total += estimateImageTokensForUrl(url3, detail);
131552
+ } else if (part.type === "input_image") {
131553
+ total += estimateInputImageTokens(part);
131554
+ }
131555
+ }
131556
+ }
131557
+ return total;
131558
+ }
131559
+ function countImageTokensInResponsesInput(input) {
131560
+ if (!Array.isArray(input))
131561
+ return 0;
131562
+ let total = 0;
131563
+ for (const item of input) {
131564
+ if (!item || typeof item !== "object")
131565
+ continue;
131566
+ if (item.type === "input_image") {
131567
+ total += estimateInputImageTokens(item);
131568
+ continue;
131569
+ }
131570
+ if (Array.isArray(item.content)) {
131571
+ for (const part of item.content) {
131572
+ if (part && typeof part === "object" && part.type === "input_image") {
131573
+ total += estimateInputImageTokens(part);
131574
+ }
131575
+ }
131576
+ }
131577
+ }
131578
+ return total;
131579
+ }
131580
+ function safeStringifyCompact(value) {
131581
+ try {
131582
+ return JSON.stringify(value);
131583
+ } catch {
131584
+ return null;
131585
+ }
131586
+ }
131587
+ function textLength(value) {
131588
+ if (typeof value === "string")
131589
+ return value.length;
131590
+ if (value !== null && typeof value === "object") {
131591
+ return safeStringifyCompact(value)?.length ?? 0;
131592
+ }
131593
+ return 0;
131594
+ }
131595
+ function countMessageTextChars(apiMessages) {
131596
+ let total = 0;
131597
+ for (const msg of apiMessages ?? []) {
131598
+ if (!msg || typeof msg !== "object")
131599
+ continue;
131600
+ const content2 = msg.content;
131601
+ if (typeof content2 === "string") {
131602
+ total += content2.length;
131603
+ } else if (Array.isArray(content2)) {
131604
+ for (const part of content2) {
131605
+ if (part && typeof part === "object" && part.type === "text") {
131606
+ total += textLength(part.text);
131607
+ }
131608
+ }
131609
+ }
131610
+ const toolCalls = msg.tool_calls;
131611
+ if (Array.isArray(toolCalls)) {
131612
+ for (const call of toolCalls) {
131613
+ const fn3 = call?.function;
131614
+ if (!fn3 || typeof fn3 !== "object")
131615
+ continue;
131616
+ total += textLength(fn3.name);
131617
+ total += textLength(fn3.arguments);
131618
+ }
131619
+ }
131620
+ }
131621
+ return total;
131622
+ }
131623
+ function countResponsesInputTextChars(input) {
131624
+ if (!Array.isArray(input))
131625
+ return 0;
131626
+ let total = 0;
131627
+ for (const item of input) {
131628
+ if (!item || typeof item !== "object")
131629
+ continue;
131630
+ const itemType = item.type;
131631
+ if (itemType === "input_text") {
131632
+ total += textLength(item.text);
131633
+ continue;
131634
+ }
131635
+ if (itemType === "function_call") {
131636
+ total += textLength(item.arguments);
131637
+ continue;
131638
+ }
131639
+ if (itemType === "function_call_output") {
131640
+ total += textLength(item.output);
131641
+ continue;
131642
+ }
131643
+ if (itemType === "reasoning") {
131644
+ const summary = item.summary;
131645
+ if (Array.isArray(summary)) {
131646
+ for (const part of summary) {
131647
+ if (part && typeof part === "object") {
131648
+ total += textLength(part.text);
131649
+ }
131650
+ }
131651
+ }
131652
+ }
131653
+ const content2 = item.content;
131654
+ if (typeof content2 === "string") {
131655
+ total += content2.length;
131656
+ } else if (Array.isArray(content2)) {
131657
+ for (const part of content2) {
131658
+ if (!part || typeof part !== "object")
131659
+ continue;
131660
+ total += textLength(part.text);
131661
+ if (typeof part.refusal === "string") {
131662
+ total += part.refusal.length;
131663
+ }
131664
+ }
131665
+ }
131666
+ }
131667
+ return total;
131668
+ }
131669
+ function sumStringChars(node) {
131670
+ if (typeof node === "string")
131671
+ return node.length;
131672
+ if (Array.isArray(node)) {
131673
+ return node.reduce((total, item) => total + sumStringChars(item), 0);
131674
+ }
131675
+ if (node && typeof node === "object") {
131676
+ let total = 0;
131677
+ for (const [key, value] of Object.entries(node)) {
131678
+ total += key.length + sumStringChars(value);
131679
+ }
131680
+ return total;
131681
+ }
131682
+ return 0;
131683
+ }
131684
+ function countPromptTokenIds(node) {
131685
+ if (typeof node === "number" && Number.isInteger(node))
131686
+ return 1;
131687
+ if (Array.isArray(node)) {
131688
+ return node.reduce((total, item) => total + countPromptTokenIds(item), 0);
131689
+ }
131690
+ return 0;
131691
+ }
131692
+ function estimateNodePromptTokens(body) {
131693
+ return Math.floor(sumStringChars(body) / 3) + countPromptTokenIds(body?.prompt);
131694
+ }
131695
+ function nodeGateSats(model2, satsPricing, requestBody, imageTokens) {
131696
+ const maxCost = satsPricing?.max_cost;
131697
+ if (typeof maxCost !== "number" || !Number.isFinite(maxCost))
131698
+ return 0;
131699
+ const toleranceFactor = 1 - NODE_TOLERANCE_PERCENT / 100;
131700
+ const promptPrice = typeof satsPricing.prompt === "number" ? satsPricing.prompt : 0;
131701
+ const completionPrice = typeof satsPricing.completion === "number" ? satsPricing.completion : 0;
131702
+ let gateMsats = Math.max(1, Math.floor(maxCost * 1000 * toleranceFactor));
131703
+ let promptAllowanceSats = typeof satsPricing.max_prompt_cost === "number" ? satsPricing.max_prompt_cost * toleranceFactor : 0;
131704
+ const topProvider = model2?.top_provider;
131705
+ const providerContextLength = topProvider?.context_length;
131706
+ const providerMaxCompletion = topProvider?.max_completion_tokens;
131707
+ let promptTokenLimit;
131708
+ if (topProvider && (providerContextLength || providerMaxCompletion)) {
131709
+ if (providerContextLength && providerMaxCompletion) {
131710
+ promptTokenLimit = Math.max(0, providerContextLength - providerMaxCompletion);
131711
+ } else if (providerContextLength) {
131712
+ promptTokenLimit = providerContextLength;
131713
+ } else {
131714
+ promptTokenLimit = 0;
131715
+ }
131716
+ } else if (model2?.context_length) {
131717
+ promptTokenLimit = model2.context_length;
131718
+ }
131719
+ if (promptTokenLimit !== undefined) {
131720
+ promptAllowanceSats = promptTokenLimit * promptPrice * toleranceFactor;
131721
+ }
131722
+ const promptTokens = estimateNodePromptTokens(requestBody) + (Array.isArray(requestBody?.messages) ? imageTokens : 0);
131723
+ if (promptTokens > 0) {
131724
+ const unusedPromptBudget = promptAllowanceSats - promptTokens * promptPrice;
131725
+ if (unusedPromptBudget > 0) {
131726
+ gateMsats -= Math.floor(unusedPromptBudget * 1000);
131727
+ }
131728
+ }
131729
+ const rawMaxTokens = requestBody?.max_tokens;
131730
+ const parsedMaxTokens = typeof rawMaxTokens === "number" ? rawMaxTokens : typeof rawMaxTokens === "string" && rawMaxTokens.trim() !== "" ? Number(rawMaxTokens) : NaN;
131731
+ if (Number.isFinite(parsedMaxTokens) && parsedMaxTokens > 0 && typeof satsPricing?.max_completion_cost === "number" && completionPrice > 0 && !isTinfoilModel(model2.id)) {
131732
+ const unusedCompletionBudget = satsPricing.max_completion_cost * toleranceFactor - parsedMaxTokens * completionPrice;
131733
+ if (unusedCompletionBudget > 0) {
131734
+ gateMsats -= Math.floor(unusedCompletionBudget * 1000);
131735
+ }
131736
+ }
131737
+ return Math.max(gateMsats, 1) / 1000;
131738
+ }
130801
131739
  function extractCostBreakdown(costObj) {
130802
131740
  if (!costObj || typeof costObj !== "object")
130803
131741
  return {};
@@ -131936,584 +132874,7 @@ var consoleLogger, InsufficientBalanceError, ProviderError, FailoverError, Provi
131936
132874
  return cachedModels.length > 0 ? cachedModels : [];
131937
132875
  }
131938
132876
  }
131939
- }, AuditLogger = class _AuditLogger {
131940
- static instance = null;
131941
- static getInstance() {
131942
- if (!_AuditLogger.instance) {
131943
- _AuditLogger.instance = new _AuditLogger;
131944
- }
131945
- return _AuditLogger.instance;
131946
- }
131947
- async log(entry) {
131948
- const fullEntry = {
131949
- ...entry,
131950
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
131951
- };
131952
- const logLine = JSON.stringify(fullEntry) + `
131953
- `;
131954
- if (typeof window === "undefined") {
131955
- try {
131956
- const fs = await import("fs");
131957
- const path2 = await import("path");
131958
- const logPath = path2.join(process.cwd(), "audit.log");
131959
- fs.appendFileSync(logPath, logLine);
131960
- } catch (error53) {
131961
- console.error("[AuditLogger] Failed to write to file:", error53);
131962
- }
131963
- } else {
131964
- console.log("[AUDIT]", logLine.trim());
131965
- }
131966
- }
131967
- async logBalanceSnapshot(action, amounts, options) {
131968
- await this.log({
131969
- action,
131970
- totalBalance: amounts.totalBalance,
131971
- providerBalances: amounts.providerBalances,
131972
- mintBalances: amounts.mintBalances,
131973
- amount: options?.amount,
131974
- mintUrl: options?.mintUrl,
131975
- baseUrl: options?.baseUrl,
131976
- status: options?.status ?? "success",
131977
- details: options?.details
131978
- });
131979
- }
131980
- }, auditLogger, CashuSpender = class _CashuSpender {
131981
- constructor(walletAdapter, storageAdapter, _discoveryAdapter, balanceManager, logger4) {
131982
- this.walletAdapter = walletAdapter;
131983
- this.storageAdapter = storageAdapter;
131984
- this._discoveryAdapter = _discoveryAdapter;
131985
- this.balanceManager = balanceManager;
131986
- this.logger = (logger4 ?? consoleLogger).child("CashuSpender");
131987
- }
131988
- walletAdapter;
131989
- storageAdapter;
131990
- _discoveryAdapter;
131991
- balanceManager;
131992
- _isBusy = false;
131993
- debugLevel = "WARN";
131994
- logger;
131995
- static MAX_REFUND_RETRIES = 3;
131996
- static REFUND_RETRY_INTERVAL_MS = 120000;
131997
- _refundRetryInterval = null;
131998
- async receiveToken(token) {
131999
- try {
132000
- const result = await this.walletAdapter.receiveToken(token);
132001
- return result;
132002
- } catch (error53) {
132003
- const errorMessage = error53 instanceof Error ? error53.message : String(error53);
132004
- if (errorMessage.includes("Failed to fetch mint")) {
132005
- const cachedTokens = this.storageAdapter.getCachedReceiveTokens();
132006
- const existingIndex = cachedTokens.findIndex((t) => t.token === token);
132007
- if (existingIndex === -1) {
132008
- const { amount: amount2, unit: unit2 } = this._decodeTokenAmount(token);
132009
- this.storageAdapter.setCachedReceiveTokens([
132010
- ...cachedTokens,
132011
- {
132012
- token,
132013
- amount: amount2,
132014
- unit: unit2,
132015
- createdAt: Date.now()
132016
- }
132017
- ]);
132018
- }
132019
- }
132020
- const { amount, unit } = this._decodeTokenAmount(token);
132021
- return { success: false, amount, unit, message: errorMessage };
132022
- }
132023
- }
132024
- _decodeTokenAmount(token) {
132025
- try {
132026
- const metadata = ur3(token);
132027
- const unit = metadata.unit || "sat";
132028
- return { amount: metadata.amount, unit };
132029
- } catch {
132030
- return { amount: 0, unit: "sat" };
132031
- }
132032
- }
132033
- async _getBalanceState() {
132034
- if (this.balanceManager) {
132035
- return this.balanceManager.getBalanceState();
132036
- }
132037
- const mintBalances = await this.walletAdapter.getBalances();
132038
- const units = this.walletAdapter.getMintUnits();
132039
- let totalMintBalance = 0;
132040
- const normalizedMintBalances = {};
132041
- for (const url3 in mintBalances) {
132042
- const balance = mintBalances[url3];
132043
- const unit = units[url3];
132044
- const balanceInSats = getBalanceInSats(balance, unit);
132045
- normalizedMintBalances[url3] = balanceInSats;
132046
- totalMintBalance += balanceInSats;
132047
- }
132048
- const providerBalances = {};
132049
- let totalProviderBalance = 0;
132050
- const apiKeys = this.storageAdapter.getAllApiKeys();
132051
- for (const apiKey of apiKeys) {
132052
- if (!providerBalances[apiKey.baseUrl]) {
132053
- providerBalances[apiKey.baseUrl] = 0;
132054
- }
132055
- providerBalances[apiKey.baseUrl] += apiKey.balance;
132056
- totalProviderBalance += apiKey.balance;
132057
- }
132058
- return {
132059
- totalBalance: totalMintBalance + totalProviderBalance,
132060
- providerBalances,
132061
- mintBalances: normalizedMintBalances
132062
- };
132063
- }
132064
- async _logTransaction(action, options) {
132065
- const balanceState = await this._getBalanceState();
132066
- await auditLogger.logBalanceSnapshot(action, balanceState, options);
132067
- }
132068
- get isBusy() {
132069
- return this._isBusy;
132070
- }
132071
- getDebugLevel() {
132072
- return this.debugLevel;
132073
- }
132074
- setDebugLevel(level) {
132075
- this.debugLevel = level;
132076
- }
132077
- _log(level, ...args) {
132078
- const levelPriority = {
132079
- DEBUG: 0,
132080
- WARN: 1,
132081
- ERROR: 2
132082
- };
132083
- if (levelPriority[level] >= levelPriority[this.debugLevel]) {
132084
- switch (level) {
132085
- case "DEBUG":
132086
- this.logger.log(...args);
132087
- break;
132088
- case "WARN":
132089
- this.logger.warn(...args);
132090
- break;
132091
- case "ERROR":
132092
- this.logger.error(...args);
132093
- break;
132094
- }
132095
- }
132096
- }
132097
- async spend(options) {
132098
- const {
132099
- mintUrl,
132100
- amount,
132101
- baseUrl,
132102
- reuseToken = false,
132103
- p2pkPubkey,
132104
- excludeMints = [],
132105
- retryCount = 0
132106
- } = options;
132107
- this._isBusy = true;
132108
- try {
132109
- const result = await this._spendInternal({
132110
- mintUrl,
132111
- amount,
132112
- baseUrl,
132113
- reuseToken,
132114
- p2pkPubkey,
132115
- excludeMints,
132116
- retryCount
132117
- });
132118
- if (result.status === "failed" || !result.token) {
132119
- const errorMsg = result.error || `Insufficient balance. Need ${amount} sats.`;
132120
- if (this._isNetworkError(errorMsg)) {
132121
- throw new Error(`Your mint ${mintUrl} is unreachable or is blocking your IP. Please try again later or switch mints.`);
132122
- }
132123
- if (result.errorDetails) {
132124
- throw new InsufficientBalanceError(result.errorDetails.required, result.errorDetails.available, result.errorDetails.maxMintBalance, result.errorDetails.maxMintUrl);
132125
- }
132126
- throw new Error(errorMsg);
132127
- }
132128
- return result;
132129
- } finally {
132130
- this._isBusy = false;
132131
- }
132132
- }
132133
- _isNetworkError(message) {
132134
- return isNetworkErrorMessage(message) || message.includes("Your mint") && message.includes("unreachable");
132135
- }
132136
- async _spendInternal(options) {
132137
- let {
132138
- mintUrl,
132139
- amount,
132140
- baseUrl,
132141
- reuseToken,
132142
- p2pkPubkey,
132143
- excludeMints,
132144
- retryCount
132145
- } = options;
132146
- this._log("DEBUG", `[CashuSpender] _spendInternal: amount=${amount}, mintUrl=${mintUrl}, baseUrl=${baseUrl}, reuseToken=${reuseToken}`);
132147
- let adjustedAmount = Math.ceil(amount);
132148
- if (!adjustedAmount || isNaN(adjustedAmount)) {
132149
- this._log("ERROR", `[CashuSpender] _spendInternal: Invalid amount: ${amount}`);
132150
- return {
132151
- token: null,
132152
- status: "failed",
132153
- balance: 0,
132154
- error: "Please enter a valid amount"
132155
- };
132156
- }
132157
- if (reuseToken && baseUrl) {
132158
- this._log("DEBUG", `[CashuSpender] _spendInternal: Attempting to reuse token for ${baseUrl}`);
132159
- const existingResult = await this._tryReuseToken(baseUrl, adjustedAmount, mintUrl);
132160
- if (existingResult) {
132161
- this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully reused token, balance: ${existingResult.balance}`);
132162
- return existingResult;
132163
- }
132164
- this._log("DEBUG", `[CashuSpender] _spendInternal: Could not reuse token, will create new token`);
132165
- }
132166
- const balanceState = await this._getBalanceState();
132167
- const totalAvailableBalance = balanceState.totalBalance;
132168
- this._log("DEBUG", `[CashuSpender] _spendInternal: totalAvailableBalance=${totalAvailableBalance}, adjustedAmount=${adjustedAmount}`);
132169
- if (totalAvailableBalance < adjustedAmount) {
132170
- this._log("ERROR", `[CashuSpender] _spendInternal: Insufficient balance, have=${totalAvailableBalance}, need=${adjustedAmount}`);
132171
- return this._createInsufficientBalanceError(adjustedAmount, balanceState.mintBalances, totalAvailableBalance);
132172
- }
132173
- let token = null;
132174
- let selectedMintUrl;
132175
- let spentAmount = adjustedAmount;
132176
- if (this.balanceManager) {
132177
- const tokenResult = await this.balanceManager.createProviderToken({
132178
- mintUrl,
132179
- baseUrl,
132180
- amount: adjustedAmount,
132181
- p2pkPubkey,
132182
- excludeMints,
132183
- retryCount
132184
- });
132185
- if (!tokenResult.success || !tokenResult.token) {
132186
- if ((tokenResult.error || "").includes("Insufficient balance")) {
132187
- return this._createInsufficientBalanceError(adjustedAmount, balanceState.mintBalances, totalAvailableBalance);
132188
- }
132189
- return {
132190
- token: null,
132191
- status: "failed",
132192
- balance: 0,
132193
- error: tokenResult.error || "Failed to create token"
132194
- };
132195
- }
132196
- token = tokenResult.token;
132197
- selectedMintUrl = tokenResult.selectedMintUrl;
132198
- spentAmount = tokenResult.amountSpent || adjustedAmount;
132199
- } else {
132200
- try {
132201
- token = await this.walletAdapter.sendToken(mintUrl, adjustedAmount, p2pkPubkey);
132202
- selectedMintUrl = mintUrl;
132203
- } catch (error53) {
132204
- const errorMsg = error53 instanceof Error ? error53.message : String(error53);
132205
- return {
132206
- token: null,
132207
- status: "failed",
132208
- balance: 0,
132209
- error: `Error generating token: ${errorMsg}`
132210
- };
132211
- }
132212
- }
132213
- if (token) {
132214
- this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`);
132215
- }
132216
- this._logTransaction("spend", {
132217
- amount: spentAmount,
132218
- mintUrl: selectedMintUrl || mintUrl,
132219
- baseUrl,
132220
- status: "success"
132221
- });
132222
- this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`);
132223
- const units = this.walletAdapter.getMintUnits();
132224
- return {
132225
- token,
132226
- status: "success",
132227
- balance: spentAmount,
132228
- unit: (selectedMintUrl ? units[selectedMintUrl] : units[mintUrl]) || "sat",
132229
- selectedMintUrl: selectedMintUrl || mintUrl
132230
- };
132231
- }
132232
- async _tryReuseToken(baseUrl, amount, mintUrl) {
132233
- const apiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
132234
- if (!apiKeyEntry)
132235
- return null;
132236
- const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
132237
- const balanceForBaseUrl = apiKeyDistribution.find((b2) => b2.baseUrl === baseUrl)?.amount || 0;
132238
- this._log("DEBUG", "Reusing API key", balanceForBaseUrl, amount);
132239
- if (balanceForBaseUrl > amount) {
132240
- const units = this.walletAdapter.getMintUnits();
132241
- const unit = units[mintUrl] || "sat";
132242
- return {
132243
- token: apiKeyEntry.key,
132244
- status: "success",
132245
- balance: balanceForBaseUrl,
132246
- unit
132247
- };
132248
- }
132249
- if (this.balanceManager) {
132250
- const topUpAmount = Math.ceil(amount * 1.2 - balanceForBaseUrl);
132251
- const topUpResult = await this.balanceManager.topUp({
132252
- mintUrl,
132253
- baseUrl,
132254
- amount: topUpAmount,
132255
- token: apiKeyEntry.key
132256
- });
132257
- this._log("DEBUG", "TOPUP ", topUpResult);
132258
- if (topUpResult.success && topUpResult.toppedUpAmount) {
132259
- const newBalance = balanceForBaseUrl + topUpResult.toppedUpAmount;
132260
- const units = this.walletAdapter.getMintUnits();
132261
- const unit = units[mintUrl] || "sat";
132262
- this._logTransaction("topup", {
132263
- amount: topUpResult.toppedUpAmount,
132264
- mintUrl,
132265
- baseUrl,
132266
- status: "success"
132267
- });
132268
- return {
132269
- token: apiKeyEntry.key,
132270
- status: "success",
132271
- balance: newBalance,
132272
- unit
132273
- };
132274
- }
132275
- const providerBalance = await this._getProviderTokenBalance(baseUrl, apiKeyEntry.key);
132276
- this._log("DEBUG", providerBalance);
132277
- if (providerBalance <= 0) {
132278
- this.storageAdapter.removeApiKey(baseUrl);
132279
- }
132280
- }
132281
- return null;
132282
- }
132283
- async refundXcashuTokens(mintUrl, excludeBaseUrls) {
132284
- const results = [];
132285
- const xcashuTokens = this.storageAdapter.getXcashuTokens();
132286
- const excludedUrls = new Set(excludeBaseUrls || []);
132287
- for (const [baseUrl, tokens] of Object.entries(xcashuTokens)) {
132288
- if (excludedUrls.has(baseUrl))
132289
- continue;
132290
- for (const xcashuToken of tokens) {
132291
- try {
132292
- if (!this.balanceManager) {
132293
- throw new Error("BalanceManager not available for xcashu refund");
132294
- }
132295
- const fetchResult = await this.balanceManager.fetchRefundToken(baseUrl, xcashuToken.token, true);
132296
- if (!fetchResult.success && fetchResult.status === 425) {
132297
- results.push({
132298
- baseUrl,
132299
- token: xcashuToken.token,
132300
- success: false,
132301
- error: fetchResult.error
132302
- });
132303
- this._log("WARN", `[CashuSpender] refundXcashuTokens: 425 "Refund is pending" for ${baseUrl}; leaving token in store, tryCount untouched`);
132304
- continue;
132305
- }
132306
- if (!fetchResult.success && fetchResult.parsedError?.type === CoreErrorType.TOKEN_ALREADY_SPENT) {
132307
- this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
132308
- results.push({
132309
- baseUrl,
132310
- token: xcashuToken.token,
132311
- success: false,
132312
- error: fetchResult.error
132313
- });
132314
- this._log("WARN", `[CashuSpender] refundXcashuTokens: token_already_spent for ${baseUrl}; removing permanently-spent xcashu token from store`);
132315
- continue;
132316
- }
132317
- if (!fetchResult.success && fetchResult.parsedError && isHandledRedemptionError(fetchResult.parsedError)) {
132318
- const directReceive = await this.receiveToken(xcashuToken.token);
132319
- if (directReceive.success) {
132320
- this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
132321
- results.push({
132322
- baseUrl,
132323
- token: xcashuToken.token,
132324
- success: true
132325
- });
132326
- this._log("DEBUG", `[CashuSpender] refundXcashuTokens: provider returned ${fetchResult.parsedError.type}/${fetchResult.parsedError.code}; recovered original token directly, amount=${directReceive.amount}`);
132327
- continue;
132328
- }
132329
- this._log("WARN", `[CashuSpender] refundXcashuTokens: provider returned ${fetchResult.parsedError.type}/${fetchResult.parsedError.code}; direct original-token recovery also failed: ${directReceive.message ?? "unknown error"}`);
132330
- }
132331
- if (!fetchResult.success && fetchResult.status === 404 && (fetchResult.error || "").includes("Refund not found")) {
132332
- const currentTryCount = xcashuToken.tryCount ?? 0;
132333
- const newTryCount = currentTryCount + 1;
132334
- if (newTryCount >= _CashuSpender.MAX_REFUND_RETRIES) {
132335
- this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
132336
- this._log("WARN", `[CashuSpender] refundXcashuTokens: 404 "Refund not found" for ${baseUrl} after ${newTryCount} retries; removing unrefundable xcashu token from store`);
132337
- } else {
132338
- this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
132339
- this._log("WARN", `[CashuSpender] refundXcashuTokens: 404 "Refund not found" for ${baseUrl}; tryCount=${newTryCount}/${_CashuSpender.MAX_REFUND_RETRIES}, will retry in ${_CashuSpender.REFUND_RETRY_INTERVAL_MS / 1000}s`);
132340
- this._startRefundRetryInterval(mintUrl);
132341
- }
132342
- results.push({
132343
- baseUrl,
132344
- token: xcashuToken.token,
132345
- success: false,
132346
- error: fetchResult.error
132347
- });
132348
- continue;
132349
- }
132350
- if (!fetchResult.success || !fetchResult.token) {
132351
- throw new Error(fetchResult.error || "Failed to fetch refund token from provider");
132352
- }
132353
- const receiveResult = await this.receiveToken(fetchResult.token);
132354
- if (receiveResult.success) {
132355
- this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
132356
- results.push({
132357
- baseUrl,
132358
- token: xcashuToken.token,
132359
- success: true
132360
- });
132361
- this._log("DEBUG", `[CashuSpender] refundXcashuTokens: Successfully refunded xcashu token for ${baseUrl}, amount=${receiveResult.amount}`);
132362
- } else {
132363
- const currentTryCount = xcashuToken.tryCount ?? 0;
132364
- const newTryCount = currentTryCount + 1;
132365
- this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
132366
- results.push({
132367
- baseUrl,
132368
- token: xcashuToken.token,
132369
- success: false,
132370
- error: receiveResult.message ?? "Refund failed"
132371
- });
132372
- this._log("DEBUG", `[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}: ${receiveResult.message}`);
132373
- }
132374
- } catch (error53) {
132375
- const currentTryCount = xcashuToken.tryCount ?? 0;
132376
- const newTryCount = currentTryCount + 1;
132377
- this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
132378
- const errorMessage = error53 instanceof Error ? error53.message : String(error53);
132379
- results.push({
132380
- baseUrl,
132381
- token: xcashuToken.token,
132382
- success: false,
132383
- error: errorMessage
132384
- });
132385
- this._log("ERROR", `[CashuSpender] refundXcashuTokens: Exception during refund for ${baseUrl}: ${errorMessage}, incremented tryCount to ${newTryCount}`);
132386
- }
132387
- }
132388
- }
132389
- return results;
132390
- }
132391
- _startRefundRetryInterval(mintUrl) {
132392
- if (this._refundRetryInterval)
132393
- return;
132394
- this._log("DEBUG", `[CashuSpender] Starting refund retry interval (every ${_CashuSpender.REFUND_RETRY_INTERVAL_MS / 1000}s)`);
132395
- this._refundRetryInterval = setInterval(async () => {
132396
- const remainingTokens = this.storageAdapter.getXcashuTokens();
132397
- const hasTokens = Object.values(remainingTokens).some((tokens) => tokens.length > 0);
132398
- if (!hasTokens) {
132399
- this._stopRefundRetryInterval();
132400
- return;
132401
- }
132402
- try {
132403
- await this.refundXcashuTokens(mintUrl);
132404
- } catch (error53) {
132405
- this._log("ERROR", `[CashuSpender] Refund retry interval error: ${error53 instanceof Error ? error53.message : String(error53)}`);
132406
- }
132407
- }, _CashuSpender.REFUND_RETRY_INTERVAL_MS);
132408
- }
132409
- _stopRefundRetryInterval() {
132410
- if (this._refundRetryInterval) {
132411
- clearInterval(this._refundRetryInterval);
132412
- this._refundRetryInterval = null;
132413
- this._log("DEBUG", `[CashuSpender] Stopped refund retry interval`);
132414
- }
132415
- }
132416
- async refundProviders(mintUrl, forceRefund) {
132417
- const results = [];
132418
- const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
132419
- for (const apiKeyEntry of apiKeyDistribution) {
132420
- const apiKeyEntryFull = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
132421
- if (apiKeyEntryFull && this.balanceManager) {
132422
- try {
132423
- const balanceResult = await this.balanceManager.getTokenBalance(apiKeyEntryFull.key, apiKeyEntry.baseUrl);
132424
- if (balanceResult.isInvalidApiKey) {
132425
- this.logger.warn(`refundProviders: ${apiKeyEntry.baseUrl} returned invalid API key; removing local key and treating as success`);
132426
- this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
132427
- results.push({
132428
- baseUrl: apiKeyEntry.baseUrl,
132429
- success: true
132430
- });
132431
- continue;
132432
- }
132433
- if (balanceResult.amount >= 0 && !balanceResult.balanceUnknown) {
132434
- const balanceSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.amount / 1000) : balanceResult.amount;
132435
- const reservedSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.reserved / 1000) : balanceResult.reserved;
132436
- this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, balanceSat, reservedSat);
132437
- } else {
132438
- this.logger.warn(`refundProviders: balance refresh for ${apiKeyEntry.baseUrl} returned negative amount; keeping stale local balance=${apiKeyEntryFull.balance}`);
132439
- }
132440
- } catch (error53) {
132441
- this.logger.warn(`refundProviders: balance refresh threw for ${apiKeyEntry.baseUrl}; proceeding with stale local balance`, error53);
132442
- }
132443
- const refreshedEntry = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
132444
- if (!refreshedEntry) {
132445
- continue;
132446
- }
132447
- const refundResult = await this.balanceManager.refundApiKey({
132448
- mintUrl,
132449
- baseUrl: apiKeyEntry.baseUrl,
132450
- apiKey: refreshedEntry.key,
132451
- forceRefund
132452
- });
132453
- if (refundResult.success) {
132454
- this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
132455
- } else {
132456
- const currentEntry = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
132457
- this.logger.warn(`refundProviders: refund failed for ${apiKeyEntry.baseUrl}; currentEntry=${Boolean(currentEntry)} balance=${currentEntry?.balance ?? "none"}. Touching lastUsed to rate-limit retries.`);
132458
- if (currentEntry) {
132459
- this.storageAdapter.touchApiKeyLastUsed(apiKeyEntry.baseUrl);
132460
- }
132461
- }
132462
- results.push({
132463
- baseUrl: apiKeyEntry.baseUrl,
132464
- success: refundResult.success
132465
- });
132466
- } else {
132467
- this.logger.warn(`refundProviders: cannot refund ${apiKeyEntry.baseUrl}; apiKeyEntryFull=${Boolean(apiKeyEntryFull)} balanceManager=${Boolean(this.balanceManager)}`);
132468
- results.push({
132469
- baseUrl: apiKeyEntry.baseUrl,
132470
- success: false
132471
- });
132472
- }
132473
- }
132474
- return results;
132475
- }
132476
- _createInsufficientBalanceError(required2, normalizedBalances, availableBalance) {
132477
- let maxBalance = 0;
132478
- let maxMintUrl = "";
132479
- for (const mintUrl in normalizedBalances) {
132480
- const balanceInSats = normalizedBalances[mintUrl];
132481
- if (balanceInSats > maxBalance) {
132482
- maxBalance = balanceInSats;
132483
- maxMintUrl = mintUrl;
132484
- }
132485
- }
132486
- const error53 = new InsufficientBalanceError(required2, availableBalance ?? maxBalance, maxBalance, maxMintUrl);
132487
- return {
132488
- token: null,
132489
- status: "failed",
132490
- balance: 0,
132491
- error: error53.message,
132492
- errorDetails: {
132493
- required: required2,
132494
- available: availableBalance ?? maxBalance,
132495
- maxMintBalance: maxBalance,
132496
- maxMintUrl
132497
- }
132498
- };
132499
- }
132500
- async _getProviderTokenBalance(baseUrl, token) {
132501
- try {
132502
- const response = await fetch(`${baseUrl}v1/wallet/info`, {
132503
- headers: {
132504
- Authorization: `Bearer ${token}`
132505
- }
132506
- });
132507
- if (response.ok) {
132508
- const data = await response.json();
132509
- return data.balance / 1000;
132510
- }
132511
- } catch {
132512
- return 0;
132513
- }
132514
- return 0;
132515
- }
132516
- }, BalanceManager = class _BalanceManager {
132877
+ }, CashuSpender, BalanceManager = class _BalanceManager {
132517
132878
  constructor(walletAdapter, storageAdapter, discoveryAdapter, cashuSpender, logger4) {
132518
132879
  this.walletAdapter = walletAdapter;
132519
132880
  this.storageAdapter = storageAdapter;
@@ -132617,7 +132978,7 @@ var consoleLogger, InsufficientBalanceError, ProviderError, FailoverError, Provi
132617
132978
  if (!forceRefund) {
132618
132979
  const apiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
132619
132980
  if (apiKeyEntry?.lastUsed) {
132620
- const fiveMinutesAgo = Date.now() - 300000;
132981
+ const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
132621
132982
  if (apiKeyEntry.lastUsed > fiveMinutesAgo) {
132622
132983
  this.logger.log(`refundApiKey: skipping ${baseUrl} - used ${Math.round((Date.now() - apiKeyEntry.lastUsed) / 1000)}s ago`);
132623
132984
  return {
@@ -133267,7 +133628,7 @@ var consoleLogger, InsufficientBalanceError, ProviderError, FailoverError, Provi
133267
133628
  } catch {
133268
133629
  return trimmed.includes(TOR_ONION_SUFFIX);
133269
133630
  }
133270
- }, USER_CACHE_SECRET_FIELD2 = "user_cache_secret", USER_CACHE_SECRET_ENV2 = "TINFOIL_USER_CACHE_SECRET", USER_CACHE_SECRET_PATHS2, USER_CACHE_SECRET_DIR_NAME2 = ".tinfoil", USER_CACHE_SECRET_FILE_NAME2 = "user_cache_secret", ephemeralUserCacheSecret2, warnedEphemeral = false, defaultUserCacheSecretPromise, customPathSecretPromises, TINFOIL_MODEL_PREFIX = "tinfoil-", clientCache, normalizeBaseUrl2 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`, fetchProviderInfo = async (adapter, baseUrl, logger4) => {
133631
+ }, OPENAI_JSON_BODY_PATHS, USER_CACHE_SECRET_FIELD2 = "user_cache_secret", USER_CACHE_SECRET_ENV2 = "TINFOIL_USER_CACHE_SECRET", USER_CACHE_SECRET_PATHS2, USER_CACHE_SECRET_DIR_NAME2 = ".tinfoil", USER_CACHE_SECRET_FILE_NAME2 = "user_cache_secret", ephemeralUserCacheSecret2, warnedEphemeral = false, defaultUserCacheSecretPromise, customPathSecretPromises, TINFOIL_MODEL_PREFIX = "tinfoil-", clientCache, normalizeBaseUrl2 = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`, fetchProviderInfo = async (adapter, baseUrl, logger4) => {
133271
133632
  const normalized = normalizeBaseUrl2(baseUrl);
133272
133633
  const cached2 = adapter.getCachedProviderInfo()[normalized];
133273
133634
  if (cached2)
@@ -133287,7 +133648,7 @@ var consoleLogger, InsufficientBalanceError, ProviderError, FailoverError, Provi
133287
133648
  logger4.warn(`Failed to fetch provider info from ${normalized}:`, error53);
133288
133649
  return null;
133289
133650
  }
133290
- }, ProviderManager = class _ProviderManager {
133651
+ }, IMAGE_PATCH_PX = 32, MAX_IMAGE_PATCHES = 30000, MAX_TILED_IMAGE_TOKENS = 765, MAX_ORIGINAL_IMAGE_TOKENS, NODE_TOLERANCE_PERCENT = 1, ProviderManager = class _ProviderManager {
133291
133652
  constructor(discoveryAdapter, store, logger4) {
133292
133653
  this.discoveryAdapter = discoveryAdapter;
133293
133654
  this.instanceId = `pm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
@@ -133529,35 +133890,45 @@ ${ranking}`);
133529
133890
  }
133530
133891
  return providerMints.includes(mintUrl);
133531
133892
  }
133532
- getRequiredSatsForModel(model2, apiMessages, maxTokens) {
133893
+ getRequiredSatsForModel(model2, apiMessages, maxTokens, requestBody) {
133533
133894
  try {
133895
+ const body = requestBody ?? {};
133534
133896
  let imageTokens = 0;
133535
- if (apiMessages) {
133536
- for (const msg of apiMessages) {
133537
- const content2 = msg?.content;
133538
- if (Array.isArray(content2)) {
133539
- for (const part of content2) {
133540
- const isImage = part && typeof part === "object" && part.type === "image_url";
133541
- const url3 = isImage ? typeof part.image_url === "string" ? part.image_url : part.image_url?.url : undefined;
133542
- if (url3 && typeof url3 === "string" && url3.startsWith("data:")) {
133543
- const res = getImageResolutionFromDataUrl(url3);
133544
- if (res) {
133545
- const tokensFromImage = calculateImageTokens(res.width, res.height);
133546
- imageTokens += tokensFromImage;
133547
- } else {}
133548
- }
133549
- }
133550
- }
133897
+ let textChars = 0;
133898
+ let sawPromptShape = false;
133899
+ let textEstimateUnknown = false;
133900
+ if (Array.isArray(apiMessages) && apiMessages.length > 0) {
133901
+ sawPromptShape = true;
133902
+ imageTokens += countImageTokensInMessages(apiMessages);
133903
+ textChars += countMessageTextChars(apiMessages);
133904
+ }
133905
+ const responsesInput = body.input;
133906
+ if (!sawPromptShape) {
133907
+ if (Array.isArray(responsesInput)) {
133908
+ sawPromptShape = true;
133909
+ imageTokens += countImageTokensInResponsesInput(responsesInput);
133910
+ textChars += countResponsesInputTextChars(responsesInput);
133911
+ } else if (typeof responsesInput === "string") {
133912
+ sawPromptShape = true;
133913
+ textChars += responsesInput.length;
133914
+ }
133915
+ }
133916
+ const instructions = body.instructions;
133917
+ if (typeof instructions === "string" && instructions) {
133918
+ sawPromptShape = true;
133919
+ textChars += instructions.length;
133920
+ }
133921
+ const tools = body.tools;
133922
+ if (Array.isArray(tools) && tools.length > 0) {
133923
+ sawPromptShape = true;
133924
+ const serialized = safeStringifyCompact(tools);
133925
+ if (serialized === null) {
133926
+ textEstimateUnknown = true;
133927
+ } else {
133928
+ textChars += serialized.length;
133551
133929
  }
133552
133930
  }
133553
- const apiMessagesNoImages = apiMessages ? apiMessages.map((m2) => {
133554
- if (Array.isArray(m2?.content)) {
133555
- const filtered = m2.content.filter((p2) => !(p2 && typeof p2 === "object" && p2.type === "image_url"));
133556
- return { ...m2, content: filtered };
133557
- }
133558
- return m2;
133559
- }) : undefined;
133560
- const approximateTokens = apiMessagesNoImages ? Math.ceil(JSON.stringify(apiMessagesNoImages, null, 2).length / 2.84) : 1e4;
133931
+ const approximateTokens = textEstimateUnknown ? 1e4 : sawPromptShape ? Math.ceil(textChars / 2.84) : 1e4;
133561
133932
  const totalInputTokens = approximateTokens + imageTokens;
133562
133933
  const sp = model2?.sats_pricing;
133563
133934
  if (!sp) {
@@ -133572,11 +133943,17 @@ ${ranking}`);
133572
133943
  if (maxTokens !== undefined && sp.completion && !isTinfoilModel(model2.id)) {
133573
133944
  completionCost = sp.completion * maxTokens;
133574
133945
  }
133575
- const totalEstimatedCosts = (promptCosts + completionCost + requestFee) * 1.05;
133946
+ const componentEstimate = (promptCosts + completionCost + requestFee) * 1.05;
133947
+ const gateEstimate = nodeGateSats(model2, sp, body, imageTokens) * 1.05;
133948
+ let totalEstimatedCosts = Math.max(componentEstimate, gateEstimate);
133949
+ if (typeof sp.max_cost === "number" && totalEstimatedCosts > sp.max_cost) {
133950
+ return sp.max_cost;
133951
+ }
133576
133952
  return totalEstimatedCosts;
133577
133953
  } catch (e41) {
133578
133954
  this.logger.error("getRequiredSatsForModel error:", e41);
133579
- return 0;
133955
+ const sp = model2?.sats_pricing;
133956
+ return sp?.max_cost ?? 50;
133580
133957
  }
133581
133958
  }
133582
133959
  }, canUseLocalStorage = () => {
@@ -134629,7 +135006,6 @@ ${ranking}`);
134629
135006
  const satsSpent = await this._handlePostResponseBalanceUpdate({
134630
135007
  token: prepared.tokenUsed,
134631
135008
  baseUrl: prepared.baseUrlUsed,
134632
- mintUrl: params.mintUrl,
134633
135009
  initialTokenBalance: prepared.tokenBalanceInSats,
134634
135010
  initialTokenBalanceUnknown: prepared.tokenBalanceUnknown,
134635
135011
  fallbackSatsSpent: usage?.satsCost,
@@ -134685,17 +135061,22 @@ ${ranking}`);
134685
135061
  messageCount: requestMessages.length,
134686
135062
  maxTokens: requestMaxTokens
134687
135063
  });
134688
- requiredSats = this.providerManager.getRequiredSatsForModel(selectedModel, requestMessages, requestMaxTokens);
135064
+ requiredSats = this.providerManager.getRequiredSatsForModel(selectedModel, requestMessages, requestMaxTokens, requestBodyForPricing);
134689
135065
  }
134690
135066
  }
134691
135067
  let requestBody = body;
134692
135068
  if (body && typeof body === "object") {
134693
135069
  const bodyObj = body;
134694
- if (!bodyObj.stream) {
135070
+ if (isOpenAiJsonBodyPath(requestPath) && !bodyObj.stream) {
134695
135071
  requestBody = { ...bodyObj, stream: false };
134696
135072
  }
134697
135073
  }
134698
135074
  const baseHeaders = this._buildBaseHeaders();
135075
+ for (const [name16, value] of Object.entries(headers)) {
135076
+ if (name16.toLowerCase() === "x-routstr-model-path") {
135077
+ baseHeaders["x-routstr-model-path"] = value;
135078
+ }
135079
+ }
134699
135080
  const tinfoilEnabled = Boolean(modelId && isTinfoilModel(modelId));
134700
135081
  if (tinfoilEnabled) {
134701
135082
  this._log("DEBUG", `[RoutstrClient] Attesting Tinfoil model ${modelId} before spend`);
@@ -135201,7 +135582,7 @@ ${ranking}`);
135201
135582
  this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: Failing over to next provider: ${nextProvider}, model: ${selectedModel.id}`);
135202
135583
  const newModel = await this.providerManager.getModelForProvider(nextProvider, selectedModel.id) ?? selectedModel;
135203
135584
  const messagesForPricing = Array.isArray(body?.messages) ? body.messages : [];
135204
- const newRequiredSats = this.providerManager.getRequiredSatsForModel(newModel, messagesForPricing, params.maxTokens);
135585
+ const newRequiredSats = this.providerManager.getRequiredSatsForModel(newModel, messagesForPricing, params.maxTokens, body && typeof body === "object" ? body : undefined);
135205
135586
  if (params.tinfoilEnabled) {
135206
135587
  this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: Attesting Tinfoil failover provider ${nextProvider} before spend`);
135207
135588
  await prepareTinfoilClient({ baseUrl: nextProvider });
@@ -135326,7 +135707,6 @@ ${ranking}`);
135326
135707
  const {
135327
135708
  token,
135328
135709
  baseUrl,
135329
- mintUrl,
135330
135710
  initialTokenBalance,
135331
135711
  initialTokenBalanceUnknown,
135332
135712
  fallbackSatsSpent,
@@ -135860,14 +136240,536 @@ var init_dist17 = __esm(() => {
135860
136240
  ]);
135861
136241
  MAX_EVENT_FUTURE_DRIFT_SECONDS = 15 * 60;
135862
136242
  DISCOVERY_KINDS = [38421, 38423, 38425];
135863
- auditLogger = AuditLogger.getInstance();
135864
- USER_CACHE_SECRET_PATHS2 = [
136243
+ CashuSpender = class _CashuSpender {
136244
+ constructor(walletAdapter, storageAdapter, _discoveryAdapter, balanceManager, logger4) {
136245
+ this.walletAdapter = walletAdapter;
136246
+ this.storageAdapter = storageAdapter;
136247
+ this._discoveryAdapter = _discoveryAdapter;
136248
+ this.balanceManager = balanceManager;
136249
+ this.logger = (logger4 ?? consoleLogger).child("CashuSpender");
136250
+ }
136251
+ walletAdapter;
136252
+ storageAdapter;
136253
+ _discoveryAdapter;
136254
+ balanceManager;
136255
+ _isBusy = false;
136256
+ debugLevel = "WARN";
136257
+ logger;
136258
+ static MAX_REFUND_RETRIES = 3;
136259
+ static REFUND_RETRY_INTERVAL_MS = 2 * 60 * 1000;
136260
+ _refundRetryInterval = null;
136261
+ async receiveToken(token) {
136262
+ try {
136263
+ const result = await this.walletAdapter.receiveToken(token);
136264
+ return result;
136265
+ } catch (error53) {
136266
+ const errorMessage = error53 instanceof Error ? error53.message : String(error53);
136267
+ if (errorMessage.includes("Failed to fetch mint")) {
136268
+ const cachedTokens = this.storageAdapter.getCachedReceiveTokens();
136269
+ const existingIndex = cachedTokens.findIndex((t) => t.token === token);
136270
+ if (existingIndex === -1) {
136271
+ const { amount: amount2, unit: unit2 } = this._decodeTokenAmount(token);
136272
+ this.storageAdapter.setCachedReceiveTokens([
136273
+ ...cachedTokens,
136274
+ {
136275
+ token,
136276
+ amount: amount2,
136277
+ unit: unit2,
136278
+ createdAt: Date.now()
136279
+ }
136280
+ ]);
136281
+ }
136282
+ }
136283
+ const { amount, unit } = this._decodeTokenAmount(token);
136284
+ return { success: false, amount, unit, message: errorMessage };
136285
+ }
136286
+ }
136287
+ _decodeTokenAmount(token) {
136288
+ try {
136289
+ const metadata = ur3(token);
136290
+ const unit = metadata.unit || "sat";
136291
+ return { amount: metadata.amount, unit };
136292
+ } catch {
136293
+ return { amount: 0, unit: "sat" };
136294
+ }
136295
+ }
136296
+ async _getBalanceState() {
136297
+ if (this.balanceManager) {
136298
+ return this.balanceManager.getBalanceState();
136299
+ }
136300
+ const mintBalances = await this.walletAdapter.getBalances();
136301
+ const units = this.walletAdapter.getMintUnits();
136302
+ let totalMintBalance = 0;
136303
+ const normalizedMintBalances = {};
136304
+ for (const url3 in mintBalances) {
136305
+ const balance = mintBalances[url3];
136306
+ const unit = units[url3];
136307
+ const balanceInSats = getBalanceInSats(balance, unit);
136308
+ normalizedMintBalances[url3] = balanceInSats;
136309
+ totalMintBalance += balanceInSats;
136310
+ }
136311
+ const providerBalances = {};
136312
+ let totalProviderBalance = 0;
136313
+ const apiKeys = this.storageAdapter.getAllApiKeys();
136314
+ for (const apiKey of apiKeys) {
136315
+ if (!providerBalances[apiKey.baseUrl]) {
136316
+ providerBalances[apiKey.baseUrl] = 0;
136317
+ }
136318
+ providerBalances[apiKey.baseUrl] += apiKey.balance;
136319
+ totalProviderBalance += apiKey.balance;
136320
+ }
136321
+ return {
136322
+ totalBalance: totalMintBalance + totalProviderBalance,
136323
+ providerBalances,
136324
+ mintBalances: normalizedMintBalances
136325
+ };
136326
+ }
136327
+ get isBusy() {
136328
+ return this._isBusy;
136329
+ }
136330
+ getDebugLevel() {
136331
+ return this.debugLevel;
136332
+ }
136333
+ setDebugLevel(level) {
136334
+ this.debugLevel = level;
136335
+ }
136336
+ _log(level, ...args) {
136337
+ const levelPriority = {
136338
+ DEBUG: 0,
136339
+ WARN: 1,
136340
+ ERROR: 2
136341
+ };
136342
+ if (levelPriority[level] >= levelPriority[this.debugLevel]) {
136343
+ switch (level) {
136344
+ case "DEBUG":
136345
+ this.logger.log(...args);
136346
+ break;
136347
+ case "WARN":
136348
+ this.logger.warn(...args);
136349
+ break;
136350
+ case "ERROR":
136351
+ this.logger.error(...args);
136352
+ break;
136353
+ }
136354
+ }
136355
+ }
136356
+ async spend(options) {
136357
+ const {
136358
+ mintUrl,
136359
+ amount,
136360
+ baseUrl,
136361
+ reuseToken = false,
136362
+ p2pkPubkey,
136363
+ excludeMints = [],
136364
+ retryCount = 0
136365
+ } = options;
136366
+ this._isBusy = true;
136367
+ try {
136368
+ const result = await this._spendInternal({
136369
+ mintUrl,
136370
+ amount,
136371
+ baseUrl,
136372
+ reuseToken,
136373
+ p2pkPubkey,
136374
+ excludeMints,
136375
+ retryCount
136376
+ });
136377
+ if (result.status === "failed" || !result.token) {
136378
+ const errorMsg = result.error || `Insufficient balance. Need ${amount} sats.`;
136379
+ if (this._isNetworkError(errorMsg)) {
136380
+ throw new Error(`Your mint ${mintUrl} is unreachable or is blocking your IP. Please try again later or switch mints.`);
136381
+ }
136382
+ if (result.errorDetails) {
136383
+ throw new InsufficientBalanceError(result.errorDetails.required, result.errorDetails.available, result.errorDetails.maxMintBalance, result.errorDetails.maxMintUrl);
136384
+ }
136385
+ throw new Error(errorMsg);
136386
+ }
136387
+ return result;
136388
+ } finally {
136389
+ this._isBusy = false;
136390
+ }
136391
+ }
136392
+ _isNetworkError(message) {
136393
+ return isNetworkErrorMessage(message) || message.includes("Your mint") && message.includes("unreachable");
136394
+ }
136395
+ async _spendInternal(options) {
136396
+ let {
136397
+ mintUrl,
136398
+ amount,
136399
+ baseUrl,
136400
+ reuseToken,
136401
+ p2pkPubkey,
136402
+ excludeMints,
136403
+ retryCount
136404
+ } = options;
136405
+ this._log("DEBUG", `[CashuSpender] _spendInternal: amount=${amount}, mintUrl=${mintUrl}, baseUrl=${baseUrl}, reuseToken=${reuseToken}`);
136406
+ let adjustedAmount = Math.ceil(amount);
136407
+ if (!adjustedAmount || isNaN(adjustedAmount)) {
136408
+ this._log("ERROR", `[CashuSpender] _spendInternal: Invalid amount: ${amount}`);
136409
+ return {
136410
+ token: null,
136411
+ status: "failed",
136412
+ balance: 0,
136413
+ error: "Please enter a valid amount"
136414
+ };
136415
+ }
136416
+ if (reuseToken && baseUrl) {
136417
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Attempting to reuse token for ${baseUrl}`);
136418
+ const existingResult = await this._tryReuseToken(baseUrl, adjustedAmount, mintUrl);
136419
+ if (existingResult) {
136420
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully reused token, balance: ${existingResult.balance}`);
136421
+ return existingResult;
136422
+ }
136423
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Could not reuse token, will create new token`);
136424
+ }
136425
+ const balanceState = await this._getBalanceState();
136426
+ const totalAvailableBalance = balanceState.totalBalance;
136427
+ this._log("DEBUG", `[CashuSpender] _spendInternal: totalAvailableBalance=${totalAvailableBalance}, adjustedAmount=${adjustedAmount}`);
136428
+ if (totalAvailableBalance < adjustedAmount) {
136429
+ this._log("ERROR", `[CashuSpender] _spendInternal: Insufficient balance, have=${totalAvailableBalance}, need=${adjustedAmount}`);
136430
+ return this._createInsufficientBalanceError(adjustedAmount, balanceState.mintBalances, totalAvailableBalance);
136431
+ }
136432
+ let token = null;
136433
+ let selectedMintUrl;
136434
+ let spentAmount = adjustedAmount;
136435
+ if (this.balanceManager) {
136436
+ const tokenResult = await this.balanceManager.createProviderToken({
136437
+ mintUrl,
136438
+ baseUrl,
136439
+ amount: adjustedAmount,
136440
+ p2pkPubkey,
136441
+ excludeMints,
136442
+ retryCount
136443
+ });
136444
+ if (!tokenResult.success || !tokenResult.token) {
136445
+ if ((tokenResult.error || "").includes("Insufficient balance")) {
136446
+ return this._createInsufficientBalanceError(adjustedAmount, balanceState.mintBalances, totalAvailableBalance);
136447
+ }
136448
+ return {
136449
+ token: null,
136450
+ status: "failed",
136451
+ balance: 0,
136452
+ error: tokenResult.error || "Failed to create token"
136453
+ };
136454
+ }
136455
+ token = tokenResult.token;
136456
+ selectedMintUrl = tokenResult.selectedMintUrl;
136457
+ spentAmount = tokenResult.amountSpent || adjustedAmount;
136458
+ } else {
136459
+ try {
136460
+ token = await this.walletAdapter.sendToken(mintUrl, adjustedAmount, p2pkPubkey);
136461
+ selectedMintUrl = mintUrl;
136462
+ } catch (error53) {
136463
+ const errorMsg = error53 instanceof Error ? error53.message : String(error53);
136464
+ return {
136465
+ token: null,
136466
+ status: "failed",
136467
+ balance: 0,
136468
+ error: `Error generating token: ${errorMsg}`
136469
+ };
136470
+ }
136471
+ }
136472
+ if (token) {
136473
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`);
136474
+ }
136475
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`);
136476
+ const units = this.walletAdapter.getMintUnits();
136477
+ return {
136478
+ token,
136479
+ status: "success",
136480
+ balance: spentAmount,
136481
+ unit: (selectedMintUrl ? units[selectedMintUrl] : units[mintUrl]) || "sat",
136482
+ selectedMintUrl: selectedMintUrl || mintUrl
136483
+ };
136484
+ }
136485
+ async _tryReuseToken(baseUrl, amount, mintUrl) {
136486
+ const apiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
136487
+ if (!apiKeyEntry)
136488
+ return null;
136489
+ const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
136490
+ const balanceForBaseUrl = apiKeyDistribution.find((b2) => b2.baseUrl === baseUrl)?.amount || 0;
136491
+ this._log("DEBUG", "Reusing API key", balanceForBaseUrl, amount);
136492
+ if (balanceForBaseUrl > amount) {
136493
+ const units = this.walletAdapter.getMintUnits();
136494
+ const unit = units[mintUrl] || "sat";
136495
+ return {
136496
+ token: apiKeyEntry.key,
136497
+ status: "success",
136498
+ balance: balanceForBaseUrl,
136499
+ unit
136500
+ };
136501
+ }
136502
+ if (this.balanceManager) {
136503
+ const topUpAmount = Math.ceil(amount * 1.2 - balanceForBaseUrl);
136504
+ const topUpResult = await this.balanceManager.topUp({
136505
+ mintUrl,
136506
+ baseUrl,
136507
+ amount: topUpAmount,
136508
+ token: apiKeyEntry.key
136509
+ });
136510
+ this._log("DEBUG", "TOPUP ", topUpResult);
136511
+ if (topUpResult.success && topUpResult.toppedUpAmount) {
136512
+ const newBalance = balanceForBaseUrl + topUpResult.toppedUpAmount;
136513
+ const units = this.walletAdapter.getMintUnits();
136514
+ const unit = units[mintUrl] || "sat";
136515
+ return {
136516
+ token: apiKeyEntry.key,
136517
+ status: "success",
136518
+ balance: newBalance,
136519
+ unit
136520
+ };
136521
+ }
136522
+ const providerBalance = await this._getProviderTokenBalance(baseUrl, apiKeyEntry.key);
136523
+ this._log("DEBUG", providerBalance);
136524
+ if (providerBalance <= 0) {
136525
+ this.storageAdapter.removeApiKey(baseUrl);
136526
+ }
136527
+ }
136528
+ return null;
136529
+ }
136530
+ async refundXcashuTokens(mintUrl, excludeBaseUrls) {
136531
+ const results = [];
136532
+ const xcashuTokens = this.storageAdapter.getXcashuTokens();
136533
+ const excludedUrls = new Set(excludeBaseUrls || []);
136534
+ for (const [baseUrl, tokens] of Object.entries(xcashuTokens)) {
136535
+ if (excludedUrls.has(baseUrl))
136536
+ continue;
136537
+ for (const xcashuToken of tokens) {
136538
+ try {
136539
+ if (!this.balanceManager) {
136540
+ throw new Error("BalanceManager not available for xcashu refund");
136541
+ }
136542
+ const fetchResult = await this.balanceManager.fetchRefundToken(baseUrl, xcashuToken.token, true);
136543
+ if (!fetchResult.success && fetchResult.status === 425) {
136544
+ results.push({
136545
+ baseUrl,
136546
+ token: xcashuToken.token,
136547
+ success: false,
136548
+ error: fetchResult.error
136549
+ });
136550
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: 425 "Refund is pending" for ${baseUrl}; leaving token in store, tryCount untouched`);
136551
+ continue;
136552
+ }
136553
+ if (!fetchResult.success && fetchResult.parsedError?.type === CoreErrorType.TOKEN_ALREADY_SPENT) {
136554
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
136555
+ results.push({
136556
+ baseUrl,
136557
+ token: xcashuToken.token,
136558
+ success: false,
136559
+ error: fetchResult.error
136560
+ });
136561
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: token_already_spent for ${baseUrl}; removing permanently-spent xcashu token from store`);
136562
+ continue;
136563
+ }
136564
+ if (!fetchResult.success && fetchResult.parsedError && isHandledRedemptionError(fetchResult.parsedError)) {
136565
+ const directReceive = await this.receiveToken(xcashuToken.token);
136566
+ if (directReceive.success) {
136567
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
136568
+ results.push({
136569
+ baseUrl,
136570
+ token: xcashuToken.token,
136571
+ success: true
136572
+ });
136573
+ this._log("DEBUG", `[CashuSpender] refundXcashuTokens: provider returned ${fetchResult.parsedError.type}/${fetchResult.parsedError.code}; recovered original token directly, amount=${directReceive.amount}`);
136574
+ continue;
136575
+ }
136576
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: provider returned ${fetchResult.parsedError.type}/${fetchResult.parsedError.code}; direct original-token recovery also failed: ${directReceive.message ?? "unknown error"}`);
136577
+ }
136578
+ if (!fetchResult.success && fetchResult.status === 404 && (fetchResult.error || "").includes("Refund not found")) {
136579
+ const currentTryCount = xcashuToken.tryCount ?? 0;
136580
+ const newTryCount = currentTryCount + 1;
136581
+ if (newTryCount >= _CashuSpender.MAX_REFUND_RETRIES) {
136582
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
136583
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: 404 "Refund not found" for ${baseUrl} after ${newTryCount} retries; removing unrefundable xcashu token from store`);
136584
+ } else {
136585
+ this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
136586
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: 404 "Refund not found" for ${baseUrl}; tryCount=${newTryCount}/${_CashuSpender.MAX_REFUND_RETRIES}, will retry in ${_CashuSpender.REFUND_RETRY_INTERVAL_MS / 1000}s`);
136587
+ this._startRefundRetryInterval(mintUrl);
136588
+ }
136589
+ results.push({
136590
+ baseUrl,
136591
+ token: xcashuToken.token,
136592
+ success: false,
136593
+ error: fetchResult.error
136594
+ });
136595
+ continue;
136596
+ }
136597
+ if (!fetchResult.success || !fetchResult.token) {
136598
+ throw new Error(fetchResult.error || "Failed to fetch refund token from provider");
136599
+ }
136600
+ const receiveResult = await this.receiveToken(fetchResult.token);
136601
+ if (receiveResult.success) {
136602
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
136603
+ results.push({
136604
+ baseUrl,
136605
+ token: xcashuToken.token,
136606
+ success: true
136607
+ });
136608
+ this._log("DEBUG", `[CashuSpender] refundXcashuTokens: Successfully refunded xcashu token for ${baseUrl}, amount=${receiveResult.amount}`);
136609
+ } else {
136610
+ const currentTryCount = xcashuToken.tryCount ?? 0;
136611
+ const newTryCount = currentTryCount + 1;
136612
+ this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
136613
+ results.push({
136614
+ baseUrl,
136615
+ token: xcashuToken.token,
136616
+ success: false,
136617
+ error: receiveResult.message ?? "Refund failed"
136618
+ });
136619
+ this._log("DEBUG", `[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}: ${receiveResult.message}`);
136620
+ }
136621
+ } catch (error53) {
136622
+ const currentTryCount = xcashuToken.tryCount ?? 0;
136623
+ const newTryCount = currentTryCount + 1;
136624
+ this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
136625
+ const errorMessage = error53 instanceof Error ? error53.message : String(error53);
136626
+ results.push({
136627
+ baseUrl,
136628
+ token: xcashuToken.token,
136629
+ success: false,
136630
+ error: errorMessage
136631
+ });
136632
+ this._log("ERROR", `[CashuSpender] refundXcashuTokens: Exception during refund for ${baseUrl}: ${errorMessage}, incremented tryCount to ${newTryCount}`);
136633
+ }
136634
+ }
136635
+ }
136636
+ return results;
136637
+ }
136638
+ _startRefundRetryInterval(mintUrl) {
136639
+ if (this._refundRetryInterval)
136640
+ return;
136641
+ this._log("DEBUG", `[CashuSpender] Starting refund retry interval (every ${_CashuSpender.REFUND_RETRY_INTERVAL_MS / 1000}s)`);
136642
+ this._refundRetryInterval = setInterval(async () => {
136643
+ const remainingTokens = this.storageAdapter.getXcashuTokens();
136644
+ const hasTokens = Object.values(remainingTokens).some((tokens) => tokens.length > 0);
136645
+ if (!hasTokens) {
136646
+ this._stopRefundRetryInterval();
136647
+ return;
136648
+ }
136649
+ try {
136650
+ await this.refundXcashuTokens(mintUrl);
136651
+ } catch (error53) {
136652
+ this._log("ERROR", `[CashuSpender] Refund retry interval error: ${error53 instanceof Error ? error53.message : String(error53)}`);
136653
+ }
136654
+ }, _CashuSpender.REFUND_RETRY_INTERVAL_MS);
136655
+ }
136656
+ _stopRefundRetryInterval() {
136657
+ if (this._refundRetryInterval) {
136658
+ clearInterval(this._refundRetryInterval);
136659
+ this._refundRetryInterval = null;
136660
+ this._log("DEBUG", `[CashuSpender] Stopped refund retry interval`);
136661
+ }
136662
+ }
136663
+ async refundProviders(mintUrl, forceRefund) {
136664
+ const results = [];
136665
+ const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
136666
+ for (const apiKeyEntry of apiKeyDistribution) {
136667
+ const apiKeyEntryFull = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
136668
+ if (apiKeyEntryFull && this.balanceManager) {
136669
+ try {
136670
+ const balanceResult = await this.balanceManager.getTokenBalance(apiKeyEntryFull.key, apiKeyEntry.baseUrl);
136671
+ if (balanceResult.isInvalidApiKey) {
136672
+ this.logger.warn(`refundProviders: ${apiKeyEntry.baseUrl} returned invalid API key; removing local key and treating as success`);
136673
+ this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
136674
+ results.push({
136675
+ baseUrl: apiKeyEntry.baseUrl,
136676
+ success: true
136677
+ });
136678
+ continue;
136679
+ }
136680
+ if (balanceResult.amount >= 0 && !balanceResult.balanceUnknown) {
136681
+ const balanceSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.amount / 1000) : balanceResult.amount;
136682
+ const reservedSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.reserved / 1000) : balanceResult.reserved;
136683
+ this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, balanceSat, reservedSat);
136684
+ } else {
136685
+ this.logger.warn(`refundProviders: balance refresh for ${apiKeyEntry.baseUrl} returned negative amount; keeping stale local balance=${apiKeyEntryFull.balance}`);
136686
+ }
136687
+ } catch (error53) {
136688
+ this.logger.warn(`refundProviders: balance refresh threw for ${apiKeyEntry.baseUrl}; proceeding with stale local balance`, error53);
136689
+ }
136690
+ const refreshedEntry = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
136691
+ if (!refreshedEntry) {
136692
+ continue;
136693
+ }
136694
+ const refundResult = await this.balanceManager.refundApiKey({
136695
+ mintUrl,
136696
+ baseUrl: apiKeyEntry.baseUrl,
136697
+ apiKey: refreshedEntry.key,
136698
+ forceRefund
136699
+ });
136700
+ if (refundResult.success) {
136701
+ this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
136702
+ } else {
136703
+ const currentEntry = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
136704
+ this.logger.warn(`refundProviders: refund failed for ${apiKeyEntry.baseUrl}; currentEntry=${Boolean(currentEntry)} balance=${currentEntry?.balance ?? "none"}. Touching lastUsed to rate-limit retries.`);
136705
+ if (currentEntry) {
136706
+ this.storageAdapter.touchApiKeyLastUsed(apiKeyEntry.baseUrl);
136707
+ }
136708
+ }
136709
+ results.push({
136710
+ baseUrl: apiKeyEntry.baseUrl,
136711
+ success: refundResult.success
136712
+ });
136713
+ } else {
136714
+ this.logger.warn(`refundProviders: cannot refund ${apiKeyEntry.baseUrl}; apiKeyEntryFull=${Boolean(apiKeyEntryFull)} balanceManager=${Boolean(this.balanceManager)}`);
136715
+ results.push({
136716
+ baseUrl: apiKeyEntry.baseUrl,
136717
+ success: false
136718
+ });
136719
+ }
136720
+ }
136721
+ return results;
136722
+ }
136723
+ _createInsufficientBalanceError(required2, normalizedBalances, availableBalance) {
136724
+ let maxBalance = 0;
136725
+ let maxMintUrl = "";
136726
+ for (const mintUrl in normalizedBalances) {
136727
+ const balanceInSats = normalizedBalances[mintUrl];
136728
+ if (balanceInSats > maxBalance) {
136729
+ maxBalance = balanceInSats;
136730
+ maxMintUrl = mintUrl;
136731
+ }
136732
+ }
136733
+ const error53 = new InsufficientBalanceError(required2, availableBalance ?? maxBalance, maxBalance, maxMintUrl);
136734
+ return {
136735
+ token: null,
136736
+ status: "failed",
136737
+ balance: 0,
136738
+ error: error53.message,
136739
+ errorDetails: {
136740
+ required: required2,
136741
+ available: availableBalance ?? maxBalance,
136742
+ maxMintBalance: maxBalance,
136743
+ maxMintUrl
136744
+ }
136745
+ };
136746
+ }
136747
+ async _getProviderTokenBalance(baseUrl, token) {
136748
+ try {
136749
+ const response = await fetch(`${baseUrl}v1/wallet/info`, {
136750
+ headers: {
136751
+ Authorization: `Bearer ${token}`
136752
+ }
136753
+ });
136754
+ if (response.ok) {
136755
+ const data = await response.json();
136756
+ return data.balance / 1000;
136757
+ }
136758
+ } catch {
136759
+ return 0;
136760
+ }
136761
+ return 0;
136762
+ }
136763
+ };
136764
+ OPENAI_JSON_BODY_PATHS = [
135865
136765
  "/chat/completions",
135866
136766
  "/completions",
135867
136767
  "/responses"
135868
136768
  ];
136769
+ USER_CACHE_SECRET_PATHS2 = OPENAI_JSON_BODY_PATHS;
135869
136770
  customPathSecretPromises = /* @__PURE__ */ new Map;
135870
136771
  clientCache = /* @__PURE__ */ new Map;
136772
+ MAX_ORIGINAL_IMAGE_TOKENS = Math.ceil(MAX_IMAGE_PATCHES * 1.2);
135871
136773
  NON_CRITICAL_KEYS = /* @__PURE__ */ new Set(["modelsFromAllProviders"]);
135872
136774
  localStorageDriver = {
135873
136775
  async getItem(key, defaultValue) {
@@ -136340,20 +137242,20 @@ __export(exports_utils3, {
136340
137242
  numberToBytesLE: () => numberToBytesLE6,
136341
137243
  numberToBytesBE: () => numberToBytesBE7,
136342
137244
  hexToNumber: () => hexToNumber7,
136343
- hexToBytes: () => hexToBytes13,
137245
+ hexToBytes: () => hexToBytes14,
136344
137246
  equalBytes: () => equalBytes8,
136345
137247
  ensureBytes: () => ensureBytes4,
136346
137248
  createHmacDrbg: () => createHmacDrbg6,
136347
137249
  concatBytes: () => concatBytes14,
136348
137250
  bytesToNumberLE: () => bytesToNumberLE6,
136349
137251
  bytesToNumberBE: () => bytesToNumberBE6,
136350
- bytesToHex: () => bytesToHex15,
137252
+ bytesToHex: () => bytesToHex16,
136351
137253
  bitSet: () => bitSet3,
136352
137254
  bitMask: () => bitMask6,
136353
137255
  bitLen: () => bitLen6,
136354
137256
  bitGet: () => bitGet3
136355
137257
  });
136356
- function bytesToHex15(bytes7) {
137258
+ function bytesToHex16(bytes7) {
136357
137259
  if (!u8a8(bytes7))
136358
137260
  throw new Error("Uint8Array expected");
136359
137261
  let hex7 = "";
@@ -136371,7 +137273,7 @@ function hexToNumber7(hex7) {
136371
137273
  throw new Error("hex string expected, got " + typeof hex7);
136372
137274
  return BigInt(hex7 === "" ? "0" : `0x${hex7}`);
136373
137275
  }
136374
- function hexToBytes13(hex7) {
137276
+ function hexToBytes14(hex7) {
136375
137277
  if (typeof hex7 !== "string")
136376
137278
  throw new Error("hex string expected, got " + typeof hex7);
136377
137279
  const len = hex7.length;
@@ -136389,27 +137291,27 @@ function hexToBytes13(hex7) {
136389
137291
  return array2;
136390
137292
  }
136391
137293
  function bytesToNumberBE6(bytes7) {
136392
- return hexToNumber7(bytesToHex15(bytes7));
137294
+ return hexToNumber7(bytesToHex16(bytes7));
136393
137295
  }
136394
137296
  function bytesToNumberLE6(bytes7) {
136395
137297
  if (!u8a8(bytes7))
136396
137298
  throw new Error("Uint8Array expected");
136397
- return hexToNumber7(bytesToHex15(Uint8Array.from(bytes7).reverse()));
137299
+ return hexToNumber7(bytesToHex16(Uint8Array.from(bytes7).reverse()));
136398
137300
  }
136399
137301
  function numberToBytesBE7(n, len) {
136400
- return hexToBytes13(n.toString(16).padStart(len * 2, "0"));
137302
+ return hexToBytes14(n.toString(16).padStart(len * 2, "0"));
136401
137303
  }
136402
137304
  function numberToBytesLE6(n, len) {
136403
137305
  return numberToBytesBE7(n, len).reverse();
136404
137306
  }
136405
137307
  function numberToVarBytesBE3(n) {
136406
- return hexToBytes13(numberToHexUnpadded6(n));
137308
+ return hexToBytes14(numberToHexUnpadded6(n));
136407
137309
  }
136408
137310
  function ensureBytes4(title, hex7, expectedLength) {
136409
137311
  let res;
136410
137312
  if (typeof hex7 === "string") {
136411
137313
  try {
136412
- res = hexToBytes13(hex7);
137314
+ res = hexToBytes14(hex7);
136413
137315
  } catch (e41) {
136414
137316
  throw new Error(`${title} must be valid hex string, got "${hex7}". Cause: ${e41}`);
136415
137317
  }
@@ -136975,7 +137877,7 @@ function weierstrassPoints3(opts) {
136975
137877
  const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
136976
137878
  if (lengths && typeof key !== "bigint") {
136977
137879
  if (key instanceof Uint8Array)
136978
- key = bytesToHex15(key);
137880
+ key = bytesToHex16(key);
136979
137881
  if (typeof key !== "string" || !lengths.includes(key.length))
136980
137882
  throw new Error("Invalid key");
136981
137883
  key = key.padStart(nByteLength * 2, "0");
@@ -137266,7 +138168,7 @@ function weierstrassPoints3(opts) {
137266
138168
  return toBytes9(Point5, this, isCompressed);
137267
138169
  }
137268
138170
  toHex(isCompressed = true) {
137269
- return bytesToHex15(this.toRawBytes(isCompressed));
138171
+ return bytesToHex16(this.toRawBytes(isCompressed));
137270
138172
  }
137271
138173
  }
137272
138174
  Point5.BASE = new Point5(CURVE.Gx, CURVE.Gy, Fp5.ONE);
@@ -137344,7 +138246,7 @@ function weierstrass6(curveDef) {
137344
138246
  }
137345
138247
  }
137346
138248
  });
137347
- const numToNByteStr = (num3) => bytesToHex15(numberToBytesBE7(num3, CURVE.nByteLength));
138249
+ const numToNByteStr = (num3) => bytesToHex16(numberToBytesBE7(num3, CURVE.nByteLength));
137348
138250
  function isBiggerThanHalfOrder(number10) {
137349
138251
  const HALF = CURVE_ORDER >> _1n31;
137350
138252
  return number10 > HALF;
@@ -137405,13 +138307,13 @@ function weierstrass6(curveDef) {
137405
138307
  return this.hasHighS() ? new Signature(this.r, modN3(-this.s), this.recovery) : this;
137406
138308
  }
137407
138309
  toDERRawBytes() {
137408
- return hexToBytes13(this.toDERHex());
138310
+ return hexToBytes14(this.toDERHex());
137409
138311
  }
137410
138312
  toDERHex() {
137411
138313
  return DER6.hexFromSig({ r: this.r, s: this.s });
137412
138314
  }
137413
138315
  toCompactRawBytes() {
137414
- return hexToBytes13(this.toCompactHex());
138316
+ return hexToBytes14(this.toCompactHex());
137415
138317
  }
137416
138318
  toCompactHex() {
137417
138319
  return numToNByteStr(this.r) + numToNByteStr(this.s);
@@ -137906,7 +138808,7 @@ var init_cryptoNode7 = __esm(() => {
137906
138808
  });
137907
138809
 
137908
138810
  // node_modules/applesauce-sqlite/node_modules/applesauce-core/node_modules/nostr-tools/node_modules/@noble/hashes/esm/utils.js
137909
- function bytesToHex16(bytes7) {
138811
+ function bytesToHex17(bytes7) {
137910
138812
  if (!u8a9(bytes7))
137911
138813
  throw new Error("Uint8Array expected");
137912
138814
  let hex7 = "";
@@ -137915,7 +138817,7 @@ function bytesToHex16(bytes7) {
137915
138817
  }
137916
138818
  return hex7;
137917
138819
  }
137918
- function hexToBytes14(hex7) {
138820
+ function hexToBytes15(hex7) {
137919
138821
  if (typeof hex7 !== "string")
137920
138822
  throw new Error("hex string expected, got " + typeof hex7);
137921
138823
  const len = hex7.length;
@@ -138597,7 +139499,7 @@ function genBech325(encoding2) {
138597
139499
  return { encode: encode5, decode: decode5, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords };
138598
139500
  }
138599
139501
  var gcd5 = (a, b2) => !b2 ? a : gcd5(b2, a % b2), radix2carry5 = (from7, to) => from7 + (to - gcd5(from7, to)), base165, base325, base32hex5, base32crockford5, base648, base64url7, genBase585 = (abc) => chain5(radix7(58), alphabet5(abc), join14("")), base585, base58flickr5, base58xrp5, XMR_BLOCK_LEN3, base58xmr3, BECH_ALPHABET5, POLYMOD_GENERATORS5, bech325, bech32m5, utf83, hex7, CODERS3, coderTypeError3;
138600
- var init_esm6 = __esm(() => {
139502
+ var init_esm7 = __esm(() => {
138601
139503
  /*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
138602
139504
  base165 = chain5(radix25(4), alphabet5("0123456789ABCDEF"), join14(""));
138603
139505
  base325 = chain5(radix25(5), alphabet5("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding5(5), join14(""));
@@ -138693,7 +139595,7 @@ function output8(out, instance) {
138693
139595
  var init__assert8 = () => {};
138694
139596
 
138695
139597
  // node_modules/applesauce-sqlite/node_modules/applesauce-core/node_modules/nostr-tools/node_modules/@noble/ciphers/esm/utils.js
138696
- function bytesToHex17(bytes9) {
139598
+ function bytesToHex18(bytes9) {
138697
139599
  bytes8(bytes9);
138698
139600
  let hex8 = "";
138699
139601
  for (let i6 = 0;i6 < bytes9.length; i6++) {
@@ -138710,7 +139612,7 @@ function asciiToBase165(char) {
138710
139612
  return char - (asciis5._a - 10);
138711
139613
  return;
138712
139614
  }
138713
- function hexToBytes15(hex8) {
139615
+ function hexToBytes16(hex8) {
138714
139616
  if (typeof hex8 !== "string")
138715
139617
  throw new Error("hex string expected, got " + typeof hex8);
138716
139618
  const hl = hex8.length;
@@ -140340,7 +141242,7 @@ function serializeEvent6(evt) {
140340
141242
  }
140341
141243
  function getEventHash6(event3) {
140342
141244
  let eventHash = sha25611(utf8Encoder9.encode(serializeEvent6(event3)));
140343
- return bytesToHex16(eventHash);
141245
+ return bytesToHex17(eventHash);
140344
141246
  }
140345
141247
  function isRegularKind5(kind) {
140346
141248
  return kind < 1e4 && kind !== 0 && kind !== 3;
@@ -140505,7 +141407,7 @@ function decode5(code) {
140505
141407
  return {
140506
141408
  type: "nprofile",
140507
141409
  data: {
140508
- pubkey: bytesToHex16(tlv[0][0]),
141410
+ pubkey: bytesToHex17(tlv[0][0]),
140509
141411
  relays: tlv[1] ? tlv[1].map((d2) => utf8Decoder7.decode(d2)) : []
140510
141412
  }
140511
141413
  };
@@ -140523,10 +141425,10 @@ function decode5(code) {
140523
141425
  return {
140524
141426
  type: "nevent",
140525
141427
  data: {
140526
- id: bytesToHex16(tlv[0][0]),
141428
+ id: bytesToHex17(tlv[0][0]),
140527
141429
  relays: tlv[1] ? tlv[1].map((d2) => utf8Decoder7.decode(d2)) : [],
140528
- author: tlv[2]?.[0] ? bytesToHex16(tlv[2][0]) : undefined,
140529
- kind: tlv[3]?.[0] ? parseInt(bytesToHex16(tlv[3][0]), 16) : undefined
141430
+ author: tlv[2]?.[0] ? bytesToHex17(tlv[2][0]) : undefined,
141431
+ kind: tlv[3]?.[0] ? parseInt(bytesToHex17(tlv[3][0]), 16) : undefined
140530
141432
  }
140531
141433
  };
140532
141434
  }
@@ -140546,8 +141448,8 @@ function decode5(code) {
140546
141448
  type: "naddr",
140547
141449
  data: {
140548
141450
  identifier: utf8Decoder7.decode(tlv[0][0]),
140549
- pubkey: bytesToHex16(tlv[2][0]),
140550
- kind: parseInt(bytesToHex16(tlv[3][0]), 16),
141451
+ pubkey: bytesToHex17(tlv[2][0]),
141452
+ kind: parseInt(bytesToHex17(tlv[3][0]), 16),
140551
141453
  relays: tlv[1] ? tlv[1].map((d2) => utf8Decoder7.decode(d2)) : []
140552
141454
  }
140553
141455
  };
@@ -140556,7 +141458,7 @@ function decode5(code) {
140556
141458
  return { type: prefix, data };
140557
141459
  case "npub":
140558
141460
  case "note":
140559
- return { type: prefix, data: bytesToHex16(data) };
141461
+ return { type: prefix, data: bytesToHex17(data) };
140560
141462
  default:
140561
141463
  throw new Error(`unknown prefix ${prefix}`);
140562
141464
  }
@@ -140580,10 +141482,10 @@ function nsecEncode3(key) {
140580
141482
  return encodeBytes3("nsec", key);
140581
141483
  }
140582
141484
  function npubEncode4(hex8) {
140583
- return encodeBytes3("npub", hexToBytes14(hex8));
141485
+ return encodeBytes3("npub", hexToBytes15(hex8));
140584
141486
  }
140585
141487
  function noteEncode3(hex8) {
140586
- return encodeBytes3("note", hexToBytes14(hex8));
141488
+ return encodeBytes3("note", hexToBytes15(hex8));
140587
141489
  }
140588
141490
  function encodeBech323(prefix, data) {
140589
141491
  let words = bech325.toWords(data);
@@ -140594,7 +141496,7 @@ function encodeBytes3(prefix, bytes9) {
140594
141496
  }
140595
141497
  function nprofileEncode3(profile3) {
140596
141498
  let data = encodeTLV3({
140597
- 0: [hexToBytes14(profile3.pubkey)],
141499
+ 0: [hexToBytes15(profile3.pubkey)],
140598
141500
  1: (profile3.relays || []).map((url3) => utf8Encoder9.encode(url3))
140599
141501
  });
140600
141502
  return encodeBech323("nprofile", data);
@@ -140605,9 +141507,9 @@ function neventEncode3(event3) {
140605
141507
  kindArray = integerToUint8Array3(event3.kind);
140606
141508
  }
140607
141509
  let data = encodeTLV3({
140608
- 0: [hexToBytes14(event3.id)],
141510
+ 0: [hexToBytes15(event3.id)],
140609
141511
  1: (event3.relays || []).map((url3) => utf8Encoder9.encode(url3)),
140610
- 2: event3.author ? [hexToBytes14(event3.author)] : [],
141512
+ 2: event3.author ? [hexToBytes15(event3.author)] : [],
140611
141513
  3: kindArray ? [new Uint8Array(kindArray)] : []
140612
141514
  });
140613
141515
  return encodeBech323("nevent", data);
@@ -140618,7 +141520,7 @@ function naddrEncode3(addr) {
140618
141520
  let data = encodeTLV3({
140619
141521
  0: [utf8Encoder9.encode(addr.identifier)],
140620
141522
  1: (addr.relays || []).map((url3) => utf8Encoder9.encode(url3)),
140621
- 2: [hexToBytes14(addr.pubkey)],
141523
+ 2: [hexToBytes15(addr.pubkey)],
140622
141524
  3: [new Uint8Array(kind)]
140623
141525
  });
140624
141526
  return encodeBech323("naddr", data);
@@ -140637,7 +141539,7 @@ function encodeTLV3(tlv) {
140637
141539
  return concatBytes15(...entries);
140638
141540
  }
140639
141541
  function encrypt7(secretKey, pubkey, text) {
140640
- const privkey = secretKey instanceof Uint8Array ? bytesToHex16(secretKey) : secretKey;
141542
+ const privkey = secretKey instanceof Uint8Array ? bytesToHex17(secretKey) : secretKey;
140641
141543
  const key = secp256k15.getSharedSecret(privkey, "02" + pubkey);
140642
141544
  const normalizedKey = getNormalizedX3(key);
140643
141545
  let iv = Uint8Array.from(randomBytes11(16));
@@ -140648,7 +141550,7 @@ function encrypt7(secretKey, pubkey, text) {
140648
141550
  return `${ctb64}?iv=${ivb64}`;
140649
141551
  }
140650
141552
  function decrypt6(secretKey, pubkey, data) {
140651
- const privkey = secretKey instanceof Uint8Array ? bytesToHex16(secretKey) : secretKey;
141553
+ const privkey = secretKey instanceof Uint8Array ? bytesToHex17(secretKey) : secretKey;
140652
141554
  let [ctb64, ivb64] = data.split("?iv=");
140653
141555
  let key = secp256k15.getSharedSecret(privkey, "02" + pubkey);
140654
141556
  let normalizedKey = getNormalizedX3(key);
@@ -140837,7 +141739,7 @@ function minePow3(unsigned, difficulty) {
140837
141739
  return event3;
140838
141740
  }
140839
141741
  function fastEventHash2(evt) {
140840
- return bytesToHex16(sha25611(utf8Encoder9.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))));
141742
+ return bytesToHex17(sha25611(utf8Encoder9.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))));
140841
141743
  }
140842
141744
  function getConversationKey3(privkeyA, pubkeyB) {
140843
141745
  const sharedX = secp256k15.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
@@ -141585,7 +142487,7 @@ function validateEventMethodTag3(event3, method) {
141585
142487
  }
141586
142488
  function hashPayload5(payload) {
141587
142489
  const hash8 = sha25611(utf8Encoder9.encode(JSON.stringify(payload)));
141588
- return bytesToHex16(hash8);
142490
+ return bytesToHex17(hash8);
141589
142491
  }
141590
142492
  function validateEventPayloadTag3(event3, payload) {
141591
142493
  const payloadTag = event3.tags.find((t) => t[0] === "payload");
@@ -141672,13 +142574,13 @@ var __defProp4, __export4 = (target, all) => {
141672
142574
  return schnorr5.utils.randomPrivateKey();
141673
142575
  }
141674
142576
  getPublicKey(secretKey) {
141675
- return bytesToHex16(schnorr5.getPublicKey(secretKey));
142577
+ return bytesToHex17(schnorr5.getPublicKey(secretKey));
141676
142578
  }
141677
142579
  finalizeEvent(t, secretKey) {
141678
142580
  const event3 = t;
141679
- event3.pubkey = bytesToHex16(schnorr5.getPublicKey(secretKey));
142581
+ event3.pubkey = bytesToHex17(schnorr5.getPublicKey(secretKey));
141680
142582
  event3.id = getEventHash6(event3);
141681
- event3.sig = bytesToHex16(schnorr5.sign(getEventHash6(event3), secretKey));
142583
+ event3.sig = bytesToHex17(schnorr5.sign(getEventHash6(event3), secretKey));
141682
142584
  event3[verifiedSymbol8] = true;
141683
142585
  return event3;
141684
142586
  }
@@ -142301,7 +143203,7 @@ var __defProp4, __export4 = (target, all) => {
142301
143203
  insert(timestamp, id) {
142302
143204
  if (this.sealed)
142303
143205
  throw Error("already sealed");
142304
- const idb = hexToBytes15(id);
143206
+ const idb = hexToBytes16(id);
142305
143207
  if (idb.byteLength !== ID_SIZE4)
142306
143208
  throw Error("bad id size for added item");
142307
143209
  this.items.push({ timestamp, id: idb });
@@ -142394,10 +143296,10 @@ var __defProp4, __export4 = (target, all) => {
142394
143296
  let output9 = new WrappedBuffer4;
142395
143297
  output9.extend(new Uint8Array([PROTOCOL_VERSION4]));
142396
143298
  this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output9);
142397
- return bytesToHex17(output9.unwrap());
143299
+ return bytesToHex18(output9.unwrap());
142398
143300
  }
142399
143301
  reconcile(queryMsg, onhave, onneed) {
142400
- const query2 = new WrappedBuffer4(hexToBytes15(queryMsg));
143302
+ const query2 = new WrappedBuffer4(hexToBytes16(queryMsg));
142401
143303
  this.lastTimestampIn = this.lastTimestampOut = 0;
142402
143304
  let fullOutput = new WrappedBuffer4;
142403
143305
  fullOutput.extend(new Uint8Array([PROTOCOL_VERSION4]));
@@ -142440,22 +143342,22 @@ var __defProp4, __export4 = (target, all) => {
142440
143342
  let theirElems = {};
142441
143343
  for (let i22 = 0;i22 < numIds; i22++) {
142442
143344
  let e41 = getBytes5(query2, ID_SIZE4);
142443
- theirElems[bytesToHex17(e41)] = e41;
143345
+ theirElems[bytesToHex18(e41)] = e41;
142444
143346
  }
142445
143347
  skip2 = true;
142446
143348
  this.storage.iterate(lower, upper, (item) => {
142447
143349
  let k5 = item.id;
142448
- const id = bytesToHex17(k5);
143350
+ const id = bytesToHex18(k5);
142449
143351
  if (!theirElems[id]) {
142450
143352
  onhave?.(id);
142451
143353
  } else {
142452
- delete theirElems[bytesToHex17(k5)];
143354
+ delete theirElems[bytesToHex18(k5)];
142453
143355
  }
142454
143356
  return true;
142455
143357
  });
142456
143358
  if (onneed) {
142457
143359
  for (let v4 of Object.values(theirElems)) {
142458
- onneed(bytesToHex17(v4));
143360
+ onneed(bytesToHex18(v4));
142459
143361
  }
142460
143362
  }
142461
143363
  } else {
@@ -142473,7 +143375,7 @@ var __defProp4, __export4 = (target, all) => {
142473
143375
  prevIndex = upper;
142474
143376
  prevBound = currBound;
142475
143377
  }
142476
- return fullOutput.length === 1 ? null : bytesToHex17(fullOutput.unwrap());
143378
+ return fullOutput.length === 1 ? null : bytesToHex18(fullOutput.unwrap());
142477
143379
  }
142478
143380
  splitRange(lower, upper, upperBound, o) {
142479
143381
  let numElems = upper - lower;
@@ -142626,17 +143528,17 @@ var __defProp4, __export4 = (target, all) => {
142626
143528
  this.subscription.close();
142627
143529
  }
142628
143530
  }, nip98_exports3, _authorizationScheme5 = "Nostr ";
142629
- var init_esm7 = __esm(() => {
143531
+ var init_esm8 = __esm(() => {
142630
143532
  init_secp256k15();
142631
143533
  init_utils22();
142632
143534
  init_sha2566();
142633
143535
  init_utils22();
142634
143536
  init_utils22();
142635
- init_esm6();
143537
+ init_esm7();
142636
143538
  init_utils22();
142637
143539
  init_secp256k15();
142638
143540
  init_aes4();
142639
- init_esm6();
143541
+ init_esm7();
142640
143542
  init_utils22();
142641
143543
  init_sha2566();
142642
143544
  init_chacha3();
@@ -142646,13 +143548,13 @@ var init_esm7 = __esm(() => {
142646
143548
  init_hmac8();
142647
143549
  init_sha2566();
142648
143550
  init_utils22();
142649
- init_esm6();
142650
- init_esm6();
143551
+ init_esm7();
143552
+ init_esm7();
142651
143553
  init_utils23();
142652
143554
  init_sha2566();
142653
143555
  init_sha2566();
142654
143556
  init_utils22();
142655
- init_esm6();
143557
+ init_esm7();
142656
143558
  __defProp4 = Object.defineProperty;
142657
143559
  verifiedSymbol8 = Symbol("verified");
142658
143560
  utils_exports3 = {};
@@ -142660,8 +143562,8 @@ var init_esm7 = __esm(() => {
142660
143562
  Queue: () => Queue2,
142661
143563
  QueueNode: () => QueueNode2,
142662
143564
  binarySearch: () => binarySearch4,
142663
- bytesToHex: () => bytesToHex16,
142664
- hexToBytes: () => hexToBytes14,
143565
+ bytesToHex: () => bytesToHex17,
143566
+ hexToBytes: () => hexToBytes15,
142665
143567
  insertEventIntoAscendingList: () => insertEventIntoAscendingList4,
142666
143568
  insertEventIntoDescendingList: () => insertEventIntoDescendingList4,
142667
143569
  normalizeURL: () => normalizeURL4,
@@ -143026,20 +143928,20 @@ function serializeEvent7(evt) {
143026
143928
  }
143027
143929
  function getEventHash7(event3) {
143028
143930
  let eventHash = sha25611(utf8Encoder10.encode(serializeEvent7(event3)));
143029
- return bytesToHex16(eventHash);
143931
+ return bytesToHex17(eventHash);
143030
143932
  }
143031
143933
  var verifiedSymbol9, isRecord7 = (obj2) => obj2 instanceof Object, utf8Decoder8, utf8Encoder10, JS7 = class {
143032
143934
  generateSecretKey() {
143033
143935
  return schnorr5.utils.randomPrivateKey();
143034
143936
  }
143035
143937
  getPublicKey(secretKey) {
143036
- return bytesToHex16(schnorr5.getPublicKey(secretKey));
143938
+ return bytesToHex17(schnorr5.getPublicKey(secretKey));
143037
143939
  }
143038
143940
  finalizeEvent(t, secretKey) {
143039
143941
  const event3 = t;
143040
- event3.pubkey = bytesToHex16(schnorr5.getPublicKey(secretKey));
143942
+ event3.pubkey = bytesToHex17(schnorr5.getPublicKey(secretKey));
143041
143943
  event3.id = getEventHash7(event3);
143042
- event3.sig = bytesToHex16(schnorr5.sign(getEventHash7(event3), secretKey));
143944
+ event3.sig = bytesToHex17(schnorr5.sign(getEventHash7(event3), secretKey));
143043
143945
  event3[verifiedSymbol9] = true;
143044
143946
  return event3;
143045
143947
  }
@@ -143165,7 +144067,7 @@ function getReplaceableIdentifier2(event3) {
143165
144067
  }
143166
144068
  var EventStoreSymbol2, EventUIDSymbol2, ReplaceableAddressSymbol2, FromCacheSymbol2, ReplaceableIdentifierSymbol2;
143167
144069
  var init_event3 = __esm(() => {
143168
- init_esm7();
144070
+ init_esm8();
143169
144071
  init_kinds2();
143170
144072
  init_utils24();
143171
144073
  EventStoreSymbol2 = Symbol.for("event-store");
@@ -143247,7 +144149,7 @@ var processTags2 = (tags3, ...fns) => {
143247
144149
 
143248
144150
  // node_modules/applesauce-sqlite/node_modules/applesauce-core/node_modules/nostr-tools/lib/esm/nip19.js
143249
144151
  function npubEncode5(hex8) {
143250
- return encodeBytes4("npub", hexToBytes14(hex8));
144152
+ return encodeBytes4("npub", hexToBytes15(hex8));
143251
144153
  }
143252
144154
  function encodeBech324(prefix, data) {
143253
144155
  let words = bech325.toWords(data);
@@ -143258,7 +144160,7 @@ function encodeBytes4(prefix, bytes9) {
143258
144160
  }
143259
144161
  function nprofileEncode4(profile3) {
143260
144162
  let data = encodeTLV4({
143261
- 0: [hexToBytes14(profile3.pubkey)],
144163
+ 0: [hexToBytes15(profile3.pubkey)],
143262
144164
  1: (profile3.relays || []).map((url3) => utf8Encoder12.encode(url3))
143263
144165
  });
143264
144166
  return encodeBech324("nprofile", data);
@@ -143279,7 +144181,7 @@ function encodeTLV4(tlv) {
143279
144181
  var utf8Decoder10, utf8Encoder12, Bech32MaxSize4 = 5000;
143280
144182
  var init_nip19 = __esm(() => {
143281
144183
  init_utils22();
143282
- init_esm6();
144184
+ init_esm7();
143283
144185
  init_utils22();
143284
144186
  utf8Decoder10 = new TextDecoder("utf-8");
143285
144187
  utf8Encoder12 = new TextEncoder;
@@ -145955,7 +146857,7 @@ var init_models3 = __esm(() => {
145955
146857
 
145956
146858
  // node_modules/applesauce-sqlite/node_modules/applesauce-core/dist/operations/client.js
145957
146859
  var NEVER_ATTACH_CLIENT_TAG2;
145958
- var init_client5 = __esm(() => {
146860
+ var init_client6 = __esm(() => {
145959
146861
  init_event3();
145960
146862
  init_pointers2();
145961
146863
  init_tags2();
@@ -145969,7 +146871,7 @@ var init_hidden_content4 = __esm(() => {
145969
146871
 
145970
146872
  // node_modules/applesauce-sqlite/node_modules/applesauce-core/dist/operations/index.js
145971
146873
  var init_operations2 = __esm(() => {
145972
- init_client5();
146874
+ init_client6();
145973
146875
  init_content5();
145974
146876
  init_encrypted_content5();
145975
146877
  init_event4();
@@ -146492,7 +147394,28 @@ function makeConsoleLogger2(prefix) {
146492
147394
  child: (p2) => makeConsoleLogger2(prefix ? `${prefix}:${p2}` : p2)
146493
147395
  };
146494
147396
  }
146495
- var consoleLogger2, ProviderBootstrapError2, NoProvidersAvailableError2, DEFAULT_NOSTR_RELAYS2, POSITIVE_REVIEW_LABELS2, NEGATIVE_REVIEW_LABELS2, PROVIDER_FETCH_TIMEOUT_MS2 = 1e4, NOSTR_QUERY_TIMEOUT_MS2 = 5000, MAX_EVENT_FUTURE_DRIFT_SECONDS2, DISCOVERY_KINDS2, PRUNE_CHUNK_SIZE2 = 500, ModelManager2 = class _ModelManager2 {
147397
+ function isInvalidTokenError2(parsed) {
147398
+ return parsed.type === CoreErrorType2.INVALID_TOKEN && parsed.code === CoreErrorCode2.INVALID_CASHU_TOKEN;
147399
+ }
147400
+ function isCashuRedemptionError2(parsed) {
147401
+ return parsed.type === CoreErrorType2.CASHU_ERROR && (parsed.code === CoreErrorCode2.CASHU_TOKEN_REDEMPTION_FAILED || parsed.code === CoreErrorCode2.CASHU_TOKEN_ZERO_VALUE);
147402
+ }
147403
+ function isTokenConsumedError2(parsed) {
147404
+ return parsed.type === CoreErrorType2.TOKEN_CONSUMED && parsed.code === CoreErrorCode2.CASHU_TOKEN_CONSUMED;
147405
+ }
147406
+ function isCoreInternalError2(parsed) {
147407
+ return parsed.type === CoreErrorType2.API_ERROR && parsed.code === CoreErrorCode2.INTERNAL_ERROR;
147408
+ }
147409
+ function isHandledRedemptionError2(parsed) {
147410
+ return isInvalidTokenError2(parsed) || isCashuRedemptionError2(parsed) || isTokenConsumedError2(parsed) || isCoreInternalError2(parsed);
147411
+ }
147412
+ function isNetworkErrorMessage2(message) {
147413
+ return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed") || message.includes("Unable to connect") || message.includes("Was there a typo in the url or port") || message.includes("ERR_TLS_CERT_ALTNAME_INVALID") || message.includes("ERR_TLS_CERT_NOT_YET_VALID") || message.includes("ERR_TLS_CERT_EXPIRED") || message.includes("UNABLE_TO_VERIFY_LEAF_SIGNATURE") || message.includes("SELF_SIGNED_CERT_IN_CHAIN");
147414
+ }
147415
+ function getBalanceInSats2(balance, unit) {
147416
+ return unit === "msat" ? balance / 1000 : balance;
147417
+ }
147418
+ var consoleLogger2, InsufficientBalanceError2, ProviderBootstrapError2, NoProvidersAvailableError2, CoreErrorType2, CoreErrorCode2, DEFAULT_NOSTR_RELAYS2, POSITIVE_REVIEW_LABELS2, NEGATIVE_REVIEW_LABELS2, PROVIDER_FETCH_TIMEOUT_MS2 = 1e4, NOSTR_QUERY_TIMEOUT_MS2 = 5000, MAX_EVENT_FUTURE_DRIFT_SECONDS2, DISCOVERY_KINDS2, PRUNE_CHUNK_SIZE2 = 500, ModelManager2 = class _ModelManager2 {
146496
147419
  constructor(adapter, config2 = {}) {
146497
147420
  this.adapter = adapter;
146498
147421
  this.providerDirectoryUrl = config2.providerDirectoryUrl || "https://api.routstr.com/v1/providers/";
@@ -147197,48 +148120,7 @@ var consoleLogger2, ProviderBootstrapError2, NoProvidersAvailableError2, DEFAULT
147197
148120
  return cachedModels.length > 0 ? cachedModels : [];
147198
148121
  }
147199
148122
  }
147200
- }, AuditLogger2 = class _AuditLogger2 {
147201
- static instance = null;
147202
- static getInstance() {
147203
- if (!_AuditLogger2.instance) {
147204
- _AuditLogger2.instance = new _AuditLogger2;
147205
- }
147206
- return _AuditLogger2.instance;
147207
- }
147208
- async log(entry) {
147209
- const fullEntry = {
147210
- ...entry,
147211
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
147212
- };
147213
- const logLine = JSON.stringify(fullEntry) + `
147214
- `;
147215
- if (typeof window === "undefined") {
147216
- try {
147217
- const fs = await import("fs");
147218
- const path2 = await import("path");
147219
- const logPath = path2.join(process.cwd(), "audit.log");
147220
- fs.appendFileSync(logPath, logLine);
147221
- } catch (error53) {
147222
- console.error("[AuditLogger] Failed to write to file:", error53);
147223
- }
147224
- } else {
147225
- console.log("[AUDIT]", logLine.trim());
147226
- }
147227
- }
147228
- async logBalanceSnapshot(action, amounts, options) {
147229
- await this.log({
147230
- action,
147231
- totalBalance: amounts.totalBalance,
147232
- providerBalances: amounts.providerBalances,
147233
- mintBalances: amounts.mintBalances,
147234
- amount: options?.amount,
147235
- mintUrl: options?.mintUrl,
147236
- baseUrl: options?.baseUrl,
147237
- status: options?.status ?? "success",
147238
- details: options?.details
147239
- });
147240
- }
147241
- }, auditLogger2, createBunPersistentEventDatabase = async (dbPath) => {
148123
+ }, CashuSpender2, MAX_IMAGE_PATCHES2 = 30000, MAX_ORIGINAL_IMAGE_TOKENS2, createBunPersistentEventDatabase = async (dbPath) => {
147242
148124
  const { BunSqliteEventDatabase: BunSqliteEventDatabase2 } = await Promise.resolve().then(() => (init_bun(), exports_bun));
147243
148125
  return new BunSqliteEventDatabase2(dbPath);
147244
148126
  }, BunModelManager;
@@ -147248,6 +148130,20 @@ var init_bun2 = __esm(() => {
147248
148130
  init_helpers();
147249
148131
  init_cashu_ts_es3();
147250
148132
  consoleLogger2 = makeConsoleLogger2();
148133
+ InsufficientBalanceError2 = class extends Error {
148134
+ constructor(required2, available, maxMintBalance = 0, maxMintUrl = "", customMessage) {
148135
+ super(customMessage ?? `Insufficient balance: need ${required2} sats, have ${available} sats available. ` + (maxMintBalance > 0 ? `Largest mint balance: ${maxMintBalance} sats from ${maxMintUrl}` : ""));
148136
+ this.required = required2;
148137
+ this.available = available;
148138
+ this.maxMintBalance = maxMintBalance;
148139
+ this.maxMintUrl = maxMintUrl;
148140
+ this.name = "InsufficientBalanceError";
148141
+ }
148142
+ required;
148143
+ available;
148144
+ maxMintBalance;
148145
+ maxMintUrl;
148146
+ };
147251
148147
  ProviderBootstrapError2 = class extends Error {
147252
148148
  constructor(failedProviders, message) {
147253
148149
  super(message || `Failed to bootstrap providers. Tried: ${failedProviders.join(", ")}`);
@@ -147262,6 +148158,32 @@ var init_bun2 = __esm(() => {
147262
148158
  this.name = "NoProvidersAvailableError";
147263
148159
  }
147264
148160
  };
148161
+ CoreErrorType2 = {
148162
+ TOKEN_ALREADY_SPENT: "token_already_spent",
148163
+ INVALID_TOKEN: "invalid_token",
148164
+ MINT_ERROR: "mint_error",
148165
+ MINT_UNREACHABLE: "mint_unreachable",
148166
+ CASHU_ERROR: "cashu_error",
148167
+ INSUFFICIENT_QUOTA: "insufficient_quota",
148168
+ MINIMUM_BALANCE_REQUIRED: "minimum_balance_required",
148169
+ TOKEN_CONSUMED: "token_consumed",
148170
+ API_ERROR: "api_error",
148171
+ PAYMENT_ERROR: "payment_error"
148172
+ };
148173
+ CoreErrorCode2 = {
148174
+ TOKEN_ALREADY_SPENT: "cashu_token_already_spent",
148175
+ INVALID_CASHU_TOKEN: "invalid_cashu_token",
148176
+ CASHU_TOKEN_SWAP_FEES_EXCEED_AMOUNT: "cashu_token_swap_fees_exceed_amount",
148177
+ CASHU_FOREIGN_MINT_SWAP_FAILED: "cashu_foreign_mint_swap_failed",
148178
+ CASHU_MINT_UNREACHABLE: "cashu_mint_unreachable",
148179
+ CASHU_TOKEN_REDEMPTION_FAILED: "cashu_token_redemption_failed",
148180
+ CASHU_TOKEN_ZERO_VALUE: "cashu_token_zero_value",
148181
+ CASHU_TOKEN_CONSUMED: "cashu_token_consumed",
148182
+ INTERNAL_ERROR: "internal_error",
148183
+ INVALID_API_KEY: "invalid_api_key",
148184
+ INSUFFICIENT_BALANCE: "insufficient_balance",
148185
+ BALANCE_LIMIT_EXCEEDED: "balance_limit_exceeded"
148186
+ };
147265
148187
  DEFAULT_NOSTR_RELAYS2 = [
147266
148188
  "wss://relay.damus.io",
147267
148189
  "wss://nos.lol",
@@ -147276,7 +148198,528 @@ var init_bun2 = __esm(() => {
147276
148198
  ]);
147277
148199
  MAX_EVENT_FUTURE_DRIFT_SECONDS2 = 15 * 60;
147278
148200
  DISCOVERY_KINDS2 = [38421, 38423, 38425];
147279
- auditLogger2 = AuditLogger2.getInstance();
148201
+ CashuSpender2 = class _CashuSpender2 {
148202
+ constructor(walletAdapter, storageAdapter, _discoveryAdapter, balanceManager, logger6) {
148203
+ this.walletAdapter = walletAdapter;
148204
+ this.storageAdapter = storageAdapter;
148205
+ this._discoveryAdapter = _discoveryAdapter;
148206
+ this.balanceManager = balanceManager;
148207
+ this.logger = (logger6 ?? consoleLogger2).child("CashuSpender");
148208
+ }
148209
+ walletAdapter;
148210
+ storageAdapter;
148211
+ _discoveryAdapter;
148212
+ balanceManager;
148213
+ _isBusy = false;
148214
+ debugLevel = "WARN";
148215
+ logger;
148216
+ static MAX_REFUND_RETRIES = 3;
148217
+ static REFUND_RETRY_INTERVAL_MS = 2 * 60 * 1000;
148218
+ _refundRetryInterval = null;
148219
+ async receiveToken(token) {
148220
+ try {
148221
+ const result = await this.walletAdapter.receiveToken(token);
148222
+ return result;
148223
+ } catch (error53) {
148224
+ const errorMessage = error53 instanceof Error ? error53.message : String(error53);
148225
+ if (errorMessage.includes("Failed to fetch mint")) {
148226
+ const cachedTokens = this.storageAdapter.getCachedReceiveTokens();
148227
+ const existingIndex = cachedTokens.findIndex((t) => t.token === token);
148228
+ if (existingIndex === -1) {
148229
+ const { amount: amount2, unit: unit2 } = this._decodeTokenAmount(token);
148230
+ this.storageAdapter.setCachedReceiveTokens([
148231
+ ...cachedTokens,
148232
+ {
148233
+ token,
148234
+ amount: amount2,
148235
+ unit: unit2,
148236
+ createdAt: Date.now()
148237
+ }
148238
+ ]);
148239
+ }
148240
+ }
148241
+ const { amount, unit } = this._decodeTokenAmount(token);
148242
+ return { success: false, amount, unit, message: errorMessage };
148243
+ }
148244
+ }
148245
+ _decodeTokenAmount(token) {
148246
+ try {
148247
+ const metadata = ur3(token);
148248
+ const unit = metadata.unit || "sat";
148249
+ return { amount: metadata.amount, unit };
148250
+ } catch {
148251
+ return { amount: 0, unit: "sat" };
148252
+ }
148253
+ }
148254
+ async _getBalanceState() {
148255
+ if (this.balanceManager) {
148256
+ return this.balanceManager.getBalanceState();
148257
+ }
148258
+ const mintBalances = await this.walletAdapter.getBalances();
148259
+ const units = this.walletAdapter.getMintUnits();
148260
+ let totalMintBalance = 0;
148261
+ const normalizedMintBalances = {};
148262
+ for (const url4 in mintBalances) {
148263
+ const balance = mintBalances[url4];
148264
+ const unit = units[url4];
148265
+ const balanceInSats = getBalanceInSats2(balance, unit);
148266
+ normalizedMintBalances[url4] = balanceInSats;
148267
+ totalMintBalance += balanceInSats;
148268
+ }
148269
+ const providerBalances = {};
148270
+ let totalProviderBalance = 0;
148271
+ const apiKeys = this.storageAdapter.getAllApiKeys();
148272
+ for (const apiKey of apiKeys) {
148273
+ if (!providerBalances[apiKey.baseUrl]) {
148274
+ providerBalances[apiKey.baseUrl] = 0;
148275
+ }
148276
+ providerBalances[apiKey.baseUrl] += apiKey.balance;
148277
+ totalProviderBalance += apiKey.balance;
148278
+ }
148279
+ return {
148280
+ totalBalance: totalMintBalance + totalProviderBalance,
148281
+ providerBalances,
148282
+ mintBalances: normalizedMintBalances
148283
+ };
148284
+ }
148285
+ get isBusy() {
148286
+ return this._isBusy;
148287
+ }
148288
+ getDebugLevel() {
148289
+ return this.debugLevel;
148290
+ }
148291
+ setDebugLevel(level) {
148292
+ this.debugLevel = level;
148293
+ }
148294
+ _log(level, ...args) {
148295
+ const levelPriority = {
148296
+ DEBUG: 0,
148297
+ WARN: 1,
148298
+ ERROR: 2
148299
+ };
148300
+ if (levelPriority[level] >= levelPriority[this.debugLevel]) {
148301
+ switch (level) {
148302
+ case "DEBUG":
148303
+ this.logger.log(...args);
148304
+ break;
148305
+ case "WARN":
148306
+ this.logger.warn(...args);
148307
+ break;
148308
+ case "ERROR":
148309
+ this.logger.error(...args);
148310
+ break;
148311
+ }
148312
+ }
148313
+ }
148314
+ async spend(options) {
148315
+ const {
148316
+ mintUrl,
148317
+ amount,
148318
+ baseUrl,
148319
+ reuseToken = false,
148320
+ p2pkPubkey,
148321
+ excludeMints = [],
148322
+ retryCount = 0
148323
+ } = options;
148324
+ this._isBusy = true;
148325
+ try {
148326
+ const result = await this._spendInternal({
148327
+ mintUrl,
148328
+ amount,
148329
+ baseUrl,
148330
+ reuseToken,
148331
+ p2pkPubkey,
148332
+ excludeMints,
148333
+ retryCount
148334
+ });
148335
+ if (result.status === "failed" || !result.token) {
148336
+ const errorMsg = result.error || `Insufficient balance. Need ${amount} sats.`;
148337
+ if (this._isNetworkError(errorMsg)) {
148338
+ throw new Error(`Your mint ${mintUrl} is unreachable or is blocking your IP. Please try again later or switch mints.`);
148339
+ }
148340
+ if (result.errorDetails) {
148341
+ throw new InsufficientBalanceError2(result.errorDetails.required, result.errorDetails.available, result.errorDetails.maxMintBalance, result.errorDetails.maxMintUrl);
148342
+ }
148343
+ throw new Error(errorMsg);
148344
+ }
148345
+ return result;
148346
+ } finally {
148347
+ this._isBusy = false;
148348
+ }
148349
+ }
148350
+ _isNetworkError(message) {
148351
+ return isNetworkErrorMessage2(message) || message.includes("Your mint") && message.includes("unreachable");
148352
+ }
148353
+ async _spendInternal(options) {
148354
+ let {
148355
+ mintUrl,
148356
+ amount,
148357
+ baseUrl,
148358
+ reuseToken,
148359
+ p2pkPubkey,
148360
+ excludeMints,
148361
+ retryCount
148362
+ } = options;
148363
+ this._log("DEBUG", `[CashuSpender] _spendInternal: amount=${amount}, mintUrl=${mintUrl}, baseUrl=${baseUrl}, reuseToken=${reuseToken}`);
148364
+ let adjustedAmount = Math.ceil(amount);
148365
+ if (!adjustedAmount || isNaN(adjustedAmount)) {
148366
+ this._log("ERROR", `[CashuSpender] _spendInternal: Invalid amount: ${amount}`);
148367
+ return {
148368
+ token: null,
148369
+ status: "failed",
148370
+ balance: 0,
148371
+ error: "Please enter a valid amount"
148372
+ };
148373
+ }
148374
+ if (reuseToken && baseUrl) {
148375
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Attempting to reuse token for ${baseUrl}`);
148376
+ const existingResult = await this._tryReuseToken(baseUrl, adjustedAmount, mintUrl);
148377
+ if (existingResult) {
148378
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully reused token, balance: ${existingResult.balance}`);
148379
+ return existingResult;
148380
+ }
148381
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Could not reuse token, will create new token`);
148382
+ }
148383
+ const balanceState = await this._getBalanceState();
148384
+ const totalAvailableBalance = balanceState.totalBalance;
148385
+ this._log("DEBUG", `[CashuSpender] _spendInternal: totalAvailableBalance=${totalAvailableBalance}, adjustedAmount=${adjustedAmount}`);
148386
+ if (totalAvailableBalance < adjustedAmount) {
148387
+ this._log("ERROR", `[CashuSpender] _spendInternal: Insufficient balance, have=${totalAvailableBalance}, need=${adjustedAmount}`);
148388
+ return this._createInsufficientBalanceError(adjustedAmount, balanceState.mintBalances, totalAvailableBalance);
148389
+ }
148390
+ let token = null;
148391
+ let selectedMintUrl;
148392
+ let spentAmount = adjustedAmount;
148393
+ if (this.balanceManager) {
148394
+ const tokenResult = await this.balanceManager.createProviderToken({
148395
+ mintUrl,
148396
+ baseUrl,
148397
+ amount: adjustedAmount,
148398
+ p2pkPubkey,
148399
+ excludeMints,
148400
+ retryCount
148401
+ });
148402
+ if (!tokenResult.success || !tokenResult.token) {
148403
+ if ((tokenResult.error || "").includes("Insufficient balance")) {
148404
+ return this._createInsufficientBalanceError(adjustedAmount, balanceState.mintBalances, totalAvailableBalance);
148405
+ }
148406
+ return {
148407
+ token: null,
148408
+ status: "failed",
148409
+ balance: 0,
148410
+ error: tokenResult.error || "Failed to create token"
148411
+ };
148412
+ }
148413
+ token = tokenResult.token;
148414
+ selectedMintUrl = tokenResult.selectedMintUrl;
148415
+ spentAmount = tokenResult.amountSpent || adjustedAmount;
148416
+ } else {
148417
+ try {
148418
+ token = await this.walletAdapter.sendToken(mintUrl, adjustedAmount, p2pkPubkey);
148419
+ selectedMintUrl = mintUrl;
148420
+ } catch (error53) {
148421
+ const errorMsg = error53 instanceof Error ? error53.message : String(error53);
148422
+ return {
148423
+ token: null,
148424
+ status: "failed",
148425
+ balance: 0,
148426
+ error: `Error generating token: ${errorMsg}`
148427
+ };
148428
+ }
148429
+ }
148430
+ if (token) {
148431
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`);
148432
+ }
148433
+ this._log("DEBUG", `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`);
148434
+ const units = this.walletAdapter.getMintUnits();
148435
+ return {
148436
+ token,
148437
+ status: "success",
148438
+ balance: spentAmount,
148439
+ unit: (selectedMintUrl ? units[selectedMintUrl] : units[mintUrl]) || "sat",
148440
+ selectedMintUrl: selectedMintUrl || mintUrl
148441
+ };
148442
+ }
148443
+ async _tryReuseToken(baseUrl, amount, mintUrl) {
148444
+ const apiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
148445
+ if (!apiKeyEntry)
148446
+ return null;
148447
+ const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
148448
+ const balanceForBaseUrl = apiKeyDistribution.find((b2) => b2.baseUrl === baseUrl)?.amount || 0;
148449
+ this._log("DEBUG", "Reusing API key", balanceForBaseUrl, amount);
148450
+ if (balanceForBaseUrl > amount) {
148451
+ const units = this.walletAdapter.getMintUnits();
148452
+ const unit = units[mintUrl] || "sat";
148453
+ return {
148454
+ token: apiKeyEntry.key,
148455
+ status: "success",
148456
+ balance: balanceForBaseUrl,
148457
+ unit
148458
+ };
148459
+ }
148460
+ if (this.balanceManager) {
148461
+ const topUpAmount = Math.ceil(amount * 1.2 - balanceForBaseUrl);
148462
+ const topUpResult = await this.balanceManager.topUp({
148463
+ mintUrl,
148464
+ baseUrl,
148465
+ amount: topUpAmount,
148466
+ token: apiKeyEntry.key
148467
+ });
148468
+ this._log("DEBUG", "TOPUP ", topUpResult);
148469
+ if (topUpResult.success && topUpResult.toppedUpAmount) {
148470
+ const newBalance = balanceForBaseUrl + topUpResult.toppedUpAmount;
148471
+ const units = this.walletAdapter.getMintUnits();
148472
+ const unit = units[mintUrl] || "sat";
148473
+ return {
148474
+ token: apiKeyEntry.key,
148475
+ status: "success",
148476
+ balance: newBalance,
148477
+ unit
148478
+ };
148479
+ }
148480
+ const providerBalance = await this._getProviderTokenBalance(baseUrl, apiKeyEntry.key);
148481
+ this._log("DEBUG", providerBalance);
148482
+ if (providerBalance <= 0) {
148483
+ this.storageAdapter.removeApiKey(baseUrl);
148484
+ }
148485
+ }
148486
+ return null;
148487
+ }
148488
+ async refundXcashuTokens(mintUrl, excludeBaseUrls) {
148489
+ const results = [];
148490
+ const xcashuTokens = this.storageAdapter.getXcashuTokens();
148491
+ const excludedUrls = new Set(excludeBaseUrls || []);
148492
+ for (const [baseUrl, tokens] of Object.entries(xcashuTokens)) {
148493
+ if (excludedUrls.has(baseUrl))
148494
+ continue;
148495
+ for (const xcashuToken of tokens) {
148496
+ try {
148497
+ if (!this.balanceManager) {
148498
+ throw new Error("BalanceManager not available for xcashu refund");
148499
+ }
148500
+ const fetchResult = await this.balanceManager.fetchRefundToken(baseUrl, xcashuToken.token, true);
148501
+ if (!fetchResult.success && fetchResult.status === 425) {
148502
+ results.push({
148503
+ baseUrl,
148504
+ token: xcashuToken.token,
148505
+ success: false,
148506
+ error: fetchResult.error
148507
+ });
148508
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: 425 "Refund is pending" for ${baseUrl}; leaving token in store, tryCount untouched`);
148509
+ continue;
148510
+ }
148511
+ if (!fetchResult.success && fetchResult.parsedError?.type === CoreErrorType2.TOKEN_ALREADY_SPENT) {
148512
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
148513
+ results.push({
148514
+ baseUrl,
148515
+ token: xcashuToken.token,
148516
+ success: false,
148517
+ error: fetchResult.error
148518
+ });
148519
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: token_already_spent for ${baseUrl}; removing permanently-spent xcashu token from store`);
148520
+ continue;
148521
+ }
148522
+ if (!fetchResult.success && fetchResult.parsedError && isHandledRedemptionError2(fetchResult.parsedError)) {
148523
+ const directReceive = await this.receiveToken(xcashuToken.token);
148524
+ if (directReceive.success) {
148525
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
148526
+ results.push({
148527
+ baseUrl,
148528
+ token: xcashuToken.token,
148529
+ success: true
148530
+ });
148531
+ this._log("DEBUG", `[CashuSpender] refundXcashuTokens: provider returned ${fetchResult.parsedError.type}/${fetchResult.parsedError.code}; recovered original token directly, amount=${directReceive.amount}`);
148532
+ continue;
148533
+ }
148534
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: provider returned ${fetchResult.parsedError.type}/${fetchResult.parsedError.code}; direct original-token recovery also failed: ${directReceive.message ?? "unknown error"}`);
148535
+ }
148536
+ if (!fetchResult.success && fetchResult.status === 404 && (fetchResult.error || "").includes("Refund not found")) {
148537
+ const currentTryCount = xcashuToken.tryCount ?? 0;
148538
+ const newTryCount = currentTryCount + 1;
148539
+ if (newTryCount >= _CashuSpender2.MAX_REFUND_RETRIES) {
148540
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
148541
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: 404 "Refund not found" for ${baseUrl} after ${newTryCount} retries; removing unrefundable xcashu token from store`);
148542
+ } else {
148543
+ this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
148544
+ this._log("WARN", `[CashuSpender] refundXcashuTokens: 404 "Refund not found" for ${baseUrl}; tryCount=${newTryCount}/${_CashuSpender2.MAX_REFUND_RETRIES}, will retry in ${_CashuSpender2.REFUND_RETRY_INTERVAL_MS / 1000}s`);
148545
+ this._startRefundRetryInterval(mintUrl);
148546
+ }
148547
+ results.push({
148548
+ baseUrl,
148549
+ token: xcashuToken.token,
148550
+ success: false,
148551
+ error: fetchResult.error
148552
+ });
148553
+ continue;
148554
+ }
148555
+ if (!fetchResult.success || !fetchResult.token) {
148556
+ throw new Error(fetchResult.error || "Failed to fetch refund token from provider");
148557
+ }
148558
+ const receiveResult = await this.receiveToken(fetchResult.token);
148559
+ if (receiveResult.success) {
148560
+ this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
148561
+ results.push({
148562
+ baseUrl,
148563
+ token: xcashuToken.token,
148564
+ success: true
148565
+ });
148566
+ this._log("DEBUG", `[CashuSpender] refundXcashuTokens: Successfully refunded xcashu token for ${baseUrl}, amount=${receiveResult.amount}`);
148567
+ } else {
148568
+ const currentTryCount = xcashuToken.tryCount ?? 0;
148569
+ const newTryCount = currentTryCount + 1;
148570
+ this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
148571
+ results.push({
148572
+ baseUrl,
148573
+ token: xcashuToken.token,
148574
+ success: false,
148575
+ error: receiveResult.message ?? "Refund failed"
148576
+ });
148577
+ this._log("DEBUG", `[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}: ${receiveResult.message}`);
148578
+ }
148579
+ } catch (error53) {
148580
+ const currentTryCount = xcashuToken.tryCount ?? 0;
148581
+ const newTryCount = currentTryCount + 1;
148582
+ this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
148583
+ const errorMessage = error53 instanceof Error ? error53.message : String(error53);
148584
+ results.push({
148585
+ baseUrl,
148586
+ token: xcashuToken.token,
148587
+ success: false,
148588
+ error: errorMessage
148589
+ });
148590
+ this._log("ERROR", `[CashuSpender] refundXcashuTokens: Exception during refund for ${baseUrl}: ${errorMessage}, incremented tryCount to ${newTryCount}`);
148591
+ }
148592
+ }
148593
+ }
148594
+ return results;
148595
+ }
148596
+ _startRefundRetryInterval(mintUrl) {
148597
+ if (this._refundRetryInterval)
148598
+ return;
148599
+ this._log("DEBUG", `[CashuSpender] Starting refund retry interval (every ${_CashuSpender2.REFUND_RETRY_INTERVAL_MS / 1000}s)`);
148600
+ this._refundRetryInterval = setInterval(async () => {
148601
+ const remainingTokens = this.storageAdapter.getXcashuTokens();
148602
+ const hasTokens = Object.values(remainingTokens).some((tokens) => tokens.length > 0);
148603
+ if (!hasTokens) {
148604
+ this._stopRefundRetryInterval();
148605
+ return;
148606
+ }
148607
+ try {
148608
+ await this.refundXcashuTokens(mintUrl);
148609
+ } catch (error53) {
148610
+ this._log("ERROR", `[CashuSpender] Refund retry interval error: ${error53 instanceof Error ? error53.message : String(error53)}`);
148611
+ }
148612
+ }, _CashuSpender2.REFUND_RETRY_INTERVAL_MS);
148613
+ }
148614
+ _stopRefundRetryInterval() {
148615
+ if (this._refundRetryInterval) {
148616
+ clearInterval(this._refundRetryInterval);
148617
+ this._refundRetryInterval = null;
148618
+ this._log("DEBUG", `[CashuSpender] Stopped refund retry interval`);
148619
+ }
148620
+ }
148621
+ async refundProviders(mintUrl, forceRefund) {
148622
+ const results = [];
148623
+ const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
148624
+ for (const apiKeyEntry of apiKeyDistribution) {
148625
+ const apiKeyEntryFull = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
148626
+ if (apiKeyEntryFull && this.balanceManager) {
148627
+ try {
148628
+ const balanceResult = await this.balanceManager.getTokenBalance(apiKeyEntryFull.key, apiKeyEntry.baseUrl);
148629
+ if (balanceResult.isInvalidApiKey) {
148630
+ this.logger.warn(`refundProviders: ${apiKeyEntry.baseUrl} returned invalid API key; removing local key and treating as success`);
148631
+ this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
148632
+ results.push({
148633
+ baseUrl: apiKeyEntry.baseUrl,
148634
+ success: true
148635
+ });
148636
+ continue;
148637
+ }
148638
+ if (balanceResult.amount >= 0 && !balanceResult.balanceUnknown) {
148639
+ const balanceSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.amount / 1000) : balanceResult.amount;
148640
+ const reservedSat = balanceResult.unit === "msat" ? Math.floor(balanceResult.reserved / 1000) : balanceResult.reserved;
148641
+ this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, balanceSat, reservedSat);
148642
+ } else {
148643
+ this.logger.warn(`refundProviders: balance refresh for ${apiKeyEntry.baseUrl} returned negative amount; keeping stale local balance=${apiKeyEntryFull.balance}`);
148644
+ }
148645
+ } catch (error53) {
148646
+ this.logger.warn(`refundProviders: balance refresh threw for ${apiKeyEntry.baseUrl}; proceeding with stale local balance`, error53);
148647
+ }
148648
+ const refreshedEntry = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
148649
+ if (!refreshedEntry) {
148650
+ continue;
148651
+ }
148652
+ const refundResult = await this.balanceManager.refundApiKey({
148653
+ mintUrl,
148654
+ baseUrl: apiKeyEntry.baseUrl,
148655
+ apiKey: refreshedEntry.key,
148656
+ forceRefund
148657
+ });
148658
+ if (refundResult.success) {
148659
+ this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
148660
+ } else {
148661
+ const currentEntry = this.storageAdapter.getApiKey(apiKeyEntry.baseUrl);
148662
+ this.logger.warn(`refundProviders: refund failed for ${apiKeyEntry.baseUrl}; currentEntry=${Boolean(currentEntry)} balance=${currentEntry?.balance ?? "none"}. Touching lastUsed to rate-limit retries.`);
148663
+ if (currentEntry) {
148664
+ this.storageAdapter.touchApiKeyLastUsed(apiKeyEntry.baseUrl);
148665
+ }
148666
+ }
148667
+ results.push({
148668
+ baseUrl: apiKeyEntry.baseUrl,
148669
+ success: refundResult.success
148670
+ });
148671
+ } else {
148672
+ this.logger.warn(`refundProviders: cannot refund ${apiKeyEntry.baseUrl}; apiKeyEntryFull=${Boolean(apiKeyEntryFull)} balanceManager=${Boolean(this.balanceManager)}`);
148673
+ results.push({
148674
+ baseUrl: apiKeyEntry.baseUrl,
148675
+ success: false
148676
+ });
148677
+ }
148678
+ }
148679
+ return results;
148680
+ }
148681
+ _createInsufficientBalanceError(required2, normalizedBalances, availableBalance) {
148682
+ let maxBalance = 0;
148683
+ let maxMintUrl = "";
148684
+ for (const mintUrl in normalizedBalances) {
148685
+ const balanceInSats = normalizedBalances[mintUrl];
148686
+ if (balanceInSats > maxBalance) {
148687
+ maxBalance = balanceInSats;
148688
+ maxMintUrl = mintUrl;
148689
+ }
148690
+ }
148691
+ const error53 = new InsufficientBalanceError2(required2, availableBalance ?? maxBalance, maxBalance, maxMintUrl);
148692
+ return {
148693
+ token: null,
148694
+ status: "failed",
148695
+ balance: 0,
148696
+ error: error53.message,
148697
+ errorDetails: {
148698
+ required: required2,
148699
+ available: availableBalance ?? maxBalance,
148700
+ maxMintBalance: maxBalance,
148701
+ maxMintUrl
148702
+ }
148703
+ };
148704
+ }
148705
+ async _getProviderTokenBalance(baseUrl, token) {
148706
+ try {
148707
+ const response = await fetch(`${baseUrl}v1/wallet/info`, {
148708
+ headers: {
148709
+ Authorization: `Bearer ${token}`
148710
+ }
148711
+ });
148712
+ if (response.ok) {
148713
+ const data = await response.json();
148714
+ return data.balance / 1000;
148715
+ }
148716
+ } catch {
148717
+ return 0;
148718
+ }
148719
+ return 0;
148720
+ }
148721
+ };
148722
+ MAX_ORIGINAL_IMAGE_TOKENS2 = Math.ceil(MAX_IMAGE_PATCHES2 * 1.2);
147280
148723
  BunModelManager = class extends ModelManager2 {
147281
148724
  constructor(adapter, config2 = {}) {
147282
148725
  super(adapter, {
@@ -151495,20 +152938,20 @@ __export(exports_utils4, {
151495
152938
  numberToBytesLE: () => numberToBytesLE7,
151496
152939
  numberToBytesBE: () => numberToBytesBE8,
151497
152940
  hexToNumber: () => hexToNumber8,
151498
- hexToBytes: () => hexToBytes16,
152941
+ hexToBytes: () => hexToBytes17,
151499
152942
  equalBytes: () => equalBytes10,
151500
152943
  ensureBytes: () => ensureBytes5,
151501
152944
  createHmacDrbg: () => createHmacDrbg7,
151502
152945
  concatBytes: () => concatBytes17,
151503
152946
  bytesToNumberLE: () => bytesToNumberLE7,
151504
152947
  bytesToNumberBE: () => bytesToNumberBE7,
151505
- bytesToHex: () => bytesToHex18,
152948
+ bytesToHex: () => bytesToHex19,
151506
152949
  bitSet: () => bitSet4,
151507
152950
  bitMask: () => bitMask7,
151508
152951
  bitLen: () => bitLen7,
151509
152952
  bitGet: () => bitGet4
151510
152953
  });
151511
- function bytesToHex18(bytes10) {
152954
+ function bytesToHex19(bytes10) {
151512
152955
  if (!u8a11(bytes10))
151513
152956
  throw new Error("Uint8Array expected");
151514
152957
  let hex8 = "";
@@ -151526,7 +152969,7 @@ function hexToNumber8(hex8) {
151526
152969
  throw new Error("hex string expected, got " + typeof hex8);
151527
152970
  return BigInt(hex8 === "" ? "0" : `0x${hex8}`);
151528
152971
  }
151529
- function hexToBytes16(hex8) {
152972
+ function hexToBytes17(hex8) {
151530
152973
  if (typeof hex8 !== "string")
151531
152974
  throw new Error("hex string expected, got " + typeof hex8);
151532
152975
  const len = hex8.length;
@@ -151544,27 +152987,27 @@ function hexToBytes16(hex8) {
151544
152987
  return array2;
151545
152988
  }
151546
152989
  function bytesToNumberBE7(bytes10) {
151547
- return hexToNumber8(bytesToHex18(bytes10));
152990
+ return hexToNumber8(bytesToHex19(bytes10));
151548
152991
  }
151549
152992
  function bytesToNumberLE7(bytes10) {
151550
152993
  if (!u8a11(bytes10))
151551
152994
  throw new Error("Uint8Array expected");
151552
- return hexToNumber8(bytesToHex18(Uint8Array.from(bytes10).reverse()));
152995
+ return hexToNumber8(bytesToHex19(Uint8Array.from(bytes10).reverse()));
151553
152996
  }
151554
152997
  function numberToBytesBE8(n, len) {
151555
- return hexToBytes16(n.toString(16).padStart(len * 2, "0"));
152998
+ return hexToBytes17(n.toString(16).padStart(len * 2, "0"));
151556
152999
  }
151557
153000
  function numberToBytesLE7(n, len) {
151558
153001
  return numberToBytesBE8(n, len).reverse();
151559
153002
  }
151560
153003
  function numberToVarBytesBE4(n) {
151561
- return hexToBytes16(numberToHexUnpadded7(n));
153004
+ return hexToBytes17(numberToHexUnpadded7(n));
151562
153005
  }
151563
153006
  function ensureBytes5(title, hex8, expectedLength) {
151564
153007
  let res;
151565
153008
  if (typeof hex8 === "string") {
151566
153009
  try {
151567
- res = hexToBytes16(hex8);
153010
+ res = hexToBytes17(hex8);
151568
153011
  } catch (e57) {
151569
153012
  throw new Error(`${title} must be valid hex string, got "${hex8}". Cause: ${e57}`);
151570
153013
  }
@@ -152130,7 +153573,7 @@ function weierstrassPoints4(opts) {
152130
153573
  const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
152131
153574
  if (lengths && typeof key !== "bigint") {
152132
153575
  if (key instanceof Uint8Array)
152133
- key = bytesToHex18(key);
153576
+ key = bytesToHex19(key);
152134
153577
  if (typeof key !== "string" || !lengths.includes(key.length))
152135
153578
  throw new Error("Invalid key");
152136
153579
  key = key.padStart(nByteLength * 2, "0");
@@ -152421,7 +153864,7 @@ function weierstrassPoints4(opts) {
152421
153864
  return toBytes12(Point6, this, isCompressed);
152422
153865
  }
152423
153866
  toHex(isCompressed = true) {
152424
- return bytesToHex18(this.toRawBytes(isCompressed));
153867
+ return bytesToHex19(this.toRawBytes(isCompressed));
152425
153868
  }
152426
153869
  }
152427
153870
  Point6.BASE = new Point6(CURVE.Gx, CURVE.Gy, Fp6.ONE);
@@ -152499,7 +153942,7 @@ function weierstrass7(curveDef) {
152499
153942
  }
152500
153943
  }
152501
153944
  });
152502
- const numToNByteStr = (num3) => bytesToHex18(numberToBytesBE8(num3, CURVE.nByteLength));
153945
+ const numToNByteStr = (num3) => bytesToHex19(numberToBytesBE8(num3, CURVE.nByteLength));
152503
153946
  function isBiggerThanHalfOrder(number13) {
152504
153947
  const HALF = CURVE_ORDER >> _1n36;
152505
153948
  return number13 > HALF;
@@ -152560,13 +154003,13 @@ function weierstrass7(curveDef) {
152560
154003
  return this.hasHighS() ? new Signature(this.r, modN4(-this.s), this.recovery) : this;
152561
154004
  }
152562
154005
  toDERRawBytes() {
152563
- return hexToBytes16(this.toDERHex());
154006
+ return hexToBytes17(this.toDERHex());
152564
154007
  }
152565
154008
  toDERHex() {
152566
154009
  return DER7.hexFromSig({ r: this.r, s: this.s });
152567
154010
  }
152568
154011
  toCompactRawBytes() {
152569
- return hexToBytes16(this.toCompactHex());
154012
+ return hexToBytes17(this.toCompactHex());
152570
154013
  }
152571
154014
  toCompactHex() {
152572
154015
  return numToNByteStr(this.r) + numToNByteStr(this.s);
@@ -153061,7 +154504,7 @@ var init_cryptoNode9 = __esm(() => {
153061
154504
  });
153062
154505
 
153063
154506
  // node_modules/applesauce-wallet-connect/node_modules/applesauce-core/node_modules/nostr-tools/node_modules/@noble/hashes/esm/utils.js
153064
- function bytesToHex19(bytes10) {
154507
+ function bytesToHex20(bytes10) {
153065
154508
  if (!u8a12(bytes10))
153066
154509
  throw new Error("Uint8Array expected");
153067
154510
  let hex8 = "";
@@ -153070,7 +154513,7 @@ function bytesToHex19(bytes10) {
153070
154513
  }
153071
154514
  return hex8;
153072
154515
  }
153073
- function hexToBytes17(hex8) {
154516
+ function hexToBytes18(hex8) {
153074
154517
  if (typeof hex8 !== "string")
153075
154518
  throw new Error("hex string expected, got " + typeof hex8);
153076
154519
  const len = hex8.length;
@@ -153752,7 +155195,7 @@ function genBech326(encoding2) {
153752
155195
  return { encode: encode5, decode: decode7, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords };
153753
155196
  }
153754
155197
  var gcd6 = (a, b3) => !b3 ? a : gcd6(b3, a % b3), radix2carry6 = (from8, to) => from8 + (to - gcd6(from8, to)), base166, base326, base32hex6, base32crockford6, base649, base64url8, genBase586 = (abc) => chain6(radix8(58), alphabet6(abc), join15("")), base586, base58flickr6, base58xrp6, XMR_BLOCK_LEN4, base58xmr4, BECH_ALPHABET6, POLYMOD_GENERATORS6, bech326, bech32m6, utf84, hex8, CODERS4, coderTypeError4;
153755
- var init_esm8 = __esm(() => {
155198
+ var init_esm9 = __esm(() => {
153756
155199
  /*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
153757
155200
  base166 = chain6(radix26(4), alphabet6("0123456789ABCDEF"), join15(""));
153758
155201
  base326 = chain6(radix26(5), alphabet6("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding6(5), join15(""));
@@ -153848,7 +155291,7 @@ function output11(out, instance) {
153848
155291
  var init__assert11 = () => {};
153849
155292
 
153850
155293
  // node_modules/applesauce-wallet-connect/node_modules/applesauce-core/node_modules/nostr-tools/node_modules/@noble/ciphers/esm/utils.js
153851
- function bytesToHex20(bytes12) {
155294
+ function bytesToHex21(bytes12) {
153852
155295
  bytes11(bytes12);
153853
155296
  let hex9 = "";
153854
155297
  for (let i8 = 0;i8 < bytes12.length; i8++) {
@@ -153865,7 +155308,7 @@ function asciiToBase166(char) {
153865
155308
  return char - (asciis6._a - 10);
153866
155309
  return;
153867
155310
  }
153868
- function hexToBytes18(hex9) {
155311
+ function hexToBytes19(hex9) {
153869
155312
  if (typeof hex9 !== "string")
153870
155313
  throw new Error("hex string expected, got " + typeof hex9);
153871
155314
  const hl = hex9.length;
@@ -155495,7 +156938,7 @@ function serializeEvent8(evt) {
155495
156938
  }
155496
156939
  function getEventHash8(event7) {
155497
156940
  let eventHash = sha25613(utf8Encoder13.encode(serializeEvent8(event7)));
155498
- return bytesToHex19(eventHash);
156941
+ return bytesToHex20(eventHash);
155499
156942
  }
155500
156943
  function isRegularKind7(kind) {
155501
156944
  return kind < 1e4 && kind !== 0 && kind !== 3;
@@ -155660,7 +157103,7 @@ function decode7(code) {
155660
157103
  return {
155661
157104
  type: "nprofile",
155662
157105
  data: {
155663
- pubkey: bytesToHex19(tlv[0][0]),
157106
+ pubkey: bytesToHex20(tlv[0][0]),
155664
157107
  relays: tlv[1] ? tlv[1].map((d2) => utf8Decoder11.decode(d2)) : []
155665
157108
  }
155666
157109
  };
@@ -155678,10 +157121,10 @@ function decode7(code) {
155678
157121
  return {
155679
157122
  type: "nevent",
155680
157123
  data: {
155681
- id: bytesToHex19(tlv[0][0]),
157124
+ id: bytesToHex20(tlv[0][0]),
155682
157125
  relays: tlv[1] ? tlv[1].map((d2) => utf8Decoder11.decode(d2)) : [],
155683
- author: tlv[2]?.[0] ? bytesToHex19(tlv[2][0]) : undefined,
155684
- kind: tlv[3]?.[0] ? parseInt(bytesToHex19(tlv[3][0]), 16) : undefined
157126
+ author: tlv[2]?.[0] ? bytesToHex20(tlv[2][0]) : undefined,
157127
+ kind: tlv[3]?.[0] ? parseInt(bytesToHex20(tlv[3][0]), 16) : undefined
155685
157128
  }
155686
157129
  };
155687
157130
  }
@@ -155701,8 +157144,8 @@ function decode7(code) {
155701
157144
  type: "naddr",
155702
157145
  data: {
155703
157146
  identifier: utf8Decoder11.decode(tlv[0][0]),
155704
- pubkey: bytesToHex19(tlv[2][0]),
155705
- kind: parseInt(bytesToHex19(tlv[3][0]), 16),
157147
+ pubkey: bytesToHex20(tlv[2][0]),
157148
+ kind: parseInt(bytesToHex20(tlv[3][0]), 16),
155706
157149
  relays: tlv[1] ? tlv[1].map((d2) => utf8Decoder11.decode(d2)) : []
155707
157150
  }
155708
157151
  };
@@ -155711,7 +157154,7 @@ function decode7(code) {
155711
157154
  return { type: prefix, data };
155712
157155
  case "npub":
155713
157156
  case "note":
155714
- return { type: prefix, data: bytesToHex19(data) };
157157
+ return { type: prefix, data: bytesToHex20(data) };
155715
157158
  default:
155716
157159
  throw new Error(`unknown prefix ${prefix}`);
155717
157160
  }
@@ -155735,10 +157178,10 @@ function nsecEncode5(key) {
155735
157178
  return encodeBytes5("nsec", key);
155736
157179
  }
155737
157180
  function npubEncode6(hex9) {
155738
- return encodeBytes5("npub", hexToBytes17(hex9));
157181
+ return encodeBytes5("npub", hexToBytes18(hex9));
155739
157182
  }
155740
157183
  function noteEncode5(hex9) {
155741
- return encodeBytes5("note", hexToBytes17(hex9));
157184
+ return encodeBytes5("note", hexToBytes18(hex9));
155742
157185
  }
155743
157186
  function encodeBech325(prefix, data) {
155744
157187
  let words = bech326.toWords(data);
@@ -155749,7 +157192,7 @@ function encodeBytes5(prefix, bytes12) {
155749
157192
  }
155750
157193
  function nprofileEncode5(profile6) {
155751
157194
  let data = encodeTLV5({
155752
- 0: [hexToBytes17(profile6.pubkey)],
157195
+ 0: [hexToBytes18(profile6.pubkey)],
155753
157196
  1: (profile6.relays || []).map((url4) => utf8Encoder13.encode(url4))
155754
157197
  });
155755
157198
  return encodeBech325("nprofile", data);
@@ -155760,9 +157203,9 @@ function neventEncode5(event7) {
155760
157203
  kindArray = integerToUint8Array4(event7.kind);
155761
157204
  }
155762
157205
  let data = encodeTLV5({
155763
- 0: [hexToBytes17(event7.id)],
157206
+ 0: [hexToBytes18(event7.id)],
155764
157207
  1: (event7.relays || []).map((url4) => utf8Encoder13.encode(url4)),
155765
- 2: event7.author ? [hexToBytes17(event7.author)] : [],
157208
+ 2: event7.author ? [hexToBytes18(event7.author)] : [],
155766
157209
  3: kindArray ? [new Uint8Array(kindArray)] : []
155767
157210
  });
155768
157211
  return encodeBech325("nevent", data);
@@ -155773,7 +157216,7 @@ function naddrEncode5(addr) {
155773
157216
  let data = encodeTLV5({
155774
157217
  0: [utf8Encoder13.encode(addr.identifier)],
155775
157218
  1: (addr.relays || []).map((url4) => utf8Encoder13.encode(url4)),
155776
- 2: [hexToBytes17(addr.pubkey)],
157219
+ 2: [hexToBytes18(addr.pubkey)],
155777
157220
  3: [new Uint8Array(kind)]
155778
157221
  });
155779
157222
  return encodeBech325("naddr", data);
@@ -155792,7 +157235,7 @@ function encodeTLV5(tlv) {
155792
157235
  return concatBytes18(...entries);
155793
157236
  }
155794
157237
  function encrypt9(secretKey, pubkey2, text) {
155795
- const privkey = secretKey instanceof Uint8Array ? bytesToHex19(secretKey) : secretKey;
157238
+ const privkey = secretKey instanceof Uint8Array ? bytesToHex20(secretKey) : secretKey;
155796
157239
  const key = secp256k16.getSharedSecret(privkey, "02" + pubkey2);
155797
157240
  const normalizedKey = getNormalizedX4(key);
155798
157241
  let iv = Uint8Array.from(randomBytes13(16));
@@ -155803,7 +157246,7 @@ function encrypt9(secretKey, pubkey2, text) {
155803
157246
  return `${ctb64}?iv=${ivb64}`;
155804
157247
  }
155805
157248
  function decrypt8(secretKey, pubkey2, data) {
155806
- const privkey = secretKey instanceof Uint8Array ? bytesToHex19(secretKey) : secretKey;
157249
+ const privkey = secretKey instanceof Uint8Array ? bytesToHex20(secretKey) : secretKey;
155807
157250
  let [ctb64, ivb64] = data.split("?iv=");
155808
157251
  let key = secp256k16.getSharedSecret(privkey, "02" + pubkey2);
155809
157252
  let normalizedKey = getNormalizedX4(key);
@@ -155992,7 +157435,7 @@ function minePow4(unsigned, difficulty) {
155992
157435
  return event7;
155993
157436
  }
155994
157437
  function fastEventHash3(evt) {
155995
- return bytesToHex19(sha25613(utf8Encoder13.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))));
157438
+ return bytesToHex20(sha25613(utf8Encoder13.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))));
155996
157439
  }
155997
157440
  function getConversationKey4(privkeyA, pubkeyB) {
155998
157441
  const sharedX = secp256k16.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
@@ -156740,7 +158183,7 @@ function validateEventMethodTag4(event7, method) {
156740
158183
  }
156741
158184
  function hashPayload6(payload) {
156742
158185
  const hash10 = sha25613(utf8Encoder13.encode(JSON.stringify(payload)));
156743
- return bytesToHex19(hash10);
158186
+ return bytesToHex20(hash10);
156744
158187
  }
156745
158188
  function validateEventPayloadTag4(event7, payload) {
156746
158189
  const payloadTag = event7.tags.find((t) => t[0] === "payload");
@@ -156827,13 +158270,13 @@ var __defProp5, __export5 = (target, all) => {
156827
158270
  return schnorr6.utils.randomPrivateKey();
156828
158271
  }
156829
158272
  getPublicKey(secretKey) {
156830
- return bytesToHex19(schnorr6.getPublicKey(secretKey));
158273
+ return bytesToHex20(schnorr6.getPublicKey(secretKey));
156831
158274
  }
156832
158275
  finalizeEvent(t, secretKey) {
156833
158276
  const event7 = t;
156834
- event7.pubkey = bytesToHex19(schnorr6.getPublicKey(secretKey));
158277
+ event7.pubkey = bytesToHex20(schnorr6.getPublicKey(secretKey));
156835
158278
  event7.id = getEventHash8(event7);
156836
- event7.sig = bytesToHex19(schnorr6.sign(getEventHash8(event7), secretKey));
158279
+ event7.sig = bytesToHex20(schnorr6.sign(getEventHash8(event7), secretKey));
156837
158280
  event7[verifiedSymbol11] = true;
156838
158281
  return event7;
156839
158282
  }
@@ -157456,7 +158899,7 @@ var __defProp5, __export5 = (target, all) => {
157456
158899
  insert(timestamp, id) {
157457
158900
  if (this.sealed)
157458
158901
  throw Error("already sealed");
157459
- const idb = hexToBytes18(id);
158902
+ const idb = hexToBytes19(id);
157460
158903
  if (idb.byteLength !== ID_SIZE5)
157461
158904
  throw Error("bad id size for added item");
157462
158905
  this.items.push({ timestamp, id: idb });
@@ -157549,10 +158992,10 @@ var __defProp5, __export5 = (target, all) => {
157549
158992
  let output12 = new WrappedBuffer5;
157550
158993
  output12.extend(new Uint8Array([PROTOCOL_VERSION5]));
157551
158994
  this.splitRange(0, this.storage.size(), this._bound(Number.MAX_VALUE), output12);
157552
- return bytesToHex20(output12.unwrap());
158995
+ return bytesToHex21(output12.unwrap());
157553
158996
  }
157554
158997
  reconcile(queryMsg, onhave, onneed) {
157555
- const query2 = new WrappedBuffer5(hexToBytes18(queryMsg));
158998
+ const query2 = new WrappedBuffer5(hexToBytes19(queryMsg));
157556
158999
  this.lastTimestampIn = this.lastTimestampOut = 0;
157557
159000
  let fullOutput = new WrappedBuffer5;
157558
159001
  fullOutput.extend(new Uint8Array([PROTOCOL_VERSION5]));
@@ -157595,22 +159038,22 @@ var __defProp5, __export5 = (target, all) => {
157595
159038
  let theirElems = {};
157596
159039
  for (let i22 = 0;i22 < numIds; i22++) {
157597
159040
  let e57 = getBytes6(query2, ID_SIZE5);
157598
- theirElems[bytesToHex20(e57)] = e57;
159041
+ theirElems[bytesToHex21(e57)] = e57;
157599
159042
  }
157600
159043
  skip3 = true;
157601
159044
  this.storage.iterate(lower, upper, (item) => {
157602
159045
  let k6 = item.id;
157603
- const id = bytesToHex20(k6);
159046
+ const id = bytesToHex21(k6);
157604
159047
  if (!theirElems[id]) {
157605
159048
  onhave?.(id);
157606
159049
  } else {
157607
- delete theirElems[bytesToHex20(k6)];
159050
+ delete theirElems[bytesToHex21(k6)];
157608
159051
  }
157609
159052
  return true;
157610
159053
  });
157611
159054
  if (onneed) {
157612
159055
  for (let v4 of Object.values(theirElems)) {
157613
- onneed(bytesToHex20(v4));
159056
+ onneed(bytesToHex21(v4));
157614
159057
  }
157615
159058
  }
157616
159059
  } else {
@@ -157628,7 +159071,7 @@ var __defProp5, __export5 = (target, all) => {
157628
159071
  prevIndex = upper;
157629
159072
  prevBound = currBound;
157630
159073
  }
157631
- return fullOutput.length === 1 ? null : bytesToHex20(fullOutput.unwrap());
159074
+ return fullOutput.length === 1 ? null : bytesToHex21(fullOutput.unwrap());
157632
159075
  }
157633
159076
  splitRange(lower, upper, upperBound, o) {
157634
159077
  let numElems = upper - lower;
@@ -157781,17 +159224,17 @@ var __defProp5, __export5 = (target, all) => {
157781
159224
  this.subscription.close();
157782
159225
  }
157783
159226
  }, nip98_exports4, _authorizationScheme6 = "Nostr ";
157784
- var init_esm9 = __esm(() => {
159227
+ var init_esm10 = __esm(() => {
157785
159228
  init_secp256k16();
157786
159229
  init_utils27();
157787
159230
  init_sha2568();
157788
159231
  init_utils27();
157789
159232
  init_utils27();
157790
- init_esm8();
159233
+ init_esm9();
157791
159234
  init_utils27();
157792
159235
  init_secp256k16();
157793
159236
  init_aes5();
157794
- init_esm8();
159237
+ init_esm9();
157795
159238
  init_utils27();
157796
159239
  init_sha2568();
157797
159240
  init_chacha4();
@@ -157801,13 +159244,13 @@ var init_esm9 = __esm(() => {
157801
159244
  init_hmac10();
157802
159245
  init_sha2568();
157803
159246
  init_utils27();
157804
- init_esm8();
157805
- init_esm8();
159247
+ init_esm9();
159248
+ init_esm9();
157806
159249
  init_utils28();
157807
159250
  init_sha2568();
157808
159251
  init_sha2568();
157809
159252
  init_utils27();
157810
- init_esm8();
159253
+ init_esm9();
157811
159254
  __defProp5 = Object.defineProperty;
157812
159255
  verifiedSymbol11 = Symbol("verified");
157813
159256
  utils_exports4 = {};
@@ -157815,8 +159258,8 @@ var init_esm9 = __esm(() => {
157815
159258
  Queue: () => Queue3,
157816
159259
  QueueNode: () => QueueNode3,
157817
159260
  binarySearch: () => binarySearch6,
157818
- bytesToHex: () => bytesToHex19,
157819
- hexToBytes: () => hexToBytes17,
159261
+ bytesToHex: () => bytesToHex20,
159262
+ hexToBytes: () => hexToBytes18,
157820
159263
  insertEventIntoAscendingList: () => insertEventIntoAscendingList6,
157821
159264
  insertEventIntoDescendingList: () => insertEventIntoDescendingList6,
157822
159265
  normalizeURL: () => normalizeURL6,
@@ -158181,20 +159624,20 @@ function serializeEvent9(evt) {
158181
159624
  }
158182
159625
  function getEventHash9(event7) {
158183
159626
  let eventHash = sha25613(utf8Encoder14.encode(serializeEvent9(event7)));
158184
- return bytesToHex19(eventHash);
159627
+ return bytesToHex20(eventHash);
158185
159628
  }
158186
159629
  var verifiedSymbol12, isRecord10 = (obj2) => obj2 instanceof Object, utf8Decoder12, utf8Encoder14, JS9 = class {
158187
159630
  generateSecretKey() {
158188
159631
  return schnorr6.utils.randomPrivateKey();
158189
159632
  }
158190
159633
  getPublicKey(secretKey) {
158191
- return bytesToHex19(schnorr6.getPublicKey(secretKey));
159634
+ return bytesToHex20(schnorr6.getPublicKey(secretKey));
158192
159635
  }
158193
159636
  finalizeEvent(t, secretKey) {
158194
159637
  const event7 = t;
158195
- event7.pubkey = bytesToHex19(schnorr6.getPublicKey(secretKey));
159638
+ event7.pubkey = bytesToHex20(schnorr6.getPublicKey(secretKey));
158196
159639
  event7.id = getEventHash9(event7);
158197
- event7.sig = bytesToHex19(schnorr6.sign(getEventHash9(event7), secretKey));
159640
+ event7.sig = bytesToHex20(schnorr6.sign(getEventHash9(event7), secretKey));
158198
159641
  event7[verifiedSymbol12] = true;
158199
159642
  return event7;
158200
159643
  }
@@ -158330,7 +159773,7 @@ function getTagValue3(event7, name16) {
158330
159773
  }
158331
159774
  var EventStoreSymbol3, EventUIDSymbol3, ReplaceableAddressSymbol3, FromCacheSymbol3, ReplaceableIdentifierSymbol3;
158332
159775
  var init_event7 = __esm(() => {
158333
- init_esm9();
159776
+ init_esm10();
158334
159777
  init_kinds3();
158335
159778
  init_pure3();
158336
159779
  init_utils29();
@@ -158481,7 +159924,7 @@ var processTags3 = (tags5, ...fns) => {
158481
159924
 
158482
159925
  // node_modules/applesauce-wallet-connect/node_modules/applesauce-core/node_modules/nostr-tools/lib/esm/nip19.js
158483
159926
  function npubEncode7(hex9) {
158484
- return encodeBytes6("npub", hexToBytes17(hex9));
159927
+ return encodeBytes6("npub", hexToBytes18(hex9));
158485
159928
  }
158486
159929
  function encodeBech326(prefix, data) {
158487
159930
  let words = bech326.toWords(data);
@@ -158492,7 +159935,7 @@ function encodeBytes6(prefix, bytes12) {
158492
159935
  }
158493
159936
  function nprofileEncode6(profile6) {
158494
159937
  let data = encodeTLV6({
158495
- 0: [hexToBytes17(profile6.pubkey)],
159938
+ 0: [hexToBytes18(profile6.pubkey)],
158496
159939
  1: (profile6.relays || []).map((url4) => utf8Encoder16.encode(url4))
158497
159940
  });
158498
159941
  return encodeBech326("nprofile", data);
@@ -158513,7 +159956,7 @@ function encodeTLV6(tlv) {
158513
159956
  var utf8Decoder14, utf8Encoder16, Bech32MaxSize6 = 5000;
158514
159957
  var init_nip192 = __esm(() => {
158515
159958
  init_utils27();
158516
- init_esm8();
159959
+ init_esm9();
158517
159960
  init_utils27();
158518
159961
  utf8Decoder14 = new TextDecoder("utf-8");
158519
159962
  utf8Encoder16 = new TextEncoder;
@@ -159657,7 +161100,7 @@ function isNIP04Encrypted(ciphertext) {
159657
161100
  return ciphertext[l2 - 28] == "?" && ciphertext[l2 - 27] == "i" && ciphertext[l2 - 26] == "v" && ciphertext[l2 - 25] == "=";
159658
161101
  }
159659
161102
  var init_encryption = __esm(() => {
159660
- init_esm9();
161103
+ init_esm10();
159661
161104
  });
159662
161105
 
159663
161106
  // node_modules/applesauce-wallet-connect/dist/helpers/encryption.js
@@ -161972,7 +163415,7 @@ var init_models4 = __esm(() => {
161972
163415
 
161973
163416
  // node_modules/applesauce-wallet-connect/node_modules/applesauce-core/dist/operations/client.js
161974
163417
  var NEVER_ATTACH_CLIENT_TAG3;
161975
- var init_client6 = __esm(() => {
163418
+ var init_client7 = __esm(() => {
161976
163419
  init_event7();
161977
163420
  init_pointers3();
161978
163421
  init_tags3();
@@ -161986,7 +163429,7 @@ var init_hidden_content6 = __esm(() => {
161986
163429
 
161987
163430
  // node_modules/applesauce-wallet-connect/node_modules/applesauce-core/dist/operations/index.js
161988
163431
  var init_operations3 = __esm(() => {
161989
- init_client6();
163432
+ init_client7();
161990
163433
  init_content6();
161991
163434
  init_encrypted_content8();
161992
163435
  init_event8();
@@ -162016,7 +163459,7 @@ function createWalletError(code, message) {
162016
163459
  case "NOT_IMPLEMENTED":
162017
163460
  return new NotImplementedError(message);
162018
163461
  case "INSUFFICIENT_BALANCE":
162019
- return new InsufficientBalanceError2(message);
163462
+ return new InsufficientBalanceError3(message);
162020
163463
  case "QUOTA_EXCEEDED":
162021
163464
  return new QuotaExceededError(message);
162022
163465
  case "RESTRICTED":
@@ -162037,7 +163480,7 @@ function createWalletError(code, message) {
162037
163480
  return new OtherError(message);
162038
163481
  }
162039
163482
  }
162040
- var WalletBaseError, RateLimitedError, NotImplementedError, InsufficientBalanceError2, QuotaExceededError, RestrictedError, UnauthorizedError, InternalError, UnsupportedEncryptionError, PaymentFailedError, NotFoundError2, OtherError;
163483
+ var WalletBaseError, RateLimitedError, NotImplementedError, InsufficientBalanceError3, QuotaExceededError, RestrictedError, UnauthorizedError, InternalError, UnsupportedEncryptionError, PaymentFailedError, NotFoundError2, OtherError;
162041
163484
  var init_error5 = __esm(() => {
162042
163485
  WalletBaseError = class WalletBaseError extends Error {
162043
163486
  constructor(message) {
@@ -162051,7 +163494,7 @@ var init_error5 = __esm(() => {
162051
163494
  NotImplementedError = class NotImplementedError extends WalletBaseError {
162052
163495
  code = "NOT_IMPLEMENTED";
162053
163496
  };
162054
- InsufficientBalanceError2 = class InsufficientBalanceError2 extends WalletBaseError {
163497
+ InsufficientBalanceError3 = class InsufficientBalanceError3 extends WalletBaseError {
162055
163498
  code = "INSUFFICIENT_BALANCE";
162056
163499
  };
162057
163500
  QuotaExceededError = class QuotaExceededError extends WalletBaseError {
@@ -163523,6 +164966,35 @@ var init_usage_summary = __esm(() => {
163523
164966
  };
163524
164967
  });
163525
164968
 
164969
+ // src/daemon/http/request-body.ts
164970
+ function isOpenAiJsonBodyPath2(pathname) {
164971
+ const path2 = (pathname.split("?")[0] ?? "").replace(/\/+$/, "");
164972
+ return OPENAI_JSON_BODY_PATH_SUFFIXES.some((suffix) => path2.endsWith(suffix));
164973
+ }
164974
+ function applyDefaultOutputTokenLimit(pathname, body, maxTokens) {
164975
+ if (!(maxTokens > 0) || !isOpenAiJsonBodyPath2(pathname)) {
164976
+ return false;
164977
+ }
164978
+ if (pathname.split("?")[0].replace(/\/+$/, "").endsWith("/responses")) {
164979
+ if (typeof body.max_output_tokens === "number")
164980
+ return false;
164981
+ body.max_output_tokens = maxTokens;
164982
+ return true;
164983
+ }
164984
+ if (typeof body.max_tokens === "number")
164985
+ return false;
164986
+ body.max_tokens = maxTokens;
164987
+ return true;
164988
+ }
164989
+ var OPENAI_JSON_BODY_PATH_SUFFIXES;
164990
+ var init_request_body = __esm(() => {
164991
+ OPENAI_JSON_BODY_PATH_SUFFIXES = [
164992
+ "/chat/completions",
164993
+ "/completions",
164994
+ "/responses"
164995
+ ];
164996
+ });
164997
+
163526
164998
  // src/daemon/http/index.ts
163527
164999
  import { randomBytes as randomBytes14 } from "crypto";
163528
165000
  import { Readable } from "stream";
@@ -164752,16 +166224,7 @@ function createDaemonRequestHandler(deps) {
164752
166224
  sendJson(res, 400, { error: "Missing required 'model' field." });
164753
166225
  return;
164754
166226
  }
164755
- if (deps.maxTokens > 0) {
164756
- const isResponsesPath = url5.pathname.includes("/responses");
164757
- if (isResponsesPath) {
164758
- if (typeof bodyObj.max_output_tokens !== "number") {
164759
- bodyObj.max_output_tokens = deps.maxTokens;
164760
- }
164761
- } else if (typeof bodyObj.max_tokens !== "number") {
164762
- bodyObj.max_tokens = deps.maxTokens;
164763
- }
164764
- }
166227
+ applyDefaultOutputTokenLimit(url5.pathname, bodyObj, deps.maxTokens);
164765
166228
  const forcedProvider = url5.searchParams.get("provider") || req.headers["x-routstr-provider"] || deps.provider || undefined;
164766
166229
  const incomingHeaders = {};
164767
166230
  for (const [key, value] of Object.entries(req.headers)) {
@@ -164874,6 +166337,7 @@ var init_http = __esm(() => {
164874
166337
  init_wallet();
164875
166338
  init_clients();
164876
166339
  init_usage_summary();
166340
+ init_request_body();
164877
166341
  HOP_BY_HOP_HEADERS = new Set([
164878
166342
  "connection",
164879
166343
  "keep-alive",
@@ -166193,46 +167657,111 @@ function truncateNpub(npub) {
166193
167657
  return npub;
166194
167658
  return npub.slice(0, 10) + "\u2026" + npub.slice(-6);
166195
167659
  }
167660
+ function tokenSegments(entry) {
167661
+ const cacheRead = Math.max(0, entry.cacheReadInputTokens || 0);
167662
+ const cacheWrite = Math.max(0, entry.cacheCreationInputTokens || 0);
167663
+ const prompt = Math.max(0, entry.promptTokens || 0);
167664
+ const output12 = Math.max(0, entry.completionTokens || 0);
167665
+ const cached2 = cacheRead + cacheWrite;
167666
+ const uncachedInput = prompt >= cached2 ? prompt - cached2 : prompt;
167667
+ const notCached = cacheWrite + uncachedInput;
167668
+ const input = cacheRead + notCached;
167669
+ return { cacheRead, notCached, input, output: output12, total: input + output12 };
167670
+ }
167671
+ function renderStackedBar(segments, trackWidth) {
167672
+ const track = Math.max(0, Math.floor(trackWidth));
167673
+ if (track === 0)
167674
+ return "";
167675
+ const values2 = segments.map((segment) => Math.max(0, segment.value));
167676
+ const total = values2.reduce((sum, value) => sum + value, 0);
167677
+ if (total <= 0)
167678
+ return " ".repeat(track);
167679
+ const exact = values2.map((value) => value / total * track);
167680
+ const cells = exact.map((value) => Math.floor(value));
167681
+ let remaining = track - cells.reduce((sum, value) => sum + value, 0);
167682
+ const remainders = exact.map((value, index) => ({ index, frac: value - Math.floor(value) })).sort((a, b3) => b3.frac - a.frac);
167683
+ for (let i10 = 0;i10 < remainders.length && remaining > 0; i10++, remaining--) {
167684
+ cells[remainders[i10].index] += 1;
167685
+ }
167686
+ const bar = segments.map((segment, i10) => cells[i10] > 0 ? `${segment.color}${"\u2588".repeat(cells[i10])}` : "").join("");
167687
+ return bar.length > 0 ? bar + COLORS.reset : " ".repeat(track);
167688
+ }
166196
167689
  function renderRecent(stats, width, naming) {
166197
167690
  const recentEntries = stats.entries.slice(0, 50);
166198
167691
  if (recentEntries.length === 0)
166199
167692
  return renderBox(["No recent entries"], width, "Recent Requests");
166200
- const timeCol = 10;
166201
- const modelCol = 18;
166202
- const tokensCol = 18;
166203
- const costCol = 18;
167693
+ const timeCol = 8;
167694
+ const costCol = 12;
167695
+ const minBarWidth = "CACHE HIT".length;
167696
+ const maxBarWidth = 20;
167697
+ const minProviderCol = 12;
167698
+ const minModelCol = 10;
167699
+ const minClientCol = 6;
166204
167700
  const hasOwnerInfo = naming.npubs.length > 0 || naming.ownersByClientId.size > 0;
166205
167701
  const clientLabels = recentEntries.map((entry) => hasOwnerInfo ? resolveClientLabel(entry.client, naming) : entry.client || "unknown");
166206
167702
  const maxLabelLen = clientLabels.reduce((max, label) => Math.max(max, label.length), 6);
166207
- const clientCol = hasOwnerInfo ? Math.min(32, Math.max(14, maxLabelLen)) : 14;
167703
+ const tokenRows = recentEntries.map((entry) => tokenSegments(entry));
167704
+ const inputTexts = tokenRows.map((row) => formatNumber2(row.input));
167705
+ const outputTexts = tokenRows.map((row) => formatNumber2(row.output));
167706
+ const inputCol = Math.max(...inputTexts.map((text) => text.length));
167707
+ const outputCol = Math.max(...outputTexts.map((text) => text.length));
167708
+ const inOutCol = inputCol + outputCol + " - ".length;
166208
167709
  const innerWidth = Math.max(0, width - 4);
166209
- const providerCol = Math.max(12, innerWidth - timeCol - modelCol - tokensCol - costCol - 5 - clientCol);
166210
- const msatsToSats = (msats) => typeof msats === "number" ? msats / 1000 : 0;
167710
+ let modelCol = 18;
167711
+ let clientCol = hasOwnerInfo ? Math.min(32, Math.max(14, maxLabelLen)) : 14;
167712
+ let barWidth = maxBarWidth;
167713
+ let showProvider = false;
167714
+ let providerCol = 0;
167715
+ const usedWidth = () => timeCol + modelCol + (barWidth + 1 + inOutCol) + costCol + clientCol + (showProvider ? providerCol + 1 : 0) + 4;
167716
+ if (innerWidth - usedWidth() > minProviderCol + 1) {
167717
+ showProvider = true;
167718
+ providerCol = innerWidth - usedWidth();
167719
+ }
167720
+ while (usedWidth() > innerWidth && barWidth > minBarWidth)
167721
+ barWidth -= 1;
167722
+ while (usedWidth() > innerWidth && providerCol > minProviderCol)
167723
+ providerCol -= 1;
167724
+ while (usedWidth() > innerWidth && modelCol > minModelCol)
167725
+ modelCol -= 1;
167726
+ while (usedWidth() > innerWidth && clientCol > minClientCol)
167727
+ clientCol -= 1;
167728
+ const tokensCol = barWidth + 1 + inOutCol;
166211
167729
  const lines = [];
166212
- lines.push(`${COLORS.bold}${["TIME".padEnd(timeCol), "MODEL".padEnd(modelCol), "I/CR/CW/O".padEnd(tokensCol), "I/O/T in sats".padEnd(costCol), "BASE:PROVIDER".padEnd(providerCol), "CLIENT".padEnd(clientCol)].join(" ")}${COLORS.reset}`);
166213
- lines.push(COLORS.dim + "\u2500".repeat(width - 4) + COLORS.reset);
167730
+ const header = [
167731
+ "TIME".padEnd(timeCol),
167732
+ "MODEL".padEnd(modelCol),
167733
+ "CACHE HIT".padEnd(tokensCol - inOutCol) + "IN".padEnd(inputCol) + " - " + "OUT".padStart(outputCol),
167734
+ "COST".padStart(costCol),
167735
+ ...showProvider ? ["BASE:PROVIDER".padEnd(providerCol)] : [],
167736
+ "CLIENT".padEnd(clientCol)
167737
+ ];
167738
+ lines.push(`${COLORS.bold}${header.join(" ")}${COLORS.reset}`);
167739
+ lines.push(COLORS.dim + "\u2500".repeat(innerWidth) + COLORS.reset);
166214
167740
  for (let i10 = 0;i10 < recentEntries.length; i10++) {
166215
167741
  const entry = recentEntries[i10];
166216
167742
  const time6 = formatTime(entry.timestamp).slice(0, 8);
166217
167743
  const model4 = entry.modelId.slice(0, modelCol).padEnd(modelCol);
166218
- const tokens = [
166219
- entry.promptTokens,
166220
- entry.cacheReadInputTokens || 0,
166221
- entry.cacheCreationInputTokens || 0,
166222
- entry.completionTokens
166223
- ].map(formatNumber2).join("/");
167744
+ const segments = tokenRows[i10];
167745
+ const bar = renderStackedBar([
167746
+ { value: segments.cacheRead, color: TOKEN_BAR_COLORS.cacheRead },
167747
+ { value: segments.notCached, color: TOKEN_BAR_COLORS.notCached }
167748
+ ], barWidth);
167749
+ const tokens = `${bar} ${inputTexts[i10].padEnd(inputCol)} - ${outputTexts[i10].padStart(outputCol)}`;
166224
167750
  const totalSats = typeof entry.totalMsats === "number" ? entry.totalMsats / 1000 : entry.satsCost;
166225
- const cost = [
166226
- formatCost(msatsToSats(entry.inputMsats)),
166227
- formatCost(msatsToSats(entry.outputMsats)),
166228
- formatCost(totalSats)
166229
- ].join("/");
167751
+ const cost = `${formatCost(totalSats)} sats`.padStart(costCol);
166230
167752
  const baseUrl = (entry.baseUrl || "unknown").replace("https://", "").replace("http://", "");
166231
167753
  const provider = `${baseUrl}:${entry.provider || "unknown"}`.slice(0, providerCol).padEnd(providerCol);
166232
167754
  const clientLabel = clientLabels[i10].slice(0, clientCol).padEnd(clientCol);
166233
167755
  const clientColor = CLIENT_COLORS[entry.client || "unknown"] || CLIENT_COLORS.default || COLORS.white;
166234
167756
  const modelColor = MODEL_COLORS[entry.modelId] || MODEL_COLORS.default;
166235
- lines.push(`${COLORS.dim}${time6}${COLORS.reset} ${modelColor}${model4}${COLORS.reset} ${tokens.padEnd(tokensCol)} ${COLORS.green}${cost.padEnd(costCol)}${COLORS.reset} ${COLORS.dim}${provider}${COLORS.reset} ${clientColor}${clientLabel}${COLORS.reset}`);
167757
+ lines.push([
167758
+ `${COLORS.dim}${time6}${COLORS.reset}`,
167759
+ `${modelColor}${model4}${COLORS.reset}`,
167760
+ tokens,
167761
+ `${COLORS.green}${cost.padEnd(costCol)}${COLORS.reset}`,
167762
+ ...showProvider ? [`${COLORS.dim}${provider}${COLORS.reset}`] : [],
167763
+ `${clientColor}${clientLabel}${COLORS.reset}`
167764
+ ].join(" "));
166236
167765
  }
166237
167766
  return renderBox(lines, width, `Recent Requests (${stats.entries.length} shown)`);
166238
167767
  }
@@ -166258,12 +167787,16 @@ function renderTabContent(activeTab, stats, balance, status, width, naming) {
166258
167787
  return "Unknown tab";
166259
167788
  }
166260
167789
  }
166261
- var _sectionMaxLabelLen;
167790
+ var _sectionMaxLabelLen, TOKEN_BAR_COLORS;
166262
167791
  var init_render = __esm(() => {
166263
167792
  init_constants2();
166264
167793
  init_data();
166265
167794
  init_state();
166266
167795
  _sectionMaxLabelLen = new Map;
167796
+ TOKEN_BAR_COLORS = {
167797
+ cacheRead: COLORS.green,
167798
+ notCached: COLORS.red
167799
+ };
166267
167800
  });
166268
167801
 
166269
167802
  // src/tui/usage/app.ts