@blockrun/clawrouter 0.12.34 → 0.12.36
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/dist/cli.js +182 -132
- package/dist/cli.js.map +1 -1
- package/dist/index.js +173 -145
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,125 +8,6 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/solana-balance.ts
|
|
12
|
-
var solana_balance_exports = {};
|
|
13
|
-
__export(solana_balance_exports, {
|
|
14
|
-
SolanaBalanceMonitor: () => SolanaBalanceMonitor
|
|
15
|
-
});
|
|
16
|
-
import { address as solAddress, createSolanaRpc } from "@solana/kit";
|
|
17
|
-
var SOLANA_USDC_MINT, SOLANA_DEFAULT_RPC, BALANCE_TIMEOUT_MS, CACHE_TTL_MS2, SolanaBalanceMonitor;
|
|
18
|
-
var init_solana_balance = __esm({
|
|
19
|
-
"src/solana-balance.ts"() {
|
|
20
|
-
"use strict";
|
|
21
|
-
SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
22
|
-
SOLANA_DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
|
|
23
|
-
BALANCE_TIMEOUT_MS = 1e4;
|
|
24
|
-
CACHE_TTL_MS2 = 3e4;
|
|
25
|
-
SolanaBalanceMonitor = class {
|
|
26
|
-
rpc;
|
|
27
|
-
walletAddress;
|
|
28
|
-
cachedBalance = null;
|
|
29
|
-
cachedAt = 0;
|
|
30
|
-
constructor(walletAddress, rpcUrl) {
|
|
31
|
-
this.walletAddress = walletAddress;
|
|
32
|
-
const url = rpcUrl || process["env"].CLAWROUTER_SOLANA_RPC_URL || SOLANA_DEFAULT_RPC;
|
|
33
|
-
this.rpc = createSolanaRpc(url);
|
|
34
|
-
}
|
|
35
|
-
async checkBalance() {
|
|
36
|
-
const now = Date.now();
|
|
37
|
-
if (this.cachedBalance !== null && this.cachedBalance > 0n && now - this.cachedAt < CACHE_TTL_MS2) {
|
|
38
|
-
return this.buildInfo(this.cachedBalance);
|
|
39
|
-
}
|
|
40
|
-
const balance = await this.fetchBalance();
|
|
41
|
-
if (balance > 0n) {
|
|
42
|
-
this.cachedBalance = balance;
|
|
43
|
-
this.cachedAt = now;
|
|
44
|
-
}
|
|
45
|
-
return this.buildInfo(balance);
|
|
46
|
-
}
|
|
47
|
-
deductEstimated(amountMicros) {
|
|
48
|
-
if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {
|
|
49
|
-
this.cachedBalance -= amountMicros;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
invalidate() {
|
|
53
|
-
this.cachedBalance = null;
|
|
54
|
-
this.cachedAt = 0;
|
|
55
|
-
}
|
|
56
|
-
async refresh() {
|
|
57
|
-
this.invalidate();
|
|
58
|
-
return this.checkBalance();
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Check if balance is sufficient for an estimated cost.
|
|
62
|
-
*/
|
|
63
|
-
async checkSufficient(estimatedCostMicros) {
|
|
64
|
-
const info = await this.checkBalance();
|
|
65
|
-
if (info.balance >= estimatedCostMicros) {
|
|
66
|
-
return { sufficient: true, info };
|
|
67
|
-
}
|
|
68
|
-
const shortfall = estimatedCostMicros - info.balance;
|
|
69
|
-
return {
|
|
70
|
-
sufficient: false,
|
|
71
|
-
info,
|
|
72
|
-
shortfall: this.formatUSDC(shortfall)
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Format USDC amount (in micros) as "$X.XX".
|
|
77
|
-
*/
|
|
78
|
-
formatUSDC(amountMicros) {
|
|
79
|
-
const dollars = Number(amountMicros) / 1e6;
|
|
80
|
-
return `$${dollars.toFixed(2)}`;
|
|
81
|
-
}
|
|
82
|
-
getWalletAddress() {
|
|
83
|
-
return this.walletAddress;
|
|
84
|
-
}
|
|
85
|
-
async fetchBalance() {
|
|
86
|
-
const owner = solAddress(this.walletAddress);
|
|
87
|
-
const mint = solAddress(SOLANA_USDC_MINT);
|
|
88
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
89
|
-
const result = await this.fetchBalanceOnce(owner, mint);
|
|
90
|
-
if (result > 0n || attempt === 1) return result;
|
|
91
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
92
|
-
}
|
|
93
|
-
return 0n;
|
|
94
|
-
}
|
|
95
|
-
async fetchBalanceOnce(owner, mint) {
|
|
96
|
-
const controller = new AbortController();
|
|
97
|
-
const timer = setTimeout(() => controller.abort(), BALANCE_TIMEOUT_MS);
|
|
98
|
-
try {
|
|
99
|
-
const response = await this.rpc.getTokenAccountsByOwner(owner, { mint }, { encoding: "jsonParsed" }).send({ abortSignal: controller.signal });
|
|
100
|
-
if (response.value.length === 0) return 0n;
|
|
101
|
-
let total = 0n;
|
|
102
|
-
for (const account of response.value) {
|
|
103
|
-
const parsed = account.account.data;
|
|
104
|
-
total += BigInt(parsed.parsed.info.tokenAmount.amount);
|
|
105
|
-
}
|
|
106
|
-
return total;
|
|
107
|
-
} catch (err) {
|
|
108
|
-
throw new Error(
|
|
109
|
-
`Failed to fetch Solana USDC balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
110
|
-
{ cause: err }
|
|
111
|
-
);
|
|
112
|
-
} finally {
|
|
113
|
-
clearTimeout(timer);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
buildInfo(balance) {
|
|
117
|
-
const dollars = Number(balance) / 1e6;
|
|
118
|
-
return {
|
|
119
|
-
balance,
|
|
120
|
-
balanceUSD: `$${dollars.toFixed(2)}`,
|
|
121
|
-
isLow: balance < 1000000n,
|
|
122
|
-
isEmpty: balance < 100n,
|
|
123
|
-
walletAddress: this.walletAddress
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
11
|
// node_modules/@noble/hashes/esm/utils.js
|
|
131
12
|
function isBytes(a) {
|
|
132
13
|
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
@@ -737,6 +618,125 @@ var init_wallet = __esm({
|
|
|
737
618
|
}
|
|
738
619
|
});
|
|
739
620
|
|
|
621
|
+
// src/solana-balance.ts
|
|
622
|
+
var solana_balance_exports = {};
|
|
623
|
+
__export(solana_balance_exports, {
|
|
624
|
+
SolanaBalanceMonitor: () => SolanaBalanceMonitor
|
|
625
|
+
});
|
|
626
|
+
import { address as solAddress, createSolanaRpc } from "@solana/kit";
|
|
627
|
+
var SOLANA_USDC_MINT, SOLANA_DEFAULT_RPC, BALANCE_TIMEOUT_MS, CACHE_TTL_MS2, SolanaBalanceMonitor;
|
|
628
|
+
var init_solana_balance = __esm({
|
|
629
|
+
"src/solana-balance.ts"() {
|
|
630
|
+
"use strict";
|
|
631
|
+
SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
632
|
+
SOLANA_DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
|
|
633
|
+
BALANCE_TIMEOUT_MS = 1e4;
|
|
634
|
+
CACHE_TTL_MS2 = 3e4;
|
|
635
|
+
SolanaBalanceMonitor = class {
|
|
636
|
+
rpc;
|
|
637
|
+
walletAddress;
|
|
638
|
+
cachedBalance = null;
|
|
639
|
+
cachedAt = 0;
|
|
640
|
+
constructor(walletAddress, rpcUrl) {
|
|
641
|
+
this.walletAddress = walletAddress;
|
|
642
|
+
const url = rpcUrl || process["env"].CLAWROUTER_SOLANA_RPC_URL || SOLANA_DEFAULT_RPC;
|
|
643
|
+
this.rpc = createSolanaRpc(url);
|
|
644
|
+
}
|
|
645
|
+
async checkBalance() {
|
|
646
|
+
const now = Date.now();
|
|
647
|
+
if (this.cachedBalance !== null && this.cachedBalance > 0n && now - this.cachedAt < CACHE_TTL_MS2) {
|
|
648
|
+
return this.buildInfo(this.cachedBalance);
|
|
649
|
+
}
|
|
650
|
+
const balance = await this.fetchBalance();
|
|
651
|
+
if (balance > 0n) {
|
|
652
|
+
this.cachedBalance = balance;
|
|
653
|
+
this.cachedAt = now;
|
|
654
|
+
}
|
|
655
|
+
return this.buildInfo(balance);
|
|
656
|
+
}
|
|
657
|
+
deductEstimated(amountMicros) {
|
|
658
|
+
if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {
|
|
659
|
+
this.cachedBalance -= amountMicros;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
invalidate() {
|
|
663
|
+
this.cachedBalance = null;
|
|
664
|
+
this.cachedAt = 0;
|
|
665
|
+
}
|
|
666
|
+
async refresh() {
|
|
667
|
+
this.invalidate();
|
|
668
|
+
return this.checkBalance();
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Check if balance is sufficient for an estimated cost.
|
|
672
|
+
*/
|
|
673
|
+
async checkSufficient(estimatedCostMicros) {
|
|
674
|
+
const info = await this.checkBalance();
|
|
675
|
+
if (info.balance >= estimatedCostMicros) {
|
|
676
|
+
return { sufficient: true, info };
|
|
677
|
+
}
|
|
678
|
+
const shortfall = estimatedCostMicros - info.balance;
|
|
679
|
+
return {
|
|
680
|
+
sufficient: false,
|
|
681
|
+
info,
|
|
682
|
+
shortfall: this.formatUSDC(shortfall)
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Format USDC amount (in micros) as "$X.XX".
|
|
687
|
+
*/
|
|
688
|
+
formatUSDC(amountMicros) {
|
|
689
|
+
const dollars = Number(amountMicros) / 1e6;
|
|
690
|
+
return `$${dollars.toFixed(2)}`;
|
|
691
|
+
}
|
|
692
|
+
getWalletAddress() {
|
|
693
|
+
return this.walletAddress;
|
|
694
|
+
}
|
|
695
|
+
async fetchBalance() {
|
|
696
|
+
const owner = solAddress(this.walletAddress);
|
|
697
|
+
const mint = solAddress(SOLANA_USDC_MINT);
|
|
698
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
699
|
+
const result = await this.fetchBalanceOnce(owner, mint);
|
|
700
|
+
if (result > 0n || attempt === 1) return result;
|
|
701
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
702
|
+
}
|
|
703
|
+
return 0n;
|
|
704
|
+
}
|
|
705
|
+
async fetchBalanceOnce(owner, mint) {
|
|
706
|
+
const controller = new AbortController();
|
|
707
|
+
const timer = setTimeout(() => controller.abort(), BALANCE_TIMEOUT_MS);
|
|
708
|
+
try {
|
|
709
|
+
const response = await this.rpc.getTokenAccountsByOwner(owner, { mint }, { encoding: "jsonParsed" }).send({ abortSignal: controller.signal });
|
|
710
|
+
if (response.value.length === 0) return 0n;
|
|
711
|
+
let total = 0n;
|
|
712
|
+
for (const account of response.value) {
|
|
713
|
+
const parsed = account.account.data;
|
|
714
|
+
total += BigInt(parsed.parsed.info.tokenAmount.amount);
|
|
715
|
+
}
|
|
716
|
+
return total;
|
|
717
|
+
} catch (err) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`Failed to fetch Solana USDC balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
720
|
+
{ cause: err }
|
|
721
|
+
);
|
|
722
|
+
} finally {
|
|
723
|
+
clearTimeout(timer);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
buildInfo(balance) {
|
|
727
|
+
const dollars = Number(balance) / 1e6;
|
|
728
|
+
return {
|
|
729
|
+
balance,
|
|
730
|
+
balanceUSD: `$${dollars.toFixed(2)}`,
|
|
731
|
+
isLow: balance < 1000000n,
|
|
732
|
+
isEmpty: balance < 100n,
|
|
733
|
+
walletAddress: this.walletAddress
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
|
|
740
740
|
// src/solana-sweep.ts
|
|
741
741
|
var solana_sweep_exports = {};
|
|
742
742
|
__export(solana_sweep_exports, {
|
|
@@ -1009,7 +1009,8 @@ var MODEL_ALIASES = {
|
|
|
1009
1009
|
// xAI
|
|
1010
1010
|
grok: "xai/grok-3",
|
|
1011
1011
|
"grok-fast": "xai/grok-4-fast-reasoning",
|
|
1012
|
-
"grok-code": "
|
|
1012
|
+
"grok-code": "deepseek/deepseek-chat",
|
|
1013
|
+
// was grok-code-fast-1, delisted due to poor retention
|
|
1013
1014
|
// NVIDIA
|
|
1014
1015
|
nvidia: "nvidia/gpt-oss-120b",
|
|
1015
1016
|
"gpt-120b": "nvidia/gpt-oss-120b",
|
|
@@ -1477,18 +1478,8 @@ var BLOCKRUN_MODELS = [
|
|
|
1477
1478
|
maxOutput: 16384,
|
|
1478
1479
|
toolCalling: true
|
|
1479
1480
|
},
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
name: "Grok Code Fast",
|
|
1483
|
-
version: "1",
|
|
1484
|
-
inputPrice: 0.2,
|
|
1485
|
-
outputPrice: 1.5,
|
|
1486
|
-
contextWindow: 131072,
|
|
1487
|
-
maxOutput: 16384
|
|
1488
|
-
// toolCalling intentionally omitted: outputs tool calls as plain text JSON,
|
|
1489
|
-
// not OpenAI-compatible structured function calls. Will be skipped when
|
|
1490
|
-
// request has tools to prevent the "talking to itself" bug.
|
|
1491
|
-
},
|
|
1481
|
+
// xai/grok-code-fast-1 delisted 2026-03-12: poor retention (coding users churn),
|
|
1482
|
+
// no structured tool calling, alias "grok-code" redirected to deepseek-chat
|
|
1492
1483
|
{
|
|
1493
1484
|
id: "xai/grok-4-0709",
|
|
1494
1485
|
name: "Grok 4 (0709)",
|
|
@@ -3063,11 +3054,14 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
3063
3054
|
primary: "moonshot/kimi-k2.5",
|
|
3064
3055
|
// $0.60/$3.00 - best quality/price for simple tasks
|
|
3065
3056
|
fallback: [
|
|
3057
|
+
"google/gemini-2.5-flash",
|
|
3058
|
+
// 60% retention (best), fast growth (+800%)
|
|
3066
3059
|
"google/gemini-2.5-flash-lite",
|
|
3067
3060
|
// 1M context, ultra cheap ($0.10/$0.40)
|
|
3068
|
-
"
|
|
3061
|
+
"deepseek/deepseek-chat",
|
|
3062
|
+
// 41% retention
|
|
3063
|
+
"nvidia/gpt-oss-120b"
|
|
3069
3064
|
// FREE fallback
|
|
3070
|
-
"deepseek/deepseek-chat"
|
|
3071
3065
|
]
|
|
3072
3066
|
},
|
|
3073
3067
|
MEDIUM: {
|
|
@@ -3075,6 +3069,9 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
3075
3069
|
// $0.50/$2.40 - strong tool use, proper function call format
|
|
3076
3070
|
fallback: [
|
|
3077
3071
|
"deepseek/deepseek-chat",
|
|
3072
|
+
// 41% retention
|
|
3073
|
+
"google/gemini-2.5-flash",
|
|
3074
|
+
// 60% retention, cheap fast model
|
|
3078
3075
|
"google/gemini-2.5-flash-lite",
|
|
3079
3076
|
// 1M context, ultra cheap ($0.10/$0.40)
|
|
3080
3077
|
"xai/grok-4-1-fast-non-reasoning"
|
|
@@ -3085,6 +3082,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
3085
3082
|
primary: "google/gemini-3.1-pro",
|
|
3086
3083
|
// Newest Gemini 3.1 - upgraded from 3.0
|
|
3087
3084
|
fallback: [
|
|
3085
|
+
"google/gemini-2.5-flash",
|
|
3086
|
+
// 60% retention, cheap failsafe before expensive models
|
|
3088
3087
|
"google/gemini-2.5-flash-lite",
|
|
3089
3088
|
// CRITICAL: 1M context, ultra-cheap failsafe ($0.10/$0.40)
|
|
3090
3089
|
"google/gemini-3-pro-preview",
|
|
@@ -3115,12 +3114,12 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
3115
3114
|
SIMPLE: {
|
|
3116
3115
|
primary: "nvidia/gpt-oss-120b",
|
|
3117
3116
|
// FREE! $0.00/$0.00
|
|
3118
|
-
fallback: ["google/gemini-2.5-flash-lite", "deepseek/deepseek-chat"]
|
|
3117
|
+
fallback: ["google/gemini-2.5-flash-lite", "google/gemini-2.5-flash", "deepseek/deepseek-chat"]
|
|
3119
3118
|
},
|
|
3120
3119
|
MEDIUM: {
|
|
3121
3120
|
primary: "google/gemini-2.5-flash-lite",
|
|
3122
3121
|
// $0.10/$0.40 - cheapest capable with 1M context
|
|
3123
|
-
fallback: ["deepseek/deepseek-chat", "nvidia/gpt-oss-120b"]
|
|
3122
|
+
fallback: ["google/gemini-2.5-flash", "deepseek/deepseek-chat", "nvidia/gpt-oss-120b"]
|
|
3124
3123
|
},
|
|
3125
3124
|
COMPLEX: {
|
|
3126
3125
|
primary: "google/gemini-2.5-flash-lite",
|
|
@@ -3140,6 +3139,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
3140
3139
|
primary: "moonshot/kimi-k2.5",
|
|
3141
3140
|
// $0.60/$3.00 - good for simple coding
|
|
3142
3141
|
fallback: [
|
|
3142
|
+
"google/gemini-2.5-flash",
|
|
3143
|
+
// 60% retention, fast growth
|
|
3143
3144
|
"anthropic/claude-haiku-4.5",
|
|
3144
3145
|
"google/gemini-2.5-flash-lite",
|
|
3145
3146
|
"deepseek/deepseek-chat"
|
|
@@ -3150,6 +3151,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
3150
3151
|
// $2.50/$10 - strong coding for medium tasks
|
|
3151
3152
|
fallback: [
|
|
3152
3153
|
"moonshot/kimi-k2.5",
|
|
3154
|
+
"google/gemini-2.5-flash",
|
|
3155
|
+
// 60% retention, good coding capability
|
|
3153
3156
|
"google/gemini-2.5-pro",
|
|
3154
3157
|
"xai/grok-4-0709",
|
|
3155
3158
|
"anthropic/claude-sonnet-4.6"
|
|
@@ -4089,9 +4092,6 @@ var BalanceMonitor = class {
|
|
|
4089
4092
|
}
|
|
4090
4093
|
};
|
|
4091
4094
|
|
|
4092
|
-
// src/proxy.ts
|
|
4093
|
-
init_solana_balance();
|
|
4094
|
-
|
|
4095
4095
|
// src/auth.ts
|
|
4096
4096
|
import { writeFile, mkdir as mkdir2 } from "fs/promises";
|
|
4097
4097
|
init_wallet();
|
|
@@ -5454,6 +5454,7 @@ var ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
|
5454
5454
|
"premium"
|
|
5455
5455
|
]);
|
|
5456
5456
|
var FREE_MODEL = "nvidia/gpt-oss-120b";
|
|
5457
|
+
var freeRequestCount = 0;
|
|
5457
5458
|
var MAX_MESSAGES = 200;
|
|
5458
5459
|
var CONTEXT_LIMIT_KB = 5120;
|
|
5459
5460
|
var HEARTBEAT_INTERVAL_MS = 2e3;
|
|
@@ -6155,7 +6156,13 @@ async function startProxy(options) {
|
|
|
6155
6156
|
const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
|
|
6156
6157
|
reuseSolanaAddress = solanaSigner.address;
|
|
6157
6158
|
}
|
|
6158
|
-
|
|
6159
|
+
let balanceMonitor2;
|
|
6160
|
+
if (paymentChain === "solana" && reuseSolanaAddress) {
|
|
6161
|
+
const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
|
|
6162
|
+
balanceMonitor2 = new SolanaBalanceMonitor2(reuseSolanaAddress);
|
|
6163
|
+
} else {
|
|
6164
|
+
balanceMonitor2 = new BalanceMonitor(account2.address);
|
|
6165
|
+
}
|
|
6159
6166
|
options.onReady?.(listenPort);
|
|
6160
6167
|
return {
|
|
6161
6168
|
port: listenPort,
|
|
@@ -6189,7 +6196,13 @@ async function startProxy(options) {
|
|
|
6189
6196
|
const payFetch = createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
6190
6197
|
skipPreAuth: paymentChain === "solana"
|
|
6191
6198
|
});
|
|
6192
|
-
|
|
6199
|
+
let balanceMonitor;
|
|
6200
|
+
if (paymentChain === "solana" && solanaAddress) {
|
|
6201
|
+
const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
|
|
6202
|
+
balanceMonitor = new SolanaBalanceMonitor2(solanaAddress);
|
|
6203
|
+
} else {
|
|
6204
|
+
balanceMonitor = new BalanceMonitor(account.address);
|
|
6205
|
+
}
|
|
6193
6206
|
const routingConfig = mergeRoutingConfig(options.routingConfig);
|
|
6194
6207
|
const modelPricing = buildModelPricing();
|
|
6195
6208
|
const routerOpts = {
|
|
@@ -6409,11 +6422,14 @@ async function startProxy(options) {
|
|
|
6409
6422
|
if (val.startsWith("data:")) {
|
|
6410
6423
|
} else if (val.startsWith("https://") || val.startsWith("http://")) {
|
|
6411
6424
|
const imgResp = await fetch(val);
|
|
6412
|
-
if (!imgResp.ok)
|
|
6425
|
+
if (!imgResp.ok)
|
|
6426
|
+
throw new Error(`Failed to download ${field} from ${val}: HTTP ${imgResp.status}`);
|
|
6413
6427
|
const contentType = imgResp.headers.get("content-type") ?? "image/png";
|
|
6414
6428
|
const buf = Buffer.from(await imgResp.arrayBuffer());
|
|
6415
6429
|
parsed[field] = `data:${contentType};base64,${buf.toString("base64")}`;
|
|
6416
|
-
console.log(
|
|
6430
|
+
console.log(
|
|
6431
|
+
`[ClawRouter] img2img: downloaded ${field} URL \u2192 data URI (${buf.length} bytes)`
|
|
6432
|
+
);
|
|
6417
6433
|
} else {
|
|
6418
6434
|
parsed[field] = readImageFileAsDataUri(val);
|
|
6419
6435
|
console.log(`[ClawRouter] img2img: read ${field} file \u2192 data URI`);
|
|
@@ -6770,6 +6786,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6770
6786
|
let modelId = "";
|
|
6771
6787
|
let maxTokens = 4096;
|
|
6772
6788
|
let routingProfile = null;
|
|
6789
|
+
let balanceFallbackNotice;
|
|
6773
6790
|
let accumulatedContent = "";
|
|
6774
6791
|
let responseInputTokens;
|
|
6775
6792
|
const isChatCompletion = req.url?.includes("/chat/completions");
|
|
@@ -7305,6 +7322,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7305
7322
|
parsed.model = freeModel;
|
|
7306
7323
|
modelId = freeModel;
|
|
7307
7324
|
bodyModified = true;
|
|
7325
|
+
freeRequestCount++;
|
|
7326
|
+
if (freeRequestCount % 5 === 0) {
|
|
7327
|
+
balanceFallbackNotice = `> **\u{1F4A1} Tip:** Not satisfied with free model quality? Fund your wallet to unlock deepseek-chat, gemini-flash, and 30+ premium models \u2014 starting at $0.001/request.
|
|
7328
|
+
|
|
7329
|
+
`;
|
|
7330
|
+
}
|
|
7308
7331
|
await logUsage({
|
|
7309
7332
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7310
7333
|
model: freeModel,
|
|
@@ -7521,7 +7544,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7521
7544
|
}
|
|
7522
7545
|
deduplicator.markInflight(dedupKey);
|
|
7523
7546
|
let estimatedCostMicros;
|
|
7524
|
-
let balanceFallbackNotice;
|
|
7525
7547
|
const isFreeModel = modelId === FREE_MODEL;
|
|
7526
7548
|
if (modelId && !options.skipBalanceCheck && !isFreeModel) {
|
|
7527
7549
|
const estimated = estimateAmount(modelId, body.length, maxTokens);
|
|
@@ -7543,6 +7565,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7543
7565
|
` : `> **\u26A0\uFE0F Insufficient balance** (${sufficiency.info.balanceUSD}) \u2014 using free model instead of ${originalModel}.
|
|
7544
7566
|
|
|
7545
7567
|
`;
|
|
7568
|
+
freeRequestCount++;
|
|
7569
|
+
if (freeRequestCount % 5 === 0) {
|
|
7570
|
+
balanceFallbackNotice = `> **\u{1F4A1} Tip:** Not satisfied with free model quality? Fund your wallet to unlock deepseek-chat, gemini-flash, and 30+ premium models \u2014 starting at $0.001/request.
|
|
7571
|
+
|
|
7572
|
+
`;
|
|
7573
|
+
}
|
|
7546
7574
|
options.onLowBalance?.({
|
|
7547
7575
|
balanceUSD: sufficiency.info.balanceUSD,
|
|
7548
7576
|
walletAddress: sufficiency.info.walletAddress
|
|
@@ -8081,7 +8109,7 @@ var PARTNER_SERVICES = [
|
|
|
8081
8109
|
id: "x_users_lookup",
|
|
8082
8110
|
name: "Twitter/X User Lookup",
|
|
8083
8111
|
partner: "AttentionVC",
|
|
8084
|
-
description: "
|
|
8112
|
+
description: "Look up real-time Twitter/X user profiles by username. Call this ONLY when the user explicitly asks to look up, check, or get information about a specific Twitter/X user's profile (follower count, bio, verification status, etc.). Do NOT call this for messages that merely contain x.com or twitter.com URLs \u2014 only invoke when the user is asking for profile information about a specific account. Returns: follower count, verification badge, bio, location, join date. Accepts up to 100 usernames per request (without @ prefix).",
|
|
8085
8113
|
proxyPath: "/x/users/lookup",
|
|
8086
8114
|
method: "POST",
|
|
8087
8115
|
params: [
|