| import { getModelsByProviderId } from "open-sse/config/providerModels.js"; |
|
|
| |
| |
| |
| |
| |
| export function formatResetTime(date) { |
| if (!date) return "-"; |
|
|
| try { |
| const resetDate = typeof date === "string" ? new Date(date) : date; |
| const now = new Date(); |
| const diffMs = resetDate - now; |
|
|
| if (diffMs <= 0) return "-"; |
|
|
| const totalMinutes = Math.ceil(diffMs / (1000 * 60)); |
| |
| |
| if (totalMinutes < 60) { |
| return `${totalMinutes}m`; |
| } |
| |
| const totalHours = Math.floor(totalMinutes / 60); |
| const remainingMinutes = totalMinutes % 60; |
| |
| |
| if (totalHours < 24) { |
| return `${totalHours}h ${remainingMinutes}m`; |
| } |
| |
| |
| const days = Math.floor(totalHours / 24); |
| const remainingHours = totalHours % 24; |
| return `${days}d ${remainingHours}h ${remainingMinutes}m`; |
| } catch (error) { |
| return "-"; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| export function getStatusColor(percentage) { |
| if (percentage > 70) return "green"; |
| if (percentage >= 30) return "yellow"; |
| return "red"; |
| } |
|
|
| |
| |
| |
| |
| |
| export function getStatusEmoji(percentage) { |
| if (percentage > 70) return "🟢"; |
| if (percentage >= 30) return "🟡"; |
| return "🔴"; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function calculatePercentage(used, total) { |
| if (!total || total === 0) return 0; |
| if (!used || used < 0) return 100; |
| if (used >= total) return 0; |
|
|
| return Math.round(((total - used) / total) * 100); |
| } |
|
|
| |
| |
| |
| |
| |
| export function getRemainingPercentage(quota) { |
| if (quota?.remaining !== undefined) { |
| return Math.max(0, Math.round(quota.remaining)); |
| } |
|
|
| if (quota?.remainingPercentage !== undefined) { |
| return Math.round(quota.remainingPercentage); |
| } |
|
|
| return calculatePercentage(quota?.used, quota?.total); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function parseQuotaData(provider, data) { |
| if (!data || typeof data !== "object") return []; |
|
|
| const normalizedQuotas = []; |
|
|
| try { |
| switch (provider.toLowerCase()) { |
| case "github": |
| if (data.quotas) { |
| Object.entries(data.quotas).forEach(([name, quota]) => { |
| normalizedQuotas.push({ |
| name, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| resetAt: quota.resetAt || null, |
| }); |
| }); |
| } |
| break; |
|
|
| case "antigravity": |
| if (data.quotas) { |
| Object.entries(data.quotas).forEach(([modelKey, quota]) => { |
| normalizedQuotas.push({ |
| name: quota.displayName || modelKey, |
| modelKey: modelKey, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| resetAt: quota.resetAt || null, |
| remainingPercentage: quota.remainingPercentage, |
| }); |
| }); |
| } |
| break; |
|
|
| case "codex": |
| if (data.quotas) { |
| Object.entries(data.quotas).forEach(([quotaType, quota]) => { |
| normalizedQuotas.push({ |
| name: quotaType, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| remaining: quota.remaining, |
| resetAt: quota.resetAt || null, |
| }); |
| }); |
| } |
| break; |
|
|
| case "kiro": |
| if (data.quotas) { |
| Object.entries(data.quotas).forEach(([quotaType, quota]) => { |
| normalizedQuotas.push({ |
| name: quotaType, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| resetAt: quota.resetAt || null, |
| }); |
| }); |
| } |
| break; |
|
|
| case "qoder": |
| |
| |
| |
| |
| |
| |
| |
| |
| if (data.quotas) { |
| Object.entries(data.quotas).forEach(([quotaType, quota]) => { |
| if (quotaType === "organization" && (!quota || (Number(quota.total) || 0) === 0)) { |
| return; |
| } |
| normalizedQuotas.push({ |
| name: quotaType === "user" ? "Personal" : quotaType === "organization" ? "Organization" : quotaType, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| unit: quota.unit, |
| resetAt: quota.resetAt || null, |
| }); |
| }); |
| } |
| break; |
|
|
| case "claude": |
| if (data.message) { |
| |
| normalizedQuotas.push({ |
| name: "error", |
| used: 0, |
| total: 0, |
| resetAt: null, |
| message: data.message, |
| }); |
| } else if (data.quotas) { |
| Object.entries(data.quotas).forEach(([name, quota]) => { |
| normalizedQuotas.push({ |
| name, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| resetAt: quota.resetAt || null, |
| }); |
| }); |
| } |
| break; |
|
|
| case "vercel-ai-gateway": |
| |
| |
| |
| |
| if (data.quotas) { |
| Object.entries(data.quotas).forEach(([name, quota]) => { |
| normalizedQuotas.push({ |
| name, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| resetAt: quota.resetAt || null, |
| remainingPercentage: quota.remainingPercentage, |
| }); |
| }); |
| } |
| break; |
|
|
| default: |
| |
| if (data.quotas) { |
| Object.entries(data.quotas).forEach(([name, quota]) => { |
| normalizedQuotas.push({ |
| name, |
| used: quota.used || 0, |
| total: quota.total || 0, |
| resetAt: quota.resetAt || null, |
| }); |
| }); |
| } |
| } |
| } catch (error) { |
| console.error(`Error parsing quota data for ${provider}:`, error); |
| return []; |
| } |
|
|
| |
| const modelOrder = getModelsByProviderId(provider); |
| if (modelOrder.length > 0) { |
| const orderMap = new Map(modelOrder.map((m, i) => [m.id, i])); |
| |
| normalizedQuotas.sort((a, b) => { |
| |
| const keyA = a.modelKey || a.name; |
| const keyB = b.modelKey || b.name; |
| const orderA = orderMap.get(keyA) ?? 999; |
| const orderB = orderMap.get(keyB) ?? 999; |
| return orderA - orderB; |
| }); |
| } |
|
|
| return normalizedQuotas; |
| } |
|
|