export function mount({ root, state, host }) { state = state || {}; const __cleanup = []; const __setInterval = (...args) => { const id = window.setInterval(...args); __cleanup.push(() => window.clearInterval(id)); return id; }; const __setTimeout = (...args) => { const id = window.setTimeout(...args); __cleanup.push(() => window.clearTimeout(id)); return id; }; if (!(root instanceof HTMLElement)) return; const subtitleEl = root.querySelector('[data-usage-subtitle]'); const statusEl = root.querySelector('[data-usage-status]'); const updatedEl = root.querySelector('[data-usage-updated]'); const gridEl = root.querySelector('[data-usage-grid]'); const monthSectionEl = root.querySelector('[data-usage-month-section]'); const tokens24hEl = root.querySelector('[data-usage-tokens-24h]'); const power24hEl = root.querySelector('[data-usage-power-24h]'); const window24hEl = root.querySelector('[data-usage-window-24h]'); const tokensMonthEl = root.querySelector('[data-usage-tokens-month]'); const powerMonthEl = root.querySelector('[data-usage-power-month]'); const windowMonthEl = root.querySelector('[data-usage-window-month]'); if (!(subtitleEl instanceof HTMLElement) || !(statusEl instanceof HTMLElement) || !(updatedEl instanceof HTMLElement) || !(gridEl instanceof HTMLElement) || !(monthSectionEl instanceof HTMLElement) || !(tokens24hEl instanceof HTMLElement) || !(power24hEl instanceof HTMLElement) || !(window24hEl instanceof HTMLElement) || !(tokensMonthEl instanceof HTMLElement) || !(powerMonthEl instanceof HTMLElement) || !(windowMonthEl instanceof HTMLElement)) return; const subtitle = typeof state.subtitle === 'string' ? state.subtitle : ''; const configuredToolName24h = typeof state.tool_name_24h === 'string' ? state.tool_name_24h.trim() : typeof state.tool_name === 'string' ? state.tool_name.trim() : ''; const configuredToolNameMonth = typeof state.tool_name_month === 'string' ? state.tool_name_month.trim() : ''; const rawToolArguments24h = state && typeof state.tool_arguments_24h === 'object' && state.tool_arguments_24h && !Array.isArray(state.tool_arguments_24h) ? state.tool_arguments_24h : state && typeof state.tool_arguments === 'object' && state.tool_arguments && !Array.isArray(state.tool_arguments) ? state.tool_arguments : {}; const rawToolArgumentsMonth = state && typeof state.tool_arguments_month === 'object' && state.tool_arguments_month && !Array.isArray(state.tool_arguments_month) ? state.tool_arguments_month : {}; const source24h = typeof state.source_url_24h === 'string' ? state.source_url_24h.trim() : ''; const sourceMonth = typeof state.source_url_month === 'string' ? state.source_url_month.trim() : ''; const refreshMsRaw = Number(state.refresh_ms); const refreshMs = Number.isFinite(refreshMsRaw) && refreshMsRaw >= 60000 ? refreshMsRaw : 15 * 60 * 1000; const tokenFormatter = new Intl.NumberFormat([], { notation: 'compact', maximumFractionDigits: 1 }); const kwhFormatter = new Intl.NumberFormat([], { minimumFractionDigits: 1, maximumFractionDigits: 1 }); const moneyFormatter = new Intl.NumberFormat([], { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 }); subtitleEl.textContent = subtitle || 'LiteLLM activity vs local UPS energy'; const hasMonthSource = Boolean( sourceMonth || configuredToolNameMonth || Object.keys(rawToolArgumentsMonth).length, ); if (!hasMonthSource) { monthSectionEl.style.display = 'none'; gridEl.style.gridTemplateColumns = 'minmax(0, 1fr)'; } const updateLiveContent = (snapshot) => { host.setLiveContent(snapshot); }; const setStatus = (label, color) => { statusEl.textContent = label; statusEl.style.color = color; }; const parseLocalTimestamp = (raw) => { if (typeof raw !== 'string' || !raw.trim()) return null; const match = raw.trim().match(/^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) ([+-]\d{2})(\d{2})$/); if (!match) return null; const value = `${match[1]}T${match[2]}${match[3]}:${match[4]}`; const parsed = new Date(value); return Number.isNaN(parsed.getTime()) ? null : parsed; }; const formatRangeLabel = (payload, fallbackLabel) => { const startRaw = typeof payload?.range_start_local === 'string' ? payload.range_start_local : ''; const endRaw = typeof payload?.range_end_local === 'string' ? payload.range_end_local : ''; const start = parseLocalTimestamp(startRaw); const end = parseLocalTimestamp(endRaw); if (!(start instanceof Date) || Number.isNaN(start.getTime()) || !(end instanceof Date) || Number.isNaN(end.getTime())) { return fallbackLabel; } return `${start.toLocaleDateString([], { month: 'short', day: 'numeric' })} to ${end.toLocaleDateString([], { month: 'short', day: 'numeric' })}`; }; const renderSection = (elements, payload, fallbackLabel) => { const tokens = Number(payload?.total_tokens_processed); const kwh = Number(payload?.total_ups_kwh_in_range); const localCost = Number(payload?.local_cost_usd_in_range); elements.tokens.textContent = Number.isFinite(tokens) ? tokenFormatter.format(tokens) : '--'; elements.power.textContent = Number.isFinite(kwh) && Number.isFinite(localCost) ? `${kwhFormatter.format(kwh)} kWh ยท ${moneyFormatter.format(localCost)}` : Number.isFinite(kwh) ? `${kwhFormatter.format(kwh)} kWh` : '--'; elements.window.textContent = formatRangeLabel(payload, fallbackLabel); }; const blankSection = (elements, fallbackLabel) => { elements.tokens.textContent = '--'; elements.power.textContent = '--'; elements.window.textContent = fallbackLabel; }; const shellEscape = (value) => `'${String(value ?? '').replace(/'/g, `'\"'\"'`)}'`; const buildLegacyExecCommand = (rawUrl) => { if (typeof rawUrl !== 'string' || !rawUrl.startsWith('/script/proxy/')) return ''; const [pathPart, queryPart = ''] = rawUrl.split('?', 2); const relativeScript = pathPart.slice('/script/proxy/'.length).replace(/^\/+/, ''); if (!relativeScript) return ''; const params = new URLSearchParams(queryPart); const args = params.getAll('arg').map((value) => value.trim()).filter(Boolean); const scriptPath = `$HOME/.nanobot/workspace/${relativeScript}`; return `python3 ${scriptPath}${args.length ? ` ${args.map(shellEscape).join(' ')}` : ''}`; }; const resolveToolCall = (toolName, toolArguments, legacySourceUrl) => { if (toolName) { return { toolName, toolArguments, }; } const legacyCommand = buildLegacyExecCommand(legacySourceUrl); if (!legacyCommand) return null; return { toolName: 'exec', toolArguments: { command: legacyCommand }, }; }; const loadPayload = async (toolCall) => { if (!toolCall) throw new Error('Missing tool_name/tool_arguments'); if (!host.callToolAsync) throw new Error('Async tool helper unavailable'); const toolResult = await host.callToolAsync( toolCall.toolName, toolCall.toolArguments, { timeoutMs: 180000 }, ); if (toolResult && toolResult.parsed && typeof toolResult.parsed === 'object' && !Array.isArray(toolResult.parsed)) { return toolResult.parsed; } if (typeof toolResult?.content === 'string' && toolResult.content.trim()) { const parsed = JSON.parse(toolResult.content); if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { return parsed; } } throw new Error('Tool returned invalid JSON'); }; const refresh = async () => { setStatus('Refreshing', 'var(--theme-status-muted)'); try { const toolCall24h = resolveToolCall(configuredToolName24h, rawToolArguments24h, source24h); const toolCallMonth = hasMonthSource ? resolveToolCall(configuredToolNameMonth, rawToolArgumentsMonth, sourceMonth) : null; const jobs = [loadPayload(toolCall24h), hasMonthSource ? loadPayload(toolCallMonth) : Promise.resolve(null)]; const results = await Promise.allSettled(jobs); const twentyFourHour = results[0].status === 'fulfilled' ? results[0].value : null; const month = hasMonthSource && results[1].status === 'fulfilled' ? results[1].value : null; if (twentyFourHour) { renderSection( { tokens: tokens24hEl, power: power24hEl, window: window24hEl }, twentyFourHour, 'Last 24 hours', ); } else { blankSection( { tokens: tokens24hEl, power: power24hEl, window: window24hEl }, 'Last 24 hours', ); } if (hasMonthSource && month) { renderSection( { tokens: tokensMonthEl, power: powerMonthEl, window: windowMonthEl }, month, 'This month', ); } else if (hasMonthSource) { blankSection( { tokens: tokensMonthEl, power: powerMonthEl, window: windowMonthEl }, 'This month', ); } const successCount = [twentyFourHour, month].filter(Boolean).length; const expectedCount = hasMonthSource ? 2 : 1; if (successCount === expectedCount) { setStatus('Live', 'var(--theme-status-live)'); } else if (successCount === 1) { setStatus('Partial', 'var(--theme-status-warning)'); } else { setStatus('Unavailable', 'var(--theme-status-danger)'); } const updatedText = new Date().toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); updatedEl.textContent = updatedText; updateLiveContent({ kind: 'litellm_ups_usage', subtitle: subtitleEl.textContent || null, status: statusEl.textContent || null, updated_at: updatedText, last_24h: twentyFourHour ? { total_tokens_processed: Number(twentyFourHour.total_tokens_processed) || 0, total_ups_kwh_in_range: Number(twentyFourHour.total_ups_kwh_in_range) || 0, local_cost_usd_in_range: Number(twentyFourHour.local_cost_usd_in_range) || 0, range_start_local: twentyFourHour.range_start_local || null, range_end_local: twentyFourHour.range_end_local || null, } : null, this_month: month ? { total_tokens_processed: Number(month.total_tokens_processed) || 0, total_ups_kwh_in_range: Number(month.total_ups_kwh_in_range) || 0, local_cost_usd_in_range: Number(month.local_cost_usd_in_range) || 0, range_start_local: month.range_start_local || null, range_end_local: month.range_end_local || null, } : null, }); } catch (error) { const errorText = String(error); blankSection({ tokens: tokens24hEl, power: power24hEl, window: window24hEl }, 'Last 24 hours'); if (hasMonthSource) { blankSection({ tokens: tokensMonthEl, power: powerMonthEl, window: windowMonthEl }, 'This month'); } updatedEl.textContent = errorText; setStatus('Unavailable', 'var(--theme-status-danger)'); updateLiveContent({ kind: 'litellm_ups_usage', subtitle: subtitleEl.textContent || null, status: 'Unavailable', updated_at: errorText, last_24h: null, this_month: null, error: errorText, }); } }; void refresh(); __setInterval(() => { void refresh(); }, refreshMs); return { destroy() { host.setRefreshHandler(null); host.setLiveContent(null); host.clearSelection(); for (const cleanup of __cleanup.splice(0)) cleanup(); }, }; }