Skip to content

Commit 960e72c

Browse files
author
1bcMax
committed
feat: add /stats clear command to reset usage statistics
Users can now reset usage stats via: - /stats clear (or /stats reset) in OpenClaw - DELETE /stats HTTP endpoint on the proxy
1 parent 82890a7 commit 960e72c

5 files changed

Lines changed: 55 additions & 7 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@blockrun/clawrouter",
3-
"version": "0.12.9",
3+
"version": "0.12.10",
44
"description": "Smart LLM router — save 92% on inference costs. 41+ models, one wallet, x402 micropayments.",
55
"type": "module",
66
"main": "dist/index.js",

src/index.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ import { homedir } from "node:os";
6767
import { join } from "node:path";
6868
import { VERSION } from "./version.js";
6969
import { privateKeyToAccount } from "viem/accounts";
70-
import { getStats, formatStatsAscii } from "./stats.js";
70+
import { getStats, formatStatsAscii, clearStats } from "./stats.js";
7171
import { buildPartnerTools, PARTNER_SERVICES } from "./partners/index.js";
7272

7373
/**
@@ -510,6 +510,21 @@ async function createStatsCommand(): Promise<OpenClawPluginCommandDefinition> {
510510
requireAuth: false,
511511
handler: async (ctx: PluginCommandContext) => {
512512
const arg = ctx.args?.trim().toLowerCase() || "7";
513+
514+
if (arg === "clear" || arg === "reset") {
515+
try {
516+
const { deletedFiles } = await clearStats();
517+
return {
518+
text: `Stats cleared — ${deletedFiles} log file(s) deleted. Fresh start!`,
519+
};
520+
} catch (err) {
521+
return {
522+
text: `Failed to clear stats: ${err instanceof Error ? err.message : String(err)}`,
523+
isError: true,
524+
};
525+
}
526+
}
527+
513528
const days = parseInt(arg, 10) || 7;
514529

515530
try {
@@ -1144,7 +1159,7 @@ export {
11441159
} from "./errors.js";
11451160
export { fetchWithRetry, isRetryable, DEFAULT_RETRY_CONFIG } from "./retry.js";
11461161
export type { RetryConfig } from "./retry.js";
1147-
export { getStats, formatStatsAscii } from "./stats.js";
1162+
export { getStats, formatStatsAscii, clearStats } from "./stats.js";
11481163
export type { DailyStats, AggregatedStats } from "./stats.js";
11491164
export {
11501165
SessionStore,

src/proxy.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import {
5454
supportsVision,
5555
} from "./models.js";
5656
import { logUsage, type UsageEntry } from "./logger.js";
57-
import { getStats } from "./stats.js";
57+
import { getStats, clearStats } from "./stats.js";
5858
import { RequestDeduplicator } from "./dedup.js";
5959
import { ResponseCache, type ResponseCacheConfig } from "./response-cache.js";
6060
import { BalanceMonitor } from "./balance.js";
@@ -1329,6 +1329,23 @@ export async function startProxy(options: ProxyOptions): Promise<ProxyHandle> {
13291329
return;
13301330
}
13311331

1332+
// Stats clear endpoint - delete all log files
1333+
if (req.url === "/stats" && req.method === "DELETE") {
1334+
try {
1335+
const result = await clearStats();
1336+
res.writeHead(200, { "Content-Type": "application/json" });
1337+
res.end(JSON.stringify({ cleared: true, deletedFiles: result.deletedFiles }));
1338+
} catch (err) {
1339+
res.writeHead(500, { "Content-Type": "application/json" });
1340+
res.end(
1341+
JSON.stringify({
1342+
error: `Failed to clear stats: ${err instanceof Error ? err.message : String(err)}`,
1343+
}),
1344+
);
1345+
}
1346+
return;
1347+
}
1348+
13321349
// Stats API endpoint - returns JSON for programmatic access
13331350
if (req.url === "/stats" || req.url?.startsWith("/stats?")) {
13341351
try {

src/stats.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Supports filtering by date range and provides multiple aggregation views.
66
*/
77

8-
import { readdir } from "node:fs/promises";
8+
import { readdir, unlink } from "node:fs/promises";
99
import { readTextFile } from "./fs-read.js";
1010
import { join } from "node:path";
1111
import { homedir } from "node:os";
@@ -294,3 +294,19 @@ export function formatStatsAscii(stats: AggregatedStats): string {
294294

295295
return lines.join("\n");
296296
}
297+
298+
/**
299+
* Delete all usage log files, resetting stats to zero.
300+
*/
301+
export async function clearStats(): Promise<{ deletedFiles: number }> {
302+
try {
303+
const files = await readdir(LOG_DIR);
304+
const logFiles = files.filter((f) => f.startsWith("usage-") && f.endsWith(".jsonl"));
305+
306+
await Promise.all(logFiles.map((f) => unlink(join(LOG_DIR, f))));
307+
308+
return { deletedFiles: logFiles.length };
309+
} catch {
310+
return { deletedFiles: 0 };
311+
}
312+
}

0 commit comments

Comments
 (0)