@hhoangphuoc/escape-room-cli
Version:
A CLI for playing AI-generated escape room games. Install globally with: npm install -g @hhoangphuoc/escape-room-cli
50 lines (49 loc) • 8.28 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { Box, Text } from 'ink';
import { formatTokens, formatCost, getCostColor } from '../utils/formatters.js';
const CostDashboard = ({ usageData }) => {
// Handle case where usageData is undefined or null
if (!usageData) {
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "red", children: "\u274C DASHBOARD ERROR" }) }), _jsx(Text, { color: "gray", children: "Unable to load usage data. Please try again." })] }));
}
// Using shared formatters for consistency
const formatDuration = (startTime) => {
if (!startTime)
return 'Unknown';
const start = new Date(startTime);
const now = new Date();
const durationMs = now.getTime() - start.getTime();
const minutes = Math.floor(durationMs / 60000);
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
if (hours > 0) {
return `${hours}h ${remainingMinutes}m`;
}
return `${minutes}m`;
};
const calculateCostRate = (cost, startTime) => {
if (!startTime || !cost || cost === 0 || isNaN(cost))
return 'N/A';
const start = new Date(startTime);
const now = new Date();
const minutes = (now.getTime() - start.getTime()) / 60000;
if (minutes > 0) {
const hourlyRate = (cost / minutes) * 60;
return formatCost(hourlyRate) + '/hour';
}
return 'N/A';
};
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "\uD83D\uDCCA DETAILED USAGE DASHBOARD" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "green", children: "SESSION OVERVIEW" }) }), _jsxs(Box, { marginBottom: 1, paddingLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "green", children: "Session Cost:" }) }), _jsx(Box, { width: 15, children: _jsx(Text, { bold: true, color: getCostColor(usageData.currentSessionCost), children: formatCost(usageData.currentSessionCost) }) }), _jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["(", formatTokens(usageData.currentSessionTokens), " tokens)"] }) })] }), usageData.sessionStartTime && (_jsxs(Box, { marginBottom: 1, paddingLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "cyan", children: "Session Duration:" }) }), _jsx(Box, { width: 15, children: _jsx(Text, { color: "white", children: formatDuration(usageData.sessionStartTime) }) }), _jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["(Rate: ", calculateCostRate(usageData.currentSessionCost, usageData.sessionStartTime), ")"] }) })] })), (() => {
const totalRequests = Object.values(usageData.modelUsageBreakdown || {}).reduce((sum, model) => sum + model.requests, 0);
if (totalRequests > 0 && usageData.currentSessionCost) {
return (_jsxs(Box, { marginBottom: 1, paddingLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "magenta", children: "Total Requests:" }) }), _jsx(Box, { width: 15, children: _jsx(Text, { color: "white", children: totalRequests }) }), _jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["(", formatCost(usageData.currentSessionCost / totalRequests), "/req avg)"] }) })] }));
}
return null;
})(), _jsx(Box, { marginBottom: 2, children: _jsx(Text, { color: "gray", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "green", children: "\uD83D\uDCB0 COST SUMMARY" }) }), _jsxs(Box, { marginBottom: 1, paddingLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "red", children: "Total Cost:" }) }), _jsx(Box, { width: 15, children: _jsx(Text, { bold: true, color: getCostColor(usageData.userTotalCost), children: formatCost(usageData.userTotalCost) }) }), _jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["(", formatTokens(usageData.userTotalTokens), " tokens)"] }) })] }), _jsxs(Box, { marginBottom: 1, paddingLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "yellow", children: "Last Request:" }) }), _jsx(Box, { width: 15, children: _jsx(Text, { color: getCostColor(usageData.lastRequestCost), children: formatCost(usageData.lastRequestCost) }) }), _jsx(Box, { children: _jsxs(Text, { color: "gray", children: ["(", formatTokens(usageData.lastRequestTokens), " tokens)"] }) })] }), _jsxs(Box, { marginBottom: 2, paddingLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "magenta", children: "Average/Request:" }) }), _jsx(Box, { width: 15, children: _jsx(Text, { color: getCostColor(usageData.averageCostPerRequest), children: formatCost(usageData.averageCostPerRequest) }) })] }), usageData.modelUsageBreakdown && Object.keys(usageData.modelUsageBreakdown).length > 0 && (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "blue", children: "\uD83E\uDD16 LATEST SESSION BREAKDOWN" }) }), _jsxs(Box, { marginBottom: 1, paddingLeft: 2, children: [_jsx(Box, { width: 20, marginRight: 1, justifyContent: 'flex-start', children: _jsx(Text, { bold: true, color: "cyan", children: "Model" }) }), _jsx(Box, { width: 15, marginX: 1, justifyContent: 'center', children: _jsx(Text, { bold: true, color: "cyan", children: "Requests" }) }), _jsx(Box, { width: 15, marginX: 1, justifyContent: 'flex-end', children: _jsx(Text, { bold: true, color: "cyan", children: "Total Cost" }) }), _jsx(Box, { width: 15, marginX: 1, justifyContent: 'flex-end', children: _jsx(Text, { bold: true, color: "cyan", children: "Tokens" }) }), _jsx(Box, { width: 20, marginX: 1, justifyContent: 'flex-end', children: _jsx(Text, { bold: true, color: "cyan", children: "Avg/Req" }) })] }), _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsx(Text, { color: "gray", children: '─'.repeat(85) }) }), Object.entries(usageData.modelUsageBreakdown)
.sort(([, a], [, b]) => b.cost - a.cost)
.map(([model, data]) => {
const avgCost = data.requests > 0 ? data.cost / data.requests : 0;
return (_jsxs(Box, { marginBottom: 0, paddingLeft: 2, children: [_jsx(Box, { width: 20, marginRight: 1, justifyContent: 'flex-start', children: _jsx(Text, { color: "gray", children: model.length > 13 ? model.substring(0, 13) + '...' : model }) }), _jsx(Box, { width: 15, marginX: 1, justifyContent: 'center', children: _jsx(Text, { color: "gray", children: data.requests.toString().padStart(6, ' ') }) }), _jsx(Box, { width: 15, marginX: 1, justifyContent: 'flex-end', children: _jsx(Text, { color: getCostColor(data.cost), children: formatCost(data.cost).padStart(10, ' ') }) }), _jsx(Box, { width: 15, marginX: 1, justifyContent: 'flex-end', children: _jsx(Text, { color: "yellow", children: formatTokens(data.tokens).padStart(12, ' ') }) }), _jsx(Box, { width: 20, marginX: 1, justifyContent: 'flex-end', children: _jsx(Text, { color: getCostColor(avgCost), children: formatCost(avgCost) }) })] }, model));
})] })), _jsxs(Box, { marginTop: 2, flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "yellow", children: "\uD83D\uDCA1 COST OPTIMIZATION TIPS" }) }), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "gray", children: "\u2022 Use gpt-4.1-mini for simple questions (90% cheaper than gpt-4o)" }) }), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "gray", children: "\u2022 Reserve o3/o3-mini for complex reasoning tasks" }) }), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "gray", children: "\u2022 Monitor session costs with \"/cost\" command regularly" }) })] })] }));
};
export default CostDashboard;