UNPKG

@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
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;