/** * Flynn Token Usage Page * * Shows per-session token usage breakdown including input/output tokens, * API calls, estimated cost, and delegation details. * Auto-refreshes every 30 seconds. */ let _timer = null; function formatNumber(n) { return (n ?? 0).toLocaleString(); } function formatCost(n) { if (!n || n === 0) {return '$0.00';} if (n < 0.01) {return `$${n.toFixed(4)}`;} return `$${n.toFixed(2)}`; } function truncateId(id) { if (!id) {return '-';} if (id.length <= 24) {return id;} return id.slice(0, 24) + '\u2026'; } async function loadUsage(el, client) { let data; try { data = await client.call('system.tokenUsage'); } catch (err) { el.innerHTML = `
Failed to load usage: ${err.message}
`; return; } const sessions = data?.sessions ?? []; // Compute totals across all sessions let totalInput = 0; let totalOutput = 0; let totalCalls = 0; let totalCost = 0; for (const s of sessions) { totalInput += s.total?.inputTokens ?? 0; totalOutput += s.total?.outputTokens ?? 0; totalCalls += s.total?.calls ?? 0; totalCost += s.total?.estimatedCost ?? 0; } // Summary cards const summaryHtml = `
Total Input Tokens
${formatNumber(totalInput)}
Total Output Tokens
${formatNumber(totalOutput)}
Total Tokens
${formatNumber(totalInput + totalOutput)}
API Calls
${formatNumber(totalCalls)}
Estimated Cost
${formatCost(totalCost)}
Active Sessions
${sessions.length}
`; // Per-session table let tableHtml = ''; if (sessions.length === 0) { tableHtml = '
No active sessions with usage data
'; } else { const rows = sessions.map(s => { const inTok = s.total?.inputTokens ?? 0; const outTok = s.total?.outputTokens ?? 0; const calls = s.total?.calls ?? 0; const cost = s.total?.estimatedCost ?? 0; // Build delegation breakdown if present const delegationEntries = Object.entries(s.delegation ?? {}); let delegationCell = '-'; if (delegationEntries.length > 0) { delegationCell = delegationEntries.map(([tier, stats]) => `${tier} ${formatNumber(stats.inputTokens)}/${formatNumber(stats.outputTokens)}`, ).join('
'); } return ` ${truncateId(s.sessionId)} ${formatNumber(inTok)} ${formatNumber(outTok)} ${formatNumber(inTok + outTok)} ${formatNumber(calls)} ${formatCost(cost)} ${delegationCell} `; }).join(''); tableHtml = ` ${rows}
Session Input Output Total Calls Cost Delegation
`; } el.innerHTML = `

Token Usage

${summaryHtml}

Per-Session Breakdown

${tableHtml} `; // Wire up refresh button const refreshBtn = el.querySelector('#usage-refresh-btn'); if (refreshBtn) { refreshBtn.addEventListener('click', () => { loadUsage(el, client).catch(() => {}); }); } } export const UsagePage = { async render(el, client) { await loadUsage(el, client); // Auto-refresh every 30 seconds _timer = setInterval(() => { loadUsage(el, client).catch(() => {}); }, 30000); }, teardown() { if (_timer) { clearInterval(_timer); _timer = null; } }, };