import { readdir, readFile, stat } from 'fs/promises'; import { join } from 'path'; import { getOpenClawConfigDir } from './paths'; import { logger } from './logger'; import { parseUsageEntriesFromJsonl, type TokenUsageHistoryEntry } from './token-usage-core'; export { parseUsageEntriesFromJsonl, type TokenUsageHistoryEntry } from './token-usage-core'; async function listRecentSessionFiles(): Promise> { const openclawDir = getOpenClawConfigDir(); const agentsDir = join(openclawDir, 'agents'); try { const agentEntries = await readdir(agentsDir); 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 = 20): Promise { const files = await listRecentSessionFiles(); const results: TokenUsageHistoryEntry[] = []; for (const file of files) { if (results.length >= limit) break; try { const content = await readFile(file.filePath, 'utf8'); const entries = parseUsageEntriesFromJsonl(content, { sessionId: file.sessionId, agentId: file.agentId, }, limit - results.length); 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 results.slice(0, limit); }