@blockrun/clawrouter 0.12.35 → 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/cli.js
CHANGED
|
@@ -1,4 +1,132 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/solana-balance.ts
|
|
13
|
+
var solana_balance_exports = {};
|
|
14
|
+
__export(solana_balance_exports, {
|
|
15
|
+
SolanaBalanceMonitor: () => SolanaBalanceMonitor
|
|
16
|
+
});
|
|
17
|
+
import { address as solAddress, createSolanaRpc } from "@solana/kit";
|
|
18
|
+
var SOLANA_USDC_MINT, SOLANA_DEFAULT_RPC, BALANCE_TIMEOUT_MS, CACHE_TTL_MS2, SolanaBalanceMonitor;
|
|
19
|
+
var init_solana_balance = __esm({
|
|
20
|
+
"src/solana-balance.ts"() {
|
|
21
|
+
"use strict";
|
|
22
|
+
SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
23
|
+
SOLANA_DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
|
|
24
|
+
BALANCE_TIMEOUT_MS = 1e4;
|
|
25
|
+
CACHE_TTL_MS2 = 3e4;
|
|
26
|
+
SolanaBalanceMonitor = class {
|
|
27
|
+
rpc;
|
|
28
|
+
walletAddress;
|
|
29
|
+
cachedBalance = null;
|
|
30
|
+
cachedAt = 0;
|
|
31
|
+
constructor(walletAddress, rpcUrl) {
|
|
32
|
+
this.walletAddress = walletAddress;
|
|
33
|
+
const url = rpcUrl || process["env"].CLAWROUTER_SOLANA_RPC_URL || SOLANA_DEFAULT_RPC;
|
|
34
|
+
this.rpc = createSolanaRpc(url);
|
|
35
|
+
}
|
|
36
|
+
async checkBalance() {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
if (this.cachedBalance !== null && this.cachedBalance > 0n && now - this.cachedAt < CACHE_TTL_MS2) {
|
|
39
|
+
return this.buildInfo(this.cachedBalance);
|
|
40
|
+
}
|
|
41
|
+
const balance = await this.fetchBalance();
|
|
42
|
+
if (balance > 0n) {
|
|
43
|
+
this.cachedBalance = balance;
|
|
44
|
+
this.cachedAt = now;
|
|
45
|
+
}
|
|
46
|
+
return this.buildInfo(balance);
|
|
47
|
+
}
|
|
48
|
+
deductEstimated(amountMicros) {
|
|
49
|
+
if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {
|
|
50
|
+
this.cachedBalance -= amountMicros;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
invalidate() {
|
|
54
|
+
this.cachedBalance = null;
|
|
55
|
+
this.cachedAt = 0;
|
|
56
|
+
}
|
|
57
|
+
async refresh() {
|
|
58
|
+
this.invalidate();
|
|
59
|
+
return this.checkBalance();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if balance is sufficient for an estimated cost.
|
|
63
|
+
*/
|
|
64
|
+
async checkSufficient(estimatedCostMicros) {
|
|
65
|
+
const info = await this.checkBalance();
|
|
66
|
+
if (info.balance >= estimatedCostMicros) {
|
|
67
|
+
return { sufficient: true, info };
|
|
68
|
+
}
|
|
69
|
+
const shortfall = estimatedCostMicros - info.balance;
|
|
70
|
+
return {
|
|
71
|
+
sufficient: false,
|
|
72
|
+
info,
|
|
73
|
+
shortfall: this.formatUSDC(shortfall)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Format USDC amount (in micros) as "$X.XX".
|
|
78
|
+
*/
|
|
79
|
+
formatUSDC(amountMicros) {
|
|
80
|
+
const dollars = Number(amountMicros) / 1e6;
|
|
81
|
+
return `$${dollars.toFixed(2)}`;
|
|
82
|
+
}
|
|
83
|
+
getWalletAddress() {
|
|
84
|
+
return this.walletAddress;
|
|
85
|
+
}
|
|
86
|
+
async fetchBalance() {
|
|
87
|
+
const owner = solAddress(this.walletAddress);
|
|
88
|
+
const mint = solAddress(SOLANA_USDC_MINT);
|
|
89
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
90
|
+
const result = await this.fetchBalanceOnce(owner, mint);
|
|
91
|
+
if (result > 0n || attempt === 1) return result;
|
|
92
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
93
|
+
}
|
|
94
|
+
return 0n;
|
|
95
|
+
}
|
|
96
|
+
async fetchBalanceOnce(owner, mint) {
|
|
97
|
+
const controller = new AbortController();
|
|
98
|
+
const timer = setTimeout(() => controller.abort(), BALANCE_TIMEOUT_MS);
|
|
99
|
+
try {
|
|
100
|
+
const response = await this.rpc.getTokenAccountsByOwner(owner, { mint }, { encoding: "jsonParsed" }).send({ abortSignal: controller.signal });
|
|
101
|
+
if (response.value.length === 0) return 0n;
|
|
102
|
+
let total = 0n;
|
|
103
|
+
for (const account of response.value) {
|
|
104
|
+
const parsed = account.account.data;
|
|
105
|
+
total += BigInt(parsed.parsed.info.tokenAmount.amount);
|
|
106
|
+
}
|
|
107
|
+
return total;
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Failed to fetch Solana USDC balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
111
|
+
{ cause: err }
|
|
112
|
+
);
|
|
113
|
+
} finally {
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
buildInfo(balance) {
|
|
118
|
+
const dollars = Number(balance) / 1e6;
|
|
119
|
+
return {
|
|
120
|
+
balance,
|
|
121
|
+
balanceUSD: `$${dollars.toFixed(2)}`,
|
|
122
|
+
isLow: balance < 1000000n,
|
|
123
|
+
isEmpty: balance < 100n,
|
|
124
|
+
walletAddress: this.walletAddress
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
});
|
|
2
130
|
|
|
3
131
|
// src/proxy.ts
|
|
4
132
|
import { createServer } from "http";
|
|
@@ -1429,11 +1557,14 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1429
1557
|
primary: "moonshot/kimi-k2.5",
|
|
1430
1558
|
// $0.60/$3.00 - best quality/price for simple tasks
|
|
1431
1559
|
fallback: [
|
|
1560
|
+
"google/gemini-2.5-flash",
|
|
1561
|
+
// 60% retention (best), fast growth (+800%)
|
|
1432
1562
|
"google/gemini-2.5-flash-lite",
|
|
1433
1563
|
// 1M context, ultra cheap ($0.10/$0.40)
|
|
1434
|
-
"
|
|
1564
|
+
"deepseek/deepseek-chat",
|
|
1565
|
+
// 41% retention
|
|
1566
|
+
"nvidia/gpt-oss-120b"
|
|
1435
1567
|
// FREE fallback
|
|
1436
|
-
"deepseek/deepseek-chat"
|
|
1437
1568
|
]
|
|
1438
1569
|
},
|
|
1439
1570
|
MEDIUM: {
|
|
@@ -1441,6 +1572,9 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1441
1572
|
// $0.50/$2.40 - strong tool use, proper function call format
|
|
1442
1573
|
fallback: [
|
|
1443
1574
|
"deepseek/deepseek-chat",
|
|
1575
|
+
// 41% retention
|
|
1576
|
+
"google/gemini-2.5-flash",
|
|
1577
|
+
// 60% retention, cheap fast model
|
|
1444
1578
|
"google/gemini-2.5-flash-lite",
|
|
1445
1579
|
// 1M context, ultra cheap ($0.10/$0.40)
|
|
1446
1580
|
"xai/grok-4-1-fast-non-reasoning"
|
|
@@ -1451,6 +1585,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1451
1585
|
primary: "google/gemini-3.1-pro",
|
|
1452
1586
|
// Newest Gemini 3.1 - upgraded from 3.0
|
|
1453
1587
|
fallback: [
|
|
1588
|
+
"google/gemini-2.5-flash",
|
|
1589
|
+
// 60% retention, cheap failsafe before expensive models
|
|
1454
1590
|
"google/gemini-2.5-flash-lite",
|
|
1455
1591
|
// CRITICAL: 1M context, ultra-cheap failsafe ($0.10/$0.40)
|
|
1456
1592
|
"google/gemini-3-pro-preview",
|
|
@@ -1481,12 +1617,12 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1481
1617
|
SIMPLE: {
|
|
1482
1618
|
primary: "nvidia/gpt-oss-120b",
|
|
1483
1619
|
// FREE! $0.00/$0.00
|
|
1484
|
-
fallback: ["google/gemini-2.5-flash-lite", "deepseek/deepseek-chat"]
|
|
1620
|
+
fallback: ["google/gemini-2.5-flash-lite", "google/gemini-2.5-flash", "deepseek/deepseek-chat"]
|
|
1485
1621
|
},
|
|
1486
1622
|
MEDIUM: {
|
|
1487
1623
|
primary: "google/gemini-2.5-flash-lite",
|
|
1488
1624
|
// $0.10/$0.40 - cheapest capable with 1M context
|
|
1489
|
-
fallback: ["deepseek/deepseek-chat", "nvidia/gpt-oss-120b"]
|
|
1625
|
+
fallback: ["google/gemini-2.5-flash", "deepseek/deepseek-chat", "nvidia/gpt-oss-120b"]
|
|
1490
1626
|
},
|
|
1491
1627
|
COMPLEX: {
|
|
1492
1628
|
primary: "google/gemini-2.5-flash-lite",
|
|
@@ -1506,6 +1642,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1506
1642
|
primary: "moonshot/kimi-k2.5",
|
|
1507
1643
|
// $0.60/$3.00 - good for simple coding
|
|
1508
1644
|
fallback: [
|
|
1645
|
+
"google/gemini-2.5-flash",
|
|
1646
|
+
// 60% retention, fast growth
|
|
1509
1647
|
"anthropic/claude-haiku-4.5",
|
|
1510
1648
|
"google/gemini-2.5-flash-lite",
|
|
1511
1649
|
"deepseek/deepseek-chat"
|
|
@@ -1516,6 +1654,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1516
1654
|
// $2.50/$10 - strong coding for medium tasks
|
|
1517
1655
|
fallback: [
|
|
1518
1656
|
"moonshot/kimi-k2.5",
|
|
1657
|
+
"google/gemini-2.5-flash",
|
|
1658
|
+
// 60% retention, good coding capability
|
|
1519
1659
|
"google/gemini-2.5-pro",
|
|
1520
1660
|
"xai/grok-4-0709",
|
|
1521
1661
|
"anthropic/claude-sonnet-4.6"
|
|
@@ -1726,7 +1866,8 @@ var MODEL_ALIASES = {
|
|
|
1726
1866
|
// xAI
|
|
1727
1867
|
grok: "xai/grok-3",
|
|
1728
1868
|
"grok-fast": "xai/grok-4-fast-reasoning",
|
|
1729
|
-
"grok-code": "
|
|
1869
|
+
"grok-code": "deepseek/deepseek-chat",
|
|
1870
|
+
// was grok-code-fast-1, delisted due to poor retention
|
|
1730
1871
|
// NVIDIA
|
|
1731
1872
|
nvidia: "nvidia/gpt-oss-120b",
|
|
1732
1873
|
"gpt-120b": "nvidia/gpt-oss-120b",
|
|
@@ -2194,18 +2335,8 @@ var BLOCKRUN_MODELS = [
|
|
|
2194
2335
|
maxOutput: 16384,
|
|
2195
2336
|
toolCalling: true
|
|
2196
2337
|
},
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
name: "Grok Code Fast",
|
|
2200
|
-
version: "1",
|
|
2201
|
-
inputPrice: 0.2,
|
|
2202
|
-
outputPrice: 1.5,
|
|
2203
|
-
contextWindow: 131072,
|
|
2204
|
-
maxOutput: 16384
|
|
2205
|
-
// toolCalling intentionally omitted: outputs tool calls as plain text JSON,
|
|
2206
|
-
// not OpenAI-compatible structured function calls. Will be skipped when
|
|
2207
|
-
// request has tools to prevent the "talking to itself" bug.
|
|
2208
|
-
},
|
|
2338
|
+
// xai/grok-code-fast-1 delisted 2026-03-12: poor retention (coding users churn),
|
|
2339
|
+
// no structured tool calling, alias "grok-code" redirected to deepseek-chat
|
|
2209
2340
|
{
|
|
2210
2341
|
id: "xai/grok-4-0709",
|
|
2211
2342
|
name: "Grok 4 (0709)",
|
|
@@ -2973,115 +3104,6 @@ var BalanceMonitor = class {
|
|
|
2973
3104
|
}
|
|
2974
3105
|
};
|
|
2975
3106
|
|
|
2976
|
-
// src/solana-balance.ts
|
|
2977
|
-
import { address as solAddress, createSolanaRpc } from "@solana/kit";
|
|
2978
|
-
var SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
2979
|
-
var SOLANA_DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
|
|
2980
|
-
var BALANCE_TIMEOUT_MS = 1e4;
|
|
2981
|
-
var CACHE_TTL_MS2 = 3e4;
|
|
2982
|
-
var SolanaBalanceMonitor = class {
|
|
2983
|
-
rpc;
|
|
2984
|
-
walletAddress;
|
|
2985
|
-
cachedBalance = null;
|
|
2986
|
-
cachedAt = 0;
|
|
2987
|
-
constructor(walletAddress, rpcUrl) {
|
|
2988
|
-
this.walletAddress = walletAddress;
|
|
2989
|
-
const url = rpcUrl || process["env"].CLAWROUTER_SOLANA_RPC_URL || SOLANA_DEFAULT_RPC;
|
|
2990
|
-
this.rpc = createSolanaRpc(url);
|
|
2991
|
-
}
|
|
2992
|
-
async checkBalance() {
|
|
2993
|
-
const now = Date.now();
|
|
2994
|
-
if (this.cachedBalance !== null && this.cachedBalance > 0n && now - this.cachedAt < CACHE_TTL_MS2) {
|
|
2995
|
-
return this.buildInfo(this.cachedBalance);
|
|
2996
|
-
}
|
|
2997
|
-
const balance = await this.fetchBalance();
|
|
2998
|
-
if (balance > 0n) {
|
|
2999
|
-
this.cachedBalance = balance;
|
|
3000
|
-
this.cachedAt = now;
|
|
3001
|
-
}
|
|
3002
|
-
return this.buildInfo(balance);
|
|
3003
|
-
}
|
|
3004
|
-
deductEstimated(amountMicros) {
|
|
3005
|
-
if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {
|
|
3006
|
-
this.cachedBalance -= amountMicros;
|
|
3007
|
-
}
|
|
3008
|
-
}
|
|
3009
|
-
invalidate() {
|
|
3010
|
-
this.cachedBalance = null;
|
|
3011
|
-
this.cachedAt = 0;
|
|
3012
|
-
}
|
|
3013
|
-
async refresh() {
|
|
3014
|
-
this.invalidate();
|
|
3015
|
-
return this.checkBalance();
|
|
3016
|
-
}
|
|
3017
|
-
/**
|
|
3018
|
-
* Check if balance is sufficient for an estimated cost.
|
|
3019
|
-
*/
|
|
3020
|
-
async checkSufficient(estimatedCostMicros) {
|
|
3021
|
-
const info = await this.checkBalance();
|
|
3022
|
-
if (info.balance >= estimatedCostMicros) {
|
|
3023
|
-
return { sufficient: true, info };
|
|
3024
|
-
}
|
|
3025
|
-
const shortfall = estimatedCostMicros - info.balance;
|
|
3026
|
-
return {
|
|
3027
|
-
sufficient: false,
|
|
3028
|
-
info,
|
|
3029
|
-
shortfall: this.formatUSDC(shortfall)
|
|
3030
|
-
};
|
|
3031
|
-
}
|
|
3032
|
-
/**
|
|
3033
|
-
* Format USDC amount (in micros) as "$X.XX".
|
|
3034
|
-
*/
|
|
3035
|
-
formatUSDC(amountMicros) {
|
|
3036
|
-
const dollars = Number(amountMicros) / 1e6;
|
|
3037
|
-
return `$${dollars.toFixed(2)}`;
|
|
3038
|
-
}
|
|
3039
|
-
getWalletAddress() {
|
|
3040
|
-
return this.walletAddress;
|
|
3041
|
-
}
|
|
3042
|
-
async fetchBalance() {
|
|
3043
|
-
const owner = solAddress(this.walletAddress);
|
|
3044
|
-
const mint = solAddress(SOLANA_USDC_MINT);
|
|
3045
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
3046
|
-
const result = await this.fetchBalanceOnce(owner, mint);
|
|
3047
|
-
if (result > 0n || attempt === 1) return result;
|
|
3048
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
3049
|
-
}
|
|
3050
|
-
return 0n;
|
|
3051
|
-
}
|
|
3052
|
-
async fetchBalanceOnce(owner, mint) {
|
|
3053
|
-
const controller = new AbortController();
|
|
3054
|
-
const timer = setTimeout(() => controller.abort(), BALANCE_TIMEOUT_MS);
|
|
3055
|
-
try {
|
|
3056
|
-
const response = await this.rpc.getTokenAccountsByOwner(owner, { mint }, { encoding: "jsonParsed" }).send({ abortSignal: controller.signal });
|
|
3057
|
-
if (response.value.length === 0) return 0n;
|
|
3058
|
-
let total = 0n;
|
|
3059
|
-
for (const account of response.value) {
|
|
3060
|
-
const parsed = account.account.data;
|
|
3061
|
-
total += BigInt(parsed.parsed.info.tokenAmount.amount);
|
|
3062
|
-
}
|
|
3063
|
-
return total;
|
|
3064
|
-
} catch (err) {
|
|
3065
|
-
throw new Error(
|
|
3066
|
-
`Failed to fetch Solana USDC balance: ${err instanceof Error ? err.message : String(err)}`,
|
|
3067
|
-
{ cause: err }
|
|
3068
|
-
);
|
|
3069
|
-
} finally {
|
|
3070
|
-
clearTimeout(timer);
|
|
3071
|
-
}
|
|
3072
|
-
}
|
|
3073
|
-
buildInfo(balance) {
|
|
3074
|
-
const dollars = Number(balance) / 1e6;
|
|
3075
|
-
return {
|
|
3076
|
-
balance,
|
|
3077
|
-
balanceUSD: `$${dollars.toFixed(2)}`,
|
|
3078
|
-
isLow: balance < 1000000n,
|
|
3079
|
-
isEmpty: balance < 100n,
|
|
3080
|
-
walletAddress: this.walletAddress
|
|
3081
|
-
};
|
|
3082
|
-
}
|
|
3083
|
-
};
|
|
3084
|
-
|
|
3085
3107
|
// src/auth.ts
|
|
3086
3108
|
import { writeFile, mkdir as mkdir2 } from "fs/promises";
|
|
3087
3109
|
import { join as join4 } from "path";
|
|
@@ -5004,6 +5026,7 @@ var ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
|
5004
5026
|
"premium"
|
|
5005
5027
|
]);
|
|
5006
5028
|
var FREE_MODEL = "nvidia/gpt-oss-120b";
|
|
5029
|
+
var freeRequestCount = 0;
|
|
5007
5030
|
var MAX_MESSAGES = 200;
|
|
5008
5031
|
var CONTEXT_LIMIT_KB = 5120;
|
|
5009
5032
|
var HEARTBEAT_INTERVAL_MS = 2e3;
|
|
@@ -5705,7 +5728,13 @@ async function startProxy(options) {
|
|
|
5705
5728
|
const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes(solanaPrivateKeyBytes);
|
|
5706
5729
|
reuseSolanaAddress = solanaSigner.address;
|
|
5707
5730
|
}
|
|
5708
|
-
|
|
5731
|
+
let balanceMonitor2;
|
|
5732
|
+
if (paymentChain === "solana" && reuseSolanaAddress) {
|
|
5733
|
+
const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
|
|
5734
|
+
balanceMonitor2 = new SolanaBalanceMonitor2(reuseSolanaAddress);
|
|
5735
|
+
} else {
|
|
5736
|
+
balanceMonitor2 = new BalanceMonitor(account2.address);
|
|
5737
|
+
}
|
|
5709
5738
|
options.onReady?.(listenPort);
|
|
5710
5739
|
return {
|
|
5711
5740
|
port: listenPort,
|
|
@@ -5739,7 +5768,13 @@ async function startProxy(options) {
|
|
|
5739
5768
|
const payFetch = createPayFetchWithPreAuth(fetch, x402, void 0, {
|
|
5740
5769
|
skipPreAuth: paymentChain === "solana"
|
|
5741
5770
|
});
|
|
5742
|
-
|
|
5771
|
+
let balanceMonitor;
|
|
5772
|
+
if (paymentChain === "solana" && solanaAddress) {
|
|
5773
|
+
const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
|
|
5774
|
+
balanceMonitor = new SolanaBalanceMonitor2(solanaAddress);
|
|
5775
|
+
} else {
|
|
5776
|
+
balanceMonitor = new BalanceMonitor(account.address);
|
|
5777
|
+
}
|
|
5743
5778
|
const routingConfig = mergeRoutingConfig(options.routingConfig);
|
|
5744
5779
|
const modelPricing = buildModelPricing();
|
|
5745
5780
|
const routerOpts = {
|
|
@@ -5959,11 +5994,14 @@ async function startProxy(options) {
|
|
|
5959
5994
|
if (val.startsWith("data:")) {
|
|
5960
5995
|
} else if (val.startsWith("https://") || val.startsWith("http://")) {
|
|
5961
5996
|
const imgResp = await fetch(val);
|
|
5962
|
-
if (!imgResp.ok)
|
|
5997
|
+
if (!imgResp.ok)
|
|
5998
|
+
throw new Error(`Failed to download ${field} from ${val}: HTTP ${imgResp.status}`);
|
|
5963
5999
|
const contentType = imgResp.headers.get("content-type") ?? "image/png";
|
|
5964
6000
|
const buf = Buffer.from(await imgResp.arrayBuffer());
|
|
5965
6001
|
parsed[field] = `data:${contentType};base64,${buf.toString("base64")}`;
|
|
5966
|
-
console.log(
|
|
6002
|
+
console.log(
|
|
6003
|
+
`[ClawRouter] img2img: downloaded ${field} URL \u2192 data URI (${buf.length} bytes)`
|
|
6004
|
+
);
|
|
5967
6005
|
} else {
|
|
5968
6006
|
parsed[field] = readImageFileAsDataUri(val);
|
|
5969
6007
|
console.log(`[ClawRouter] img2img: read ${field} file \u2192 data URI`);
|
|
@@ -6320,6 +6358,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6320
6358
|
let modelId = "";
|
|
6321
6359
|
let maxTokens = 4096;
|
|
6322
6360
|
let routingProfile = null;
|
|
6361
|
+
let balanceFallbackNotice;
|
|
6323
6362
|
let accumulatedContent = "";
|
|
6324
6363
|
let responseInputTokens;
|
|
6325
6364
|
const isChatCompletion = req.url?.includes("/chat/completions");
|
|
@@ -6855,6 +6894,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6855
6894
|
parsed.model = freeModel;
|
|
6856
6895
|
modelId = freeModel;
|
|
6857
6896
|
bodyModified = true;
|
|
6897
|
+
freeRequestCount++;
|
|
6898
|
+
if (freeRequestCount % 5 === 0) {
|
|
6899
|
+
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.
|
|
6900
|
+
|
|
6901
|
+
`;
|
|
6902
|
+
}
|
|
6858
6903
|
await logUsage({
|
|
6859
6904
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6860
6905
|
model: freeModel,
|
|
@@ -7071,7 +7116,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7071
7116
|
}
|
|
7072
7117
|
deduplicator.markInflight(dedupKey);
|
|
7073
7118
|
let estimatedCostMicros;
|
|
7074
|
-
let balanceFallbackNotice;
|
|
7075
7119
|
const isFreeModel = modelId === FREE_MODEL;
|
|
7076
7120
|
if (modelId && !options.skipBalanceCheck && !isFreeModel) {
|
|
7077
7121
|
const estimated = estimateAmount(modelId, body.length, maxTokens);
|
|
@@ -7093,6 +7137,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7093
7137
|
` : `> **\u26A0\uFE0F Insufficient balance** (${sufficiency.info.balanceUSD}) \u2014 using free model instead of ${originalModel}.
|
|
7094
7138
|
|
|
7095
7139
|
`;
|
|
7140
|
+
freeRequestCount++;
|
|
7141
|
+
if (freeRequestCount % 5 === 0) {
|
|
7142
|
+
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.
|
|
7143
|
+
|
|
7144
|
+
`;
|
|
7145
|
+
}
|
|
7096
7146
|
options.onLowBalance?.({
|
|
7097
7147
|
balanceUSD: sufficiency.info.balanceUSD,
|
|
7098
7148
|
walletAddress: sufficiency.info.walletAddress
|
|
@@ -7976,7 +8026,7 @@ var PARTNER_SERVICES = [
|
|
|
7976
8026
|
id: "x_users_lookup",
|
|
7977
8027
|
name: "Twitter/X User Lookup",
|
|
7978
8028
|
partner: "AttentionVC",
|
|
7979
|
-
description: "
|
|
8029
|
+
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).",
|
|
7980
8030
|
proxyPath: "/x/users/lookup",
|
|
7981
8031
|
method: "POST",
|
|
7982
8032
|
params: [
|