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 3102b341..605ac404 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,131 +1,15 @@ // MiniMax Coding Plan Provider -import { readAuthFile } from '../../opencode/auth.js'; -import { - getAuthEntry, - normalizeAuthEntry, - buildResult, - toUsageWindow, - toNumber, - toTimestamp, -} from '../utils/index.js'; +import { createMiniMaxCodingPlanProvider } from './minimax-shared.js'; -export const providerId = 'minimax-cn-coding-plan'; -export const providerName = 'MiniMax Coding Plan (minimaxi.com)'; -export const aliases = ['minimax-cn-coding-plan']; +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 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 windows = {}; - const modelRemains = payload?.model_remains; - - if (Array.isArray(modelRemains) && modelRemains.length > 0) { - const firstModel = modelRemains[0]; - const total = toNumber(firstModel?.current_interval_total_count); - const used = 600 - toNumber(firstModel?.current_interval_usage_count); - - if (total === null || used === null) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: 'Missing required quota fields', - }); - } - - const usedPercent = - total > 0 ? Math.max(0, Math.min(100, (used / total) * 100)) : null; - - const startTime = toTimestamp(firstModel?.start_time); - const endTime = toTimestamp(firstModel?.end_time); - const windowSeconds = - startTime && endTime && endTime > startTime - ? Math.floor((endTime - startTime) / 1000) - : null; - - windows['5h'] = toUsageWindow({ - usedPercent, - windowSeconds, - resetAt: endTime, - }); - } else { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: 'No model quota data available', - }); - } - - 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', - }); - } -}; +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; 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 d9fad8ac..5c860344 100644 --- a/packages/web/server/lib/quota/providers/minimax-coding-plan.js +++ b/packages/web/server/lib/quota/providers/minimax-coding-plan.js @@ -1,131 +1,15 @@ // MiniMax Coding Plan Provider -import { readAuthFile } from '../../opencode/auth.js'; -import { - getAuthEntry, - normalizeAuthEntry, - buildResult, - toUsageWindow, - toNumber, - toTimestamp, -} from '../utils/index.js'; +import { createMiniMaxCodingPlanProvider } from './minimax-shared.js'; -export const providerId = 'minimax-coding-plan'; -export const providerName = 'MiniMax Coding Plan (minimax.io)'; -export const aliases = ['minimax-coding-plan']; +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 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.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 windows = {}; - const modelRemains = payload?.model_remains; - - if (Array.isArray(modelRemains) && modelRemains.length > 0) { - const firstModel = modelRemains[0]; - const total = toNumber(firstModel?.current_interval_total_count); - const used = 600-toNumber(firstModel?.current_interval_usage_count); - - if (total === null || used === null) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: 'Missing required quota fields', - }); - } - - const usedPercent = - total > 0 ? Math.max(0, Math.min(100, (used / total) * 100)) : null; - - const startTime = toTimestamp(firstModel?.start_time); - const endTime = toTimestamp(firstModel?.end_time); - const windowSeconds = - startTime && endTime && endTime > startTime - ? Math.floor((endTime - startTime) / 1000) - : null; - - windows['5h'] = toUsageWindow({ - usedPercent, - windowSeconds, - resetAt: endTime, - }); - } else { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: 'No model quota data available', - }); - } - - 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', - }); - } -}; +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; diff --git a/packages/web/server/lib/quota/providers/minimax-shared.js b/packages/web/server/lib/quota/providers/minimax-shared.js new file mode 100644 index 00000000..0edd7638 --- /dev/null +++ b/packages/web/server/lib/quota/providers/minimax-shared.js @@ -0,0 +1,136 @@ +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, + }; +};