@blockrun/llm 1.7.0 → 1.8.1
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/README.md +83 -6
- package/dist/index.cjs +561 -12
- package/dist/index.d.cts +211 -1
- package/dist/index.d.ts +211 -1
- package/dist/index.js +557 -11
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -40,14 +40,17 @@ __export(index_exports, {
|
|
|
40
40
|
MusicClient: () => MusicClient,
|
|
41
41
|
OpenAI: () => OpenAI,
|
|
42
42
|
PaymentError: () => PaymentError,
|
|
43
|
+
PriceClient: () => PriceClient,
|
|
43
44
|
SOLANA_NETWORK: () => SOLANA_NETWORK,
|
|
44
45
|
SOLANA_WALLET_FILE_PATH: () => SOLANA_WALLET_FILE,
|
|
46
|
+
SearchClient: () => SearchClient,
|
|
45
47
|
SolanaLLMClient: () => SolanaLLMClient,
|
|
46
48
|
USDC_BASE: () => USDC_BASE,
|
|
47
49
|
USDC_BASE_CONTRACT: () => USDC_BASE_CONTRACT,
|
|
48
50
|
USDC_SOLANA: () => USDC_SOLANA,
|
|
49
51
|
WALLET_DIR_PATH: () => WALLET_DIR_PATH,
|
|
50
52
|
WALLET_FILE_PATH: () => WALLET_FILE_PATH,
|
|
53
|
+
XClient: () => XClient,
|
|
51
54
|
clearCache: () => clearCache,
|
|
52
55
|
createPaymentPayload: () => createPaymentPayload,
|
|
53
56
|
createSolanaPaymentPayload: () => createSolanaPaymentPayload,
|
|
@@ -1229,7 +1232,10 @@ var LLMClient = class _LLMClient {
|
|
|
1229
1232
|
contextWindow: m.contextWindow ?? m.context_window ?? 0,
|
|
1230
1233
|
maxOutput: m.maxOutput ?? m.max_output ?? 0,
|
|
1231
1234
|
categories: m.categories || [],
|
|
1232
|
-
available: true
|
|
1235
|
+
available: true,
|
|
1236
|
+
billingMode: m.billingMode ?? m.billing_mode,
|
|
1237
|
+
flatPrice: m.flatPrice ?? m.flat_price ?? m.pricing?.flat,
|
|
1238
|
+
hidden: m.hidden
|
|
1233
1239
|
}));
|
|
1234
1240
|
}
|
|
1235
1241
|
/**
|
|
@@ -2054,8 +2060,548 @@ var MusicClient = class {
|
|
|
2054
2060
|
}
|
|
2055
2061
|
};
|
|
2056
2062
|
|
|
2057
|
-
// src/
|
|
2063
|
+
// src/search.ts
|
|
2058
2064
|
var import_accounts5 = require("viem/accounts");
|
|
2065
|
+
var DEFAULT_API_URL4 = "https://blockrun.ai/api";
|
|
2066
|
+
var DEFAULT_TIMEOUT4 = 6e4;
|
|
2067
|
+
var SearchClient = class {
|
|
2068
|
+
account;
|
|
2069
|
+
privateKey;
|
|
2070
|
+
apiUrl;
|
|
2071
|
+
timeout;
|
|
2072
|
+
constructor(options = {}) {
|
|
2073
|
+
const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
|
|
2074
|
+
const privateKey = options.privateKey || envKey;
|
|
2075
|
+
if (!privateKey) {
|
|
2076
|
+
throw new Error(
|
|
2077
|
+
"Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
2080
|
+
validatePrivateKey(privateKey);
|
|
2081
|
+
this.privateKey = privateKey;
|
|
2082
|
+
this.account = (0, import_accounts5.privateKeyToAccount)(privateKey);
|
|
2083
|
+
const apiUrl = options.apiUrl || DEFAULT_API_URL4;
|
|
2084
|
+
validateApiUrl(apiUrl);
|
|
2085
|
+
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
2086
|
+
this.timeout = options.timeout || DEFAULT_TIMEOUT4;
|
|
2087
|
+
}
|
|
2088
|
+
async search(query, options) {
|
|
2089
|
+
if (!query || query.length > 1e3) {
|
|
2090
|
+
throw new Error("query must be 1-1000 characters");
|
|
2091
|
+
}
|
|
2092
|
+
const maxResults = options?.maxResults ?? 10;
|
|
2093
|
+
if (maxResults < 1 || maxResults > 50) {
|
|
2094
|
+
throw new Error("maxResults must be between 1 and 50");
|
|
2095
|
+
}
|
|
2096
|
+
const body = {
|
|
2097
|
+
query,
|
|
2098
|
+
max_results: maxResults
|
|
2099
|
+
};
|
|
2100
|
+
if (options?.sources) body.sources = options.sources;
|
|
2101
|
+
if (options?.fromDate) body.from_date = options.fromDate;
|
|
2102
|
+
if (options?.toDate) body.to_date = options.toDate;
|
|
2103
|
+
return this.requestWithPayment("/v1/search", body);
|
|
2104
|
+
}
|
|
2105
|
+
async requestWithPayment(endpoint, body) {
|
|
2106
|
+
const url = `${this.apiUrl}${endpoint}`;
|
|
2107
|
+
const response = await this.fetchWithTimeout(url, {
|
|
2108
|
+
method: "POST",
|
|
2109
|
+
headers: { "Content-Type": "application/json" },
|
|
2110
|
+
body: JSON.stringify(body)
|
|
2111
|
+
});
|
|
2112
|
+
if (response.status === 402) {
|
|
2113
|
+
return this.handlePaymentAndRetry(url, body, response);
|
|
2114
|
+
}
|
|
2115
|
+
if (!response.ok) {
|
|
2116
|
+
let errorBody;
|
|
2117
|
+
try {
|
|
2118
|
+
errorBody = await response.json();
|
|
2119
|
+
} catch {
|
|
2120
|
+
errorBody = { error: "Request failed" };
|
|
2121
|
+
}
|
|
2122
|
+
throw new APIError(
|
|
2123
|
+
`API error: ${response.status}`,
|
|
2124
|
+
response.status,
|
|
2125
|
+
sanitizeErrorResponse(errorBody)
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
return response.json();
|
|
2129
|
+
}
|
|
2130
|
+
async handlePaymentAndRetry(url, body, response) {
|
|
2131
|
+
let paymentHeader = response.headers.get("payment-required");
|
|
2132
|
+
if (!paymentHeader) {
|
|
2133
|
+
try {
|
|
2134
|
+
const respBody = await response.json();
|
|
2135
|
+
if (respBody.x402 || respBody.accepts) {
|
|
2136
|
+
paymentHeader = btoa(JSON.stringify(respBody));
|
|
2137
|
+
}
|
|
2138
|
+
} catch {
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
if (!paymentHeader) {
|
|
2142
|
+
throw new PaymentError("402 response but no payment requirements found");
|
|
2143
|
+
}
|
|
2144
|
+
const paymentRequired = parsePaymentRequired(paymentHeader);
|
|
2145
|
+
const details = extractPaymentDetails(paymentRequired);
|
|
2146
|
+
const paymentPayload = await createPaymentPayload(
|
|
2147
|
+
this.privateKey,
|
|
2148
|
+
this.account.address,
|
|
2149
|
+
details.recipient,
|
|
2150
|
+
details.amount,
|
|
2151
|
+
details.network || "eip155:8453",
|
|
2152
|
+
{
|
|
2153
|
+
resourceUrl: details.resource?.url || url,
|
|
2154
|
+
resourceDescription: details.resource?.description || "BlockRun Search",
|
|
2155
|
+
maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
|
|
2156
|
+
extra: details.extra
|
|
2157
|
+
}
|
|
2158
|
+
);
|
|
2159
|
+
const retry = await this.fetchWithTimeout(url, {
|
|
2160
|
+
method: "POST",
|
|
2161
|
+
headers: {
|
|
2162
|
+
"Content-Type": "application/json",
|
|
2163
|
+
"PAYMENT-SIGNATURE": paymentPayload
|
|
2164
|
+
},
|
|
2165
|
+
body: JSON.stringify(body)
|
|
2166
|
+
});
|
|
2167
|
+
if (retry.status === 402) {
|
|
2168
|
+
throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
2169
|
+
}
|
|
2170
|
+
if (!retry.ok) {
|
|
2171
|
+
let errorBody;
|
|
2172
|
+
try {
|
|
2173
|
+
errorBody = await retry.json();
|
|
2174
|
+
} catch {
|
|
2175
|
+
errorBody = { error: "Request failed" };
|
|
2176
|
+
}
|
|
2177
|
+
throw new APIError(
|
|
2178
|
+
`API error after payment: ${retry.status}`,
|
|
2179
|
+
retry.status,
|
|
2180
|
+
sanitizeErrorResponse(errorBody)
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
return retry.json();
|
|
2184
|
+
}
|
|
2185
|
+
async fetchWithTimeout(url, init) {
|
|
2186
|
+
const controller = new AbortController();
|
|
2187
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2188
|
+
try {
|
|
2189
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2190
|
+
} finally {
|
|
2191
|
+
clearTimeout(timeoutId);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
getWalletAddress() {
|
|
2195
|
+
return this.account.address;
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
|
|
2199
|
+
// src/x-client.ts
|
|
2200
|
+
var import_accounts6 = require("viem/accounts");
|
|
2201
|
+
var DEFAULT_API_URL5 = "https://blockrun.ai/api";
|
|
2202
|
+
var DEFAULT_TIMEOUT5 = 6e4;
|
|
2203
|
+
var XClient = class {
|
|
2204
|
+
account;
|
|
2205
|
+
privateKey;
|
|
2206
|
+
apiUrl;
|
|
2207
|
+
timeout;
|
|
2208
|
+
constructor(options = {}) {
|
|
2209
|
+
const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
|
|
2210
|
+
const privateKey = options.privateKey || envKey;
|
|
2211
|
+
if (!privateKey) {
|
|
2212
|
+
throw new Error(
|
|
2213
|
+
"Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
validatePrivateKey(privateKey);
|
|
2217
|
+
this.privateKey = privateKey;
|
|
2218
|
+
this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
|
|
2219
|
+
const apiUrl = options.apiUrl || DEFAULT_API_URL5;
|
|
2220
|
+
validateApiUrl(apiUrl);
|
|
2221
|
+
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
2222
|
+
this.timeout = options.timeout || DEFAULT_TIMEOUT5;
|
|
2223
|
+
}
|
|
2224
|
+
// ───────── User endpoints ─────────
|
|
2225
|
+
userLookup(usernames) {
|
|
2226
|
+
return this.post("/v1/x/users/lookup", { usernames });
|
|
2227
|
+
}
|
|
2228
|
+
userInfo(username) {
|
|
2229
|
+
return this.post("/v1/x/users/info", { username });
|
|
2230
|
+
}
|
|
2231
|
+
followers(username, cursor) {
|
|
2232
|
+
const body = { username };
|
|
2233
|
+
if (cursor) body.cursor = cursor;
|
|
2234
|
+
return this.post("/v1/x/users/followers", body);
|
|
2235
|
+
}
|
|
2236
|
+
/** `/v1/x/users/following` (singular path variant). */
|
|
2237
|
+
following(username, cursor) {
|
|
2238
|
+
const body = { username };
|
|
2239
|
+
if (cursor) body.cursor = cursor;
|
|
2240
|
+
return this.post("/v1/x/users/following", body);
|
|
2241
|
+
}
|
|
2242
|
+
/** `/v1/x/users/followings` (plural path variant). */
|
|
2243
|
+
followings(username, cursor) {
|
|
2244
|
+
const body = { username };
|
|
2245
|
+
if (cursor) body.cursor = cursor;
|
|
2246
|
+
return this.post("/v1/x/users/followings", body);
|
|
2247
|
+
}
|
|
2248
|
+
verifiedFollowers(userId, cursor) {
|
|
2249
|
+
const body = { userId };
|
|
2250
|
+
if (cursor) body.cursor = cursor;
|
|
2251
|
+
return this.post(
|
|
2252
|
+
"/v1/x/users/verified-followers",
|
|
2253
|
+
body
|
|
2254
|
+
);
|
|
2255
|
+
}
|
|
2256
|
+
userTweets(options) {
|
|
2257
|
+
if (!options.username && !options.userId) {
|
|
2258
|
+
throw new Error("Either username or userId is required");
|
|
2259
|
+
}
|
|
2260
|
+
const body = {};
|
|
2261
|
+
if (options.username) body.username = options.username;
|
|
2262
|
+
if (options.userId) body.userId = options.userId;
|
|
2263
|
+
if (options.cursor) body.cursor = options.cursor;
|
|
2264
|
+
if (options.includeReplies !== void 0) body.includeReplies = options.includeReplies;
|
|
2265
|
+
return this.post("/v1/x/users/tweets", body);
|
|
2266
|
+
}
|
|
2267
|
+
mentions(username, options) {
|
|
2268
|
+
const body = { username };
|
|
2269
|
+
if (options?.sinceTime) body.sinceTime = options.sinceTime;
|
|
2270
|
+
if (options?.untilTime) body.untilTime = options.untilTime;
|
|
2271
|
+
if (options?.cursor) body.cursor = options.cursor;
|
|
2272
|
+
return this.post("/v1/x/users/mentions", body);
|
|
2273
|
+
}
|
|
2274
|
+
// ───────── Tweet endpoints ─────────
|
|
2275
|
+
tweetLookup(tweetIds) {
|
|
2276
|
+
return this.post("/v1/x/tweets/lookup", {
|
|
2277
|
+
tweet_ids: tweetIds
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
tweetReplies(tweetId, options) {
|
|
2281
|
+
const body = { tweetId };
|
|
2282
|
+
if (options?.cursor) body.cursor = options.cursor;
|
|
2283
|
+
if (options?.queryType) body.queryType = options.queryType;
|
|
2284
|
+
return this.post("/v1/x/tweets/replies", body);
|
|
2285
|
+
}
|
|
2286
|
+
tweetThread(tweetId, cursor) {
|
|
2287
|
+
const body = { tweetId };
|
|
2288
|
+
if (cursor) body.cursor = cursor;
|
|
2289
|
+
return this.post("/v1/x/tweets/thread", body);
|
|
2290
|
+
}
|
|
2291
|
+
// ───────── Search & discovery ─────────
|
|
2292
|
+
search(query, options) {
|
|
2293
|
+
const body = { query };
|
|
2294
|
+
if (options?.queryType) body.queryType = options.queryType;
|
|
2295
|
+
if (options?.cursor) body.cursor = options.cursor;
|
|
2296
|
+
return this.post("/v1/x/search", body);
|
|
2297
|
+
}
|
|
2298
|
+
trending() {
|
|
2299
|
+
return this.post("/v1/x/trending", {});
|
|
2300
|
+
}
|
|
2301
|
+
articlesRising() {
|
|
2302
|
+
return this.post("/v1/x/articles/rising", {});
|
|
2303
|
+
}
|
|
2304
|
+
// ───────── Internals ─────────
|
|
2305
|
+
async post(endpoint, body) {
|
|
2306
|
+
const url = `${this.apiUrl}${endpoint}`;
|
|
2307
|
+
const response = await this.fetchWithTimeout(url, {
|
|
2308
|
+
method: "POST",
|
|
2309
|
+
headers: { "Content-Type": "application/json" },
|
|
2310
|
+
body: JSON.stringify(body)
|
|
2311
|
+
});
|
|
2312
|
+
if (response.status === 402) {
|
|
2313
|
+
return this.payAndRetry(url, body, response);
|
|
2314
|
+
}
|
|
2315
|
+
if (!response.ok) {
|
|
2316
|
+
let errorBody;
|
|
2317
|
+
try {
|
|
2318
|
+
errorBody = await response.json();
|
|
2319
|
+
} catch {
|
|
2320
|
+
errorBody = { error: "Request failed" };
|
|
2321
|
+
}
|
|
2322
|
+
throw new APIError(
|
|
2323
|
+
`API error: ${response.status}`,
|
|
2324
|
+
response.status,
|
|
2325
|
+
sanitizeErrorResponse(errorBody)
|
|
2326
|
+
);
|
|
2327
|
+
}
|
|
2328
|
+
return response.json();
|
|
2329
|
+
}
|
|
2330
|
+
async payAndRetry(url, body, response) {
|
|
2331
|
+
let paymentHeader = response.headers.get("payment-required");
|
|
2332
|
+
if (!paymentHeader) {
|
|
2333
|
+
try {
|
|
2334
|
+
const respBody = await response.json();
|
|
2335
|
+
if (respBody.x402 || respBody.accepts) {
|
|
2336
|
+
paymentHeader = btoa(JSON.stringify(respBody));
|
|
2337
|
+
}
|
|
2338
|
+
} catch {
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
if (!paymentHeader) {
|
|
2342
|
+
throw new PaymentError("402 response but no payment requirements found");
|
|
2343
|
+
}
|
|
2344
|
+
const paymentRequired = parsePaymentRequired(paymentHeader);
|
|
2345
|
+
const details = extractPaymentDetails(paymentRequired);
|
|
2346
|
+
const paymentPayload = await createPaymentPayload(
|
|
2347
|
+
this.privateKey,
|
|
2348
|
+
this.account.address,
|
|
2349
|
+
details.recipient,
|
|
2350
|
+
details.amount,
|
|
2351
|
+
details.network || "eip155:8453",
|
|
2352
|
+
{
|
|
2353
|
+
resourceUrl: details.resource?.url || url,
|
|
2354
|
+
resourceDescription: details.resource?.description || "BlockRun X API",
|
|
2355
|
+
maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
|
|
2356
|
+
extra: details.extra
|
|
2357
|
+
}
|
|
2358
|
+
);
|
|
2359
|
+
const retry = await this.fetchWithTimeout(url, {
|
|
2360
|
+
method: "POST",
|
|
2361
|
+
headers: {
|
|
2362
|
+
"Content-Type": "application/json",
|
|
2363
|
+
"PAYMENT-SIGNATURE": paymentPayload
|
|
2364
|
+
},
|
|
2365
|
+
body: JSON.stringify(body)
|
|
2366
|
+
});
|
|
2367
|
+
if (retry.status === 402) {
|
|
2368
|
+
throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
2369
|
+
}
|
|
2370
|
+
if (!retry.ok) {
|
|
2371
|
+
let errorBody;
|
|
2372
|
+
try {
|
|
2373
|
+
errorBody = await retry.json();
|
|
2374
|
+
} catch {
|
|
2375
|
+
errorBody = { error: "Request failed" };
|
|
2376
|
+
}
|
|
2377
|
+
throw new APIError(
|
|
2378
|
+
`API error after payment: ${retry.status}`,
|
|
2379
|
+
retry.status,
|
|
2380
|
+
sanitizeErrorResponse(errorBody)
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
return retry.json();
|
|
2384
|
+
}
|
|
2385
|
+
async fetchWithTimeout(url, init) {
|
|
2386
|
+
const controller = new AbortController();
|
|
2387
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2388
|
+
try {
|
|
2389
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2390
|
+
} finally {
|
|
2391
|
+
clearTimeout(timeoutId);
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
getWalletAddress() {
|
|
2395
|
+
return this.account.address;
|
|
2396
|
+
}
|
|
2397
|
+
};
|
|
2398
|
+
|
|
2399
|
+
// src/price.ts
|
|
2400
|
+
var import_accounts7 = require("viem/accounts");
|
|
2401
|
+
var DEFAULT_API_URL6 = "https://blockrun.ai/api";
|
|
2402
|
+
var DEFAULT_TIMEOUT6 = 3e4;
|
|
2403
|
+
var PriceClient = class {
|
|
2404
|
+
account = null;
|
|
2405
|
+
privateKey = null;
|
|
2406
|
+
apiUrl;
|
|
2407
|
+
timeout;
|
|
2408
|
+
constructor(options = {}) {
|
|
2409
|
+
const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
|
|
2410
|
+
const privateKey = options.privateKey || envKey;
|
|
2411
|
+
const requireWallet = options.requireWallet ?? true;
|
|
2412
|
+
if (!privateKey && requireWallet) {
|
|
2413
|
+
throw new Error(
|
|
2414
|
+
"Private key required for paid endpoints. Pass privateKey in options, set BLOCKRUN_WALLET_KEY, or pass requireWallet: false for free-only usage."
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
if (privateKey) {
|
|
2418
|
+
validatePrivateKey(privateKey);
|
|
2419
|
+
this.privateKey = privateKey;
|
|
2420
|
+
this.account = (0, import_accounts7.privateKeyToAccount)(privateKey);
|
|
2421
|
+
}
|
|
2422
|
+
const apiUrl = options.apiUrl || DEFAULT_API_URL6;
|
|
2423
|
+
validateApiUrl(apiUrl);
|
|
2424
|
+
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
2425
|
+
this.timeout = options.timeout || DEFAULT_TIMEOUT6;
|
|
2426
|
+
}
|
|
2427
|
+
async price(category, symbol, options) {
|
|
2428
|
+
if (!symbol) throw new Error("symbol is required");
|
|
2429
|
+
const path5 = categoryPath(category, options?.market, "price", symbol);
|
|
2430
|
+
const query = {};
|
|
2431
|
+
if (options?.session) query.session = options.session;
|
|
2432
|
+
const data = await this.getWithPayment(path5, query);
|
|
2433
|
+
return {
|
|
2434
|
+
symbol: data.symbol ?? symbol.toUpperCase(),
|
|
2435
|
+
price: data.price,
|
|
2436
|
+
publishTime: data.publishTime,
|
|
2437
|
+
confidence: data.confidence,
|
|
2438
|
+
feedId: data.feedId,
|
|
2439
|
+
timestamp: data.timestamp,
|
|
2440
|
+
assetType: data.assetType,
|
|
2441
|
+
category: data.category,
|
|
2442
|
+
source: data.source,
|
|
2443
|
+
free: data.free
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
async history(category, symbol, options) {
|
|
2447
|
+
if (!symbol) throw new Error("symbol is required");
|
|
2448
|
+
if (!options.from || options.from <= 0) {
|
|
2449
|
+
throw new Error("history requires options.from (unix seconds)");
|
|
2450
|
+
}
|
|
2451
|
+
const path5 = categoryPath(category, options.market, "history", symbol);
|
|
2452
|
+
const query = {
|
|
2453
|
+
resolution: options.resolution ?? "D",
|
|
2454
|
+
from: String(options.from)
|
|
2455
|
+
};
|
|
2456
|
+
if (options.to) query.to = String(options.to);
|
|
2457
|
+
if (options.session) query.session = options.session;
|
|
2458
|
+
const data = await this.getWithPayment(path5, query);
|
|
2459
|
+
return {
|
|
2460
|
+
symbol: data.symbol ?? symbol.toUpperCase(),
|
|
2461
|
+
resolution: data.resolution ?? (options.resolution ?? "D"),
|
|
2462
|
+
from: data.from,
|
|
2463
|
+
to: data.to,
|
|
2464
|
+
bars: data.bars ?? [],
|
|
2465
|
+
source: data.source,
|
|
2466
|
+
category: data.category
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
async listSymbols(category, options) {
|
|
2470
|
+
const path5 = categoryPath(category, options?.market, "list");
|
|
2471
|
+
const query = {
|
|
2472
|
+
limit: String(options?.limit && options.limit > 0 ? options.limit : 100)
|
|
2473
|
+
};
|
|
2474
|
+
if (options?.query) query.q = options.query;
|
|
2475
|
+
const data = await this.getWithPayment(path5, query);
|
|
2476
|
+
if (Array.isArray(data)) {
|
|
2477
|
+
return { symbols: data, count: data.length };
|
|
2478
|
+
}
|
|
2479
|
+
const obj = data;
|
|
2480
|
+
const symbols = obj.symbols ?? obj.feeds ?? [];
|
|
2481
|
+
return {
|
|
2482
|
+
symbols,
|
|
2483
|
+
count: obj.count ?? symbols.length
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
getWalletAddress() {
|
|
2487
|
+
return this.account?.address ?? null;
|
|
2488
|
+
}
|
|
2489
|
+
async getWithPayment(endpoint, query) {
|
|
2490
|
+
const url = buildUrl(`${this.apiUrl}${endpoint}`, query);
|
|
2491
|
+
const response = await this.fetchWithTimeout(url, { method: "GET" });
|
|
2492
|
+
if (response.status === 402) {
|
|
2493
|
+
if (!this.privateKey || !this.account) {
|
|
2494
|
+
throw new PaymentError(
|
|
2495
|
+
`${endpoint} returned 402 Payment Required but no wallet is configured.`
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2498
|
+
return this.payAndRetry(url, response);
|
|
2499
|
+
}
|
|
2500
|
+
if (!response.ok) {
|
|
2501
|
+
let errorBody;
|
|
2502
|
+
try {
|
|
2503
|
+
errorBody = await response.json();
|
|
2504
|
+
} catch {
|
|
2505
|
+
errorBody = { error: "Request failed" };
|
|
2506
|
+
}
|
|
2507
|
+
throw new APIError(
|
|
2508
|
+
`API error: ${response.status}`,
|
|
2509
|
+
response.status,
|
|
2510
|
+
sanitizeErrorResponse(errorBody)
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
return response.json();
|
|
2514
|
+
}
|
|
2515
|
+
async payAndRetry(url, response) {
|
|
2516
|
+
if (!this.privateKey || !this.account) {
|
|
2517
|
+
throw new PaymentError("Wallet required to sign payment.");
|
|
2518
|
+
}
|
|
2519
|
+
let paymentHeader = response.headers.get("payment-required");
|
|
2520
|
+
if (!paymentHeader) {
|
|
2521
|
+
try {
|
|
2522
|
+
const respBody = await response.json();
|
|
2523
|
+
if (respBody.x402) {
|
|
2524
|
+
paymentHeader = btoa(JSON.stringify(respBody.x402));
|
|
2525
|
+
} else if (respBody.accepts) {
|
|
2526
|
+
paymentHeader = btoa(JSON.stringify(respBody));
|
|
2527
|
+
}
|
|
2528
|
+
} catch {
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
if (!paymentHeader) {
|
|
2532
|
+
throw new PaymentError("402 response but no payment requirements found");
|
|
2533
|
+
}
|
|
2534
|
+
const paymentRequired = parsePaymentRequired(paymentHeader);
|
|
2535
|
+
const details = extractPaymentDetails(paymentRequired);
|
|
2536
|
+
const paymentPayload = await createPaymentPayload(
|
|
2537
|
+
this.privateKey,
|
|
2538
|
+
this.account.address,
|
|
2539
|
+
details.recipient,
|
|
2540
|
+
details.amount,
|
|
2541
|
+
details.network || "eip155:8453",
|
|
2542
|
+
{
|
|
2543
|
+
resourceUrl: details.resource?.url || url,
|
|
2544
|
+
resourceDescription: details.resource?.description || "BlockRun Price Data",
|
|
2545
|
+
maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
|
|
2546
|
+
extra: details.extra
|
|
2547
|
+
}
|
|
2548
|
+
);
|
|
2549
|
+
const retry = await this.fetchWithTimeout(url, {
|
|
2550
|
+
method: "GET",
|
|
2551
|
+
headers: { "PAYMENT-SIGNATURE": paymentPayload }
|
|
2552
|
+
});
|
|
2553
|
+
if (retry.status === 402) {
|
|
2554
|
+
throw new PaymentError("Payment was rejected. Check your wallet balance.");
|
|
2555
|
+
}
|
|
2556
|
+
if (!retry.ok) {
|
|
2557
|
+
let errorBody;
|
|
2558
|
+
try {
|
|
2559
|
+
errorBody = await retry.json();
|
|
2560
|
+
} catch {
|
|
2561
|
+
errorBody = { error: "Request failed" };
|
|
2562
|
+
}
|
|
2563
|
+
throw new APIError(
|
|
2564
|
+
`API error after payment: ${retry.status}`,
|
|
2565
|
+
retry.status,
|
|
2566
|
+
sanitizeErrorResponse(errorBody)
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
return retry.json();
|
|
2570
|
+
}
|
|
2571
|
+
async fetchWithTimeout(url, init) {
|
|
2572
|
+
const controller = new AbortController();
|
|
2573
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2574
|
+
try {
|
|
2575
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2576
|
+
} finally {
|
|
2577
|
+
clearTimeout(timeoutId);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
};
|
|
2581
|
+
function categoryPath(category, market, kind, symbol) {
|
|
2582
|
+
let base;
|
|
2583
|
+
if (category === "stocks") {
|
|
2584
|
+
if (!market) {
|
|
2585
|
+
throw new Error("market is required when category === 'stocks'");
|
|
2586
|
+
}
|
|
2587
|
+
base = `/v1/stocks/${market}`;
|
|
2588
|
+
} else if (["crypto", "fx", "commodity", "usstock"].includes(category)) {
|
|
2589
|
+
base = `/v1/${category}`;
|
|
2590
|
+
} else {
|
|
2591
|
+
throw new Error(`unknown category: ${category}`);
|
|
2592
|
+
}
|
|
2593
|
+
if (!symbol) return `${base}/${kind}`;
|
|
2594
|
+
return `${base}/${kind}/${encodeURIComponent(symbol.toUpperCase())}`;
|
|
2595
|
+
}
|
|
2596
|
+
function buildUrl(base, query) {
|
|
2597
|
+
const params = Object.entries(query);
|
|
2598
|
+
if (params.length === 0) return base;
|
|
2599
|
+
const qs = params.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
2600
|
+
return `${base}?${qs}`;
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// src/wallet.ts
|
|
2604
|
+
var import_accounts8 = require("viem/accounts");
|
|
2059
2605
|
var fs = __toESM(require("fs"), 1);
|
|
2060
2606
|
var path = __toESM(require("path"), 1);
|
|
2061
2607
|
var os = __toESM(require("os"), 1);
|
|
@@ -2064,8 +2610,8 @@ var BASE_CHAIN_ID2 = "8453";
|
|
|
2064
2610
|
var WALLET_DIR = path.join(os.homedir(), ".blockrun");
|
|
2065
2611
|
var WALLET_FILE = path.join(WALLET_DIR, ".session");
|
|
2066
2612
|
function createWallet() {
|
|
2067
|
-
const privateKey = (0,
|
|
2068
|
-
const account = (0,
|
|
2613
|
+
const privateKey = (0, import_accounts8.generatePrivateKey)();
|
|
2614
|
+
const account = (0, import_accounts8.privateKeyToAccount)(privateKey);
|
|
2069
2615
|
return {
|
|
2070
2616
|
address: account.address,
|
|
2071
2617
|
privateKey
|
|
@@ -2121,12 +2667,12 @@ function loadWallet() {
|
|
|
2121
2667
|
function getOrCreateWallet() {
|
|
2122
2668
|
const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
|
|
2123
2669
|
if (envKey) {
|
|
2124
|
-
const account = (0,
|
|
2670
|
+
const account = (0, import_accounts8.privateKeyToAccount)(envKey);
|
|
2125
2671
|
return { address: account.address, privateKey: envKey, isNew: false };
|
|
2126
2672
|
}
|
|
2127
2673
|
const fileKey = loadWallet();
|
|
2128
2674
|
if (fileKey) {
|
|
2129
|
-
const account = (0,
|
|
2675
|
+
const account = (0, import_accounts8.privateKeyToAccount)(fileKey);
|
|
2130
2676
|
return { address: account.address, privateKey: fileKey, isNew: false };
|
|
2131
2677
|
}
|
|
2132
2678
|
const { address, privateKey } = createWallet();
|
|
@@ -2136,11 +2682,11 @@ function getOrCreateWallet() {
|
|
|
2136
2682
|
function getWalletAddress() {
|
|
2137
2683
|
const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
|
|
2138
2684
|
if (envKey) {
|
|
2139
|
-
return (0,
|
|
2685
|
+
return (0, import_accounts8.privateKeyToAccount)(envKey).address;
|
|
2140
2686
|
}
|
|
2141
2687
|
const fileKey = loadWallet();
|
|
2142
2688
|
if (fileKey) {
|
|
2143
|
-
return (0,
|
|
2689
|
+
return (0, import_accounts8.privateKeyToAccount)(fileKey).address;
|
|
2144
2690
|
}
|
|
2145
2691
|
return null;
|
|
2146
2692
|
}
|
|
@@ -2320,7 +2866,7 @@ async function getOrCreateSolanaWallet() {
|
|
|
2320
2866
|
// src/solana-client.ts
|
|
2321
2867
|
var SOLANA_API_URL = "https://sol.blockrun.ai/api";
|
|
2322
2868
|
var DEFAULT_MAX_TOKENS2 = 1024;
|
|
2323
|
-
var
|
|
2869
|
+
var DEFAULT_TIMEOUT7 = 6e4;
|
|
2324
2870
|
var SDK_VERSION2 = "0.3.0";
|
|
2325
2871
|
var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
|
|
2326
2872
|
var SolanaLLMClient = class {
|
|
@@ -2345,7 +2891,7 @@ var SolanaLLMClient = class {
|
|
|
2345
2891
|
validateApiUrl(apiUrl);
|
|
2346
2892
|
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
2347
2893
|
this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
|
|
2348
|
-
this.timeout = options.timeout ||
|
|
2894
|
+
this.timeout = options.timeout || DEFAULT_TIMEOUT7;
|
|
2349
2895
|
}
|
|
2350
2896
|
/** Get Solana wallet address (public key in base58). */
|
|
2351
2897
|
async getWalletAddress() {
|
|
@@ -3290,7 +3836,7 @@ var OpenAI = class {
|
|
|
3290
3836
|
};
|
|
3291
3837
|
|
|
3292
3838
|
// src/anthropic-compat.ts
|
|
3293
|
-
var
|
|
3839
|
+
var import_accounts9 = require("viem/accounts");
|
|
3294
3840
|
var AnthropicClient = class {
|
|
3295
3841
|
_client = null;
|
|
3296
3842
|
_clientPromise = null;
|
|
@@ -3303,7 +3849,7 @@ var AnthropicClient = class {
|
|
|
3303
3849
|
const key = options.privateKey ?? wallet.privateKey;
|
|
3304
3850
|
validatePrivateKey(key);
|
|
3305
3851
|
this._privateKey = key;
|
|
3306
|
-
this._account = (0,
|
|
3852
|
+
this._account = (0, import_accounts9.privateKeyToAccount)(this._privateKey);
|
|
3307
3853
|
const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
|
|
3308
3854
|
validateApiUrl(apiUrl);
|
|
3309
3855
|
this._apiUrl = apiUrl.replace(/\/$/, "");
|
|
@@ -3409,14 +3955,17 @@ var AnthropicClient = class {
|
|
|
3409
3955
|
MusicClient,
|
|
3410
3956
|
OpenAI,
|
|
3411
3957
|
PaymentError,
|
|
3958
|
+
PriceClient,
|
|
3412
3959
|
SOLANA_NETWORK,
|
|
3413
3960
|
SOLANA_WALLET_FILE_PATH,
|
|
3961
|
+
SearchClient,
|
|
3414
3962
|
SolanaLLMClient,
|
|
3415
3963
|
USDC_BASE,
|
|
3416
3964
|
USDC_BASE_CONTRACT,
|
|
3417
3965
|
USDC_SOLANA,
|
|
3418
3966
|
WALLET_DIR_PATH,
|
|
3419
3967
|
WALLET_FILE_PATH,
|
|
3968
|
+
XClient,
|
|
3420
3969
|
clearCache,
|
|
3421
3970
|
createPaymentPayload,
|
|
3422
3971
|
createSolanaPaymentPayload,
|