@routstr/sdk 0.3.11 → 0.3.13
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/browser.d.mts +2 -1
- package/dist/browser.d.ts +2 -1
- package/dist/browser.js +360 -28
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +355 -29
- package/dist/browser.mjs.map +1 -1
- package/dist/bun.d.mts +2 -1
- package/dist/bun.d.ts +2 -1
- package/dist/bun.js +367 -31
- package/dist/bun.js.map +1 -1
- package/dist/bun.mjs +362 -32
- package/dist/bun.mjs.map +1 -1
- package/dist/client/index.d.mts +85 -1
- package/dist/client/index.d.ts +85 -1
- package/dist/client/index.js +358 -27
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +353 -28
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +360 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +355 -29
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +2 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.js +367 -31
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +362 -32
- package/dist/node.mjs.map +1 -1
- package/dist/storage/bun.js +6 -1
- package/dist/storage/bun.js.map +1 -1
- package/dist/storage/bun.mjs +6 -1
- package/dist/storage/bun.mjs.map +1 -1
- package/dist/storage/index.js +2 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +2 -1
- package/dist/storage/index.mjs.map +1 -1
- package/dist/storage/node.js +6 -1
- package/dist/storage/node.js.map +1 -1
- package/dist/storage/node.mjs +6 -1
- package/dist/storage/node.mjs.map +1 -1
- package/dist/wallet/index.js +36 -8
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +36 -8
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +9 -3
package/dist/index.mjs
CHANGED
|
@@ -1858,15 +1858,27 @@ var BalanceManager = class _BalanceManager {
|
|
|
1858
1858
|
clearTimeout(timeoutId);
|
|
1859
1859
|
const requestId = response.headers.get("x-routstr-request-id") || void 0;
|
|
1860
1860
|
if (!response.ok) {
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1861
|
+
const responseBody = await response.text().catch(() => void 0);
|
|
1862
|
+
let errorData = {};
|
|
1863
|
+
if (responseBody) {
|
|
1864
|
+
try {
|
|
1865
|
+
errorData = JSON.parse(responseBody);
|
|
1866
|
+
} catch {
|
|
1867
|
+
errorData = {};
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
this.logger.error("Upstream wallet refund error response", {
|
|
1871
|
+
baseUrl,
|
|
1872
|
+
url,
|
|
1873
|
+
status: response.status,
|
|
1874
|
+
statusText: response.statusText,
|
|
1875
|
+
requestId,
|
|
1876
|
+
body: responseBody ?? "<unable to read response body>"
|
|
1877
|
+
});
|
|
1866
1878
|
return {
|
|
1867
1879
|
success: false,
|
|
1868
1880
|
requestId,
|
|
1869
|
-
error: `API key refund failed: ${errorData?.detail || response.statusText}`
|
|
1881
|
+
error: `API key refund failed: ${errorData?.detail || responseBody || response.statusText}`
|
|
1870
1882
|
};
|
|
1871
1883
|
}
|
|
1872
1884
|
const data = await response.json();
|
|
@@ -2202,11 +2214,27 @@ var BalanceManager = class _BalanceManager {
|
|
|
2202
2214
|
clearTimeout(timeoutId);
|
|
2203
2215
|
const requestId = response.headers.get("x-routstr-request-id") || void 0;
|
|
2204
2216
|
if (!response.ok) {
|
|
2205
|
-
const
|
|
2217
|
+
const responseBody = await response.text().catch(() => void 0);
|
|
2218
|
+
let errorData = {};
|
|
2219
|
+
if (responseBody) {
|
|
2220
|
+
try {
|
|
2221
|
+
errorData = JSON.parse(responseBody);
|
|
2222
|
+
} catch {
|
|
2223
|
+
errorData = {};
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
this.logger.error("Upstream wallet topup error response", {
|
|
2227
|
+
baseUrl,
|
|
2228
|
+
url,
|
|
2229
|
+
status: response.status,
|
|
2230
|
+
statusText: response.statusText,
|
|
2231
|
+
requestId,
|
|
2232
|
+
body: responseBody ?? "<unable to read response body>"
|
|
2233
|
+
});
|
|
2206
2234
|
return {
|
|
2207
2235
|
success: false,
|
|
2208
2236
|
requestId,
|
|
2209
|
-
error: errorData?.detail || `Top up failed with status ${response.status}`
|
|
2237
|
+
error: errorData?.detail || responseBody || `Top up failed with status ${response.status}`
|
|
2210
2238
|
};
|
|
2211
2239
|
}
|
|
2212
2240
|
return { success: true, requestId };
|
|
@@ -3039,7 +3067,7 @@ var openDatabase = (dbName, storeName) => {
|
|
|
3039
3067
|
return Promise.reject(new Error("IndexedDB is not available"));
|
|
3040
3068
|
}
|
|
3041
3069
|
return new Promise((resolve, reject) => {
|
|
3042
|
-
const request = indexedDB.open(dbName,
|
|
3070
|
+
const request = indexedDB.open(dbName, 3);
|
|
3043
3071
|
request.onupgradeneeded = () => {
|
|
3044
3072
|
const db = request.result;
|
|
3045
3073
|
if (!db.objectStoreNames.contains(storeName)) {
|
|
@@ -3052,6 +3080,7 @@ var openDatabase = (dbName, storeName) => {
|
|
|
3052
3080
|
utStore.createIndex("baseUrl", "baseUrl", { unique: false });
|
|
3053
3081
|
utStore.createIndex("sessionId", "sessionId", { unique: false });
|
|
3054
3082
|
utStore.createIndex("client", "client", { unique: false });
|
|
3083
|
+
utStore.createIndex("provider", "provider", { unique: false });
|
|
3055
3084
|
}
|
|
3056
3085
|
if (storeName !== "sdk_storage" && !db.objectStoreNames.contains("sdk_storage")) {
|
|
3057
3086
|
db.createObjectStore("sdk_storage");
|
|
@@ -4614,6 +4643,29 @@ function extractUsageFromSSEJson(parsed, fallbackSatsCost = 0) {
|
|
|
4614
4643
|
}
|
|
4615
4644
|
return result;
|
|
4616
4645
|
}
|
|
4646
|
+
function extractUsageFromResponseHeaders(headers) {
|
|
4647
|
+
const get = (name) => {
|
|
4648
|
+
if (headers instanceof Headers) return headers.get(name);
|
|
4649
|
+
const lower = name.toLowerCase();
|
|
4650
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
4651
|
+
if (k.toLowerCase() === lower) return v;
|
|
4652
|
+
}
|
|
4653
|
+
return null;
|
|
4654
|
+
};
|
|
4655
|
+
const totalMsats = Number(get("X-Routstr-Cost-Msats"));
|
|
4656
|
+
if (!totalMsats || !Number.isFinite(totalMsats)) return null;
|
|
4657
|
+
return {
|
|
4658
|
+
promptTokens: 0,
|
|
4659
|
+
completionTokens: 0,
|
|
4660
|
+
totalTokens: 0,
|
|
4661
|
+
cost: Number(get("X-Routstr-Cost-Usd")) || 0,
|
|
4662
|
+
satsCost: totalMsats / 1e3,
|
|
4663
|
+
totalMsats,
|
|
4664
|
+
inputMsats: Number(get("X-Routstr-Input-Cost-Msats")) || 0,
|
|
4665
|
+
outputMsats: Number(get("X-Routstr-Output-Cost-Msats")) || 0,
|
|
4666
|
+
totalUsd: Number(get("X-Routstr-Cost-Usd")) || void 0
|
|
4667
|
+
};
|
|
4668
|
+
}
|
|
4617
4669
|
function toUsageStats(usage) {
|
|
4618
4670
|
if (!usage) return void 0;
|
|
4619
4671
|
return {
|
|
@@ -4700,14 +4752,11 @@ async function inspectSSEWebStream(stream, onUsage, onResponseId, options) {
|
|
|
4700
4752
|
}
|
|
4701
4753
|
const usage = extractUsageFromSSEJson(data);
|
|
4702
4754
|
if (usage) {
|
|
4703
|
-
console.log("[routstr:sse] \u2192 usage detected:", usage);
|
|
4704
4755
|
const merged = mergeUsage(capturedUsage, usage);
|
|
4705
4756
|
if (hasUsageChanged(capturedUsage, merged)) {
|
|
4706
4757
|
capturedUsage = merged;
|
|
4707
|
-
console.log("[routstr:sse] \u2192 merged (changed):", merged);
|
|
4708
4758
|
onUsage(merged);
|
|
4709
4759
|
} else {
|
|
4710
|
-
console.log("[routstr:sse] \u2192 merged (no change)");
|
|
4711
4760
|
}
|
|
4712
4761
|
}
|
|
4713
4762
|
} catch {
|
|
@@ -4864,6 +4913,177 @@ function createSSEParserTransform(onUsage, onResponseId) {
|
|
|
4864
4913
|
});
|
|
4865
4914
|
}
|
|
4866
4915
|
|
|
4916
|
+
// client/TinfoilSecure.ts
|
|
4917
|
+
var TINFOIL_MODEL_PREFIX = "tinfoil-";
|
|
4918
|
+
var clientCache = /* @__PURE__ */ new Map();
|
|
4919
|
+
function isTinfoilModel(modelId) {
|
|
4920
|
+
return modelId.startsWith(TINFOIL_MODEL_PREFIX);
|
|
4921
|
+
}
|
|
4922
|
+
function getTinfoilUpstreamModelId(modelId) {
|
|
4923
|
+
return modelId.slice(TINFOIL_MODEL_PREFIX.length);
|
|
4924
|
+
}
|
|
4925
|
+
function normalizeBaseUrl5(baseUrl) {
|
|
4926
|
+
return baseUrl.replace(/\/+$/, "");
|
|
4927
|
+
}
|
|
4928
|
+
function cacheKey(options) {
|
|
4929
|
+
return JSON.stringify({
|
|
4930
|
+
baseUrl: options.baseUrl,
|
|
4931
|
+
attestationBundleURL: options.attestationBundleURL,
|
|
4932
|
+
enclaveURL: options.enclaveURL,
|
|
4933
|
+
configRepo: options.configRepo
|
|
4934
|
+
});
|
|
4935
|
+
}
|
|
4936
|
+
function envOrUndefined(name) {
|
|
4937
|
+
const maybeProcess = globalThis;
|
|
4938
|
+
const value = maybeProcess.process?.env?.[name];
|
|
4939
|
+
return value && value.trim() ? value.trim() : void 0;
|
|
4940
|
+
}
|
|
4941
|
+
function resolveOptions(options) {
|
|
4942
|
+
return {
|
|
4943
|
+
...options,
|
|
4944
|
+
baseUrl: normalizeBaseUrl5(options.baseUrl),
|
|
4945
|
+
attestationBundleURL: options.attestationBundleURL ?? envOrUndefined("ROUTSTR_TINFOIL_ATTESTATION_BUNDLE_URL"),
|
|
4946
|
+
enclaveURL: options.enclaveURL ?? envOrUndefined("ROUTSTR_TINFOIL_ENCLAVE_URL"),
|
|
4947
|
+
configRepo: options.configRepo ?? envOrUndefined("ROUTSTR_TINFOIL_CONFIG_REPO")
|
|
4948
|
+
};
|
|
4949
|
+
}
|
|
4950
|
+
async function prepareTinfoilClient(options) {
|
|
4951
|
+
const resolved = resolveOptions(options);
|
|
4952
|
+
const key = cacheKey(resolved);
|
|
4953
|
+
let pending = clientCache.get(key);
|
|
4954
|
+
if (!pending) {
|
|
4955
|
+
pending = (async () => {
|
|
4956
|
+
const { SecureClient } = await import('tinfoil');
|
|
4957
|
+
const client = new SecureClient({
|
|
4958
|
+
// baseURL is the proxy/provider URL that receives the EHBP request.
|
|
4959
|
+
baseURL: resolved.baseUrl,
|
|
4960
|
+
// Leave undefined by default so tinfoil uses its public ATC. If set,
|
|
4961
|
+
// SecureClient will fetch `${attestationBundleURL}/attestation`.
|
|
4962
|
+
attestationBundleURL: resolved.attestationBundleURL,
|
|
4963
|
+
enclaveURL: resolved.enclaveURL,
|
|
4964
|
+
configRepo: resolved.configRepo,
|
|
4965
|
+
transport: "ehbp"
|
|
4966
|
+
});
|
|
4967
|
+
await client.ready();
|
|
4968
|
+
const verification = client.getVerificationDocument();
|
|
4969
|
+
return { client, verification };
|
|
4970
|
+
})();
|
|
4971
|
+
clientCache.set(key, pending);
|
|
4972
|
+
}
|
|
4973
|
+
try {
|
|
4974
|
+
return await pending;
|
|
4975
|
+
} catch (error) {
|
|
4976
|
+
clientCache.delete(key);
|
|
4977
|
+
throw error;
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
function normalizeFetchArgs(input, init) {
|
|
4981
|
+
if (typeof input === "string") {
|
|
4982
|
+
return { url: input, init };
|
|
4983
|
+
}
|
|
4984
|
+
if (input instanceof URL) {
|
|
4985
|
+
return { url: input.toString(), init };
|
|
4986
|
+
}
|
|
4987
|
+
const cloned = input.clone();
|
|
4988
|
+
return {
|
|
4989
|
+
url: cloned.url,
|
|
4990
|
+
init: {
|
|
4991
|
+
method: cloned.method,
|
|
4992
|
+
headers: new Headers(cloned.headers),
|
|
4993
|
+
body: cloned.body ?? void 0,
|
|
4994
|
+
signal: cloned.signal,
|
|
4995
|
+
...init
|
|
4996
|
+
}
|
|
4997
|
+
};
|
|
4998
|
+
}
|
|
4999
|
+
function isProblemJsonContentType(contentType) {
|
|
5000
|
+
const mediaType = contentType?.split(";", 1)[0]?.trim().toLowerCase();
|
|
5001
|
+
return mediaType === "application/problem+json";
|
|
5002
|
+
}
|
|
5003
|
+
async function isEhbpKeyConfigMismatchResponse(response, protocol) {
|
|
5004
|
+
if (response.status !== 422) {
|
|
5005
|
+
return false;
|
|
5006
|
+
}
|
|
5007
|
+
if (!isProblemJsonContentType(response.headers.get("content-type"))) {
|
|
5008
|
+
return false;
|
|
5009
|
+
}
|
|
5010
|
+
try {
|
|
5011
|
+
const problem = await response.clone().json();
|
|
5012
|
+
return problem?.type === protocol.KEY_CONFIG_PROBLEM_TYPE;
|
|
5013
|
+
} catch {
|
|
5014
|
+
return false;
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
async function fetchTinfoilEhbpOnce(context, options, normalized, ehbp) {
|
|
5018
|
+
const { Identity, PROTOCOL, decryptResponseWithToken, extractSessionRecoveryToken } = ehbp;
|
|
5019
|
+
const resolved = resolveOptions(options);
|
|
5020
|
+
const baseURL = context.client.getBaseURL() ?? resolved.baseUrl;
|
|
5021
|
+
const enclaveURL = context.client.getEnclaveURL();
|
|
5022
|
+
const baseOrigin = new URL(baseURL).origin;
|
|
5023
|
+
const allowedOrigins = /* @__PURE__ */ new Set([baseOrigin]);
|
|
5024
|
+
if (enclaveURL) {
|
|
5025
|
+
allowedOrigins.add(new URL(enclaveURL).origin);
|
|
5026
|
+
}
|
|
5027
|
+
const targetUrl = new URL(normalized.url, baseURL);
|
|
5028
|
+
if (!allowedOrigins.has(targetUrl.origin)) {
|
|
5029
|
+
throw new Error(
|
|
5030
|
+
`refusing to send Tinfoil request to ${targetUrl.origin}: client is bound to the verified enclave/proxy`
|
|
5031
|
+
);
|
|
5032
|
+
}
|
|
5033
|
+
const headers = new Headers(normalized.init?.headers);
|
|
5034
|
+
if (enclaveURL && new URL(enclaveURL).origin !== baseOrigin) {
|
|
5035
|
+
headers.set("X-Tinfoil-Enclave-Url", enclaveURL);
|
|
5036
|
+
}
|
|
5037
|
+
const method = normalized.init?.method ?? "GET";
|
|
5038
|
+
const body = normalized.init?.body ?? null;
|
|
5039
|
+
const serverIdentity = await Identity.fromPublicKeyHex(
|
|
5040
|
+
context.verification.hpkePublicKey
|
|
5041
|
+
);
|
|
5042
|
+
const request = new Request(targetUrl.toString(), {
|
|
5043
|
+
method,
|
|
5044
|
+
headers,
|
|
5045
|
+
body,
|
|
5046
|
+
duplex: "half"
|
|
5047
|
+
});
|
|
5048
|
+
const { request: encryptedRequest, context: requestContext } = await serverIdentity.encryptRequestWithContext(request);
|
|
5049
|
+
const response = await fetch(encryptedRequest);
|
|
5050
|
+
if (!requestContext) {
|
|
5051
|
+
return response;
|
|
5052
|
+
}
|
|
5053
|
+
if (await isEhbpKeyConfigMismatchResponse(response, PROTOCOL)) {
|
|
5054
|
+
throw new ehbp.KeyConfigMismatchError("EHBP key configuration mismatch");
|
|
5055
|
+
}
|
|
5056
|
+
if (!response.headers.get(PROTOCOL.RESPONSE_NONCE_HEADER)) {
|
|
5057
|
+
return response;
|
|
5058
|
+
}
|
|
5059
|
+
const token = await extractSessionRecoveryToken(requestContext);
|
|
5060
|
+
return await decryptResponseWithToken(response, token);
|
|
5061
|
+
}
|
|
5062
|
+
async function fetchTinfoilPreservingPlaintextErrors(options, input, init) {
|
|
5063
|
+
const context = await prepareTinfoilClient(options);
|
|
5064
|
+
const ehbp = await import('ehbp');
|
|
5065
|
+
const normalized = normalizeFetchArgs(input, init);
|
|
5066
|
+
try {
|
|
5067
|
+
return await fetchTinfoilEhbpOnce(context, options, normalized, ehbp);
|
|
5068
|
+
} catch (error) {
|
|
5069
|
+
if (error instanceof ehbp.KeyConfigMismatchError) {
|
|
5070
|
+
context.client.reset();
|
|
5071
|
+
try {
|
|
5072
|
+
await context.client.ready();
|
|
5073
|
+
context.verification = context.client.getVerificationDocument();
|
|
5074
|
+
} catch (reattestError) {
|
|
5075
|
+
clientCache.delete(cacheKey(resolveOptions(options)));
|
|
5076
|
+
throw reattestError;
|
|
5077
|
+
}
|
|
5078
|
+
return await fetchTinfoilEhbpOnce(context, options, normalized, ehbp);
|
|
5079
|
+
}
|
|
5080
|
+
throw error;
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
function clearTinfoilClientCache() {
|
|
5084
|
+
clientCache.clear();
|
|
5085
|
+
}
|
|
5086
|
+
|
|
4867
5087
|
// client/RoutstrClient.ts
|
|
4868
5088
|
var TOPUP_MARGIN = 1.2;
|
|
4869
5089
|
var RoutstrClient = class {
|
|
@@ -5049,11 +5269,6 @@ var RoutstrClient = class {
|
|
|
5049
5269
|
);
|
|
5050
5270
|
}
|
|
5051
5271
|
}
|
|
5052
|
-
const { token, tokenBalance, tokenBalanceUnit, tokenBalanceUnknown } = await this._spendToken({
|
|
5053
|
-
mintUrl,
|
|
5054
|
-
amount: requiredSats,
|
|
5055
|
-
baseUrl
|
|
5056
|
-
});
|
|
5057
5272
|
let requestBody = body;
|
|
5058
5273
|
if (body && typeof body === "object") {
|
|
5059
5274
|
const bodyObj = body;
|
|
@@ -5062,7 +5277,36 @@ var RoutstrClient = class {
|
|
|
5062
5277
|
}
|
|
5063
5278
|
}
|
|
5064
5279
|
const baseHeaders = this._buildBaseHeaders();
|
|
5065
|
-
const
|
|
5280
|
+
const tinfoilEnabled = Boolean(modelId && isTinfoilModel(modelId));
|
|
5281
|
+
if (tinfoilEnabled) {
|
|
5282
|
+
this._log(
|
|
5283
|
+
"DEBUG",
|
|
5284
|
+
`[RoutstrClient] Attesting Tinfoil model ${modelId} before spend`
|
|
5285
|
+
);
|
|
5286
|
+
const { verification } = await prepareTinfoilClient({ baseUrl });
|
|
5287
|
+
this._log(
|
|
5288
|
+
"DEBUG",
|
|
5289
|
+
`[RoutstrClient] Tinfoil attestation passed, enclave=${verification.enclaveHost}, codeFingerprint=${verification.codeFingerprint.slice(0, 16)}...`
|
|
5290
|
+
);
|
|
5291
|
+
if (requestBody && typeof requestBody === "object" && modelId) {
|
|
5292
|
+
requestBody = {
|
|
5293
|
+
...requestBody,
|
|
5294
|
+
model: getTinfoilUpstreamModelId(modelId)
|
|
5295
|
+
};
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
const spendResult = await this._spendToken({
|
|
5299
|
+
mintUrl,
|
|
5300
|
+
amount: requiredSats,
|
|
5301
|
+
baseUrl
|
|
5302
|
+
});
|
|
5303
|
+
const { token, tokenBalance, tokenBalanceUnit, tokenBalanceUnknown } = spendResult;
|
|
5304
|
+
const finalHeaders = this._withAuthAndTinfoilHeaders(
|
|
5305
|
+
baseHeaders,
|
|
5306
|
+
token,
|
|
5307
|
+
tinfoilEnabled,
|
|
5308
|
+
modelId
|
|
5309
|
+
);
|
|
5066
5310
|
const response = await this._makeRequest({
|
|
5067
5311
|
path: requestPath,
|
|
5068
5312
|
method,
|
|
@@ -5071,9 +5315,10 @@ var RoutstrClient = class {
|
|
|
5071
5315
|
mintUrl,
|
|
5072
5316
|
token,
|
|
5073
5317
|
requiredSats,
|
|
5074
|
-
headers:
|
|
5318
|
+
headers: finalHeaders,
|
|
5075
5319
|
baseHeaders,
|
|
5076
|
-
selectedModel
|
|
5320
|
+
selectedModel,
|
|
5321
|
+
tinfoilEnabled
|
|
5077
5322
|
});
|
|
5078
5323
|
let tokenBalanceInSats = tokenBalanceUnit === "msat" ? tokenBalance / 1e3 : tokenBalance;
|
|
5079
5324
|
let initialTokenBalanceUnknown = tokenBalanceUnknown;
|
|
@@ -5161,7 +5406,7 @@ var RoutstrClient = class {
|
|
|
5161
5406
|
* Make the API request with failover support
|
|
5162
5407
|
*/
|
|
5163
5408
|
async _makeRequest(params) {
|
|
5164
|
-
const { path, method, body, baseUrl, token, headers } = params;
|
|
5409
|
+
const { path, method, body, baseUrl, token, headers, tinfoilEnabled } = params;
|
|
5165
5410
|
try {
|
|
5166
5411
|
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
5167
5412
|
const requestBodyText = body === void 0 || method === "GET" ? void 0 : JSON.stringify(body);
|
|
@@ -5175,7 +5420,15 @@ var RoutstrClient = class {
|
|
|
5175
5420
|
rawBody: requestBodyText
|
|
5176
5421
|
});
|
|
5177
5422
|
if (this.mode === "xcashu") this._log("DEBUG", "HEADERS,", headers);
|
|
5178
|
-
const response = await
|
|
5423
|
+
const response = tinfoilEnabled ? await fetchTinfoilPreservingPlaintextErrors(
|
|
5424
|
+
{ baseUrl },
|
|
5425
|
+
url,
|
|
5426
|
+
{
|
|
5427
|
+
method,
|
|
5428
|
+
headers,
|
|
5429
|
+
body: requestBodyText
|
|
5430
|
+
}
|
|
5431
|
+
) : await fetch(url, {
|
|
5179
5432
|
method,
|
|
5180
5433
|
headers,
|
|
5181
5434
|
body: requestBodyText
|
|
@@ -5195,6 +5448,15 @@ var RoutstrClient = class {
|
|
|
5195
5448
|
} catch (e) {
|
|
5196
5449
|
bodyText = void 0;
|
|
5197
5450
|
}
|
|
5451
|
+
this._log("ERROR", "[RoutstrClient] Upstream error response", {
|
|
5452
|
+
baseUrl,
|
|
5453
|
+
url,
|
|
5454
|
+
path,
|
|
5455
|
+
status: response.status,
|
|
5456
|
+
statusText: response.statusText,
|
|
5457
|
+
requestId,
|
|
5458
|
+
body: bodyText ?? "<unable to read response body>"
|
|
5459
|
+
});
|
|
5198
5460
|
return await this._handleErrorResponse(
|
|
5199
5461
|
params,
|
|
5200
5462
|
token,
|
|
@@ -5225,6 +5487,9 @@ var RoutstrClient = class {
|
|
|
5225
5487
|
throw error;
|
|
5226
5488
|
}
|
|
5227
5489
|
}
|
|
5490
|
+
/**
|
|
5491
|
+
* Store request details to a file in the reqs/ folder before fetch.
|
|
5492
|
+
*/
|
|
5228
5493
|
/**
|
|
5229
5494
|
* Handle error responses with failover
|
|
5230
5495
|
*/
|
|
@@ -5376,7 +5641,12 @@ var RoutstrClient = class {
|
|
|
5376
5641
|
return this._makeRequest({
|
|
5377
5642
|
...params,
|
|
5378
5643
|
token: params.token,
|
|
5379
|
-
headers: this.
|
|
5644
|
+
headers: this._withAuthAndTinfoilHeaders(
|
|
5645
|
+
params.baseHeaders,
|
|
5646
|
+
params.token,
|
|
5647
|
+
params.tinfoilEnabled,
|
|
5648
|
+
params.selectedModel?.id
|
|
5649
|
+
),
|
|
5380
5650
|
retryCount: retryCount + 1
|
|
5381
5651
|
});
|
|
5382
5652
|
} else {
|
|
@@ -5437,7 +5707,12 @@ var RoutstrClient = class {
|
|
|
5437
5707
|
return this._makeRequest({
|
|
5438
5708
|
...params,
|
|
5439
5709
|
token: retryToken,
|
|
5440
|
-
headers: this.
|
|
5710
|
+
headers: this._withAuthAndTinfoilHeaders(
|
|
5711
|
+
params.baseHeaders,
|
|
5712
|
+
retryToken,
|
|
5713
|
+
params.tinfoilEnabled,
|
|
5714
|
+
params.selectedModel?.id
|
|
5715
|
+
),
|
|
5441
5716
|
retryCount: retryCount + 1
|
|
5442
5717
|
});
|
|
5443
5718
|
} else {
|
|
@@ -5532,6 +5807,13 @@ var RoutstrClient = class {
|
|
|
5532
5807
|
messagesForPricing,
|
|
5533
5808
|
params.maxTokens
|
|
5534
5809
|
);
|
|
5810
|
+
if (params.tinfoilEnabled) {
|
|
5811
|
+
this._log(
|
|
5812
|
+
"DEBUG",
|
|
5813
|
+
`[RoutstrClient] _handleErrorResponse: Attesting Tinfoil failover provider ${nextProvider} before spend`
|
|
5814
|
+
);
|
|
5815
|
+
await prepareTinfoilClient({ baseUrl: nextProvider });
|
|
5816
|
+
}
|
|
5535
5817
|
this._log(
|
|
5536
5818
|
"DEBUG",
|
|
5537
5819
|
`[RoutstrClient] _handleErrorResponse: Creating new token for failover provider ${nextProvider}, required sats: ${newRequiredSats}`
|
|
@@ -5550,7 +5832,12 @@ var RoutstrClient = class {
|
|
|
5550
5832
|
selectedModel: newModel,
|
|
5551
5833
|
token: spendResult.token,
|
|
5552
5834
|
requiredSats: newRequiredSats,
|
|
5553
|
-
headers: this.
|
|
5835
|
+
headers: this._withAuthAndTinfoilHeaders(
|
|
5836
|
+
params.baseHeaders,
|
|
5837
|
+
spendResult.token,
|
|
5838
|
+
params.tinfoilEnabled,
|
|
5839
|
+
newModel.id
|
|
5840
|
+
),
|
|
5554
5841
|
retryCount: 0
|
|
5555
5842
|
});
|
|
5556
5843
|
retryResponse.initialTokenBalanceInSats = spendResult.tokenBalanceUnit === "msat" ? spendResult.tokenBalance / 1e3 : spendResult.tokenBalance;
|
|
@@ -5617,10 +5904,10 @@ var RoutstrClient = class {
|
|
|
5617
5904
|
if (latestTokenBalance !== void 0) {
|
|
5618
5905
|
this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
|
|
5619
5906
|
}
|
|
5620
|
-
satsSpent = latestTokenBalance !== void 0 && !initialTokenBalanceUnknown ? Math.max(0, initialTokenBalance - latestTokenBalance) : fallbackSatsSpent ?? usage?.satsCost ?? 0;
|
|
5907
|
+
satsSpent = latestTokenBalance !== void 0 && !initialTokenBalanceUnknown ? Math.max(0, initialTokenBalance - latestTokenBalance) : fallbackSatsSpent ?? usage?.satsCost ?? this._headerSatsCost(response) ?? 0;
|
|
5621
5908
|
} catch (e) {
|
|
5622
5909
|
this._log("WARN", "Could not get updated API key balance:", e);
|
|
5623
|
-
satsSpent = fallbackSatsSpent ?? usage?.satsCost ?? 0;
|
|
5910
|
+
satsSpent = fallbackSatsSpent ?? usage?.satsCost ?? this._headerSatsCost(response) ?? 0;
|
|
5624
5911
|
}
|
|
5625
5912
|
}
|
|
5626
5913
|
await this._trackResponseUsage({
|
|
@@ -5637,6 +5924,15 @@ var RoutstrClient = class {
|
|
|
5637
5924
|
})();
|
|
5638
5925
|
return satsSpent;
|
|
5639
5926
|
}
|
|
5927
|
+
/**
|
|
5928
|
+
* Extract sats cost from EHBP/Tinfoil response headers as a last-resort
|
|
5929
|
+
* fallback when neither balance delta nor SSE/body usage provides a cost.
|
|
5930
|
+
*/
|
|
5931
|
+
_headerSatsCost(response) {
|
|
5932
|
+
if (!response) return void 0;
|
|
5933
|
+
const headerUsage = extractUsageFromResponseHeaders(response.headers);
|
|
5934
|
+
return headerUsage?.satsCost;
|
|
5935
|
+
}
|
|
5640
5936
|
async _trackResponseUsage(params) {
|
|
5641
5937
|
const {
|
|
5642
5938
|
token,
|
|
@@ -5670,7 +5966,24 @@ var RoutstrClient = class {
|
|
|
5670
5966
|
}
|
|
5671
5967
|
}
|
|
5672
5968
|
if (!usage) {
|
|
5673
|
-
|
|
5969
|
+
const headerUsage = extractUsageFromResponseHeaders(response.headers);
|
|
5970
|
+
if (headerUsage) {
|
|
5971
|
+
usage = headerUsage;
|
|
5972
|
+
} else {
|
|
5973
|
+
return;
|
|
5974
|
+
}
|
|
5975
|
+
} else {
|
|
5976
|
+
const headerUsage = extractUsageFromResponseHeaders(response.headers);
|
|
5977
|
+
if (headerUsage) {
|
|
5978
|
+
if (headerUsage.totalMsats) {
|
|
5979
|
+
usage.totalMsats = headerUsage.totalMsats;
|
|
5980
|
+
usage.satsCost = headerUsage.satsCost;
|
|
5981
|
+
}
|
|
5982
|
+
if (headerUsage.cost) usage.cost = headerUsage.cost;
|
|
5983
|
+
if (headerUsage.inputMsats) usage.inputMsats = headerUsage.inputMsats;
|
|
5984
|
+
if (headerUsage.outputMsats) usage.outputMsats = headerUsage.outputMsats;
|
|
5985
|
+
if (headerUsage.totalUsd) usage.totalUsd = headerUsage.totalUsd;
|
|
5986
|
+
}
|
|
5674
5987
|
}
|
|
5675
5988
|
const finalRequestId = requestId || "unknown";
|
|
5676
5989
|
const store = this.sdkStore ?? await getDefaultSdkStore();
|
|
@@ -5867,6 +6180,19 @@ var RoutstrClient = class {
|
|
|
5867
6180
|
}
|
|
5868
6181
|
return nextHeaders;
|
|
5869
6182
|
}
|
|
6183
|
+
/**
|
|
6184
|
+
* Attach auth headers and preserve the plaintext model hint required by the
|
|
6185
|
+
* Routstr proxy for Tinfoil/EHBP requests. EHBP encrypts the JSON body, so
|
|
6186
|
+
* retries/failover must not rebuild headers from baseHeaders alone or the
|
|
6187
|
+
* proxy cannot route/price the encrypted request.
|
|
6188
|
+
*/
|
|
6189
|
+
_withAuthAndTinfoilHeaders(headers, token, tinfoilEnabled, modelId) {
|
|
6190
|
+
const nextHeaders = this._withAuthHeader(headers, token);
|
|
6191
|
+
if (tinfoilEnabled && modelId) {
|
|
6192
|
+
nextHeaders["X-Routstr-Model"] = modelId;
|
|
6193
|
+
}
|
|
6194
|
+
return nextHeaders;
|
|
6195
|
+
}
|
|
5870
6196
|
};
|
|
5871
6197
|
|
|
5872
6198
|
// client/StreamProcessor.ts
|
|
@@ -6356,6 +6682,6 @@ function extractStream(requestBody) {
|
|
|
6356
6682
|
return typeof stream === "boolean" ? stream : void 0;
|
|
6357
6683
|
}
|
|
6358
6684
|
|
|
6359
|
-
export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, consoleLogger, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromDiscoveryAdapter, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createShardedDiscoveryAdapter, createStorageAdapterFromStore, extractResponseId, extractUsageFromResponseBody, extractUsageFromSSEJson, fetchAIResponse, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, inspectSSEWebStream, isOnionUrl, isTorContext, localStorageDriver, noopLogger, normalizeProviderUrl, routeRequests, setDefaultUsageTrackingDriver, toUsageStats };
|
|
6685
|
+
export { BalanceManager, CashuSpender, FailoverError, InsufficientBalanceError, MintDiscovery, MintDiscoveryError, MintUnreachableError, ModelManager, ModelNotFoundError, NoProvidersAvailableError, ProviderBootstrapError, ProviderError, ProviderManager, RoutstrClient, SDK_STORAGE_KEYS, StreamProcessor, StreamingError, TokenOperationError, clearTinfoilClientCache, consoleLogger, createDiscoveryAdapterFromStore, createIndexedDBDriver, createIndexedDBUsageTrackingDriver, createMemoryDriver, createMemoryUsageTrackingDriver, createProviderRegistryFromDiscoveryAdapter, createProviderRegistryFromStore, createSSEParserTransform, createSdkStore, createShardedDiscoveryAdapter, createStorageAdapterFromStore, extractResponseId, extractUsageFromResponseBody, extractUsageFromResponseHeaders, extractUsageFromSSEJson, fetchAIResponse, fetchTinfoilPreservingPlaintextErrors, filterBaseUrlsForTor, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, getDefaultUsageTrackingDriver, getProviderEndpoints, getTinfoilUpstreamModelId, inspectSSEWebStream, isOnionUrl, isTinfoilModel, isTorContext, localStorageDriver, noopLogger, normalizeProviderUrl, prepareTinfoilClient, routeRequests, setDefaultUsageTrackingDriver, toUsageStats };
|
|
6360
6686
|
//# sourceMappingURL=index.mjs.map
|
|
6361
6687
|
//# sourceMappingURL=index.mjs.map
|