@blockrun/clawrouter 0.9.3 โ†’ 0.9.5

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>`:
@@ -351,15 +335,31 @@ Track your savings with `/stats` in any OpenClaw conversation.
351
335
 
352
336
  They're built for developers. ClawRouter is built for **agents**.
353
337
 
354
- | | OpenRouter / LiteLLM | ClawRouter |
355
- | ----------- | --------------------------- | -------------------------------- |
356
- | **Setup** | Human creates account | Agent generates wallet |
357
- | **Auth** | API key (shared secret) | Wallet signature (cryptographic) |
358
- | **Payment** | Prepaid balance (custodial) | Per-request (non-custodial) |
359
- | **Routing** | Proprietary / closed | Open source, client-side |
338
+ | | OpenRouter / LiteLLM | ClawRouter |
339
+ | --------------- | --------------------------- | -------------------------------- |
340
+ | **Setup** | Human creates account | Agent generates wallet |
341
+ | **Auth** | API key (shared secret) | Wallet signature (cryptographic) |
342
+ | **Payment** | Prepaid balance (custodial) | Per-request (non-custodial) |
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
@@ -1388,6 +1388,7 @@ function resolveModelAlias(model) {
1388
1388
  const withoutPrefix = normalized.slice("blockrun/".length);
1389
1389
  const resolvedWithoutPrefix = MODEL_ALIASES[withoutPrefix];
1390
1390
  if (resolvedWithoutPrefix) return resolvedWithoutPrefix;
1391
+ return withoutPrefix;
1391
1392
  }
1392
1393
  return model;
1393
1394
  }
@@ -1989,26 +1990,14 @@ var RequestDeduplicator = class {
1989
1990
  getInflight(key) {
1990
1991
  const entry = this.inflight.get(key);
1991
1992
  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
- );
1993
+ return new Promise((resolve) => {
1994
+ entry.resolvers.push(resolve);
2003
1995
  });
2004
- return promise;
2005
1996
  }
2006
1997
  /** Mark a request as in-flight. */
2007
1998
  markInflight(key) {
2008
1999
  this.inflight.set(key, {
2009
- resolve: () => {
2010
- },
2011
- waiters: []
2000
+ resolvers: []
2012
2001
  });
2013
2002
  }
2014
2003
  /** Complete an in-flight request โ€” cache result and notify waiters. */
@@ -2018,14 +2007,33 @@ var RequestDeduplicator = class {
2018
2007
  }
2019
2008
  const entry = this.inflight.get(key);
2020
2009
  if (entry) {
2021
- entry.resolve(result);
2010
+ for (const resolve of entry.resolvers) {
2011
+ resolve(result);
2012
+ }
2022
2013
  this.inflight.delete(key);
2023
2014
  }
2024
2015
  this.prune();
2025
2016
  }
2026
- /** Remove an in-flight entry on error (don't cache failures). */
2017
+ /** Remove an in-flight entry on error (don't cache failures).
2018
+ * Also rejects any waiters so they can retry independently. */
2027
2019
  removeInflight(key) {
2028
- this.inflight.delete(key);
2020
+ const entry = this.inflight.get(key);
2021
+ if (entry) {
2022
+ const errorBody = Buffer.from(
2023
+ JSON.stringify({
2024
+ error: { message: "Original request failed, please retry", type: "dedup_origin_failed" }
2025
+ })
2026
+ );
2027
+ for (const resolve of entry.resolvers) {
2028
+ resolve({
2029
+ status: 503,
2030
+ headers: { "content-type": "application/json" },
2031
+ body: errorBody,
2032
+ completedAt: Date.now()
2033
+ });
2034
+ }
2035
+ this.inflight.delete(key);
2036
+ }
2029
2037
  }
2030
2038
  /** Prune expired completed entries. */
2031
2039
  prune() {
@@ -3886,7 +3894,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3886
3894
  }
3887
3895
  const autoCompress = options.autoCompressRequests ?? true;
3888
3896
  const compressionThreshold = options.compressionThresholdKB ?? 180;
3889
- const sizeLimit = options.maxRequestSizeKB ?? 200;
3890
3897
  const requestSizeKB = Math.ceil(body.length / 1024);
3891
3898
  if (autoCompress && requestSizeKB > compressionThreshold) {
3892
3899
  try {
@@ -3928,21 +3935,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3928
3935
  );
3929
3936
  parsed.messages = compressionResult.messages;
3930
3937
  body = Buffer.from(JSON.stringify(parsed));
3931
- if (compressedSizeKB > sizeLimit) {
3932
- const errorMsg = {
3933
- error: {
3934
- message: `Request size ${compressedSizeKB}KB still exceeds limit after compression (original: ${requestSizeKB}KB). Please reduce context size.`,
3935
- type: "request_too_large",
3936
- original_size_kb: requestSizeKB,
3937
- compressed_size_kb: compressedSizeKB,
3938
- limit_kb: sizeLimit,
3939
- help: "Try: 1) Remove old messages from history, 2) Summarize large tool results, 3) Use direct API for very large contexts"
3940
- }
3941
- };
3942
- res.writeHead(413, { "Content-Type": "application/json" });
3943
- res.end(JSON.stringify(errorMsg));
3944
- return;
3945
- }
3946
3938
  }
3947
3939
  } catch (err) {
3948
3940
  console.warn(
@@ -3950,21 +3942,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
3950
3942
  );
3951
3943
  }
3952
3944
  }
3953
- const finalSizeKB = Math.ceil(body.length / 1024);
3954
- if (finalSizeKB > sizeLimit) {
3955
- const errorMsg = {
3956
- error: {
3957
- message: `Request size ${finalSizeKB}KB exceeds limit ${sizeLimit}KB. Please reduce context size.`,
3958
- type: "request_too_large",
3959
- size_kb: finalSizeKB,
3960
- limit_kb: sizeLimit,
3961
- help: "Try: 1) Remove old messages from history, 2) Summarize large tool results, 3) Enable compression (autoCompressRequests: true)"
3962
- }
3963
- };
3964
- res.writeHead(413, { "Content-Type": "application/json" });
3965
- res.end(JSON.stringify(errorMsg));
3966
- return;
3967
- }
3968
3945
  const dedupKey = RequestDeduplicator.hash(body);
3969
3946
  const cached = deduplicator.getCached(dedupKey);
3970
3947
  if (cached) {