openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
704 lines (703 loc) • 23.2 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js";
import { _ as parseStrictFiniteNumber, g as parseFiniteNumber$1, j as resolveTimerTimeoutMs, o as asDateTimestampMs } from "./number-coercion-CJQ8TR--.js";
import "./parse-finite-number-Z7n6tXLk.js";
import { c as isRecord } from "./utils-CCC-BEJH.js";
import { c as resolveProviderRequestHeaders } from "./provider-request-config-0Zg8QIAI.js";
import { n as PROVIDER_LABELS, r as clampPercent } from "./provider-usage.shared-DOxrEMuU.js";
//#region src/infra/provider-usage.fetch.shared.ts
/** Fetches JSON-compatible provider usage endpoints with an abort timeout. */
async function fetchJson(url, init, timeoutMs, fetchFn) {
const safeTimeoutMs = resolveTimerTimeoutMs(timeoutMs, 1);
const controller = new AbortController();
const timer = setTimeout(controller.abort.bind(controller), safeTimeoutMs);
try {
return await fetchFn(url, {
...init,
signal: controller.signal
});
} finally {
clearTimeout(timer);
}
}
function parseFiniteNumber(value) {
return parseFiniteNumber$1(value);
}
/** Builds a provider usage snapshot for non-HTTP fetch or parse failures. */
function buildUsageErrorSnapshot(provider, error) {
return {
provider,
displayName: PROVIDER_LABELS[provider],
windows: [],
error
};
}
function buildUsageHttpErrorSnapshot(options) {
if ((options.tokenExpiredStatuses ?? []).includes(options.status)) return buildUsageErrorSnapshot(options.provider, "Token expired");
const suffix = options.message?.trim() ? `: ${options.message.trim()}` : "";
return buildUsageErrorSnapshot(options.provider, `HTTP ${options.status}${suffix}`);
}
async function readUsageJson(provider, response) {
try {
return {
ok: true,
data: await response.json()
};
} catch {
return {
ok: false,
snapshot: buildUsageErrorSnapshot(provider, "Malformed usage response")
};
}
}
//#endregion
//#region src/infra/provider-usage.fetch.claude.ts
function buildClaudeUsageWindows(data) {
const windows = [];
if (data.five_hour?.utilization !== void 0) windows.push({
label: "5h",
usedPercent: clampPercent(data.five_hour.utilization),
resetAt: data.five_hour.resets_at ? new Date(data.five_hour.resets_at).getTime() : void 0
});
if (data.seven_day?.utilization !== void 0) windows.push({
label: "Week",
usedPercent: clampPercent(data.seven_day.utilization),
resetAt: data.seven_day.resets_at ? new Date(data.seven_day.resets_at).getTime() : void 0
});
const modelWindow = data.seven_day_sonnet || data.seven_day_opus;
if (modelWindow?.utilization !== void 0) windows.push({
label: data.seven_day_sonnet ? "Sonnet" : "Opus",
usedPercent: clampPercent(modelWindow.utilization)
});
return windows;
}
function resolveClaudeWebSessionKey() {
const direct = process.env.CLAUDE_AI_SESSION_KEY?.trim() ?? process.env.CLAUDE_WEB_SESSION_KEY?.trim();
if (direct?.startsWith("sk-ant-")) return direct;
const cookieHeader = process.env.CLAUDE_WEB_COOKIE?.trim();
if (!cookieHeader) return;
const value = cookieHeader.replace(/^cookie:\s*/i, "").match(/(?:^|;\s*)sessionKey=([^;\s]+)/i)?.[1]?.trim();
return value?.startsWith("sk-ant-") ? value : void 0;
}
async function fetchClaudeWebUsage(sessionKey, timeoutMs, fetchFn) {
const headers = {
Cookie: `sessionKey=${sessionKey}`,
Accept: "application/json"
};
const orgRes = await fetchJson("https://claude.ai/api/organizations", { headers }, timeoutMs, fetchFn);
if (!orgRes.ok) return null;
const parsedOrgs = await readUsageJson("anthropic", orgRes);
if (!parsedOrgs.ok) return null;
const orgId = parsedOrgs.data?.[0]?.uuid?.trim();
if (!orgId) return null;
const usageRes = await fetchJson(`https://claude.ai/api/organizations/${orgId}/usage`, { headers }, timeoutMs, fetchFn);
if (!usageRes.ok) return null;
const parsedUsage = await readUsageJson("anthropic", usageRes);
if (!parsedUsage.ok) return null;
const data = parsedUsage.data;
const windows = buildClaudeUsageWindows(data);
if (windows.length === 0) return null;
return {
provider: "anthropic",
displayName: PROVIDER_LABELS.anthropic,
windows
};
}
async function fetchClaudeUsage(token, timeoutMs, fetchFn) {
const res = await fetchJson("https://api.anthropic.com/api/oauth/usage", { headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "openclaw",
Accept: "application/json",
"anthropic-version": "2023-06-01",
"anthropic-beta": "oauth-2025-04-20"
} }, timeoutMs, fetchFn);
if (!res.ok) {
let message;
try {
const raw = (await res.json())?.error?.message;
if (typeof raw === "string" && raw.trim()) message = raw.trim();
} catch {}
if (res.status === 403 && message?.includes("scope requirement user:profile")) {
const sessionKey = resolveClaudeWebSessionKey();
if (sessionKey) {
const web = await fetchClaudeWebUsage(sessionKey, timeoutMs, fetchFn);
if (web) return web;
}
}
return buildUsageHttpErrorSnapshot({
provider: "anthropic",
status: res.status,
message
});
}
const parsed = await readUsageJson("anthropic", res);
if (!parsed.ok) return parsed.snapshot;
const data = parsed.data;
const windows = buildClaudeUsageWindows(data);
return {
provider: "anthropic",
displayName: PROVIDER_LABELS.anthropic,
windows
};
}
//#endregion
//#region src/infra/provider-usage.fetch.codex.ts
const WEEKLY_RESET_GAP_SECONDS = 4320 * 60;
function resolveSecondaryWindowLabel(params) {
if (params.windowHours >= 168) return "Week";
if (params.windowHours < 24) return `${params.windowHours}h`;
if (typeof params.secondaryResetAt === "number" && typeof params.primaryResetAt === "number" && params.secondaryResetAt - params.primaryResetAt >= WEEKLY_RESET_GAP_SECONDS) return "Week";
return "Day";
}
async function fetchCodexUsage(token, accountId, timeoutMs, fetchFn) {
const version = process.env.OPENCLAW_VERSION?.trim();
const defaultHeaders = {
Authorization: `Bearer ${token}`,
Accept: "application/json",
originator: "openclaw",
...version ? { version } : {},
"User-Agent": `openclaw/${version || "dev"}`
};
if (accountId) defaultHeaders["ChatGPT-Account-Id"] = accountId;
const res = await fetchJson("https://chatgpt.com/backend-api/wham/usage", {
method: "GET",
headers: resolveProviderRequestHeaders({
provider: "openai",
baseUrl: "https://chatgpt.com/backend-api/wham/usage",
capability: "other",
transport: "http",
defaultHeaders
}) ?? defaultHeaders
}, timeoutMs, fetchFn);
if (!res.ok) return buildUsageHttpErrorSnapshot({
provider: "openai",
status: res.status,
tokenExpiredStatuses: [401, 403]
});
const parsed = await readUsageJson("openai", res);
if (!parsed.ok) return parsed.snapshot;
const data = parsed.data;
const windows = [];
if (data.rate_limit?.primary_window) {
const pw = data.rate_limit.primary_window;
const windowHours = Math.round((pw.limit_window_seconds || 10800) / 3600);
windows.push({
label: `${windowHours}h`,
usedPercent: clampPercent(pw.used_percent || 0),
resetAt: pw.reset_at ? pw.reset_at * 1e3 : void 0
});
}
if (data.rate_limit?.secondary_window) {
const sw = data.rate_limit.secondary_window;
const label = resolveSecondaryWindowLabel({
windowHours: Math.round((sw.limit_window_seconds || 86400) / 3600),
primaryResetAt: data.rate_limit?.primary_window?.reset_at,
secondaryResetAt: sw.reset_at
});
windows.push({
label,
usedPercent: clampPercent(sw.used_percent || 0),
resetAt: sw.reset_at ? sw.reset_at * 1e3 : void 0
});
}
let plan = data.plan_type;
if (data.credits?.balance !== void 0 && data.credits.balance !== null) {
const balance = typeof data.credits.balance === "number" ? data.credits.balance : parseStrictFiniteNumber(data.credits.balance) ?? 0;
plan = plan ? `${plan} ($${balance.toFixed(2)})` : `$${balance.toFixed(2)}`;
}
return {
provider: "openai",
displayName: PROVIDER_LABELS.openai,
windows,
plan
};
}
//#endregion
//#region src/infra/provider-usage.fetch.deepseek.ts
const DEEPSEEK_BALANCE_URL = "https://api.deepseek.com/user/balance";
function formatCurrencyAmount(amount, currency) {
const normalized = currency?.trim().toUpperCase();
if (normalized === "CNY" || normalized === "RMB") return `¥${amount.toFixed(2)}`;
if (normalized === "USD") return `$${amount.toFixed(2)}`;
return normalized ? `${amount.toFixed(2)} ${normalized}` : amount.toFixed(2);
}
function parseBalanceAmount(value) {
return parseFiniteNumber(value);
}
function buildBalanceSummary(info) {
const total = parseBalanceAmount(info.total_balance);
if (total === void 0) return;
const granted = parseBalanceAmount(info.granted_balance);
const toppedUp = parseBalanceAmount(info.topped_up_balance);
const parts = [`Balance ${formatCurrencyAmount(total, info.currency)}`];
if (granted !== void 0 && granted > 0) parts.push(`Granted ${formatCurrencyAmount(granted, info.currency)}`);
if (toppedUp !== void 0 && toppedUp > 0 && toppedUp !== total) parts.push(`Topped up ${formatCurrencyAmount(toppedUp, info.currency)}`);
return parts.join(" · ");
}
async function fetchDeepSeekUsage(apiKey, timeoutMs, fetchFn) {
const res = await fetchJson(DEEPSEEK_BALANCE_URL, {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "application/json"
}
}, timeoutMs, fetchFn);
if (!res.ok) return buildUsageHttpErrorSnapshot({
provider: "deepseek",
status: res.status
});
const parsed = await readUsageJson("deepseek", res);
if (!parsed.ok) return parsed.snapshot;
const data = parsed.data;
const summary = (Array.isArray(data.balance_infos) ? data.balance_infos : []).map((info) => buildBalanceSummary(info)).filter((entry) => Boolean(entry)).join(" · ");
if (!summary) return {
provider: "deepseek",
displayName: PROVIDER_LABELS.deepseek,
windows: [],
error: "No balance data"
};
return {
provider: "deepseek",
displayName: PROVIDER_LABELS.deepseek,
windows: [],
summary,
...data.is_available === false ? { plan: "Unavailable" } : {}
};
}
//#endregion
//#region src/infra/provider-usage.fetch.gemini.ts
async function fetchGeminiUsage(token, timeoutMs, fetchFn, provider) {
const res = await fetchJson("https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: "{}"
}, timeoutMs, fetchFn);
if (!res.ok) return buildUsageHttpErrorSnapshot({
provider,
status: res.status
});
const parsed = await readUsageJson(provider, res);
if (!parsed.ok) return parsed.snapshot;
const data = parsed.data;
const quotas = {};
for (const bucket of data.buckets || []) {
const model = bucket.modelId || "unknown";
const frac = bucket.remainingFraction ?? 1;
if (!quotas[model] || frac < quotas[model]) quotas[model] = frac;
}
const windows = [];
let proMin = 1;
let flashMin = 1;
let hasPro = false;
let hasFlash = false;
for (const [model, frac] of Object.entries(quotas)) {
const lower = normalizeLowercaseStringOrEmpty(model);
if (lower.includes("pro")) {
hasPro = true;
if (frac < proMin) proMin = frac;
}
if (lower.includes("flash")) {
hasFlash = true;
if (frac < flashMin) flashMin = frac;
}
}
if (hasPro) windows.push({
label: "Pro",
usedPercent: clampPercent((1 - proMin) * 100)
});
if (hasFlash) windows.push({
label: "Flash",
usedPercent: clampPercent((1 - flashMin) * 100)
});
return {
provider,
displayName: PROVIDER_LABELS[provider],
windows
};
}
//#endregion
//#region src/infra/provider-usage.fetch.minimax.ts
const DEFAULT_MINIMAX_USAGE_ORIGIN = "https://api.minimaxi.com";
const MINIMAX_USAGE_PATH = "/v1/token_plan/remains";
const RESET_KEYS = [
"reset_at",
"resetAt",
"reset_time",
"resetTime",
"next_reset_at",
"nextResetAt",
"next_reset_time",
"nextResetTime",
"expires_at",
"expiresAt",
"expire_at",
"expireAt",
"end_time",
"endTime",
"window_end",
"windowEnd"
];
const PERCENT_KEYS = [
"used_percent",
"usedPercent",
"used_rate",
"usage_rate",
"used_ratio",
"usage_ratio",
"usedRatio",
"usageRatio"
];
const REMAINING_PERCENT_KEYS = ["usage_percent", "usagePercent"];
const USED_KEYS = [
"used",
"usage",
"used_amount",
"usedAmount",
"used_tokens",
"usedTokens",
"used_quota",
"usedQuota",
"used_times",
"usedTimes",
"prompt_used",
"promptUsed",
"used_prompt",
"usedPrompt",
"prompts_used",
"promptsUsed",
"consumed"
];
const TOTAL_KEYS = [
"total",
"total_amount",
"totalAmount",
"total_tokens",
"totalTokens",
"total_quota",
"totalQuota",
"total_times",
"totalTimes",
"prompt_total",
"promptTotal",
"total_prompt",
"totalPrompt",
"prompt_limit",
"promptLimit",
"limit_prompt",
"limitPrompt",
"prompts_total",
"promptsTotal",
"total_prompts",
"totalPrompts",
"current_interval_total_count",
"currentIntervalTotalCount",
"current_weekly_total_count",
"currentWeeklyTotalCount",
"limit",
"quota",
"quota_limit",
"quotaLimit",
"max"
];
const REMAINING_KEYS = [
"remain",
"remaining",
"remain_amount",
"remainingAmount",
"remaining_amount",
"remain_tokens",
"remainingTokens",
"remaining_tokens",
"remain_quota",
"remainingQuota",
"remaining_quota",
"remain_times",
"remainingTimes",
"remaining_times",
"prompt_remain",
"promptRemain",
"remain_prompt",
"remainPrompt",
"prompt_remaining",
"promptRemaining",
"remaining_prompt",
"remainingPrompt",
"prompts_remaining",
"promptsRemaining",
"prompt_left",
"promptLeft",
"prompts_left",
"promptsLeft",
"left",
"current_interval_usage_count",
"currentIntervalUsageCount",
"current_weekly_usage_count",
"currentWeeklyUsageCount"
];
const PLAN_KEYS = [
"plan",
"plan_name",
"planName",
"product",
"tier"
];
const WINDOW_HOUR_KEYS = [
"window_hours",
"windowHours",
"duration_hours",
"durationHours",
"hours"
];
const WINDOW_MINUTE_KEYS = [
"window_minutes",
"windowMinutes",
"duration_minutes",
"durationMinutes",
"minutes"
];
function pickNumber(record, keys) {
for (const key of keys) {
const parsed = parseFiniteNumber(record[key]);
if (parsed !== void 0) return parsed;
}
}
function pickString(record, keys) {
for (const key of keys) {
const value = record[key];
if (typeof value === "string" && value.trim()) return value.trim();
}
}
function parseEpoch(value) {
if (typeof value === "number" && Number.isFinite(value)) return asDateTimestampMs(value < 0xe8d4a51000 ? Math.floor(value * 1e3) : Math.floor(value));
if (typeof value === "string" && value.trim()) return asDateTimestampMs(Date.parse(value));
}
function hasAny(record, keys) {
return keys.some((key) => key in record);
}
function scoreUsageRecord(record) {
let score = 0;
if (hasAny(record, PERCENT_KEYS)) score += 4;
if (hasAny(record, TOTAL_KEYS)) score += 3;
if (hasAny(record, USED_KEYS) || hasAny(record, REMAINING_KEYS)) score += 2;
if (hasAny(record, RESET_KEYS)) score += 1;
if (hasAny(record, PLAN_KEYS)) score += 1;
return score;
}
function collectUsageCandidates(root) {
const MAX_SCAN_DEPTH = 4;
const MAX_SCAN_NODES = 60;
const queue = [{
value: root,
depth: 0
}];
const seen = /* @__PURE__ */ new Set();
const candidates = [];
let scanned = 0;
while (queue.length && scanned < MAX_SCAN_NODES) {
const next = queue.shift();
scanned += 1;
const { value, depth } = next;
if (isRecord(value)) {
if (seen.has(value)) continue;
seen.add(value);
const score = scoreUsageRecord(value);
if (score > 0) candidates.push({
record: value,
score,
depth
});
if (depth < MAX_SCAN_DEPTH) {
for (const nested of Object.values(value)) if (isRecord(nested) || Array.isArray(nested)) queue.push({
value: nested,
depth: depth + 1
});
}
continue;
}
if (Array.isArray(value) && depth < MAX_SCAN_DEPTH) {
for (const nested of value) if (isRecord(nested) || Array.isArray(nested)) queue.push({
value: nested,
depth: depth + 1
});
}
}
candidates.sort((a, b) => b.score - a.score || a.depth - b.depth);
return candidates.map((candidate) => candidate.record);
}
function deriveWindowLabelFromTimestamps(record) {
const startTime = parseEpoch(record.start_time ?? record.startTime);
const endTime = parseEpoch(record.end_time ?? record.endTime);
if (startTime !== void 0 && endTime !== void 0 && endTime > startTime) {
const durationHours = (endTime - startTime) / 36e5;
if (durationHours >= 1 && Number.isFinite(durationHours)) return `${Math.round(durationHours)}h`;
const durationMinutes = Math.round((endTime - startTime) / 6e4);
if (durationMinutes > 0) return `${durationMinutes}m`;
}
}
function deriveWindowLabel(payload) {
const hours = pickNumber(payload, WINDOW_HOUR_KEYS);
if (hours && Number.isFinite(hours)) return `${hours}h`;
const minutes = pickNumber(payload, WINDOW_MINUTE_KEYS);
if (minutes && Number.isFinite(minutes)) return `${minutes}m`;
const fromTimestamps = deriveWindowLabelFromTimestamps(payload);
if (fromTimestamps) return fromTimestamps;
return "5h";
}
function deriveUsedPercent(payload) {
const total = pickNumber(payload, TOTAL_KEYS);
let used = pickNumber(payload, USED_KEYS);
const remaining = pickNumber(payload, REMAINING_KEYS);
if (used === void 0 && remaining !== void 0 && total !== void 0) used = total - remaining;
const fromCounts = total && total > 0 && used !== void 0 && Number.isFinite(used) ? clampPercent(used / total * 100) : null;
if (fromCounts !== null) return fromCounts;
const percentRaw = pickNumber(payload, PERCENT_KEYS);
if (percentRaw !== void 0) return clampPercent(percentRaw <= 1 ? percentRaw * 100 : percentRaw);
const remainingPercentRaw = pickNumber(payload, REMAINING_PERCENT_KEYS);
if (remainingPercentRaw !== void 0) return clampPercent(100 - clampPercent(remainingPercentRaw <= 1 ? remainingPercentRaw * 100 : remainingPercentRaw));
return null;
}
function pickChatModelRemains(modelRemains) {
const records = modelRemains.filter(isRecord);
if (records.length === 0) return;
const chatRecord = records.find((r) => {
const name = typeof r.model_name === "string" ? r.model_name : "";
const total = parseFiniteNumber(r.current_interval_total_count);
return normalizeLowercaseStringOrEmpty(name).startsWith("minimax-m") && total !== void 0 && total > 0;
});
if (chatRecord) return chatRecord;
return records.find((r) => {
const total = parseFiniteNumber(r.current_interval_total_count);
return total !== void 0 && total > 0;
});
}
function resolveMinimaxUsageUrl(baseUrl) {
const trimmed = baseUrl?.trim();
if (!trimmed) return `${DEFAULT_MINIMAX_USAGE_ORIGIN}${MINIMAX_USAGE_PATH}`;
try {
const parsed = new URL(trimmed);
if (parsed.protocol === "http:" || parsed.protocol === "https:") return `${parsed.origin}${MINIMAX_USAGE_PATH}`;
} catch {}
return `${DEFAULT_MINIMAX_USAGE_ORIGIN}${MINIMAX_USAGE_PATH}`;
}
async function fetchMinimaxUsage(apiKey, timeoutMs, fetchFn, options) {
const res = await fetchJson(resolveMinimaxUsageUrl(options?.baseUrl), {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"MM-API-Source": "OpenClaw"
}
}, timeoutMs, fetchFn);
if (!res.ok) return buildUsageHttpErrorSnapshot({
provider: "minimax",
status: res.status
});
const data = await res.json().catch(() => null);
if (!isRecord(data)) return {
provider: "minimax",
displayName: PROVIDER_LABELS.minimax,
windows: [],
error: "Invalid JSON"
};
const baseResp = isRecord(data.base_resp) ? data.base_resp : void 0;
if (baseResp && typeof baseResp.status_code === "number" && baseResp.status_code !== 0) return {
provider: "minimax",
displayName: PROVIDER_LABELS.minimax,
windows: [],
error: baseResp.status_msg?.trim() || "API error"
};
const payload = isRecord(data.data) ? data.data : data;
const modelRemains = Array.isArray(payload.model_remains) ? payload.model_remains : null;
const chatRemains = modelRemains ? pickChatModelRemains(modelRemains) : void 0;
const usageSource = chatRemains ?? payload;
const candidates = collectUsageCandidates(usageSource);
let usageRecord = usageSource;
let usedPercent = null;
for (const candidate of candidates) {
const candidatePercent = deriveUsedPercent(candidate);
if (candidatePercent !== null) {
usageRecord = candidate;
usedPercent = candidatePercent;
break;
}
}
if (usedPercent === null) usedPercent = deriveUsedPercent(usageSource);
if (usedPercent === null) return {
provider: "minimax",
displayName: PROVIDER_LABELS.minimax,
windows: [],
error: "Unsupported response shape"
};
const resetAt = parseEpoch(pickString(usageRecord, RESET_KEYS)) ?? parseEpoch(pickNumber(usageRecord, RESET_KEYS)) ?? parseEpoch(pickString(payload, RESET_KEYS)) ?? parseEpoch(pickNumber(payload, RESET_KEYS));
const windows = [{
label: chatRemains ? deriveWindowLabel(chatRemains) : deriveWindowLabel(usageRecord),
usedPercent,
resetAt
}];
const modelName = chatRemains && typeof chatRemains.model_name === "string" ? chatRemains.model_name : void 0;
const plan = pickString(usageRecord, PLAN_KEYS) ?? pickString(payload, PLAN_KEYS) ?? (modelName ? `Coding Plan · ${modelName}` : void 0);
return {
provider: "minimax",
displayName: PROVIDER_LABELS.minimax,
windows,
plan
};
}
//#endregion
//#region src/infra/provider-usage.fetch.zai.ts
async function fetchZaiUsage(apiKey, timeoutMs, fetchFn) {
const res = await fetchJson("https://api.z.ai/api/monitor/usage/quota/limit", {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "application/json"
}
}, timeoutMs, fetchFn);
if (!res.ok) return buildUsageHttpErrorSnapshot({
provider: "zai",
status: res.status
});
const parsed = await readUsageJson("zai", res);
if (!parsed.ok) return parsed.snapshot;
const data = parsed.data;
if (!data.success || data.code !== 200) {
const errorMessage = typeof data.msg === "string" ? data.msg.trim() : "";
return {
provider: "zai",
displayName: PROVIDER_LABELS.zai,
windows: [],
error: errorMessage || "API error"
};
}
const windows = [];
const limits = data.data?.limits || [];
for (const limit of limits) {
const percent = clampPercent(limit.percentage || 0);
const nextReset = limit.nextResetTime ? new Date(limit.nextResetTime).getTime() : void 0;
let windowLabel = "Limit";
if (limit.unit === 1) windowLabel = `${limit.number}d`;
else if (limit.unit === 3) windowLabel = `${limit.number}h`;
else if (limit.unit === 5) windowLabel = `${limit.number}m`;
if (limit.type === "TOKENS_LIMIT") windows.push({
label: `Tokens (${windowLabel})`,
usedPercent: percent,
resetAt: nextReset
});
else if (limit.type === "TIME_LIMIT") windows.push({
label: "Monthly",
usedPercent: percent,
resetAt: nextReset
});
}
const planName = data.data?.planName || data.data?.plan || void 0;
return {
provider: "zai",
displayName: PROVIDER_LABELS.zai,
windows,
plan: planName
};
}
//#endregion
export { fetchCodexUsage as a, buildUsageHttpErrorSnapshot as c, fetchDeepSeekUsage as i, fetchJson as l, fetchMinimaxUsage as n, fetchClaudeUsage as o, fetchGeminiUsage as r, buildUsageErrorSnapshot as s, fetchZaiUsage as t };