diff --git a/packages/web/server/lib/quota/providers/minimax-cn-coding-plan.js b/packages/web/server/lib/quota/providers/minimax-cn-coding-plan.js index 605ac404..b501d3f7 100644 --- a/packages/web/server/lib/quota/providers/minimax-cn-coding-plan.js +++ b/packages/web/server/lib/quota/providers/minimax-cn-coding-plan.js @@ -1,15 +1,141 @@ -// MiniMax Coding Plan Provider -import { createMiniMaxCodingPlanProvider } from './minimax-shared.js'; +// MiniMax Coding Plan Provider (minimaxi.com) +import { readAuthFile } from '../../opencode/auth.js'; +import { + getAuthEntry, + normalizeAuthEntry, + buildResult, + toUsageWindow, + toNumber, + toTimestamp, +} from '../utils/index.js'; -const provider = createMiniMaxCodingPlanProvider({ - providerId: 'minimax-cn-coding-plan', - providerName: 'MiniMax Coding Plan (minimaxi.com)', - aliases: ['minimax-cn-coding-plan'], - endpoint: 'https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains', -}); +export const providerId = 'minimax-cn-coding-plan'; +export const providerName = 'MiniMax Coding Plan (minimaxi.com)'; +export const aliases = ['minimax-cn-coding-plan']; -export const providerId = provider.providerId; -export const providerName = provider.providerName; -export const aliases = provider.aliases; -export const isConfigured = provider.isConfigured; -export const fetchQuota = provider.fetchQuota; +export const isConfigured = () => { + const auth = readAuthFile(); + const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); + return Boolean(entry?.key || entry?.token); +}; + +export const fetchQuota = async () => { + const auth = readAuthFile(); + const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); + const apiKey = entry?.key ?? entry?.token; + + if (!apiKey) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: false, + error: 'Not configured', + }); + } + + try { + const response = await fetch( + 'https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains', + { + method: 'GET', + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + } + ); + + if (!response.ok) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: `API error: ${response.status}`, + }); + } + + const payload = await response.json(); + const baseResp = payload?.base_resp; + if (baseResp && baseResp.status_code !== 0) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: baseResp.status_msg || `API error: ${baseResp.status_code}`, + }); + } + + const firstModel = payload?.model_remains?.[0]; + if (!firstModel) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: 'No model quota data available', + }); + } + + const intervalTotal = toNumber(firstModel.current_interval_total_count); + const intervalUsage = toNumber(firstModel.current_interval_usage_count); + const intervalStartAt = toTimestamp(firstModel.start_time); + const intervalResetAt = toTimestamp(firstModel.end_time); + const weeklyTotal = toNumber(firstModel.current_weekly_total_count); + const weeklyUsage = toNumber(firstModel.current_weekly_usage_count); + const weeklyStartAt = toTimestamp(firstModel.weekly_start_time); + const weeklyResetAt = toTimestamp(firstModel.weekly_end_time); + + // For minimaxi.com: usage_count represents USED credits + const intervalUsed = intervalUsage; + const weeklyUsed = weeklyUsage; + + const intervalUsedPercent = + intervalTotal > 0 && intervalUsed != null + ? Math.max(0, Math.min(100, (intervalUsed / intervalTotal) * 100)) + : null; + const intervalWindowSeconds = + intervalStartAt && intervalResetAt && intervalResetAt > intervalStartAt + ? Math.floor((intervalResetAt - intervalStartAt) / 1000) + : null; + const weeklyUsedPercent = + weeklyTotal > 0 && weeklyUsed != null + ? Math.max(0, Math.min(100, (weeklyUsed / weeklyTotal) * 100)) + : null; + const weeklyWindowSeconds = + weeklyStartAt && weeklyResetAt && weeklyResetAt > weeklyStartAt + ? Math.floor((weeklyResetAt - weeklyStartAt) / 1000) + : null; + + const windows = { + '5h': toUsageWindow({ + usedPercent: intervalUsedPercent, + windowSeconds: intervalWindowSeconds, + resetAt: intervalResetAt, + }), + weekly: toUsageWindow({ + usedPercent: weeklyUsedPercent, + windowSeconds: weeklyWindowSeconds, + resetAt: weeklyResetAt, + }), + }; + + return buildResult({ + providerId, + providerName, + ok: true, + configured: true, + usage: { windows }, + }); + } catch (error) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: error instanceof Error ? error.message : 'Request failed', + }); + } +}; diff --git a/packages/web/server/lib/quota/providers/minimax-coding-plan.js b/packages/web/server/lib/quota/providers/minimax-coding-plan.js index 5c860344..ae74f75f 100644 --- a/packages/web/server/lib/quota/providers/minimax-coding-plan.js +++ b/packages/web/server/lib/quota/providers/minimax-coding-plan.js @@ -1,15 +1,139 @@ -// MiniMax Coding Plan Provider -import { createMiniMaxCodingPlanProvider } from './minimax-shared.js'; +import { readAuthFile } from '../../opencode/auth.js'; +import { + getAuthEntry, + normalizeAuthEntry, + buildResult, + toUsageWindow, + toNumber, + toTimestamp, +} from '../utils/index.js'; -const provider = createMiniMaxCodingPlanProvider({ - providerId: 'minimax-coding-plan', - providerName: 'MiniMax Coding Plan (minimax.io)', - aliases: ['minimax-coding-plan'], - endpoint: 'https://www.minimax.io/v1/api/openplatform/coding_plan/remains', -}); +export const providerId = 'minimax-coding-plan'; +export const providerName = 'MiniMax Coding Plan (minimax.io)'; +export const aliases = ['minimax-coding-plan']; -export const providerId = provider.providerId; -export const providerName = provider.providerName; -export const aliases = provider.aliases; -export const isConfigured = provider.isConfigured; -export const fetchQuota = provider.fetchQuota; +export const isConfigured = () => { + const auth = readAuthFile(); + const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); + return Boolean(entry?.key || entry?.token); +}; + +export const fetchQuota = async () => { + const auth = readAuthFile(); + const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); + const apiKey = entry?.key ?? entry?.token; + + if (!apiKey) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: false, + error: 'Not configured', + }); + } + + try { + const response = await fetch( + 'https://api.minimax.io/v1/api/openplatform/coding_plan/remains', + { + method: 'GET', + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + } + ); + + if (!response.ok) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: `API error: ${response.status}`, + }); + } + + const payload = await response.json(); + const baseResp = payload?.base_resp; + if (baseResp && baseResp.status_code !== 0) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: baseResp.status_msg || `API error: ${baseResp.status_code}`, + }); + } + + const firstModel = payload?.model_remains?.[0]; + if (!firstModel) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: 'No model quota data available', + }); + } + + const intervalTotal = toNumber(firstModel.current_interval_total_count); + const intervalUsage = toNumber(firstModel.current_interval_usage_count); + const intervalStartAt = toTimestamp(firstModel.start_time); + const intervalResetAt = toTimestamp(firstModel.end_time); + const weeklyTotal = toNumber(firstModel.current_weekly_total_count); + const weeklyUsage = toNumber(firstModel.current_weekly_usage_count); + const weeklyStartAt = toTimestamp(firstModel.weekly_start_time); + const weeklyResetAt = toTimestamp(firstModel.weekly_end_time); + + const intervalUsed = intervalUsage; + const weeklyUsed = weeklyUsage; + + const intervalUsedPercent = + intervalTotal > 0 && intervalUsed !== null + ? Math.max(0, Math.min(100, (intervalUsed / intervalTotal) * 100)) + : null; + const intervalWindowSeconds = + intervalStartAt && intervalResetAt && intervalResetAt > intervalStartAt + ? Math.floor((intervalResetAt - intervalStartAt) / 1000) + : null; + const weeklyUsedPercent = + weeklyTotal > 0 && weeklyUsed !== null + ? Math.max(0, Math.min(100, (weeklyUsed / weeklyTotal) * 100)) + : null; + const weeklyWindowSeconds = + weeklyStartAt && weeklyResetAt && weeklyResetAt > weeklyStartAt + ? Math.floor((weeklyResetAt - weeklyStartAt) / 1000) + : null; + + const windows = { + '5h': toUsageWindow({ + usedPercent: intervalUsedPercent, + windowSeconds: intervalWindowSeconds, + resetAt: intervalResetAt, + }), + weekly: toUsageWindow({ + usedPercent: weeklyUsedPercent, + windowSeconds: weeklyWindowSeconds, + resetAt: weeklyResetAt, + }), + }; + + return buildResult({ + providerId, + providerName, + ok: true, + configured: true, + usage: { windows }, + }); + } catch (error) { + return buildResult({ + providerId, + providerName, + ok: false, + configured: true, + error: error instanceof Error ? error.message : 'Request failed', + }); + } +}; diff --git a/packages/web/server/lib/quota/providers/minimax-shared.js b/packages/web/server/lib/quota/providers/minimax-shared.js deleted file mode 100644 index 0edd7638..00000000 --- a/packages/web/server/lib/quota/providers/minimax-shared.js +++ /dev/null @@ -1,136 +0,0 @@ -import { readAuthFile } from '../../opencode/auth.js'; -import { - getAuthEntry, - normalizeAuthEntry, - buildResult, - toUsageWindow, - toNumber, - toTimestamp, -} from '../utils/index.js'; - -export const createMiniMaxCodingPlanProvider = ({ providerId, providerName, aliases, endpoint }) => { - const isConfigured = () => { - const auth = readAuthFile(); - const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); - return Boolean(entry?.key || entry?.token); - }; - - const fetchQuota = async () => { - const auth = readAuthFile(); - const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); - const apiKey = entry?.key ?? entry?.token; - - if (!apiKey) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: false, - error: 'Not configured', - }); - } - - try { - const response = await fetch(endpoint, { - method: 'GET', - headers: { - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: `API error: ${response.status}`, - }); - } - - const payload = await response.json(); - const baseResp = payload?.base_resp; - if (baseResp && baseResp.status_code !== 0) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: baseResp.status_msg || `API error: ${baseResp.status_code}`, - }); - } - - const firstModel = payload?.model_remains?.[0]; - if (!firstModel) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: 'No model quota data available', - }); - } - - const intervalTotal = toNumber(firstModel.current_interval_total_count); - const intervalUsage = toNumber(firstModel.current_interval_usage_count); - const intervalStartAt = toTimestamp(firstModel.start_time); - const intervalResetAt = toTimestamp(firstModel.end_time); - const weeklyTotal = toNumber(firstModel.current_weekly_total_count); - const weeklyUsage = toNumber(firstModel.current_weekly_usage_count); - const weeklyStartAt = toTimestamp(firstModel.weekly_start_time); - const weeklyResetAt = toTimestamp(firstModel.weekly_end_time); - const intervalUsed = intervalTotal - intervalUsage; - const weeklyUsed = weeklyTotal - weeklyUsage; - const intervalUsedPercent = - intervalTotal > 0 ? Math.max(0, Math.min(100, (intervalUsed / intervalTotal) * 100)) : null; - const intervalWindowSeconds = - intervalStartAt && intervalResetAt && intervalResetAt > intervalStartAt - ? Math.floor((intervalResetAt - intervalStartAt) / 1000) - : null; - const weeklyUsedPercent = - weeklyTotal > 0 ? Math.max(0, Math.min(100, (weeklyUsed / weeklyTotal) * 100)) : null; - const weeklyWindowSeconds = - weeklyStartAt && weeklyResetAt && weeklyResetAt > weeklyStartAt - ? Math.floor((weeklyResetAt - weeklyStartAt) / 1000) - : null; - - const windows = { - '5h': toUsageWindow({ - usedPercent: intervalUsedPercent, - windowSeconds: intervalWindowSeconds, - resetAt: intervalResetAt, - }), - weekly: toUsageWindow({ - usedPercent: weeklyUsedPercent, - windowSeconds: weeklyWindowSeconds, - resetAt: weeklyResetAt, - }), - }; - - return buildResult({ - providerId, - providerName, - ok: true, - configured: true, - usage: { windows }, - }); - } catch (error) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: error instanceof Error ? error.message : 'Request failed', - }); - } - }; - - return { - providerId, - providerName, - aliases, - isConfigured, - fetchQuota, - }; -};