@routstr/sdk 0.1.5 → 0.1.7
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/client/index.js +94 -9
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +94 -9
- package/dist/client/index.mjs.map +1 -1
- package/dist/discovery/index.d.mts +3 -1
- package/dist/discovery/index.d.ts +3 -1
- package/dist/discovery/index.js +17 -13
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +17 -13
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/index.js +115 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +115 -26
- package/dist/index.mjs.map +1 -1
- package/dist/storage/index.js +4 -4
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +4 -4
- package/dist/storage/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +1 -0
- package/dist/wallet/index.d.ts +1 -0
- package/dist/wallet/index.js +8 -0
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +8 -0
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -123,7 +123,7 @@ var ModelManager = class _ModelManager {
|
|
|
123
123
|
const manager = new _ModelManager(adapter, config);
|
|
124
124
|
const torMode = options.torMode ?? false;
|
|
125
125
|
const forceRefresh = options.forceRefresh ?? false;
|
|
126
|
-
const providers = await manager.bootstrapProviders(torMode);
|
|
126
|
+
const providers = await manager.bootstrapProviders(torMode, forceRefresh);
|
|
127
127
|
await manager.fetchModels(providers, forceRefresh);
|
|
128
128
|
return manager;
|
|
129
129
|
}
|
|
@@ -131,17 +131,20 @@ var ModelManager = class _ModelManager {
|
|
|
131
131
|
* Bootstrap provider list from the provider directory
|
|
132
132
|
* First tries to fetch from Nostr (kind 30421), falls back to HTTP
|
|
133
133
|
* @param torMode Whether running in Tor context
|
|
134
|
+
* @param forceRefresh Ignore provider cache and refresh provider sources
|
|
134
135
|
* @returns Array of provider base URLs
|
|
135
136
|
* @throws ProviderBootstrapError if all providers fail to fetch
|
|
136
137
|
*/
|
|
137
|
-
async bootstrapProviders(torMode = false) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
async bootstrapProviders(torMode = false, forceRefresh = false) {
|
|
139
|
+
if (!forceRefresh) {
|
|
140
|
+
const cachedUrls = this.adapter.getBaseUrlsList();
|
|
141
|
+
if (cachedUrls.length > 0) {
|
|
142
|
+
const lastUpdate = this.adapter.getBaseUrlsLastUpdate();
|
|
143
|
+
const cacheValid = lastUpdate && Date.now() - lastUpdate <= this.cacheTTL;
|
|
144
|
+
if (cacheValid) {
|
|
145
|
+
await this.fetchRoutstr21Models(forceRefresh);
|
|
146
|
+
return this.filterBaseUrlsForTor(cachedUrls, torMode);
|
|
147
|
+
}
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
150
|
try {
|
|
@@ -150,13 +153,13 @@ var ModelManager = class _ModelManager {
|
|
|
150
153
|
const filtered = this.filterBaseUrlsForTor(nostrProviders, torMode);
|
|
151
154
|
this.adapter.setBaseUrlsList(filtered);
|
|
152
155
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
153
|
-
await this.fetchRoutstr21Models();
|
|
156
|
+
await this.fetchRoutstr21Models(forceRefresh);
|
|
154
157
|
return filtered;
|
|
155
158
|
}
|
|
156
159
|
} catch (e) {
|
|
157
160
|
console.warn("Nostr bootstrap failed, falling back to HTTP:", e);
|
|
158
161
|
}
|
|
159
|
-
return this.bootstrapFromHttp(torMode);
|
|
162
|
+
return this.bootstrapFromHttp(torMode, forceRefresh);
|
|
160
163
|
}
|
|
161
164
|
/**
|
|
162
165
|
* Bootstrap providers from Nostr network (kind 30421)
|
|
@@ -252,9 +255,10 @@ var ModelManager = class _ModelManager {
|
|
|
252
255
|
/**
|
|
253
256
|
* Bootstrap providers from HTTP endpoint
|
|
254
257
|
* @param torMode Whether running in Tor context
|
|
258
|
+
* @param forceRefresh Ignore routstr21 cache and fetch fresh data
|
|
255
259
|
* @returns Array of provider base URLs
|
|
256
260
|
*/
|
|
257
|
-
async bootstrapFromHttp(torMode) {
|
|
261
|
+
async bootstrapFromHttp(torMode, forceRefresh = false) {
|
|
258
262
|
try {
|
|
259
263
|
const res = await fetch(this.providerDirectoryUrl);
|
|
260
264
|
if (!res.ok) {
|
|
@@ -282,7 +286,7 @@ var ModelManager = class _ModelManager {
|
|
|
282
286
|
if (list.length > 0) {
|
|
283
287
|
this.adapter.setBaseUrlsList(list);
|
|
284
288
|
this.adapter.setBaseUrlsLastUpdate(Date.now());
|
|
285
|
-
await this.fetchRoutstr21Models();
|
|
289
|
+
await this.fetchRoutstr21Models(forceRefresh);
|
|
286
290
|
}
|
|
287
291
|
return list;
|
|
288
292
|
} catch (e) {
|
|
@@ -1816,6 +1820,14 @@ var BalanceManager = class {
|
|
|
1816
1820
|
console.log(response.status);
|
|
1817
1821
|
const data = await response.json();
|
|
1818
1822
|
console.log("FAILED ", data);
|
|
1823
|
+
const isInvalidApiKey = response.status === 401 && data?.code === "invalid_api_key" && data?.message?.includes("proofs already spent");
|
|
1824
|
+
return {
|
|
1825
|
+
amount: -1,
|
|
1826
|
+
reserved: data.reserved ?? 0,
|
|
1827
|
+
unit: "msat",
|
|
1828
|
+
apiKey: data.api_key,
|
|
1829
|
+
isInvalidApiKey
|
|
1830
|
+
};
|
|
1819
1831
|
}
|
|
1820
1832
|
} catch (error) {
|
|
1821
1833
|
console.error("ERRORR IN RESTPONSE", error);
|
|
@@ -2764,7 +2776,8 @@ var RoutstrClient = class {
|
|
|
2764
2776
|
response.status,
|
|
2765
2777
|
requestId,
|
|
2766
2778
|
this.mode === "xcashu" ? response.headers.get("x-cashu") ?? void 0 : void 0,
|
|
2767
|
-
bodyText
|
|
2779
|
+
bodyText,
|
|
2780
|
+
params.retryCount ?? 0
|
|
2768
2781
|
);
|
|
2769
2782
|
}
|
|
2770
2783
|
return response;
|
|
@@ -2773,8 +2786,12 @@ var RoutstrClient = class {
|
|
|
2773
2786
|
return await this._handleErrorResponse(
|
|
2774
2787
|
params,
|
|
2775
2788
|
token,
|
|
2776
|
-
-1
|
|
2789
|
+
-1,
|
|
2777
2790
|
// just for Network Error to skip all statuses
|
|
2791
|
+
void 0,
|
|
2792
|
+
void 0,
|
|
2793
|
+
void 0,
|
|
2794
|
+
params.retryCount ?? 0
|
|
2778
2795
|
);
|
|
2779
2796
|
}
|
|
2780
2797
|
throw error;
|
|
@@ -2783,7 +2800,8 @@ var RoutstrClient = class {
|
|
|
2783
2800
|
/**
|
|
2784
2801
|
* Handle error responses with failover
|
|
2785
2802
|
*/
|
|
2786
|
-
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody) {
|
|
2803
|
+
async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
|
|
2804
|
+
const MAX_RETRIES_PER_PROVIDER = 2;
|
|
2787
2805
|
const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
|
|
2788
2806
|
let tryNextProvider = false;
|
|
2789
2807
|
this._log(
|
|
@@ -2853,8 +2871,7 @@ var RoutstrClient = class {
|
|
|
2853
2871
|
);
|
|
2854
2872
|
}
|
|
2855
2873
|
}
|
|
2856
|
-
if (
|
|
2857
|
-
console.log("RESPONSFE ", responseBody);
|
|
2874
|
+
if (status === 402 && !tryNextProvider && (this.mode === "apikeys" || this.mode === "lazyrefund")) {
|
|
2858
2875
|
const topupResult = await this.balanceManager.topUp({
|
|
2859
2876
|
mintUrl,
|
|
2860
2877
|
baseUrl,
|
|
@@ -2890,12 +2907,83 @@ var RoutstrClient = class {
|
|
|
2890
2907
|
`[RoutstrClient] _handleErrorResponse: Topup successful, will retry with new token`
|
|
2891
2908
|
);
|
|
2892
2909
|
}
|
|
2893
|
-
if (!tryNextProvider)
|
|
2910
|
+
if (!tryNextProvider) {
|
|
2911
|
+
if (retryCount < MAX_RETRIES_PER_PROVIDER) {
|
|
2912
|
+
this._log(
|
|
2913
|
+
"DEBUG",
|
|
2914
|
+
`[RoutstrClient] _handleErrorResponse: Retrying 402 (attempt ${retryCount + 1}/${MAX_RETRIES_PER_PROVIDER})`
|
|
2915
|
+
);
|
|
2916
|
+
return this._makeRequest({
|
|
2917
|
+
...params,
|
|
2918
|
+
token: params.token,
|
|
2919
|
+
headers: this._withAuthHeader(params.baseHeaders, params.token),
|
|
2920
|
+
retryCount: retryCount + 1
|
|
2921
|
+
});
|
|
2922
|
+
} else {
|
|
2923
|
+
this._log(
|
|
2924
|
+
"DEBUG",
|
|
2925
|
+
`[RoutstrClient] _handleErrorResponse: 402 retry limit reached (${retryCount}/${MAX_RETRIES_PER_PROVIDER}), failing over to next provider`
|
|
2926
|
+
);
|
|
2927
|
+
tryNextProvider = true;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
const isInsufficientBalance413 = status === 413 && responseBody?.includes("Insufficient balance");
|
|
2932
|
+
if (isInsufficientBalance413 && !tryNextProvider && this.mode === "apikeys") {
|
|
2933
|
+
let retryToken = params.token;
|
|
2934
|
+
try {
|
|
2935
|
+
const latestBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
2936
|
+
params.token,
|
|
2937
|
+
baseUrl
|
|
2938
|
+
);
|
|
2939
|
+
if (latestBalanceInfo.isInvalidApiKey) {
|
|
2940
|
+
this._log(
|
|
2941
|
+
"DEBUG",
|
|
2942
|
+
`[RoutstrClient] _handleErrorResponse: Invalid API key (proofs already spent), removing for ${baseUrl}`
|
|
2943
|
+
);
|
|
2944
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
2945
|
+
tryNextProvider = true;
|
|
2946
|
+
} else {
|
|
2947
|
+
const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
|
|
2948
|
+
if (latestBalanceInfo.apiKey) {
|
|
2949
|
+
const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
|
|
2950
|
+
if (storedApiKeyEntry?.key !== latestBalanceInfo.apiKey) {
|
|
2951
|
+
if (storedApiKeyEntry) {
|
|
2952
|
+
this.storageAdapter.removeApiKey(baseUrl);
|
|
2953
|
+
}
|
|
2954
|
+
this.storageAdapter.setApiKey(baseUrl, latestBalanceInfo.apiKey);
|
|
2955
|
+
}
|
|
2956
|
+
retryToken = latestBalanceInfo.apiKey;
|
|
2957
|
+
}
|
|
2958
|
+
if (latestTokenBalance >= 0) {
|
|
2959
|
+
this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
} catch (error) {
|
|
2963
|
+
this._log(
|
|
2964
|
+
"WARN",
|
|
2965
|
+
`[RoutstrClient] _handleErrorResponse: Failed to refresh API key after 413 insufficient balance for ${baseUrl}`,
|
|
2966
|
+
error
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2969
|
+
if (retryCount < MAX_RETRIES_PER_PROVIDER) {
|
|
2970
|
+
this._log(
|
|
2971
|
+
"DEBUG",
|
|
2972
|
+
`[RoutstrClient] _handleErrorResponse: Retrying 413 (attempt ${retryCount + 1}/${MAX_RETRIES_PER_PROVIDER})`
|
|
2973
|
+
);
|
|
2894
2974
|
return this._makeRequest({
|
|
2895
2975
|
...params,
|
|
2896
|
-
token:
|
|
2897
|
-
headers: this._withAuthHeader(params.baseHeaders,
|
|
2976
|
+
token: retryToken,
|
|
2977
|
+
headers: this._withAuthHeader(params.baseHeaders, retryToken),
|
|
2978
|
+
retryCount: retryCount + 1
|
|
2898
2979
|
});
|
|
2980
|
+
} else {
|
|
2981
|
+
this._log(
|
|
2982
|
+
"DEBUG",
|
|
2983
|
+
`[RoutstrClient] _handleErrorResponse: 413 retry limit reached (${retryCount}/${MAX_RETRIES_PER_PROVIDER}), failing over to next provider`
|
|
2984
|
+
);
|
|
2985
|
+
tryNextProvider = true;
|
|
2986
|
+
}
|
|
2899
2987
|
}
|
|
2900
2988
|
if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
|
|
2901
2989
|
this._log(
|
|
@@ -3011,7 +3099,8 @@ var RoutstrClient = class {
|
|
|
3011
3099
|
selectedModel: newModel,
|
|
3012
3100
|
token: spendResult.token,
|
|
3013
3101
|
requiredSats: newRequiredSats,
|
|
3014
|
-
headers: this._withAuthHeader(params.baseHeaders, spendResult.token)
|
|
3102
|
+
headers: this._withAuthHeader(params.baseHeaders, spendResult.token),
|
|
3103
|
+
retryCount: 0
|
|
3015
3104
|
});
|
|
3016
3105
|
}
|
|
3017
3106
|
throw new FailoverError(baseUrl, Array.from(this.providerManager));
|
|
@@ -3646,7 +3735,7 @@ var SDK_STORAGE_KEYS = {
|
|
|
3646
3735
|
|
|
3647
3736
|
// storage/store.ts
|
|
3648
3737
|
var normalizeBaseUrl = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
3649
|
-
var
|
|
3738
|
+
var getCashuTokenBalance = (token) => {
|
|
3650
3739
|
try {
|
|
3651
3740
|
const decoded = getDecodedToken(token);
|
|
3652
3741
|
const unitDivisor = decoded.unit === "msat" ? 1e3 : 1;
|
|
@@ -3739,7 +3828,7 @@ var createSdkStore = async ({
|
|
|
3739
3828
|
const cachedTokens = rawCachedTokens.map((entry) => ({
|
|
3740
3829
|
...entry,
|
|
3741
3830
|
baseUrl: normalizeBaseUrl(entry.baseUrl),
|
|
3742
|
-
balance: typeof entry.balance === "number" ? entry.balance :
|
|
3831
|
+
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
3743
3832
|
lastUsed: entry.lastUsed ?? null
|
|
3744
3833
|
}));
|
|
3745
3834
|
const apiKeys = rawApiKeys.map((entry) => ({
|
|
@@ -3843,7 +3932,7 @@ var createSdkStore = async ({
|
|
|
3843
3932
|
const normalized = updates.map((entry) => ({
|
|
3844
3933
|
...entry,
|
|
3845
3934
|
baseUrl: normalizeBaseUrl(entry.baseUrl),
|
|
3846
|
-
balance: typeof entry.balance === "number" ? entry.balance :
|
|
3935
|
+
balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
|
|
3847
3936
|
lastUsed: entry.lastUsed ?? null
|
|
3848
3937
|
}));
|
|
3849
3938
|
void driver.setItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, normalized);
|
|
@@ -3942,7 +4031,7 @@ var createStorageAdapterFromStore = (store) => ({
|
|
|
3942
4031
|
setToken: (baseUrl, token) => {
|
|
3943
4032
|
const normalized = normalizeBaseUrl(baseUrl);
|
|
3944
4033
|
const tokens = store.getState().cachedTokens;
|
|
3945
|
-
const balance =
|
|
4034
|
+
const balance = getCashuTokenBalance(token);
|
|
3946
4035
|
const existingIndex = tokens.findIndex(
|
|
3947
4036
|
(entry) => entry.baseUrl === normalized
|
|
3948
4037
|
);
|