@blockrun/clawrouter 0.9.2 โ†’ 0.9.4

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
@@ -11,7 +11,7 @@ One wallet, 30+ models, zero API keys.
11
11
  [![Node](https://img.shields.io/badge/node-%E2%89%A520-brightgreen.svg)](https://nodejs.org)
12
12
  [![USDC Hackathon Winner](https://img.shields.io/badge/๐Ÿ†_USDC_Hackathon-Agentic_Commerce_Winner-gold)](https://x.com/USDC/status/2021625822294216977)
13
13
 
14
- [Docs](https://blockrun.ai/docs) · [Models](https://blockrun.ai/models) · [Configuration](docs/configuration.md) · [Features](docs/features.md) · [Windows](docs/windows-installation.md) · [Troubleshooting](docs/troubleshooting.md) · [Telegram](https://t.me/blockrunAI) · [X](https://x.com/BlockRunAI)
14
+ [Docs](https://blockrun.ai/docs) · [Models](https://blockrun.ai/models) · [vs OpenRouter](docs/vs-openrouter.md) · [Configuration](docs/configuration.md) · [Features](docs/features.md) · [Troubleshooting](docs/troubleshooting.md) · [Telegram](https://t.me/blockrunAI) · [X](https://x.com/BlockRunAI)
15
15
 
16
16
  **Winner โ€” Agentic Commerce Track** at the [USDC AI Agent Hackathon](https://x.com/USDC/status/2021625822294216977)<br>
17
17
  _The world's first hackathon run entirely by AI agents, powered by USDC_
@@ -58,22 +58,6 @@ openclaw gateway restart
58
58
 
59
59
  Done! Smart routing (`blockrun/auto`) is now your default model.
60
60
 
61
- ### Windows Installation
62
-
63
- โš ๏ธ **Current Status:** Windows installation is temporarily unavailable due to an OpenClaw CLI bug. The issue is with the OpenClaw framework, not ClawRouter itself.
64
-
65
- **๐Ÿ“– Full Windows Guide:** [docs/windows-installation.md](docs/windows-installation.md)
66
-
67
- **Quick Summary:**
68
-
69
- - โœ… ClawRouter code is Windows-compatible
70
- - โŒ OpenClaw CLI has a `spawn EINVAL` bug on Windows
71
- - โœ… Works perfectly on **Linux** and **macOS**
72
- - ๐Ÿ”ง Manual installation workaround available for advanced users
73
- - ๐Ÿงช Full Windows test infrastructure ready ([.github/workflows/test-windows.yml](.github/workflows/test-windows.yml))
74
-
75
- **For advanced users:** See the [complete manual installation guide](docs/windows-installation.md) with step-by-step PowerShell instructions.
76
-
77
61
  ### Routing Profiles
78
62
 
79
63
  Choose your routing strategy with `/model <profile>`:
@@ -357,9 +341,25 @@ They're built for developers. ClawRouter is built for **agents**.
357
341
  | **Auth** | API key (shared secret) | Wallet signature (cryptographic) |
358
342
  | **Payment** | Prepaid balance (custodial) | Per-request (non-custodial) |
359
343
  | **Routing** | Proprietary / closed | Open source, client-side |
344
+ | **Rate limits** | Per-key quotas | None (your wallet, your limits) |
345
+ | **Cost** | $25/M (Opus equivalent) | $2.05/M blended average |
360
346
 
361
347
  Agents shouldn't need a human to paste API keys. They should generate a wallet, receive funds, and pay per request โ€” programmatically.
362
348
 
349
+ ### Real Problems with OpenRouter
350
+
351
+ Based on [50+ OpenClaw issues](https://github.com/openclaw/openclaw/issues?q=openrouter):
352
+
353
+ | Issue | Problem | ClawRouter |
354
+ |-------|---------|------------|
355
+ | [#11202](https://github.com/openclaw/openclaw/issues/11202) | API keys leaked in every LLM prompt | No API keys to leak |
356
+ | [#2373](https://github.com/openclaw/openclaw/issues/2373) | `openrouter/auto` path broken | `blockrun/auto` just works |
357
+ | [#8615](https://github.com/openclaw/openclaw/issues/8615) | Single API key rate limit hell | Non-custodial, no limits |
358
+ | [#2963](https://github.com/openclaw/openclaw/issues/2963) | Tool calling fails silently | Full tool support |
359
+ | [#10687](https://github.com/openclaw/openclaw/issues/10687) | "Unknown model" errors | 30+ models, auto-update |
360
+
361
+ **[Full comparison โ†’](docs/vs-openrouter.md)**
362
+
363
363
  ---
364
364
 
365
365
  ## Troubleshooting
@@ -399,6 +399,17 @@ BLOCKRUN_WALLET_KEY=0x... npx tsx test-e2e.ts
399
399
 
400
400
  ---
401
401
 
402
+ ## Uninstall
403
+
404
+ ```bash
405
+ openclaw plugins uninstall clawrouter
406
+ openclaw gateway restart
407
+ ```
408
+
409
+ Your wallet key remains at `~/.openclaw/blockrun/wallet.key` โ€” back it up before deleting if you have funds.
410
+
411
+ ---
412
+
402
413
  ## Roadmap
403
414
 
404
415
  - [x] Smart routing โ€” 15-dimension weighted scoring, 4-tier model selection
package/dist/cli.js CHANGED
@@ -1989,26 +1989,14 @@ var RequestDeduplicator = class {
1989
1989
  getInflight(key) {
1990
1990
  const entry = this.inflight.get(key);
1991
1991
  if (!entry) return void 0;
1992
- const promise = new Promise((resolve) => {
1993
- entry.waiters.push(
1994
- new Promise((r) => {
1995
- const orig = entry.resolve;
1996
- entry.resolve = (result) => {
1997
- orig(result);
1998
- resolve(result);
1999
- r(result);
2000
- };
2001
- })
2002
- );
1992
+ return new Promise((resolve) => {
1993
+ entry.resolvers.push(resolve);
2003
1994
  });
2004
- return promise;
2005
1995
  }
2006
1996
  /** Mark a request as in-flight. */
2007
1997
  markInflight(key) {
2008
1998
  this.inflight.set(key, {
2009
- resolve: () => {
2010
- },
2011
- waiters: []
1999
+ resolvers: []
2012
2000
  });
2013
2001
  }
2014
2002
  /** Complete an in-flight request โ€” cache result and notify waiters. */
@@ -2018,14 +2006,31 @@ var RequestDeduplicator = class {
2018
2006
  }
2019
2007
  const entry = this.inflight.get(key);
2020
2008
  if (entry) {
2021
- entry.resolve(result);
2009
+ for (const resolve of entry.resolvers) {
2010
+ resolve(result);
2011
+ }
2022
2012
  this.inflight.delete(key);
2023
2013
  }
2024
2014
  this.prune();
2025
2015
  }
2026
- /** Remove an in-flight entry on error (don't cache failures). */
2016
+ /** Remove an in-flight entry on error (don't cache failures).
2017
+ * Also rejects any waiters so they can retry independently. */
2027
2018
  removeInflight(key) {
2028
- this.inflight.delete(key);
2019
+ const entry = this.inflight.get(key);
2020
+ if (entry) {
2021
+ const errorBody = Buffer.from(JSON.stringify({
2022
+ error: { message: "Original request failed, please retry", type: "dedup_origin_failed" }
2023
+ }));
2024
+ for (const resolve of entry.resolvers) {
2025
+ resolve({
2026
+ status: 503,
2027
+ headers: { "content-type": "application/json" },
2028
+ body: errorBody,
2029
+ completedAt: Date.now()
2030
+ });
2031
+ }
2032
+ this.inflight.delete(key);
2033
+ }
2029
2034
  }
2030
2035
  /** Prune expired completed entries. */
2031
2036
  prune() {
@@ -3251,6 +3256,8 @@ var FALLBACK_STATUS_CODES = [
3251
3256
  // Payment required - but from upstream, not x402
3252
3257
  403,
3253
3258
  // Forbidden - provider restrictions
3259
+ 413,
3260
+ // Payload too large - request exceeds model's context limit
3254
3261
  429,
3255
3262
  // Rate limited
3256
3263
  500,
@@ -3884,7 +3891,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3884
3891
  }
3885
3892
  const autoCompress = options.autoCompressRequests ?? true;
3886
3893
  const compressionThreshold = options.compressionThresholdKB ?? 180;
3887
- const sizeLimit = options.maxRequestSizeKB ?? 200;
3888
3894
  const requestSizeKB = Math.ceil(body.length / 1024);
3889
3895
  if (autoCompress && requestSizeKB > compressionThreshold) {
3890
3896
  try {
@@ -3926,21 +3932,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3926
3932
  );
3927
3933
  parsed.messages = compressionResult.messages;
3928
3934
  body = Buffer.from(JSON.stringify(parsed));
3929
- if (compressedSizeKB > sizeLimit) {
3930
- const errorMsg = {
3931
- error: {
3932
- message: `Request size ${compressedSizeKB}KB still exceeds limit after compression (original: ${requestSizeKB}KB). Please reduce context size.`,
3933
- type: "request_too_large",
3934
- original_size_kb: requestSizeKB,
3935
- compressed_size_kb: compressedSizeKB,
3936
- limit_kb: sizeLimit,
3937
- help: "Try: 1) Remove old messages from history, 2) Summarize large tool results, 3) Use direct API for very large contexts"
3938
- }
3939
- };
3940
- res.writeHead(413, { "Content-Type": "application/json" });
3941
- res.end(JSON.stringify(errorMsg));
3942
- return;
3943
- }
3944
3935
  }
3945
3936
  } catch (err) {
3946
3937
  console.warn(
@@ -3948,21 +3939,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3948
3939
  );
3949
3940
  }
3950
3941
  }
3951
- const finalSizeKB = Math.ceil(body.length / 1024);
3952
- if (finalSizeKB > sizeLimit) {
3953
- const errorMsg = {
3954
- error: {
3955
- message: `Request size ${finalSizeKB}KB exceeds limit ${sizeLimit}KB. Please reduce context size.`,
3956
- type: "request_too_large",
3957
- size_kb: finalSizeKB,
3958
- limit_kb: sizeLimit,
3959
- help: "Try: 1) Remove old messages from history, 2) Summarize large tool results, 3) Enable compression (autoCompressRequests: true)"
3960
- }
3961
- };
3962
- res.writeHead(413, { "Content-Type": "application/json" });
3963
- res.end(JSON.stringify(errorMsg));
3964
- return;
3965
- }
3966
3942
  const dedupKey = RequestDeduplicator.hash(body);
3967
3943
  const cached = deduplicator.getCached(dedupKey);
3968
3944
  if (cached) {