@blockrun/clawrouter 0.12.24 → 0.12.26

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 CHANGED
@@ -48,6 +48,8 @@ One wallet, 41+ models, zero API keys.
48
48
  | [vs OpenRouter](#-vs-openrouter) | Why ClawRouter wins |
49
49
  | [Support](#-support) | Telegram, X, founders |
50
50
 
51
+ **API Docs:** [Image Generation](docs/image-generation.md) · [Architecture](docs/architecture.md) · [Configuration](docs/configuration.md)
52
+
51
53
  ---
52
54
 
53
55
  ## 🚀 Quick Start
@@ -202,14 +204,17 @@ Request → 402 (price: $0.003) → wallet signs USDC → retry → response
202
204
 
203
205
  USDC stays in your wallet until spent - non-custodial. Price is visible in the 402 header before signing.
204
206
 
205
- **Dual-chain support:** Pay with USDC on **Base (EVM)** or **Solana**. Both wallets are derived from a single BIP-39 mnemonic on first run. Existing EVM-only users can enable Solana with `/wallet setup-solana`.
207
+ **Dual-chain support:** Pay with **USDC** on **Base (EVM)** or **USDC on Solana** — no SOL token accepted. Both wallets are derived from a single BIP-39 mnemonic on first run.
206
208
 
207
209
  ```bash
208
210
  /wallet # Check balance and address (both chains)
209
- /wallet export # Export keys and mnemonic for backup
210
- /wallet solana # Enable Solana payments / switch to Solana
211
- /wallet base # Switch back to Base (EVM)
211
+ /wallet export # Export mnemonic + keys for backup
212
+ /wallet recover # Restore wallet from mnemonic on a new machine
213
+ /wallet solana # Switch to Solana USDC payments
214
+ /wallet base # Switch back to Base (EVM) USDC payments
215
+ /chain solana # Alias for /wallet solana
212
216
  /stats # View usage and savings
217
+ /stats clear # Reset usage statistics
213
218
  ```
214
219
 
215
220
  **Fund your wallet:**
@@ -262,7 +267,7 @@ npx @blockrun/clawrouter doctor
262
267
  This collects diagnostics and sends them to Claude Sonnet for AI-powered analysis:
263
268
 
264
269
  ```
265
- 🩺 BlockRun Doctor v0.10.4
270
+ 🩺 BlockRun Doctor v0.12.24
266
271
 
267
272
  System
268
273
  ✓ OS: darwin arm64
@@ -325,16 +330,17 @@ npm test
325
330
 
326
331
  ## 📚 More Resources
327
332
 
328
- | Resource | Description |
329
- | -------------------------------------------- | ------------------------ |
330
- | [Documentation](https://blockrun.ai/docs) | Full docs |
331
- | [Model Pricing](https://blockrun.ai/models) | All models & prices |
332
- | [Routing Profiles](docs/routing-profiles.md) | ECO/AUTO/PREMIUM details |
333
- | [Architecture](docs/architecture.md) | Technical deep dive |
334
- | [Configuration](docs/configuration.md) | Environment variables |
335
- | [vs OpenRouter](docs/vs-openrouter.md) | Why ClawRouter wins |
336
- | [Features](docs/features.md) | All features |
337
- | [Troubleshooting](docs/troubleshooting.md) | Common issues |
333
+ | Resource | Description |
334
+ | ------------------------------------------------- | ------------------------ |
335
+ | [Documentation](https://blockrun.ai/docs) | Full docs |
336
+ | [Model Pricing](https://blockrun.ai/models) | All models & prices |
337
+ | [Image Generation](docs/image-generation.md) | API examples, 5 models |
338
+ | [Routing Profiles](docs/routing-profiles.md) | ECO/AUTO/PREMIUM details |
339
+ | [Architecture](docs/architecture.md) | Technical deep dive |
340
+ | [Configuration](docs/configuration.md) | Environment variables |
341
+ | [vs OpenRouter](docs/vs-openrouter.md) | Why ClawRouter wins |
342
+ | [Features](docs/features.md) | All features |
343
+ | [Troubleshooting](docs/troubleshooting.md) | Common issues |
338
344
 
339
345
  ---
340
346
 
package/dist/cli.js CHANGED
@@ -3,6 +3,9 @@
3
3
  // src/proxy.ts
4
4
  import { createServer } from "http";
5
5
  import { finished } from "stream";
6
+ import { homedir as homedir4 } from "os";
7
+ import { join as join5 } from "path";
8
+ import { mkdir as mkdir3, writeFile as writeFile2, readFile, stat as fsStat } from "fs/promises";
6
9
  import { createPublicClient as createPublicClient2, http as http2 } from "viem";
7
10
  import { base as base2 } from "viem/chains";
8
11
  import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
@@ -5032,6 +5035,7 @@ ${lines.join("\n")}`;
5032
5035
  // src/proxy.ts
5033
5036
  var BLOCKRUN_API = "https://blockrun.ai/api";
5034
5037
  var BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
5038
+ var IMAGE_DIR = join5(homedir4(), ".openclaw", "blockrun", "images");
5035
5039
  var AUTO_MODEL = "blockrun/auto";
5036
5040
  var ROUTING_PROFILES = /* @__PURE__ */ new Set([
5037
5041
  "blockrun/free",
@@ -5813,6 +5817,81 @@ async function startProxy(options) {
5813
5817
  res.end(JSON.stringify({ object: "list", data: models }));
5814
5818
  return;
5815
5819
  }
5820
+ if (req.url?.startsWith("/images/") && req.method === "GET") {
5821
+ const filename = req.url.slice("/images/".length).split("?")[0].replace(/[^a-zA-Z0-9._-]/g, "");
5822
+ if (!filename) {
5823
+ res.writeHead(400);
5824
+ res.end("Bad request");
5825
+ return;
5826
+ }
5827
+ const filePath = join5(IMAGE_DIR, filename);
5828
+ try {
5829
+ const s = await fsStat(filePath);
5830
+ if (!s.isFile()) throw new Error("not a file");
5831
+ const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
5832
+ const mime = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", webp: "image/webp", gif: "image/gif" };
5833
+ const data = await readFile(filePath);
5834
+ res.writeHead(200, { "Content-Type": mime[ext] ?? "application/octet-stream", "Content-Length": data.length });
5835
+ res.end(data);
5836
+ } catch {
5837
+ res.writeHead(404, { "Content-Type": "application/json" });
5838
+ res.end(JSON.stringify({ error: "Image not found" }));
5839
+ }
5840
+ return;
5841
+ }
5842
+ if (req.url === "/v1/images/generations" && req.method === "POST") {
5843
+ const chunks = [];
5844
+ for await (const chunk of req) {
5845
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
5846
+ }
5847
+ const reqBody = Buffer.concat(chunks);
5848
+ try {
5849
+ const upstream = await payFetch(`${apiBase}/v1/images/generations`, {
5850
+ method: "POST",
5851
+ headers: { "content-type": "application/json", "user-agent": USER_AGENT },
5852
+ body: reqBody
5853
+ });
5854
+ const text = await upstream.text();
5855
+ if (!upstream.ok) {
5856
+ res.writeHead(upstream.status, { "Content-Type": "application/json" });
5857
+ res.end(text);
5858
+ return;
5859
+ }
5860
+ let result;
5861
+ try {
5862
+ result = JSON.parse(text);
5863
+ } catch {
5864
+ res.writeHead(200, { "Content-Type": "application/json" });
5865
+ res.end(text);
5866
+ return;
5867
+ }
5868
+ if (result.data?.length) {
5869
+ await mkdir3(IMAGE_DIR, { recursive: true });
5870
+ const port2 = server.address()?.port ?? 8402;
5871
+ for (const img of result.data) {
5872
+ const m = img.url?.match(/^data:(image\/\w+);base64,(.+)$/);
5873
+ if (m) {
5874
+ const [, mimeType, b64] = m;
5875
+ const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
5876
+ const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
5877
+ await writeFile2(join5(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
5878
+ img.url = `http://localhost:${port2}/images/${filename}`;
5879
+ console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
5880
+ }
5881
+ }
5882
+ }
5883
+ res.writeHead(200, { "Content-Type": "application/json" });
5884
+ res.end(JSON.stringify(result));
5885
+ } catch (err) {
5886
+ const msg = err instanceof Error ? err.message : String(err);
5887
+ console.error(`[ClawRouter] Image generation error: ${msg}`);
5888
+ if (!res.headersSent) {
5889
+ res.writeHead(502, { "Content-Type": "application/json" });
5890
+ res.end(JSON.stringify({ error: "Image generation failed", details: msg }));
5891
+ }
5892
+ }
5893
+ return;
5894
+ }
5816
5895
  if (req.url?.match(/^\/v1\/(?:x|partner)\//)) {
5817
5896
  try {
5818
5897
  await proxyPartnerRequest(req, res, apiBase, payFetch);