@blockrun/llm 1.10.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -12
- package/dist/index.cjs +71 -6
- package/dist/index.d.cts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +71 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @blockrun/llm (TypeScript SDK)
|
|
2
2
|
|
|
3
3
|
> **@blockrun/llm** is a TypeScript/Node.js SDK for accessing 41+ large language models (GPT-5, Claude, Gemini, Grok, DeepSeek, Kimi, and more) with automatic pay-per-request USDC micropayments via the x402 protocol. No API keys required — your wallet signature is your authentication. Supports **streaming**, smart routing, Base and Solana chains.
|
|
4
|
+
>
|
|
5
|
+
> 🆓 **Includes 9 fully-free NVIDIA-hosted models** — DeepSeek V4 Pro/Flash (1M context), Nemotron Nano Omni (vision), Qwen3, Llama 4, GLM-4.7, Mistral. Zero USDC, no rate-limit gimmicks. Use `routingProfile: 'free'` or call any `nvidia/*` model directly.
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/@blockrun/llm)
|
|
6
8
|
[](LICENSE)
|
|
@@ -39,6 +41,40 @@ const response = await client.chat('openai/gpt-4o', 'Hello!');
|
|
|
39
41
|
|
|
40
42
|
That's it. The SDK handles x402 payment automatically.
|
|
41
43
|
|
|
44
|
+
### Try It Free (No USDC Required)
|
|
45
|
+
|
|
46
|
+
Want to kick the tires before funding a wallet? Route to BlockRun's free NVIDIA tier:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { LLMClient } from '@blockrun/llm';
|
|
50
|
+
|
|
51
|
+
const client = new LLMClient(); // Wallet still required for signing, but $0 charged
|
|
52
|
+
|
|
53
|
+
// Option 1: call a free model directly
|
|
54
|
+
const reply = await client.chat('nvidia/qwen3-next-80b-a3b-thinking', 'Explain x402 in 1 sentence');
|
|
55
|
+
|
|
56
|
+
// Option 2: let the smart router pick the best free model per request
|
|
57
|
+
const result = await client.smartChat('What is 2+2?', { routingProfile: 'free' });
|
|
58
|
+
console.log(result.model); // e.g. 'nvidia/deepseek-v4-flash' (cheapest capable for SIMPLE tier)
|
|
59
|
+
console.log(result.response); // '4'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Available free models** (input + output both $0, all NVIDIA-hosted, last refreshed 2026-04-28):
|
|
63
|
+
|
|
64
|
+
| Model ID | Context | Best For |
|
|
65
|
+
|----------|---------|----------|
|
|
66
|
+
| `nvidia/deepseek-v4-pro` | 1M | Flagship reasoning — MMLU-Pro 87.5, GPQA 90.1, SWE-bench 80.6, LiveCodeBench 93.5 |
|
|
67
|
+
| `nvidia/deepseek-v4-flash` | 1M | ~5× faster than V4 Pro — chat, summarization, light reasoning (weaker factual recall) |
|
|
68
|
+
| `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning` | 256K | Only vision-capable free model — text + images + video (≤2 min) + audio (≤1 hr) |
|
|
69
|
+
| `nvidia/qwen3-next-80b-a3b-thinking` | 131K | 116 tok/s reasoning with thinking mode |
|
|
70
|
+
| `nvidia/mistral-small-4-119b` | 131K | 114 tok/s — fastest free chat |
|
|
71
|
+
| `nvidia/glm-4.7` | 131K | 237 tok/s — GLM-4.7 with thinking mode |
|
|
72
|
+
| `nvidia/llama-4-maverick` | 131K | Meta Llama 4 Maverick MoE |
|
|
73
|
+
| `nvidia/qwen3-coder-480b` | 131K | Coding-optimised 480B MoE |
|
|
74
|
+
| `nvidia/deepseek-v3.2` | 131K | Legacy V3.2 — auto-upgrades to V4 Pro via fallback |
|
|
75
|
+
|
|
76
|
+
> Note: `nvidia/gpt-oss-120b` and `nvidia/gpt-oss-20b` were retired 2026-04-28 — NVIDIA's free build.nvidia.com tier reserves the right to use prompts/outputs for service improvement, which conflicts with our data-privacy policy.
|
|
77
|
+
|
|
42
78
|
## Quick Start (Solana)
|
|
43
79
|
|
|
44
80
|
```typescript
|
|
@@ -115,7 +151,7 @@ console.log(complex.model); // 'xai/grok-4-1-fast-reasoning'
|
|
|
115
151
|
|
|
116
152
|
| Profile | Description | Best For |
|
|
117
153
|
|---------|-------------|----------|
|
|
118
|
-
| `free` |
|
|
154
|
+
| `free` | NVIDIA free tier — smart-routes across 9 models (DeepSeek V4 Pro/Flash, Nemotron Nano Omni, Qwen3, GLM-4.7, Llama 4, Mistral) | Zero-cost testing, dev, prod |
|
|
119
155
|
| `eco` | Cheapest models per tier (DeepSeek, xAI) | Cost-sensitive production |
|
|
120
156
|
| `auto` | Best balance of cost/quality (default) | General use |
|
|
121
157
|
| `premium` | Top-tier models (OpenAI, Anthropic) | Quality-critical tasks |
|
|
@@ -151,6 +187,13 @@ The classifier runs in <1ms, 100% locally, and routes to one of four tiers:
|
|
|
151
187
|
|
|
152
188
|
## Available Models
|
|
153
189
|
|
|
190
|
+
### OpenAI GPT-5.5 Family
|
|
191
|
+
Released 2026-04-23 — first fully retrained base since GPT-4.5. 1M context, 128K output, native agent + computer use.
|
|
192
|
+
|
|
193
|
+
| Model | Input Price | Output Price |
|
|
194
|
+
|-------|-------------|--------------|
|
|
195
|
+
| `openai/gpt-5.5` | $5.00/M | $30.00/M |
|
|
196
|
+
|
|
154
197
|
### OpenAI GPT-5.4 Family
|
|
155
198
|
| Model | Input Price | Output Price |
|
|
156
199
|
|-------|-------------|--------------|
|
|
@@ -238,20 +281,23 @@ The classifier runs in <1ms, 100% locally, and routes to one of four tiers:
|
|
|
238
281
|
|
|
239
282
|
### NVIDIA (Free) + Moonshot
|
|
240
283
|
|
|
241
|
-
Free tier refreshed 2026-04-
|
|
242
|
-
`
|
|
243
|
-
|
|
284
|
+
Free tier refreshed 2026-04-28: added DeepSeek V4 Pro/Flash and Nemotron Nano
|
|
285
|
+
Omni (vision); retired `nvidia/gpt-oss-120b` / `nvidia/gpt-oss-20b` over data
|
|
286
|
+
privacy (NVIDIA's free build.nvidia.com tier reserves the right to use prompts
|
|
287
|
+
for service improvement, which conflicts with our policy). Backend
|
|
288
|
+
auto-redirects retired IDs to the replacements below.
|
|
244
289
|
|
|
245
290
|
| Model | Input Price | Output Price | Notes |
|
|
246
291
|
|-------|-------------|--------------|-------|
|
|
247
|
-
| `nvidia/
|
|
248
|
-
| `nvidia/
|
|
249
|
-
| `nvidia/
|
|
250
|
-
| `nvidia/
|
|
292
|
+
| `nvidia/deepseek-v4-pro` | **FREE** | **FREE** | 1.6T MoE / 49B active, 1M context — flagship reasoning (MMLU-Pro 87.5, GPQA 90.1) |
|
|
293
|
+
| `nvidia/deepseek-v4-flash` | **FREE** | **FREE** | 284B / 13B active MoE, 1M context — ~5× faster than V4 Pro |
|
|
294
|
+
| `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning` | **FREE** | **FREE** | 31B / 3.2B active MoE, 256K — only vision-capable free model |
|
|
295
|
+
| `nvidia/qwen3-next-80b-a3b-thinking` | **FREE** | **FREE** | 116 tok/s — reasoning flagship with thinking mode |
|
|
296
|
+
| `nvidia/mistral-small-4-119b` | **FREE** | **FREE** | 114 tok/s — fastest free chat |
|
|
297
|
+
| `nvidia/glm-4.7` | **FREE** | **FREE** | 237 tok/s — GLM-4.7 with thinking mode |
|
|
298
|
+
| `nvidia/llama-4-maverick` | **FREE** | **FREE** | Meta Llama 4 Maverick MoE |
|
|
251
299
|
| `nvidia/qwen3-coder-480b` | **FREE** | **FREE** | Coding-optimised 480B MoE |
|
|
252
|
-
| `nvidia/deepseek-v3.2` | **FREE** | **FREE** |
|
|
253
|
-
| `nvidia/gpt-oss-120b` | **FREE** | **FREE** | OpenAI open-weight 120B — 123 tok/s |
|
|
254
|
-
| `nvidia/gpt-oss-20b` | **FREE** | **FREE** | OpenAI open-weight 20B — 155 tok/s |
|
|
300
|
+
| `nvidia/deepseek-v3.2` | **FREE** | **FREE** | Legacy V3.2 — auto-upgrades to V4 Pro via fallback |
|
|
255
301
|
| `moonshot/kimi-k2.5` | $0.60/M | $3.00/M | Direct from Moonshot — replaces `nvidia/kimi-k2.5` |
|
|
256
302
|
|
|
257
303
|
### E2E Verified Models
|
|
@@ -537,7 +583,7 @@ const premium = await client.smartChat('Write a legal brief', { routingProfile:
|
|
|
537
583
|
|
|
538
584
|
| Profile | Description | Best For |
|
|
539
585
|
|---------|-------------|----------|
|
|
540
|
-
| `free` | NVIDIA free models
|
|
586
|
+
| `free` | NVIDIA free tier (9 models, smart-routed) | Zero-cost testing, dev, prod |
|
|
541
587
|
| `eco` | Budget-optimized | Cost-sensitive workloads |
|
|
542
588
|
| `auto` | Intelligent routing (default) | General use |
|
|
543
589
|
| `premium` | Best quality models | Critical tasks |
|
package/dist/index.cjs
CHANGED
|
@@ -1686,6 +1686,8 @@ var DEFAULT_API_URL2 = "https://blockrun.ai/api";
|
|
|
1686
1686
|
var DEFAULT_MODEL = "google/nano-banana";
|
|
1687
1687
|
var DEFAULT_SIZE = "1024x1024";
|
|
1688
1688
|
var DEFAULT_TIMEOUT2 = 2e5;
|
|
1689
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
1690
|
+
var POLL_MAX_DURATION_MS = 3e5;
|
|
1689
1691
|
var ImageClient = class {
|
|
1690
1692
|
account;
|
|
1691
1693
|
privateKey;
|
|
@@ -1870,7 +1872,70 @@ var ImageClient = class {
|
|
|
1870
1872
|
sanitizeErrorResponse(errorBody)
|
|
1871
1873
|
);
|
|
1872
1874
|
}
|
|
1873
|
-
|
|
1875
|
+
const responseBody = await retryResponse.json();
|
|
1876
|
+
if (retryResponse.status === 202 || responseBody.status === "queued" || responseBody.status === "in_progress") {
|
|
1877
|
+
if (!responseBody.poll_url) {
|
|
1878
|
+
throw new APIError(
|
|
1879
|
+
`Server returned ${retryResponse.status} but no poll_url to follow.`,
|
|
1880
|
+
retryResponse.status,
|
|
1881
|
+
sanitizeErrorResponse(responseBody)
|
|
1882
|
+
);
|
|
1883
|
+
}
|
|
1884
|
+
return this.pollImageJob(responseBody.poll_url, paymentPayload);
|
|
1885
|
+
}
|
|
1886
|
+
return responseBody;
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Poll the async image generation endpoint until the job reaches a terminal
|
|
1890
|
+
* state (completed/failed). Used when the POST returns 202 + poll_url for
|
|
1891
|
+
* slow models that exceeded the server's inline window.
|
|
1892
|
+
*
|
|
1893
|
+
* Sends the SAME payment header on every poll — the server binds the payer
|
|
1894
|
+
* to the job id and re-verifies on each call. Settlement happens server-side
|
|
1895
|
+
* on the first poll where status=completed.
|
|
1896
|
+
*/
|
|
1897
|
+
async pollImageJob(pollPath, paymentHeader) {
|
|
1898
|
+
const pollUrl = pollPath.startsWith("/api/") ? `${this.apiUrl.replace(/\/api$/, "")}${pollPath}` : `${this.apiUrl}${pollPath.startsWith("/") ? "" : "/"}${pollPath}`;
|
|
1899
|
+
const startedAt = Date.now();
|
|
1900
|
+
while (Date.now() - startedAt < POLL_MAX_DURATION_MS) {
|
|
1901
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
1902
|
+
const pollResp = await this.fetchWithTimeout(pollUrl, {
|
|
1903
|
+
method: "GET",
|
|
1904
|
+
headers: {
|
|
1905
|
+
"PAYMENT-SIGNATURE": paymentHeader,
|
|
1906
|
+
"X-PAYMENT": paymentHeader
|
|
1907
|
+
}
|
|
1908
|
+
});
|
|
1909
|
+
if (pollResp.status === 202) {
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1912
|
+
const pollBody = await pollResp.json().catch(() => ({}));
|
|
1913
|
+
if (!pollResp.ok) {
|
|
1914
|
+
throw new APIError(
|
|
1915
|
+
`API error during poll: ${pollResp.status}`,
|
|
1916
|
+
pollResp.status,
|
|
1917
|
+
sanitizeErrorResponse(pollBody)
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
if (pollBody.status === "failed") {
|
|
1921
|
+
throw new APIError(
|
|
1922
|
+
pollBody.error || "Upstream image generation failed",
|
|
1923
|
+
200,
|
|
1924
|
+
sanitizeErrorResponse(pollBody)
|
|
1925
|
+
);
|
|
1926
|
+
}
|
|
1927
|
+
if (pollBody.data) {
|
|
1928
|
+
return pollBody;
|
|
1929
|
+
}
|
|
1930
|
+
if (pollBody.status === "queued" || pollBody.status === "in_progress") {
|
|
1931
|
+
continue;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
throw new APIError(
|
|
1935
|
+
`Image generation poll timed out after ${POLL_MAX_DURATION_MS / 1e3}s`,
|
|
1936
|
+
504,
|
|
1937
|
+
{ error: "poll_timeout", poll_url: pollPath }
|
|
1938
|
+
);
|
|
1874
1939
|
}
|
|
1875
1940
|
/**
|
|
1876
1941
|
* Fetch with timeout.
|
|
@@ -2759,13 +2824,13 @@ var path2 = __toESM(require("path"), 1);
|
|
|
2759
2824
|
var os2 = __toESM(require("os"), 1);
|
|
2760
2825
|
var WALLET_DIR2 = path2.join(os2.homedir(), ".blockrun");
|
|
2761
2826
|
var SOLANA_WALLET_FILE = path2.join(WALLET_DIR2, ".solana-session");
|
|
2762
|
-
function createSolanaWallet() {
|
|
2763
|
-
const { Keypair } =
|
|
2764
|
-
const bs58 =
|
|
2827
|
+
async function createSolanaWallet() {
|
|
2828
|
+
const { Keypair } = await import("@solana/web3.js");
|
|
2829
|
+
const bs58 = await import("bs58");
|
|
2765
2830
|
const keypair = Keypair.generate();
|
|
2766
2831
|
return {
|
|
2767
2832
|
address: keypair.publicKey.toBase58(),
|
|
2768
|
-
privateKey: bs58.default
|
|
2833
|
+
privateKey: (bs58.default ?? bs58).encode(keypair.secretKey)
|
|
2769
2834
|
};
|
|
2770
2835
|
}
|
|
2771
2836
|
async function solanaKeyToBytes(privateKey) {
|
|
@@ -2858,7 +2923,7 @@ async function getOrCreateSolanaWallet() {
|
|
|
2858
2923
|
return { privateKey: fileKey, address: address2, isNew: false };
|
|
2859
2924
|
}
|
|
2860
2925
|
}
|
|
2861
|
-
const { address, privateKey } = createSolanaWallet();
|
|
2926
|
+
const { address, privateKey } = await createSolanaWallet();
|
|
2862
2927
|
saveSolanaWallet(privateKey);
|
|
2863
2928
|
return { address, privateKey, isNew: true };
|
|
2864
2929
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1195,6 +1195,16 @@ declare class ImageClient {
|
|
|
1195
1195
|
* Handle 402 response: parse requirements, sign payment, retry.
|
|
1196
1196
|
*/
|
|
1197
1197
|
private handlePaymentAndRetry;
|
|
1198
|
+
/**
|
|
1199
|
+
* Poll the async image generation endpoint until the job reaches a terminal
|
|
1200
|
+
* state (completed/failed). Used when the POST returns 202 + poll_url for
|
|
1201
|
+
* slow models that exceeded the server's inline window.
|
|
1202
|
+
*
|
|
1203
|
+
* Sends the SAME payment header on every poll — the server binds the payer
|
|
1204
|
+
* to the job id and re-verifies on each call. Settlement happens server-side
|
|
1205
|
+
* on the first poll where status=completed.
|
|
1206
|
+
*/
|
|
1207
|
+
private pollImageJob;
|
|
1198
1208
|
/**
|
|
1199
1209
|
* Fetch with timeout.
|
|
1200
1210
|
*/
|
|
@@ -1736,12 +1746,14 @@ interface SolanaWalletInfo {
|
|
|
1736
1746
|
}
|
|
1737
1747
|
/**
|
|
1738
1748
|
* Create a new Solana wallet.
|
|
1739
|
-
* Requires @solana/web3.js (optional dep)
|
|
1749
|
+
* Requires @solana/web3.js (optional dep) — loaded lazily via dynamic
|
|
1750
|
+
* import so callers that never touch Solana don't pay the resolution cost
|
|
1751
|
+
* and ESM consumers don't trip over esbuild's __require shim.
|
|
1740
1752
|
*/
|
|
1741
|
-
declare function createSolanaWallet(): {
|
|
1753
|
+
declare function createSolanaWallet(): Promise<{
|
|
1742
1754
|
address: string;
|
|
1743
1755
|
privateKey: string;
|
|
1744
|
-
}
|
|
1756
|
+
}>;
|
|
1745
1757
|
/**
|
|
1746
1758
|
* Convert a bs58 private key string to Uint8Array (64 bytes).
|
|
1747
1759
|
* Accepts: bs58-encoded 64-byte key (standard Solana format).
|
package/dist/index.d.ts
CHANGED
|
@@ -1195,6 +1195,16 @@ declare class ImageClient {
|
|
|
1195
1195
|
* Handle 402 response: parse requirements, sign payment, retry.
|
|
1196
1196
|
*/
|
|
1197
1197
|
private handlePaymentAndRetry;
|
|
1198
|
+
/**
|
|
1199
|
+
* Poll the async image generation endpoint until the job reaches a terminal
|
|
1200
|
+
* state (completed/failed). Used when the POST returns 202 + poll_url for
|
|
1201
|
+
* slow models that exceeded the server's inline window.
|
|
1202
|
+
*
|
|
1203
|
+
* Sends the SAME payment header on every poll — the server binds the payer
|
|
1204
|
+
* to the job id and re-verifies on each call. Settlement happens server-side
|
|
1205
|
+
* on the first poll where status=completed.
|
|
1206
|
+
*/
|
|
1207
|
+
private pollImageJob;
|
|
1198
1208
|
/**
|
|
1199
1209
|
* Fetch with timeout.
|
|
1200
1210
|
*/
|
|
@@ -1736,12 +1746,14 @@ interface SolanaWalletInfo {
|
|
|
1736
1746
|
}
|
|
1737
1747
|
/**
|
|
1738
1748
|
* Create a new Solana wallet.
|
|
1739
|
-
* Requires @solana/web3.js (optional dep)
|
|
1749
|
+
* Requires @solana/web3.js (optional dep) — loaded lazily via dynamic
|
|
1750
|
+
* import so callers that never touch Solana don't pay the resolution cost
|
|
1751
|
+
* and ESM consumers don't trip over esbuild's __require shim.
|
|
1740
1752
|
*/
|
|
1741
|
-
declare function createSolanaWallet(): {
|
|
1753
|
+
declare function createSolanaWallet(): Promise<{
|
|
1742
1754
|
address: string;
|
|
1743
1755
|
privateKey: string;
|
|
1744
|
-
}
|
|
1756
|
+
}>;
|
|
1745
1757
|
/**
|
|
1746
1758
|
* Convert a bs58 private key string to Uint8Array (64 bytes).
|
|
1747
1759
|
* Accepts: bs58-encoded 64-byte key (standard Solana format).
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/client.ts
|
|
9
2
|
import { privateKeyToAccount } from "viem/accounts";
|
|
10
3
|
|
|
@@ -1597,6 +1590,8 @@ var DEFAULT_API_URL2 = "https://blockrun.ai/api";
|
|
|
1597
1590
|
var DEFAULT_MODEL = "google/nano-banana";
|
|
1598
1591
|
var DEFAULT_SIZE = "1024x1024";
|
|
1599
1592
|
var DEFAULT_TIMEOUT2 = 2e5;
|
|
1593
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
1594
|
+
var POLL_MAX_DURATION_MS = 3e5;
|
|
1600
1595
|
var ImageClient = class {
|
|
1601
1596
|
account;
|
|
1602
1597
|
privateKey;
|
|
@@ -1781,7 +1776,70 @@ var ImageClient = class {
|
|
|
1781
1776
|
sanitizeErrorResponse(errorBody)
|
|
1782
1777
|
);
|
|
1783
1778
|
}
|
|
1784
|
-
|
|
1779
|
+
const responseBody = await retryResponse.json();
|
|
1780
|
+
if (retryResponse.status === 202 || responseBody.status === "queued" || responseBody.status === "in_progress") {
|
|
1781
|
+
if (!responseBody.poll_url) {
|
|
1782
|
+
throw new APIError(
|
|
1783
|
+
`Server returned ${retryResponse.status} but no poll_url to follow.`,
|
|
1784
|
+
retryResponse.status,
|
|
1785
|
+
sanitizeErrorResponse(responseBody)
|
|
1786
|
+
);
|
|
1787
|
+
}
|
|
1788
|
+
return this.pollImageJob(responseBody.poll_url, paymentPayload);
|
|
1789
|
+
}
|
|
1790
|
+
return responseBody;
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Poll the async image generation endpoint until the job reaches a terminal
|
|
1794
|
+
* state (completed/failed). Used when the POST returns 202 + poll_url for
|
|
1795
|
+
* slow models that exceeded the server's inline window.
|
|
1796
|
+
*
|
|
1797
|
+
* Sends the SAME payment header on every poll — the server binds the payer
|
|
1798
|
+
* to the job id and re-verifies on each call. Settlement happens server-side
|
|
1799
|
+
* on the first poll where status=completed.
|
|
1800
|
+
*/
|
|
1801
|
+
async pollImageJob(pollPath, paymentHeader) {
|
|
1802
|
+
const pollUrl = pollPath.startsWith("/api/") ? `${this.apiUrl.replace(/\/api$/, "")}${pollPath}` : `${this.apiUrl}${pollPath.startsWith("/") ? "" : "/"}${pollPath}`;
|
|
1803
|
+
const startedAt = Date.now();
|
|
1804
|
+
while (Date.now() - startedAt < POLL_MAX_DURATION_MS) {
|
|
1805
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
1806
|
+
const pollResp = await this.fetchWithTimeout(pollUrl, {
|
|
1807
|
+
method: "GET",
|
|
1808
|
+
headers: {
|
|
1809
|
+
"PAYMENT-SIGNATURE": paymentHeader,
|
|
1810
|
+
"X-PAYMENT": paymentHeader
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
if (pollResp.status === 202) {
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
const pollBody = await pollResp.json().catch(() => ({}));
|
|
1817
|
+
if (!pollResp.ok) {
|
|
1818
|
+
throw new APIError(
|
|
1819
|
+
`API error during poll: ${pollResp.status}`,
|
|
1820
|
+
pollResp.status,
|
|
1821
|
+
sanitizeErrorResponse(pollBody)
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
if (pollBody.status === "failed") {
|
|
1825
|
+
throw new APIError(
|
|
1826
|
+
pollBody.error || "Upstream image generation failed",
|
|
1827
|
+
200,
|
|
1828
|
+
sanitizeErrorResponse(pollBody)
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1831
|
+
if (pollBody.data) {
|
|
1832
|
+
return pollBody;
|
|
1833
|
+
}
|
|
1834
|
+
if (pollBody.status === "queued" || pollBody.status === "in_progress") {
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
throw new APIError(
|
|
1839
|
+
`Image generation poll timed out after ${POLL_MAX_DURATION_MS / 1e3}s`,
|
|
1840
|
+
504,
|
|
1841
|
+
{ error: "poll_timeout", poll_url: pollPath }
|
|
1842
|
+
);
|
|
1785
1843
|
}
|
|
1786
1844
|
/**
|
|
1787
1845
|
* Fetch with timeout.
|
|
@@ -2670,13 +2728,13 @@ import * as path2 from "path";
|
|
|
2670
2728
|
import * as os2 from "os";
|
|
2671
2729
|
var WALLET_DIR2 = path2.join(os2.homedir(), ".blockrun");
|
|
2672
2730
|
var SOLANA_WALLET_FILE = path2.join(WALLET_DIR2, ".solana-session");
|
|
2673
|
-
function createSolanaWallet() {
|
|
2674
|
-
const { Keypair } =
|
|
2675
|
-
const bs58 =
|
|
2731
|
+
async function createSolanaWallet() {
|
|
2732
|
+
const { Keypair } = await import("@solana/web3.js");
|
|
2733
|
+
const bs58 = await import("bs58");
|
|
2676
2734
|
const keypair = Keypair.generate();
|
|
2677
2735
|
return {
|
|
2678
2736
|
address: keypair.publicKey.toBase58(),
|
|
2679
|
-
privateKey: bs58.default
|
|
2737
|
+
privateKey: (bs58.default ?? bs58).encode(keypair.secretKey)
|
|
2680
2738
|
};
|
|
2681
2739
|
}
|
|
2682
2740
|
async function solanaKeyToBytes(privateKey) {
|
|
@@ -2769,7 +2827,7 @@ async function getOrCreateSolanaWallet() {
|
|
|
2769
2827
|
return { privateKey: fileKey, address: address2, isNew: false };
|
|
2770
2828
|
}
|
|
2771
2829
|
}
|
|
2772
|
-
const { address, privateKey } = createSolanaWallet();
|
|
2830
|
+
const { address, privateKey } = await createSolanaWallet();
|
|
2773
2831
|
saveSolanaWallet(privateKey);
|
|
2774
2832
|
return { address, privateKey, isNew: true };
|
|
2775
2833
|
}
|