Files
DeskClaw/electron/utils/token-usage.ts

74 lines
2.6 KiB
TypeScript

import { readdir, readFile, stat } from 'fs/promises';
import { join } from 'path';
import { getOpenClawConfigDir } from './paths';
import { logger } from './logger';
import { listConfiguredAgentIds } from './agent-config';
import { parseUsageEntriesFromJsonl, type TokenUsageHistoryEntry } from './token-usage-core';
export { parseUsageEntriesFromJsonl, type TokenUsageHistoryEntry } from './token-usage-core';
async function listRecentSessionFiles(): Promise<Array<{ filePath: string; sessionId: string; agentId: string; mtimeMs: number }>> {
const openclawDir = getOpenClawConfigDir();
const agentsDir = join(openclawDir, 'agents');
try {
const agentEntries = await listConfiguredAgentIds();
const files: Array<{ filePath: string; sessionId: string; agentId: string; mtimeMs: number }> = [];
for (const agentId of agentEntries) {
const sessionsDir = join(agentsDir, agentId, 'sessions');
try {
const sessionEntries = await readdir(sessionsDir);
for (const fileName of sessionEntries) {
if (!fileName.endsWith('.jsonl') || fileName.includes('.deleted.')) continue;
const filePath = join(sessionsDir, fileName);
try {
const fileStat = await stat(filePath);
files.push({
filePath,
sessionId: fileName.replace(/\.jsonl$/, ''),
agentId,
mtimeMs: fileStat.mtimeMs,
});
} catch {
continue;
}
}
} catch {
continue;
}
}
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
return files;
} catch {
return [];
}
}
export async function getRecentTokenUsageHistory(limit?: number): Promise<TokenUsageHistoryEntry[]> {
const files = await listRecentSessionFiles();
const results: TokenUsageHistoryEntry[] = [];
const maxEntries = typeof limit === 'number' && Number.isFinite(limit)
? Math.max(Math.floor(limit), 0)
: Number.POSITIVE_INFINITY;
for (const file of files) {
if (results.length >= maxEntries) break;
try {
const content = await readFile(file.filePath, 'utf8');
const entries = parseUsageEntriesFromJsonl(content, {
sessionId: file.sessionId,
agentId: file.agentId,
}, Number.isFinite(maxEntries) ? maxEntries - results.length : undefined);
results.push(...entries);
} catch (error) {
logger.debug(`Failed to read token usage transcript ${file.filePath}:`, error);
}
}
results.sort((a, b) => Date.parse(b.timestamp) - Date.parse(a.timestamp));
return Number.isFinite(maxEntries) ? results.slice(0, maxEntries) : results;
}