merge: sync upstream v0.5.81 into MIBP fork

# Conflicts:
#	.gitignore
#	Dockerfile
#	open-sse/handlers/chatCore.js
#	open-sse/providers/registry/cline.js
#	open-sse/providers/registry/index.js
#	open-sse/services/usage.js
#	open-sse/utils/streamHandler.js
#	package.json
#	src/app/(dashboard)/dashboard/profile/page.js
#	src/app/(dashboard)/dashboard/providers/[id]/page.js
This commit is contained in:
MUH. IQRAM BAHRING committed 2026-09-19 11:35:34 +08:00
commit 0ac771bad8
208 files changed
+13062 -977

No files matched your search

@@ -0,0 +1,150 @@
/**
* Antigravity weekly quota — best-effort retrieval from retrieveUserQuotaSummary.
* Failure never breaks existing per-model quota display.
*/
import { U, parseResetTime, fetchWithTimeout } from "./shared.js";
import { ANTIGRAVITY_IDE_USER_AGENT, ANTIGRAVITY_IDE_VERSION } from "../../providers/shared.js";
// — Weekly quota summary config ——————————————————————————————
const WEEKLY_CONFIG = {
...U("antigravity"),
userAgent: ANTIGRAVITY_IDE_USER_AGENT,
};
// — Cache: TTL + in-flight dedup per project ———————————————
const WEEKLY_CACHE_TTL_MS = 180_000; // 3 minutes
const weeklyCache = new Map(); // cacheKey -> { result, expiresAt } | { promise }
function cacheKey(accessToken, projectId) {
return `${accessToken}::${projectId || ""}`;
}
// Exported for tests only
export function _clearWeeklyCache() {
weeklyCache.clear();
}
// — Group-name to stable key mapping ——————————————————————
const GROUP_MATCHERS = [
{ pattern: /gemini/i, key: "gemini_weekly", displayName: "Gemini (Weekly)" },
{ pattern: /claude|gpt/i, key: "claude_gpt_weekly", displayName: "Claude & GPT (Weekly)" },
];
/**
* Parse a retrieveUserQuotaSummary response into normalized weekly quotas.
* Pure function — safe to unit-test without network.
*
* @param {Object|null} data Raw JSON response
* @returns {Object} e.g. { gemini_weekly: { used, total, ... }, claude_gpt_weekly: { ... } }
*/
export function parseWeeklyQuotaSummary(data) {
if (!data || typeof data !== "object") return {};
// Groups may live at data.groups or data.quotaSummary.groups
const groups = Array.isArray(data.groups)
? data.groups
: Array.isArray(data.quotaSummary?.groups)
? data.quotaSummary.groups
: null;
if (!groups) return {};
const result = {};
for (const group of groups) {
if (!group || typeof group !== "object") continue;
const displayName = group.displayName || "";
const buckets = Array.isArray(group.buckets) ? group.buckets : [];
for (const bucket of buckets) {
if (!bucket || typeof bucket !== "object") continue;
// Identify weekly buckets by checking bucketId + displayName for "weekly"
const bucketText = `${bucket.bucketId || ""} ${bucket.displayName || ""}`.toLowerCase();
if (!bucketText.includes("weekly")) continue;
// Skip disabled buckets
if (bucket.disabled === true) continue;
const remainingFraction = Number(bucket.remainingFraction);
if (!Number.isFinite(remainingFraction)) continue;
// Match group to a known family
for (const matcher of GROUP_MATCHERS) {
if (matcher.pattern.test(displayName)) {
const total = 1000;
const remaining = Math.round(total * remainingFraction);
const used = Math.max(0, total - remaining);
result[matcher.key] = {
used,
total,
resetAt: parseResetTime(bucket.resetTime),
remainingPercentage: remainingFraction * 100,
unlimited: false,
displayName: matcher.displayName,
};
break; // first matching bucket per family wins
}
}
}
}
return result;
}
/**
* Fetch weekly quota summary — cached, deduped, never throws.
*/
export async function fetchAntigravityWeeklyQuota(accessToken, projectId, proxyOptions = null) {
const key = cacheKey(accessToken, projectId);
// Serve in-flight or cached
const hit = weeklyCache.get(key);
if (hit?.promise) return hit.promise;
if (hit && hit.expiresAt > Date.now()) return hit.result;
const promise = (async () => {
try {
const url = WEEKLY_CONFIG.quotaSummaryApiUrl;
if (!url) return {};
const response = await fetchWithTimeout(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${accessToken}`,
"User-Agent": WEEKLY_CONFIG.userAgent,
"Content-Type": "application/json",
"X-Client-Name": "antigravity",
"X-Client-Version": ANTIGRAVITY_IDE_VERSION,
},
body: JSON.stringify({
...(projectId ? { project: projectId } : {}),
}),
}, 10000, proxyOptions);
if (!response.ok) return {};
const data = await response.json();
return parseWeeklyQuotaSummary(data);
} catch {
return {};
}
})();
weeklyCache.set(key, { promise });
try {
const result = await promise;
if (result && Object.keys(result).length > 0) {
weeklyCache.set(key, { result, expiresAt: Date.now() + WEEKLY_CACHE_TTL_MS });
} else {
weeklyCache.delete(key);
}
return result;
} catch {
weeklyCache.delete(key);
return {};
}
}
+16
View File
@@ -110,6 +110,22 @@ async function fetchClaudeUsageRaw(accessToken, proxyOptions = null) {
}
}
// Model-scoped weekly limits (e.g. Fable) arrive in limits[], not as
// seven_day_* keys: { kind: "weekly_scoped", percent, resets_at,
// scope: { model: { display_name: "Fable" } } }. No limits entry means
// the account has no such window — omit the row, never fabricate one.
if (Array.isArray(data.limits)) {
for (const limit of data.limits) {
if (limit?.kind !== "weekly_scoped") continue;
const modelName = String(limit?.scope?.model?.display_name || "").trim().toLowerCase();
if (!modelName || typeof limit.percent !== "number") continue;
quotas[`weekly ${modelName} (7d)`] = createQuotaObject({
utilization: Math.max(0, Math.min(100, limit.percent)),
resets_at: limit.resets_at,
});
}
}
return {
plan: "Claude Code",
extraUsage: data.extra_usage ?? null,
+8 -1
View File
@@ -21,6 +21,13 @@ function toIsoDate(value) {
return Number.isFinite(time) ? date.toISOString() : null;
}
function errorMessage(value, fallback) {
if (!value) return fallback;
if (typeof value === "string") return value;
if (typeof value.message === "string") return value.message;
return JSON.stringify(value);
}
function getCodexAccountId(providerSpecificData) {
return providerSpecificData?.workspaceId || providerSpecificData?.accountId || providerSpecificData?.chatgptAccountId || null;
}
@@ -162,7 +169,7 @@ export async function getCodexRateLimitResetCredits(accessToken, proxyOptions =
}
if (!response.ok) {
const message = data?.message || data?.error || data?.detail || `Codex reset credits API unavailable (${response.status}).`;
const message = errorMessage(data?.message || data?.error || data?.detail, `Codex reset credits API unavailable (${response.status}).`);
throw new Error(message);
}
+134
View File
@@ -0,0 +1,134 @@
/**
* Command Code usage — billing credits + 5h/weekly rate windows.
* Mirrors ~/cc-usage.mjs: whoami → credits + subscriptions.
*/
import { proxyAwareFetch } from "../../utils/proxyFetch.js";
import { parseResetTime, toFiniteNumber } from "./shared.js";
const BASE = (process.env.COMMAND_CODE_API_BASE_URL || "https://api.commandcode.ai").replace(/\/$/, "");
const PLAN_NAMES = {
"individual-go": "Go",
"individual-goat": "GOAT",
"individual-pro": "Pro",
"individual-pro-v1": "Pro",
"individual-provider": "Provider",
"individual-max": "Max",
"individual-ultra": "Ultra",
"teams-pro": "Teams Pro",
};
const PLAN_CAPS = {
"individual-go": 10,
"individual-goat": 70,
"individual-pro": 30,
"individual-pro-v1": 80,
"individual-provider": 15,
"individual-max": 150,
"individual-ultra": 300,
"teams-pro": 40,
};
function qs(route, params) {
const s = new URLSearchParams(
Object.entries(params || {}).filter(([, v]) => v != null),
).toString();
return s ? `${route}?${s}` : route;
}
function windowQuota(win) {
if (!win || typeof win !== "object") return null;
const used = toFiniteNumber(win.used, 0);
const total = toFiniteNumber(win.cap, 0);
if (total <= 0 && used <= 0) return null;
return {
used,
total,
remaining: Math.max(0, total - used),
unlimited: false,
resetAt: parseResetTime(win.resetAt),
};
}
/**
* @param {string|null|undefined} apiKey
* @param {object|null} proxyOptions
*/
export async function getCommandCodeUsage(apiKey, proxyOptions = null) {
if (!apiKey || typeof apiKey !== "string" || !apiKey.trim()) {
return { message: "Command Code API key not available. Add a key to view usage." };
}
const headers = {
Authorization: `Bearer ${apiKey.trim()}`,
Accept: "application/json",
};
const get = async (route) => {
const response = await proxyAwareFetch(
BASE + route,
{ method: "GET", headers },
proxyOptions,
);
return response;
};
try {
const whoamiRes = await get(qs("/alpha/whoami", { limits: "1" }));
if (whoamiRes.status === 401 || whoamiRes.status === 403) {
return { plan: "Command Code", message: "Command Code authentication failed. Check the API key." };
}
if (!whoamiRes.ok) {
return { plan: "Command Code", message: `Command Code usage API error (${whoamiRes.status})` };
}
const whoami = await whoamiRes.json().catch(() => ({}));
const orgId = whoami?.org?.id ?? null;
const [creditsRes, subsRes] = await Promise.all([
get(qs("/alpha/billing/credits", { orgId })),
get(qs("/alpha/billing/subscriptions", { orgId })),
]);
if (creditsRes.status === 401 || creditsRes.status === 403 || subsRes.status === 401 || subsRes.status === 403) {
return { plan: "Command Code", message: "Command Code authentication failed. Check the API key." };
}
if (!creditsRes.ok) {
return { plan: "Command Code", message: `Command Code credits API error (${creditsRes.status})` };
}
if (!subsRes.ok) {
return { plan: "Command Code", message: `Command Code subscriptions API error (${subsRes.status})` };
}
const creditsBody = await creditsRes.json().catch(() => ({}));
const subsBody = await subsRes.json().catch(() => ({}));
const planId = subsBody?.data?.planId ?? null;
const plan = (planId && PLAN_NAMES[planId]) || planId || "Command Code";
const cap = planId ? (PLAN_CAPS[planId] || 0) : 0;
const c = creditsBody?.credits || {};
const remaining =
toFiniteNumber(c.monthlyCredits, 0) +
toFiniteNumber(c.purchasedCredits, 0) +
toFiniteNumber(c.freeCredits, 0);
const used = cap > 0 ? Math.max(0, cap - remaining) : 0;
const total = cap > 0 ? cap : remaining;
const quotas = {};
quotas.Credits = {
used,
total,
remaining,
unlimited: cap <= 0,
resetAt: parseResetTime(subsBody?.data?.currentPeriodEnd),
};
const fiveHour = windowQuota(creditsBody?.windowLimits?.fiveHour);
if (fiveHour) quotas["Session (5h)"] = fiveHour;
const weekly = windowQuota(creditsBody?.windowLimits?.weekly);
if (weekly) quotas.Weekly = weekly;
return { plan, quotas };
} catch (error) {
return { message: `Command Code error: ${error.message}` };
}
}
+4 -3
View File
@@ -91,14 +91,15 @@ export async function getDeepseekUsage(apiKey = null, proxyOptions = null) {
const quotas = {};
for (const b of balances) {
const total = Math.max(0, b.totalBalance);
// Credit pot: show full remaining against current balance; never set absolute
// `remaining` — QuotaTable treats it as a 0–100 percentage.
// Credit balance: show as "Credit: $X.XX USD" not a usage quota
quotas[`Balance (${b.currency})`] = {
used: 0,
total,
remainingPercentage: total > 0 ? 100 : 0,
resetAt: null,
unlimited: total > 0,
unlimited: false,
isCreditBalance: true,
currency: b.currency,
};
}
+60 -2
View File
@@ -5,6 +5,7 @@
import { CLIENT_METADATA } from "../../config/appConstants.js";
import { ANTIGRAVITY_IDE_USER_AGENT, ANTIGRAVITY_IDE_VERSION, ANTIGRAVITY_OAUTH_CLIENT } from "../../providers/shared.js";
import { U, parseResetTime, normalizeCloudCodeProjectId, fetchWithTimeout } from "./shared.js";
import { fetchAntigravityWeeklyQuota } from "./antigravity-weekly.js";
// Antigravity API config (from Quotio) — urls from registry, oauth client + dynamic UA kept here
const ANTIGRAVITY_CONFIG = {
@@ -157,8 +158,15 @@ export async function getAntigravityUsage(accessToken, providerSpecificData, pro
const data = await response.json();
const quotas = {};
// Parse model quotas (inspired by vscode-antigravity-cockpit)
if (data.models) {
// Detect tier: free-tier accounts only have weekly quotas (no separate 5h window).
// On free-tier, fetchAvailableModels returns misleading per-model quota info
// (missing remainingFraction defaults to 0, or reflects the weekly limit not a 5h window).
const paidTierId = subscriptionInfo?.paidTier?.id;
const isFreeTier = !paidTierId || paidTierId === "free-tier";
// Parse model quotas only for paid-tier accounts.
// Free-tier accounts skip this — their only meaningful quota is the weekly limit.
if (!isFreeTier && data.models) {
// Filter only recommended/important models (must match PROVIDER_MODELS ag ids)
const importantModels = [
'gemini-3.8-flash-high',
@@ -212,6 +220,56 @@ export async function getAntigravityUsage(accessToken, providerSpecificData, pro
}
}
// Best-effort weekly quota overlay — never blocks or breaks per-model results
try {
const weeklyQuotas = await fetchAntigravityWeeklyQuota(
accessToken,
projectId,
proxyOptions
);
// Reconcile weekly quota against model family status:
// If every model in a family is locked/exhausted (remainingPercentage === 0)
// until a future reset time, the weekly limit cannot be 100% available.
// On Google's Free Starter tier, retrieveUserQuotaSummary buggily reports
// remainingFraction: 1 even after the starter quota is depleted and all models 429.
const entries = Object.entries(quotas);
const geminiModels = entries.filter(([k]) => k.startsWith("gemini-") && !k.includes("image"));
const claudeModels = entries.filter(([k]) => k.startsWith("claude-"));
if (weeklyQuotas.gemini_weekly && geminiModels.length > 0) {
const allGeminiExhausted = geminiModels.every(([, q]) => (q.remainingPercentage ?? 0) === 0);
if (allGeminiExhausted && weeklyQuotas.gemini_weekly.remainingPercentage > 0) {
const maxResetAt = geminiModels.reduce((max, [, q]) =>
!max || (q.resetAt && new Date(q.resetAt) > new Date(max)) ? q.resetAt : max, null
);
weeklyQuotas.gemini_weekly.used = weeklyQuotas.gemini_weekly.total;
weeklyQuotas.gemini_weekly.remainingPercentage = 0;
if (maxResetAt) {
weeklyQuotas.gemini_weekly.resetAt = maxResetAt;
}
}
}
if (weeklyQuotas.claude_gpt_weekly && claudeModels.length > 0) {
const allClaudeExhausted = claudeModels.every(([, q]) => (q.remainingPercentage ?? 0) === 0);
if (allClaudeExhausted && weeklyQuotas.claude_gpt_weekly.remainingPercentage > 0) {
const maxResetAt = claudeModels.reduce((max, [, q]) =>
!max || (q.resetAt && new Date(q.resetAt) > new Date(max)) ? q.resetAt : max, null
);
weeklyQuotas.claude_gpt_weekly.used = weeklyQuotas.claude_gpt_weekly.total;
weeklyQuotas.claude_gpt_weekly.remainingPercentage = 0;
if (maxResetAt) {
weeklyQuotas.claude_gpt_weekly.resetAt = maxResetAt;
}
}
}
Object.assign(quotas, weeklyQuotas);
} catch {
// Silently ignore — weekly is best-effort
}
return {
plan: subscriptionInfo?.currentTier?.name || "Unknown",
quotas,
+107
View File
@@ -0,0 +1,107 @@
/**
* OpenCode Go usage — GET https://opencode.ai/zen/go/v1/usage
* Auth: Bearer <apiKey>
*/
import { proxyAwareFetch } from "../../utils/proxyFetch.js";
import { parseResetTime, toFiniteNumber, U } from "./shared.js";
const USAGE_URL = U("opencode-go").url;
const QUOTA_NAMES = {
rolling: "Rolling",
weekly: "Weekly",
monthly: "Monthly",
};
function parsePercent(value) {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value === "string" && value.trim()) {
const parsed = Number(value);
if (Number.isFinite(parsed)) return parsed;
}
return null;
}
export async function getOpenCodeGoUsage(apiKey = null, proxyOptions = null) {
if (!apiKey || typeof apiKey !== "string" || !apiKey.trim()) {
return {
message: "OpenCode Go API key not available. Add a key to view usage.",
};
}
try {
const response = await proxyAwareFetch(
USAGE_URL,
{
method: "GET",
headers: {
Authorization: `Bearer ${apiKey.trim()}`,
Accept: "application/json",
},
},
proxyOptions,
);
if (response.status === 401) {
return {
plan: "OpenCode Go",
message: "OpenCode Go authentication failed. Check the API key.",
};
}
if (response.status === 403) {
const error = await response.json().catch(() => null);
const subscriptionRequired = error?.error?.type === "EntitlementError";
return {
plan: "OpenCode Go",
message: subscriptionRequired
? "OpenCode Go subscription required for this API key."
: "OpenCode Go access forbidden for this API key.",
};
}
if (!response.ok) {
return {
plan: "OpenCode Go",
message: `OpenCode Go usage API error (${response.status}).`,
};
}
const data = await response.json().catch(() => null);
if (!data?.usage || typeof data.usage !== "object") {
return {
plan: "OpenCode Go",
message: "OpenCode Go usage response did not contain quota data.",
};
}
const quotas = {};
for (const [period, name] of Object.entries(QUOTA_NAMES)) {
const quota = data.usage[period];
if (!quota || typeof quota !== "object") continue;
const percent = parsePercent(quota.percent);
if (percent === null) continue;
const used = Math.max(0, Math.min(100, toFiniteNumber(percent, 0)));
quotas[name] = {
used,
total: 100,
remaining: 100 - used,
remainingPercentage: 100 - used,
resetAt: parseResetTime(quota.resetsAt),
unlimited: false,
};
}
if (Object.keys(quotas).length === 0) {
return {
plan: "OpenCode Go",
message: "OpenCode Go usage response did not contain valid quota data.",
};
}
return { plan: "OpenCode Go", quotas };
} catch (error) {
return { message: `OpenCode Go error: ${error.message}` };
}
}
+125
View File
@@ -0,0 +1,125 @@
/**
* Xiaomi MiMo usage — weekly quota from the Xiaomi account session.
*
* Primary path: GET {mimo-server}/api/user/usage authorized by the account-session
* cookie (see shared/mimoAccount.js). Response: { code: 0, data: { percent (remaining
* %), resetDate, resetAt } }.
*
* Fallback: the sk- API key cannot read the quota, so when no account session is
* available we surface a graceful message instead of failing.
*/
import { proxyAwareFetch } from "../../utils/proxyFetch.js";
import { getMimoAccountUsage } from "../../shared/mimoAccount.js";
const USAGE_URL = "https://aistudio.xiaomimimo.com/open-apis/v1/user/usage";
/**
* @param {string|null|undefined} accessToken - sk- API key
* @param {object|null} providerSpecificData - may contain mimoPassToken, uid, etc.
* @param {object|null} proxyOptions
*/
export async function getXiaomiMimoUsage(accessToken = null, providerSpecificData = null, proxyOptions = null) {
// Preferred path: the weekly quota comes from the account service session
// (mimo-server /api/user/usage), which the sk- key cannot reach. The session is
// derived from MiMo Desktop's persisted passToken via the SSO/sts handshake.
const account = await getMimoAccountUsage(providerSpecificData, proxyOptions);
if (typeof account.percent === "number" && Number.isFinite(account.percent)) {
const remaining = Math.max(0, Math.min(100, Math.round(account.percent)));
const used = 100 - remaining;
let resetAt = null;
if (typeof account.resetAt === "number" && account.resetAt > 0) {
resetAt = new Date(account.resetAt * 1000).toISOString();
} else if (typeof account.resetDate === "string") {
const parsed = new Date(`${account.resetDate}T00:00:00Z`);
if (!Number.isNaN(parsed.getTime())) resetAt = parsed.toISOString();
}
return {
plan: "Xiaomi MiMo Desktop",
quotas: {
Weekly: { used, total: 100, remainingPercentage: remaining, resetAt, unlimited: false },
},
};
}
// Fallback: no account session available (Desktop never logged in, or its cookie
// store is locked). The sk- key cannot read the quota, so surface a clear message.
const key = accessToken || providerSpecificData?.apiKey;
if (!key || typeof key !== "string" || !key.trim()) {
return { message: "Xiaomi MiMo Desktop not connected. Add credentials to view usage." };
}
try {
const response = await proxyAwareFetch(
USAGE_URL,
{
method: "GET",
headers: {
Authorization: `Bearer ${key.trim()}`,
"X-Mimo-Source": "mimocode-cli",
Accept: "application/json",
},
signal: AbortSignal.timeout(10000),
},
proxyOptions,
);
if (response.status === 401) {
return {
plan: "Xiaomi MiMo Desktop",
message: "Weekly quota requires Xiaomi account session. API key alone is insufficient.",
};
}
if (!response.ok) {
return {
plan: "Xiaomi MiMo Desktop",
message: `Usage API error (${response.status})`,
};
}
const data = await response.json().catch(() => null);
if (!data || data.code !== 0 || !data.data) {
return {
plan: "Xiaomi MiMo Desktop",
message: "Usage endpoint returned unexpected response.",
};
}
const { percent, resetDate } = data.data;
if (typeof percent !== "number" || !Number.isFinite(percent)) {
return {
plan: "Xiaomi MiMo Desktop",
message: "Usage data missing percent field.",
};
}
// percent = remaining percentage (e.g. 94 means 94% remaining)
const remaining = Math.max(0, Math.min(100, Math.round(percent)));
const used = 100 - remaining;
// Parse resetDate — expected format "2026-09-16"
let resetAt = null;
if (resetDate && typeof resetDate === "string") {
const parsed = new Date(`${resetDate}T00:00:00Z`);
if (!Number.isNaN(parsed.getTime())) {
resetAt = parsed.toISOString();
}
}
return {
plan: "Xiaomi MiMo Desktop",
quotas: {
Weekly: {
used,
total: 100,
remainingPercentage: remaining,
resetAt,
unlimited: false,
},
},
};
} catch (error) {
return { message: `Xiaomi MiMo Desktop usage error: ${error.message}` };
}
}