@blockrun/clawrouter 0.12.261 → 0.12.263
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 +39 -12
- package/dist/index.d.ts +0 -6
- package/dist/index.js +37 -11
- package/package.json +2 -2
- package/src/top-models.json +4 -0
package/dist/cli.js
CHANGED
|
@@ -32632,7 +32632,7 @@ var init_spend_control = __esm({
|
|
|
32632
32632
|
}
|
|
32633
32633
|
}
|
|
32634
32634
|
if (this.limits.hourly !== void 0) {
|
|
32635
|
-
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
32635
|
+
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
32636
32636
|
const remaining = this.limits.hourly - hourlySpent;
|
|
32637
32637
|
if (estimatedCost2 > remaining) {
|
|
32638
32638
|
const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - HOUR_MS);
|
|
@@ -32647,7 +32647,7 @@ var init_spend_control = __esm({
|
|
|
32647
32647
|
}
|
|
32648
32648
|
}
|
|
32649
32649
|
if (this.limits.daily !== void 0) {
|
|
32650
|
-
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
32650
|
+
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
32651
32651
|
const remaining = this.limits.daily - dailySpent;
|
|
32652
32652
|
if (estimatedCost2 > remaining) {
|
|
32653
32653
|
const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - DAY_MS);
|
|
@@ -32744,17 +32744,30 @@ var init_spend_control = __esm({
|
|
|
32744
32744
|
}
|
|
32745
32745
|
}
|
|
32746
32746
|
}
|
|
32747
|
-
|
|
32747
|
+
// `now` is the single clock reading the caller already took to build the
|
|
32748
|
+
// window; it must be passed in, not re-read here. In-flight reservations are
|
|
32749
|
+
// "now" holds, so they count only against a window that reaches the present
|
|
32750
|
+
// (`to >= now`). The bug this guards against: reading the clock a SECOND time
|
|
32751
|
+
// inside this method (the old `to >= this.now()`) could land a millisecond
|
|
32752
|
+
// after the caller's `now`, flip the guard false, and silently drop the
|
|
32753
|
+
// pending total from the hourly/daily check — letting two concurrent payments
|
|
32754
|
+
// both clear the same remaining budget. Threading the caller's `now` keeps the
|
|
32755
|
+
// guard meaningful (a genuinely historical window with `to < now` still
|
|
32756
|
+
// excludes live holds) without a second, racing read. Every existing test
|
|
32757
|
+
// injects a frozen clock (`now: () => clock`), so the two reads always matched
|
|
32758
|
+
// and the sub-ms window went uncaught; see the live-clock test in
|
|
32759
|
+
// spend-control.test.ts.
|
|
32760
|
+
getSpendingInWindow(from15, to, now2) {
|
|
32748
32761
|
const recorded = this.history.filter((r2) => r2.timestamp >= from15 && r2.timestamp <= to).reduce((sum, r2) => sum + r2.amount, 0);
|
|
32749
|
-
return recorded + (to >=
|
|
32762
|
+
return recorded + (to >= now2 ? this.pendingTotal() : 0);
|
|
32750
32763
|
}
|
|
32751
32764
|
getSpending(window2) {
|
|
32752
32765
|
const now2 = this.now();
|
|
32753
32766
|
switch (window2) {
|
|
32754
32767
|
case "hourly":
|
|
32755
|
-
return this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
32768
|
+
return this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
32756
32769
|
case "daily":
|
|
32757
|
-
return this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
32770
|
+
return this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
32758
32771
|
case "session":
|
|
32759
32772
|
return this.sessionSpent + this.pendingTotal();
|
|
32760
32773
|
}
|
|
@@ -32766,8 +32779,8 @@ var init_spend_control = __esm({
|
|
|
32766
32779
|
}
|
|
32767
32780
|
getStatus() {
|
|
32768
32781
|
const now2 = this.now();
|
|
32769
|
-
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
32770
|
-
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
32782
|
+
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
32783
|
+
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
32771
32784
|
return {
|
|
32772
32785
|
limits: cloneLimits(this.limits),
|
|
32773
32786
|
spending: {
|
|
@@ -37672,7 +37685,9 @@ var init_top_models = __esm({
|
|
|
37672
37685
|
"openai/gpt-5.4-nano",
|
|
37673
37686
|
"openai/gpt-5.3-codex",
|
|
37674
37687
|
"google/gemini-3.1-pro",
|
|
37688
|
+
"google/gemini-3.6-flash",
|
|
37675
37689
|
"google/gemini-3.5-flash",
|
|
37690
|
+
"google/gemini-3.5-flash-lite",
|
|
37676
37691
|
"google/gemini-3.1-flash-lite",
|
|
37677
37692
|
"google/gemini-3-flash-preview",
|
|
37678
37693
|
"xai/grok-4.5",
|
|
@@ -37685,11 +37700,13 @@ var init_top_models = __esm({
|
|
|
37685
37700
|
"zai/glm-5-turbo",
|
|
37686
37701
|
"zai/glm-5",
|
|
37687
37702
|
"xiaomi/mimo-v2.5",
|
|
37703
|
+
"xiaomi/mimo-v2.5-pro",
|
|
37688
37704
|
"minimax/minimax-m3",
|
|
37689
37705
|
"minimax/minimax-m2.7",
|
|
37690
37706
|
"moonshot/kimi-k3",
|
|
37691
37707
|
"qwen/qwen3.7-max",
|
|
37692
37708
|
"qwen/qwen3.8-flash",
|
|
37709
|
+
"tencent/hy3",
|
|
37693
37710
|
"deepseek/deepseek-v4-flash-vision-exp",
|
|
37694
37711
|
"deepseek/deepseek-v4-pro",
|
|
37695
37712
|
"deepseek/deepseek-chat",
|
|
@@ -39916,6 +39933,13 @@ var init_version4 = __esm({
|
|
|
39916
39933
|
import { readdir, unlink } from "fs/promises";
|
|
39917
39934
|
import { join as join5 } from "path";
|
|
39918
39935
|
import { homedir as homedir3 } from "os";
|
|
39936
|
+
function resolveStatsDays(raw) {
|
|
39937
|
+
const parsed = parseInt(raw ?? "", 10);
|
|
39938
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
39939
|
+
return DEFAULT_STATS_DAYS;
|
|
39940
|
+
}
|
|
39941
|
+
return Math.min(parsed, MAX_STATS_DAYS);
|
|
39942
|
+
}
|
|
39919
39943
|
async function parseLogFile(filePath) {
|
|
39920
39944
|
try {
|
|
39921
39945
|
const content = await readTextFile(filePath);
|
|
@@ -40101,7 +40125,8 @@ function formatStatsAscii(stats) {
|
|
|
40101
40125
|
lines.push("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
40102
40126
|
return lines.join("\n");
|
|
40103
40127
|
}
|
|
40104
|
-
async function formatRecentLogs(
|
|
40128
|
+
async function formatRecentLogs(requestedDays = 1) {
|
|
40129
|
+
const days = Number.isFinite(requestedDays) && requestedDays > 0 ? Math.floor(requestedDays) : 1;
|
|
40105
40130
|
const logFiles = await getLogFiles();
|
|
40106
40131
|
const filesToRead = logFiles.slice(0, days);
|
|
40107
40132
|
const allEntries = [];
|
|
@@ -40157,13 +40182,15 @@ async function clearStats() {
|
|
|
40157
40182
|
return { deletedFiles: 0 };
|
|
40158
40183
|
}
|
|
40159
40184
|
}
|
|
40160
|
-
var LOG_DIR2;
|
|
40185
|
+
var LOG_DIR2, DEFAULT_STATS_DAYS, MAX_STATS_DAYS;
|
|
40161
40186
|
var init_stats = __esm({
|
|
40162
40187
|
"src/stats.ts"() {
|
|
40163
40188
|
"use strict";
|
|
40164
40189
|
init_fs_read();
|
|
40165
40190
|
init_version4();
|
|
40166
40191
|
LOG_DIR2 = join5(homedir3(), ".openclaw", "blockrun", "logs");
|
|
40192
|
+
DEFAULT_STATS_DAYS = 7;
|
|
40193
|
+
MAX_STATS_DAYS = 30;
|
|
40167
40194
|
}
|
|
40168
40195
|
});
|
|
40169
40196
|
|
|
@@ -91082,8 +91109,8 @@ async function startProxy(options) {
|
|
|
91082
91109
|
if (req.url === "/stats" || req.url?.startsWith("/stats?")) {
|
|
91083
91110
|
try {
|
|
91084
91111
|
const url2 = new URL(req.url, "http://localhost");
|
|
91085
|
-
const days =
|
|
91086
|
-
const stats = await getStats(
|
|
91112
|
+
const days = resolveStatsDays(url2.searchParams.get("days"));
|
|
91113
|
+
const stats = await getStats(days);
|
|
91087
91114
|
res.writeHead(200, {
|
|
91088
91115
|
"Content-Type": "application/json",
|
|
91089
91116
|
"Cache-Control": "no-cache"
|
package/dist/index.d.ts
CHANGED
|
@@ -1467,12 +1467,6 @@ declare function fetchWithRetry(fetchFn: (url: string, init?: RequestInit) => Pr
|
|
|
1467
1467
|
*/
|
|
1468
1468
|
declare function isRetryable(errorOrResponse: Error | Response, config?: Partial<RetryConfig>): boolean;
|
|
1469
1469
|
|
|
1470
|
-
/**
|
|
1471
|
-
* Usage Statistics Aggregator
|
|
1472
|
-
*
|
|
1473
|
-
* Reads usage log files and aggregates statistics for terminal display.
|
|
1474
|
-
* Supports filtering by date range and provides multiple aggregation views.
|
|
1475
|
-
*/
|
|
1476
1470
|
type DailyStats = {
|
|
1477
1471
|
date: string;
|
|
1478
1472
|
totalRequests: number;
|
package/dist/index.js
CHANGED
|
@@ -75,7 +75,9 @@ var init_top_models = __esm({
|
|
|
75
75
|
"openai/gpt-5.4-nano",
|
|
76
76
|
"openai/gpt-5.3-codex",
|
|
77
77
|
"google/gemini-3.1-pro",
|
|
78
|
+
"google/gemini-3.6-flash",
|
|
78
79
|
"google/gemini-3.5-flash",
|
|
80
|
+
"google/gemini-3.5-flash-lite",
|
|
79
81
|
"google/gemini-3.1-flash-lite",
|
|
80
82
|
"google/gemini-3-flash-preview",
|
|
81
83
|
"xai/grok-4.5",
|
|
@@ -88,11 +90,13 @@ var init_top_models = __esm({
|
|
|
88
90
|
"zai/glm-5-turbo",
|
|
89
91
|
"zai/glm-5",
|
|
90
92
|
"xiaomi/mimo-v2.5",
|
|
93
|
+
"xiaomi/mimo-v2.5-pro",
|
|
91
94
|
"minimax/minimax-m3",
|
|
92
95
|
"minimax/minimax-m2.7",
|
|
93
96
|
"moonshot/kimi-k3",
|
|
94
97
|
"qwen/qwen3.7-max",
|
|
95
98
|
"qwen/qwen3.8-flash",
|
|
99
|
+
"tencent/hy3",
|
|
96
100
|
"deepseek/deepseek-v4-flash-vision-exp",
|
|
97
101
|
"deepseek/deepseek-v4-pro",
|
|
98
102
|
"deepseek/deepseek-chat",
|
|
@@ -34764,7 +34768,7 @@ var init_spend_control = __esm({
|
|
|
34764
34768
|
}
|
|
34765
34769
|
}
|
|
34766
34770
|
if (this.limits.hourly !== void 0) {
|
|
34767
|
-
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
34771
|
+
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
34768
34772
|
const remaining = this.limits.hourly - hourlySpent;
|
|
34769
34773
|
if (estimatedCost2 > remaining) {
|
|
34770
34774
|
const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - HOUR_MS);
|
|
@@ -34779,7 +34783,7 @@ var init_spend_control = __esm({
|
|
|
34779
34783
|
}
|
|
34780
34784
|
}
|
|
34781
34785
|
if (this.limits.daily !== void 0) {
|
|
34782
|
-
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
34786
|
+
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
34783
34787
|
const remaining = this.limits.daily - dailySpent;
|
|
34784
34788
|
if (estimatedCost2 > remaining) {
|
|
34785
34789
|
const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - DAY_MS);
|
|
@@ -34876,17 +34880,30 @@ var init_spend_control = __esm({
|
|
|
34876
34880
|
}
|
|
34877
34881
|
}
|
|
34878
34882
|
}
|
|
34879
|
-
|
|
34883
|
+
// `now` is the single clock reading the caller already took to build the
|
|
34884
|
+
// window; it must be passed in, not re-read here. In-flight reservations are
|
|
34885
|
+
// "now" holds, so they count only against a window that reaches the present
|
|
34886
|
+
// (`to >= now`). The bug this guards against: reading the clock a SECOND time
|
|
34887
|
+
// inside this method (the old `to >= this.now()`) could land a millisecond
|
|
34888
|
+
// after the caller's `now`, flip the guard false, and silently drop the
|
|
34889
|
+
// pending total from the hourly/daily check — letting two concurrent payments
|
|
34890
|
+
// both clear the same remaining budget. Threading the caller's `now` keeps the
|
|
34891
|
+
// guard meaningful (a genuinely historical window with `to < now` still
|
|
34892
|
+
// excludes live holds) without a second, racing read. Every existing test
|
|
34893
|
+
// injects a frozen clock (`now: () => clock`), so the two reads always matched
|
|
34894
|
+
// and the sub-ms window went uncaught; see the live-clock test in
|
|
34895
|
+
// spend-control.test.ts.
|
|
34896
|
+
getSpendingInWindow(from15, to, now2) {
|
|
34880
34897
|
const recorded = this.history.filter((r2) => r2.timestamp >= from15 && r2.timestamp <= to).reduce((sum, r2) => sum + r2.amount, 0);
|
|
34881
|
-
return recorded + (to >=
|
|
34898
|
+
return recorded + (to >= now2 ? this.pendingTotal() : 0);
|
|
34882
34899
|
}
|
|
34883
34900
|
getSpending(window2) {
|
|
34884
34901
|
const now2 = this.now();
|
|
34885
34902
|
switch (window2) {
|
|
34886
34903
|
case "hourly":
|
|
34887
|
-
return this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
34904
|
+
return this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
34888
34905
|
case "daily":
|
|
34889
|
-
return this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
34906
|
+
return this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
34890
34907
|
case "session":
|
|
34891
34908
|
return this.sessionSpent + this.pendingTotal();
|
|
34892
34909
|
}
|
|
@@ -34898,8 +34915,8 @@ var init_spend_control = __esm({
|
|
|
34898
34915
|
}
|
|
34899
34916
|
getStatus() {
|
|
34900
34917
|
const now2 = this.now();
|
|
34901
|
-
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
34902
|
-
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
34918
|
+
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
34919
|
+
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
34903
34920
|
return {
|
|
34904
34921
|
limits: cloneLimits(this.limits),
|
|
34905
34922
|
spending: {
|
|
@@ -39831,6 +39848,13 @@ var init_version4 = __esm({
|
|
|
39831
39848
|
import { readdir, unlink } from "fs/promises";
|
|
39832
39849
|
import { join as join5 } from "path";
|
|
39833
39850
|
import { homedir as homedir3 } from "os";
|
|
39851
|
+
function resolveStatsDays(raw) {
|
|
39852
|
+
const parsed = parseInt(raw ?? "", 10);
|
|
39853
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
39854
|
+
return DEFAULT_STATS_DAYS;
|
|
39855
|
+
}
|
|
39856
|
+
return Math.min(parsed, MAX_STATS_DAYS);
|
|
39857
|
+
}
|
|
39834
39858
|
async function parseLogFile(filePath) {
|
|
39835
39859
|
try {
|
|
39836
39860
|
const content = await readTextFile(filePath);
|
|
@@ -40026,13 +40050,15 @@ async function clearStats() {
|
|
|
40026
40050
|
return { deletedFiles: 0 };
|
|
40027
40051
|
}
|
|
40028
40052
|
}
|
|
40029
|
-
var LOG_DIR2;
|
|
40053
|
+
var LOG_DIR2, DEFAULT_STATS_DAYS, MAX_STATS_DAYS;
|
|
40030
40054
|
var init_stats = __esm({
|
|
40031
40055
|
"src/stats.ts"() {
|
|
40032
40056
|
"use strict";
|
|
40033
40057
|
init_fs_read();
|
|
40034
40058
|
init_version4();
|
|
40035
40059
|
LOG_DIR2 = join5(homedir3(), ".openclaw", "blockrun", "logs");
|
|
40060
|
+
DEFAULT_STATS_DAYS = 7;
|
|
40061
|
+
MAX_STATS_DAYS = 30;
|
|
40036
40062
|
}
|
|
40037
40063
|
});
|
|
40038
40064
|
|
|
@@ -90902,8 +90928,8 @@ async function startProxy(options) {
|
|
|
90902
90928
|
if (req.url === "/stats" || req.url?.startsWith("/stats?")) {
|
|
90903
90929
|
try {
|
|
90904
90930
|
const url2 = new URL(req.url, "http://localhost");
|
|
90905
|
-
const days =
|
|
90906
|
-
const stats = await getStats(
|
|
90931
|
+
const days = resolveStatsDays(url2.searchParams.get("days"));
|
|
90932
|
+
const stats = await getStats(days);
|
|
90907
90933
|
res.writeHead(200, {
|
|
90908
90934
|
"Content-Type": "application/json",
|
|
90909
90935
|
"Cache-Control": "no-cache"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/clawrouter",
|
|
3
|
-
"version": "0.12.
|
|
4
|
-
"description": "Smart LLM router
|
|
3
|
+
"version": "0.12.263",
|
|
4
|
+
"description": "Smart LLM router \u2014 save 84% on inference costs. 76 models (7 free), one wallet, x402 micropayments.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
package/src/top-models.json
CHANGED
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"openai/gpt-5.4-nano",
|
|
22
22
|
"openai/gpt-5.3-codex",
|
|
23
23
|
"google/gemini-3.1-pro",
|
|
24
|
+
"google/gemini-3.6-flash",
|
|
24
25
|
"google/gemini-3.5-flash",
|
|
26
|
+
"google/gemini-3.5-flash-lite",
|
|
25
27
|
"google/gemini-3.1-flash-lite",
|
|
26
28
|
"google/gemini-3-flash-preview",
|
|
27
29
|
"xai/grok-4.5",
|
|
@@ -34,11 +36,13 @@
|
|
|
34
36
|
"zai/glm-5-turbo",
|
|
35
37
|
"zai/glm-5",
|
|
36
38
|
"xiaomi/mimo-v2.5",
|
|
39
|
+
"xiaomi/mimo-v2.5-pro",
|
|
37
40
|
"minimax/minimax-m3",
|
|
38
41
|
"minimax/minimax-m2.7",
|
|
39
42
|
"moonshot/kimi-k3",
|
|
40
43
|
"qwen/qwen3.7-max",
|
|
41
44
|
"qwen/qwen3.8-flash",
|
|
45
|
+
"tencent/hy3",
|
|
42
46
|
"deepseek/deepseek-v4-flash-vision-exp",
|
|
43
47
|
"deepseek/deepseek-v4-pro",
|
|
44
48
|
"deepseek/deepseek-chat",
|