@blockrun/clawrouter 0.12.262 → 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 +35 -12
- package/dist/index.d.ts +0 -6
- package/dist/index.js +33 -11
- package/package.json +2 -2
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: {
|
|
@@ -39920,6 +39933,13 @@ var init_version4 = __esm({
|
|
|
39920
39933
|
import { readdir, unlink } from "fs/promises";
|
|
39921
39934
|
import { join as join5 } from "path";
|
|
39922
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
|
+
}
|
|
39923
39943
|
async function parseLogFile(filePath) {
|
|
39924
39944
|
try {
|
|
39925
39945
|
const content = await readTextFile(filePath);
|
|
@@ -40105,7 +40125,8 @@ function formatStatsAscii(stats) {
|
|
|
40105
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");
|
|
40106
40126
|
return lines.join("\n");
|
|
40107
40127
|
}
|
|
40108
|
-
async function formatRecentLogs(
|
|
40128
|
+
async function formatRecentLogs(requestedDays = 1) {
|
|
40129
|
+
const days = Number.isFinite(requestedDays) && requestedDays > 0 ? Math.floor(requestedDays) : 1;
|
|
40109
40130
|
const logFiles = await getLogFiles();
|
|
40110
40131
|
const filesToRead = logFiles.slice(0, days);
|
|
40111
40132
|
const allEntries = [];
|
|
@@ -40161,13 +40182,15 @@ async function clearStats() {
|
|
|
40161
40182
|
return { deletedFiles: 0 };
|
|
40162
40183
|
}
|
|
40163
40184
|
}
|
|
40164
|
-
var LOG_DIR2;
|
|
40185
|
+
var LOG_DIR2, DEFAULT_STATS_DAYS, MAX_STATS_DAYS;
|
|
40165
40186
|
var init_stats = __esm({
|
|
40166
40187
|
"src/stats.ts"() {
|
|
40167
40188
|
"use strict";
|
|
40168
40189
|
init_fs_read();
|
|
40169
40190
|
init_version4();
|
|
40170
40191
|
LOG_DIR2 = join5(homedir3(), ".openclaw", "blockrun", "logs");
|
|
40192
|
+
DEFAULT_STATS_DAYS = 7;
|
|
40193
|
+
MAX_STATS_DAYS = 30;
|
|
40171
40194
|
}
|
|
40172
40195
|
});
|
|
40173
40196
|
|
|
@@ -91086,8 +91109,8 @@ async function startProxy(options) {
|
|
|
91086
91109
|
if (req.url === "/stats" || req.url?.startsWith("/stats?")) {
|
|
91087
91110
|
try {
|
|
91088
91111
|
const url2 = new URL(req.url, "http://localhost");
|
|
91089
|
-
const days =
|
|
91090
|
-
const stats = await getStats(
|
|
91112
|
+
const days = resolveStatsDays(url2.searchParams.get("days"));
|
|
91113
|
+
const stats = await getStats(days);
|
|
91091
91114
|
res.writeHead(200, {
|
|
91092
91115
|
"Content-Type": "application/json",
|
|
91093
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
|
@@ -34768,7 +34768,7 @@ var init_spend_control = __esm({
|
|
|
34768
34768
|
}
|
|
34769
34769
|
}
|
|
34770
34770
|
if (this.limits.hourly !== void 0) {
|
|
34771
|
-
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
34771
|
+
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
34772
34772
|
const remaining = this.limits.hourly - hourlySpent;
|
|
34773
34773
|
if (estimatedCost2 > remaining) {
|
|
34774
34774
|
const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - HOUR_MS);
|
|
@@ -34783,7 +34783,7 @@ var init_spend_control = __esm({
|
|
|
34783
34783
|
}
|
|
34784
34784
|
}
|
|
34785
34785
|
if (this.limits.daily !== void 0) {
|
|
34786
|
-
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
34786
|
+
const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
34787
34787
|
const remaining = this.limits.daily - dailySpent;
|
|
34788
34788
|
if (estimatedCost2 > remaining) {
|
|
34789
34789
|
const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - DAY_MS);
|
|
@@ -34880,17 +34880,30 @@ var init_spend_control = __esm({
|
|
|
34880
34880
|
}
|
|
34881
34881
|
}
|
|
34882
34882
|
}
|
|
34883
|
-
|
|
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) {
|
|
34884
34897
|
const recorded = this.history.filter((r2) => r2.timestamp >= from15 && r2.timestamp <= to).reduce((sum, r2) => sum + r2.amount, 0);
|
|
34885
|
-
return recorded + (to >=
|
|
34898
|
+
return recorded + (to >= now2 ? this.pendingTotal() : 0);
|
|
34886
34899
|
}
|
|
34887
34900
|
getSpending(window2) {
|
|
34888
34901
|
const now2 = this.now();
|
|
34889
34902
|
switch (window2) {
|
|
34890
34903
|
case "hourly":
|
|
34891
|
-
return this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
34904
|
+
return this.getSpendingInWindow(now2 - HOUR_MS, now2, now2);
|
|
34892
34905
|
case "daily":
|
|
34893
|
-
return this.getSpendingInWindow(now2 - DAY_MS, now2);
|
|
34906
|
+
return this.getSpendingInWindow(now2 - DAY_MS, now2, now2);
|
|
34894
34907
|
case "session":
|
|
34895
34908
|
return this.sessionSpent + this.pendingTotal();
|
|
34896
34909
|
}
|
|
@@ -34902,8 +34915,8 @@ var init_spend_control = __esm({
|
|
|
34902
34915
|
}
|
|
34903
34916
|
getStatus() {
|
|
34904
34917
|
const now2 = this.now();
|
|
34905
|
-
const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
|
|
34906
|
-
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);
|
|
34907
34920
|
return {
|
|
34908
34921
|
limits: cloneLimits(this.limits),
|
|
34909
34922
|
spending: {
|
|
@@ -39835,6 +39848,13 @@ var init_version4 = __esm({
|
|
|
39835
39848
|
import { readdir, unlink } from "fs/promises";
|
|
39836
39849
|
import { join as join5 } from "path";
|
|
39837
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
|
+
}
|
|
39838
39858
|
async function parseLogFile(filePath) {
|
|
39839
39859
|
try {
|
|
39840
39860
|
const content = await readTextFile(filePath);
|
|
@@ -40030,13 +40050,15 @@ async function clearStats() {
|
|
|
40030
40050
|
return { deletedFiles: 0 };
|
|
40031
40051
|
}
|
|
40032
40052
|
}
|
|
40033
|
-
var LOG_DIR2;
|
|
40053
|
+
var LOG_DIR2, DEFAULT_STATS_DAYS, MAX_STATS_DAYS;
|
|
40034
40054
|
var init_stats = __esm({
|
|
40035
40055
|
"src/stats.ts"() {
|
|
40036
40056
|
"use strict";
|
|
40037
40057
|
init_fs_read();
|
|
40038
40058
|
init_version4();
|
|
40039
40059
|
LOG_DIR2 = join5(homedir3(), ".openclaw", "blockrun", "logs");
|
|
40060
|
+
DEFAULT_STATS_DAYS = 7;
|
|
40061
|
+
MAX_STATS_DAYS = 30;
|
|
40040
40062
|
}
|
|
40041
40063
|
});
|
|
40042
40064
|
|
|
@@ -90906,8 +90928,8 @@ async function startProxy(options) {
|
|
|
90906
90928
|
if (req.url === "/stats" || req.url?.startsWith("/stats?")) {
|
|
90907
90929
|
try {
|
|
90908
90930
|
const url2 = new URL(req.url, "http://localhost");
|
|
90909
|
-
const days =
|
|
90910
|
-
const stats = await getStats(
|
|
90931
|
+
const days = resolveStatsDays(url2.searchParams.get("days"));
|
|
90932
|
+
const stats = await getStats(days);
|
|
90911
90933
|
res.writeHead(200, {
|
|
90912
90934
|
"Content-Type": "application/json",
|
|
90913
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",
|