@blockrun/clawrouter 0.5.9 → 0.6.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 +42 -457
- package/dist/index.js +103 -72
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/scripts/reinstall.sh +177 -0
- package/scripts/uninstall.sh +135 -0
package/dist/index.js
CHANGED
|
@@ -1996,9 +1996,13 @@ var InsufficientFundsError = class extends Error {
|
|
|
1996
1996
|
requiredUSD;
|
|
1997
1997
|
walletAddress;
|
|
1998
1998
|
constructor(opts) {
|
|
1999
|
-
|
|
2000
|
-
`Insufficient
|
|
2001
|
-
|
|
1999
|
+
const msg = [
|
|
2000
|
+
`Insufficient balance. Current: ${opts.currentBalanceUSD}, Required: ${opts.requiredUSD}`,
|
|
2001
|
+
`Options:`,
|
|
2002
|
+
` 1. Fund wallet: ${opts.walletAddress}`,
|
|
2003
|
+
` 2. Use free model: /model free`
|
|
2004
|
+
].join("\n");
|
|
2005
|
+
super(msg);
|
|
2002
2006
|
this.name = "InsufficientFundsError";
|
|
2003
2007
|
this.currentBalanceUSD = opts.currentBalanceUSD;
|
|
2004
2008
|
this.requiredUSD = opts.requiredUSD;
|
|
@@ -2009,7 +2013,14 @@ var EmptyWalletError = class extends Error {
|
|
|
2009
2013
|
code = "EMPTY_WALLET";
|
|
2010
2014
|
walletAddress;
|
|
2011
2015
|
constructor(walletAddress) {
|
|
2012
|
-
|
|
2016
|
+
const msg = [
|
|
2017
|
+
`No USDC balance.`,
|
|
2018
|
+
`Options:`,
|
|
2019
|
+
` 1. Fund wallet: ${walletAddress}`,
|
|
2020
|
+
` 2. Use free model: /model free`,
|
|
2021
|
+
` 3. Uninstall: bash ~/.openclaw/extensions/clawrouter/scripts/uninstall.sh`
|
|
2022
|
+
].join("\n");
|
|
2023
|
+
super(msg);
|
|
2013
2024
|
this.name = "EmptyWalletError";
|
|
2014
2025
|
this.walletAddress = walletAddress;
|
|
2015
2026
|
}
|
|
@@ -2431,6 +2442,18 @@ function normalizeMessagesForGoogle(messages) {
|
|
|
2431
2442
|
function isGoogleModel(modelId) {
|
|
2432
2443
|
return modelId.startsWith("google/") || modelId.startsWith("gemini");
|
|
2433
2444
|
}
|
|
2445
|
+
function normalizeMessagesForThinking(messages) {
|
|
2446
|
+
if (!messages || messages.length === 0) return messages;
|
|
2447
|
+
let hasChanges = false;
|
|
2448
|
+
const normalized = messages.map((msg) => {
|
|
2449
|
+
if (msg.role === "assistant" && msg.tool_calls && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && msg.reasoning_content === void 0) {
|
|
2450
|
+
hasChanges = true;
|
|
2451
|
+
return { ...msg, reasoning_content: "" };
|
|
2452
|
+
}
|
|
2453
|
+
return msg;
|
|
2454
|
+
});
|
|
2455
|
+
return hasChanges ? normalized : messages;
|
|
2456
|
+
}
|
|
2434
2457
|
var KIMI_BLOCK_RE = /<[||][^<>]*begin[^<>]*[||]>[\s\S]*?<[||][^<>]*end[^<>]*[||]>/gi;
|
|
2435
2458
|
var KIMI_TOKEN_RE = /<[||][^<>]*[||]>/g;
|
|
2436
2459
|
var THINKING_TAG_RE = /<\s*\/?\s*(?:think(?:ing)?|thought|antthinking)\b[^>]*>/gi;
|
|
@@ -2642,6 +2665,9 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
2642
2665
|
if (isGoogleModel(modelId) && Array.isArray(parsed.messages)) {
|
|
2643
2666
|
parsed.messages = normalizeMessagesForGoogle(parsed.messages);
|
|
2644
2667
|
}
|
|
2668
|
+
if (parsed.thinking && Array.isArray(parsed.messages)) {
|
|
2669
|
+
parsed.messages = normalizeMessagesForThinking(parsed.messages);
|
|
2670
|
+
}
|
|
2645
2671
|
requestBody = Buffer.from(JSON.stringify(parsed));
|
|
2646
2672
|
} catch {
|
|
2647
2673
|
}
|
|
@@ -2801,42 +2827,18 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
2801
2827
|
const bufferedCostMicros = estimatedCostMicros * BigInt(Math.ceil(BALANCE_CHECK_BUFFER * 100)) / 100n;
|
|
2802
2828
|
const sufficiency = await balanceMonitor.checkSufficient(bufferedCostMicros);
|
|
2803
2829
|
if (sufficiency.info.isEmpty || !sufficiency.sufficient) {
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
} else {
|
|
2817
|
-
deduplicator.removeInflight(dedupKey);
|
|
2818
|
-
if (sufficiency.info.isEmpty) {
|
|
2819
|
-
const error = new EmptyWalletError(sufficiency.info.walletAddress);
|
|
2820
|
-
options.onInsufficientFunds?.({
|
|
2821
|
-
balanceUSD: sufficiency.info.balanceUSD,
|
|
2822
|
-
requiredUSD: balanceMonitor.formatUSDC(bufferedCostMicros),
|
|
2823
|
-
walletAddress: sufficiency.info.walletAddress
|
|
2824
|
-
});
|
|
2825
|
-
throw error;
|
|
2826
|
-
} else {
|
|
2827
|
-
const error = new InsufficientFundsError({
|
|
2828
|
-
currentBalanceUSD: sufficiency.info.balanceUSD,
|
|
2829
|
-
requiredUSD: balanceMonitor.formatUSDC(bufferedCostMicros),
|
|
2830
|
-
walletAddress: sufficiency.info.walletAddress
|
|
2831
|
-
});
|
|
2832
|
-
options.onInsufficientFunds?.({
|
|
2833
|
-
balanceUSD: sufficiency.info.balanceUSD,
|
|
2834
|
-
requiredUSD: balanceMonitor.formatUSDC(bufferedCostMicros),
|
|
2835
|
-
walletAddress: sufficiency.info.walletAddress
|
|
2836
|
-
});
|
|
2837
|
-
throw error;
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2830
|
+
const originalModel = modelId;
|
|
2831
|
+
console.log(
|
|
2832
|
+
`[ClawRouter] Wallet ${sufficiency.info.isEmpty ? "empty" : "insufficient"} ($${sufficiency.info.balanceUSD}), falling back to free model: ${FREE_MODEL} (requested: ${originalModel})`
|
|
2833
|
+
);
|
|
2834
|
+
modelId = FREE_MODEL;
|
|
2835
|
+
const parsed = JSON.parse(body.toString());
|
|
2836
|
+
parsed.model = FREE_MODEL;
|
|
2837
|
+
body = Buffer.from(JSON.stringify(parsed));
|
|
2838
|
+
options.onLowBalance?.({
|
|
2839
|
+
balanceUSD: sufficiency.info.balanceUSD,
|
|
2840
|
+
walletAddress: sufficiency.info.walletAddress
|
|
2841
|
+
});
|
|
2840
2842
|
} else if (sufficiency.info.isLow) {
|
|
2841
2843
|
options.onLowBalance?.({
|
|
2842
2844
|
balanceUSD: sufficiency.info.balanceUSD,
|
|
@@ -3265,6 +3267,18 @@ function isRetryable(errorOrResponse, config) {
|
|
|
3265
3267
|
}
|
|
3266
3268
|
|
|
3267
3269
|
// src/index.ts
|
|
3270
|
+
async function waitForProxyHealth(port, timeoutMs = 3e3) {
|
|
3271
|
+
const start = Date.now();
|
|
3272
|
+
while (Date.now() - start < timeoutMs) {
|
|
3273
|
+
try {
|
|
3274
|
+
const res = await fetch(`http://127.0.0.1:${port}/health`);
|
|
3275
|
+
if (res.ok) return true;
|
|
3276
|
+
} catch {
|
|
3277
|
+
}
|
|
3278
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
3279
|
+
}
|
|
3280
|
+
return false;
|
|
3281
|
+
}
|
|
3268
3282
|
function isCompletionMode() {
|
|
3269
3283
|
const args = process.argv;
|
|
3270
3284
|
return args.some((arg, i) => arg === "completion" && i >= 1 && i <= 3);
|
|
@@ -3314,20 +3328,34 @@ function injectModelsConfig(logger) {
|
|
|
3314
3328
|
config.agents.defaults.model.primary = "blockrun/auto";
|
|
3315
3329
|
needsWrite = true;
|
|
3316
3330
|
}
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3331
|
+
const KEY_MODEL_ALIASES = [
|
|
3332
|
+
{ id: "auto", alias: "auto" },
|
|
3333
|
+
{ id: "free", alias: "free" },
|
|
3334
|
+
{ id: "sonnet", alias: "sonnet" },
|
|
3335
|
+
{ id: "opus", alias: "opus" },
|
|
3336
|
+
{ id: "haiku", alias: "haiku" },
|
|
3337
|
+
{ id: "grok", alias: "grok" },
|
|
3338
|
+
{ id: "deepseek", alias: "deepseek" },
|
|
3339
|
+
{ id: "kimi", alias: "kimi" },
|
|
3340
|
+
{ id: "gemini", alias: "gemini" },
|
|
3341
|
+
{ id: "flash", alias: "flash" },
|
|
3342
|
+
{ id: "gpt", alias: "gpt" },
|
|
3343
|
+
{ id: "reasoner", alias: "reasoner" }
|
|
3344
|
+
];
|
|
3345
|
+
if (!config.agents) config.agents = {};
|
|
3346
|
+
if (!config.agents.defaults) config.agents.defaults = {};
|
|
3347
|
+
if (!config.agents.defaults.models) config.agents.defaults.models = {};
|
|
3348
|
+
const allowlist = config.agents.defaults.models;
|
|
3349
|
+
for (const m of KEY_MODEL_ALIASES) {
|
|
3350
|
+
const fullId = `blockrun/${m.id}`;
|
|
3351
|
+
if (!allowlist[fullId]) {
|
|
3352
|
+
allowlist[fullId] = { alias: m.alias };
|
|
3353
|
+
needsWrite = true;
|
|
3326
3354
|
}
|
|
3327
3355
|
}
|
|
3328
3356
|
if (needsWrite) {
|
|
3329
3357
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
3330
|
-
logger.info("
|
|
3358
|
+
logger.info("Smart routing enabled (blockrun/auto)");
|
|
3331
3359
|
}
|
|
3332
3360
|
} catch {
|
|
3333
3361
|
}
|
|
@@ -3399,29 +3427,11 @@ async function startProxyInBackground(api) {
|
|
|
3399
3427
|
const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();
|
|
3400
3428
|
if (source === "generated") {
|
|
3401
3429
|
api.logger.info(`Generated new wallet: ${address}`);
|
|
3402
|
-
api.logger.info(`Fund with USDC on Base to start using ClawRouter.`);
|
|
3403
3430
|
} else if (source === "saved") {
|
|
3404
3431
|
api.logger.info(`Using saved wallet: ${address}`);
|
|
3405
3432
|
} else {
|
|
3406
3433
|
api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${address}`);
|
|
3407
3434
|
}
|
|
3408
|
-
const startupMonitor = new BalanceMonitor(address);
|
|
3409
|
-
try {
|
|
3410
|
-
const startupBalance = await startupMonitor.checkBalance();
|
|
3411
|
-
if (startupBalance.isEmpty) {
|
|
3412
|
-
api.logger.warn(`[!] No USDC balance. Fund wallet to use ClawRouter: ${address}`);
|
|
3413
|
-
} else if (startupBalance.isLow) {
|
|
3414
|
-
api.logger.warn(
|
|
3415
|
-
`[!] Low balance: ${startupBalance.balanceUSD} remaining. Fund wallet: ${address}`
|
|
3416
|
-
);
|
|
3417
|
-
} else {
|
|
3418
|
-
api.logger.info(`Wallet balance: ${startupBalance.balanceUSD}`);
|
|
3419
|
-
}
|
|
3420
|
-
} catch (err) {
|
|
3421
|
-
api.logger.warn(
|
|
3422
|
-
`Could not check wallet balance: ${err instanceof Error ? err.message : String(err)}`
|
|
3423
|
-
);
|
|
3424
|
-
}
|
|
3425
3435
|
const routingConfig = api.pluginConfig?.routing;
|
|
3426
3436
|
const proxy = await startProxy({
|
|
3427
3437
|
walletKey,
|
|
@@ -3450,7 +3460,21 @@ async function startProxyInBackground(api) {
|
|
|
3450
3460
|
});
|
|
3451
3461
|
setActiveProxy(proxy);
|
|
3452
3462
|
activeProxyHandle = proxy;
|
|
3453
|
-
api.logger.info(`
|
|
3463
|
+
api.logger.info(`ClawRouter ready \u2014 smart routing enabled`);
|
|
3464
|
+
api.logger.info(`Pricing: Simple ~$0.001 | Code ~$0.01 | Complex ~$0.05 | Free: $0`);
|
|
3465
|
+
const startupMonitor = new BalanceMonitor(address);
|
|
3466
|
+
startupMonitor.checkBalance().then((balance) => {
|
|
3467
|
+
if (balance.isEmpty) {
|
|
3468
|
+
api.logger.info(`Wallet: ${address} | Balance: $0.00`);
|
|
3469
|
+
api.logger.info(`Using FREE model. Fund wallet for premium models.`);
|
|
3470
|
+
} else if (balance.isLow) {
|
|
3471
|
+
api.logger.info(`Wallet: ${address} | Balance: ${balance.balanceUSD} (low)`);
|
|
3472
|
+
} else {
|
|
3473
|
+
api.logger.info(`Wallet: ${address} | Balance: ${balance.balanceUSD}`);
|
|
3474
|
+
}
|
|
3475
|
+
}).catch(() => {
|
|
3476
|
+
api.logger.info(`Wallet: ${address} | Balance: (checking...)`);
|
|
3477
|
+
});
|
|
3454
3478
|
}
|
|
3455
3479
|
async function createStatsCommand() {
|
|
3456
3480
|
return {
|
|
@@ -3556,7 +3580,7 @@ var plugin = {
|
|
|
3556
3580
|
name: "ClawRouter",
|
|
3557
3581
|
description: "Smart LLM router \u2014 30+ models, x402 micropayments, 78% cost savings",
|
|
3558
3582
|
version: VERSION,
|
|
3559
|
-
register(api) {
|
|
3583
|
+
async register(api) {
|
|
3560
3584
|
const isDisabled = process.env.CLAWROUTER_DISABLED === "true" || process.env.CLAWROUTER_DISABLED === "1";
|
|
3561
3585
|
if (isDisabled) {
|
|
3562
3586
|
api.logger.info("ClawRouter disabled (CLAWROUTER_DISABLED=true). Using default routing.");
|
|
@@ -3622,11 +3646,18 @@ var plugin = {
|
|
|
3622
3646
|
}
|
|
3623
3647
|
}
|
|
3624
3648
|
});
|
|
3625
|
-
|
|
3649
|
+
try {
|
|
3650
|
+
await startProxyInBackground(api);
|
|
3651
|
+
const port = getProxyPort();
|
|
3652
|
+
const healthy = await waitForProxyHealth(port);
|
|
3653
|
+
if (!healthy) {
|
|
3654
|
+
api.logger.warn(`Proxy health check timed out, commands may not work immediately`);
|
|
3655
|
+
}
|
|
3656
|
+
} catch (err) {
|
|
3626
3657
|
api.logger.error(
|
|
3627
3658
|
`Failed to start BlockRun proxy: ${err instanceof Error ? err.message : String(err)}`
|
|
3628
3659
|
);
|
|
3629
|
-
}
|
|
3660
|
+
}
|
|
3630
3661
|
}
|
|
3631
3662
|
};
|
|
3632
3663
|
var index_default = plugin;
|