fix(auth): billing backoff + cooldown UX
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
listProfilesForProvider,
|
||||
resolveAuthProfileDisplayLabel,
|
||||
resolveAuthStorePathForDisplay,
|
||||
resolveProfileUnusableUntilForDisplay,
|
||||
} from "../../agents/auth-profiles.js";
|
||||
import {
|
||||
getCustomProviderApiKey,
|
||||
@@ -174,15 +175,36 @@ function resolveProviderAuthOverview(params: {
|
||||
modelsPath: string;
|
||||
}): ProviderAuthOverview {
|
||||
const { provider, cfg, store } = params;
|
||||
const now = Date.now();
|
||||
const profiles = listProfilesForProvider(store, provider);
|
||||
const withUnusableSuffix = (base: string, profileId: string) => {
|
||||
const unusableUntil = resolveProfileUnusableUntilForDisplay(
|
||||
store,
|
||||
profileId,
|
||||
);
|
||||
if (!unusableUntil || now >= unusableUntil) return base;
|
||||
const stats = store.usageStats?.[profileId];
|
||||
const kind =
|
||||
typeof stats?.disabledUntil === "number" && now < stats.disabledUntil
|
||||
? `disabled${stats.disabledReason ? `:${stats.disabledReason}` : ""}`
|
||||
: "cooldown";
|
||||
const remaining = formatRemainingShort(unusableUntil - now);
|
||||
return `${base} [${kind} ${remaining}]`;
|
||||
};
|
||||
const labels = profiles.map((profileId) => {
|
||||
const profile = store.profiles[profileId];
|
||||
if (!profile) return `${profileId}=missing`;
|
||||
if (profile.type === "api_key") {
|
||||
return `${profileId}=${maskApiKey(profile.key)}`;
|
||||
return withUnusableSuffix(
|
||||
`${profileId}=${maskApiKey(profile.key)}`,
|
||||
profileId,
|
||||
);
|
||||
}
|
||||
if (profile.type === "token") {
|
||||
return `${profileId}=token:${maskApiKey(profile.token)}`;
|
||||
return withUnusableSuffix(
|
||||
`${profileId}=token:${maskApiKey(profile.token)}`,
|
||||
profileId,
|
||||
);
|
||||
}
|
||||
const display = resolveAuthProfileDisplayLabel({ cfg, store, profileId });
|
||||
const suffix =
|
||||
@@ -191,7 +213,8 @@ function resolveProviderAuthOverview(params: {
|
||||
: display.startsWith(profileId)
|
||||
? display.slice(profileId.length).trim()
|
||||
: `(${display})`;
|
||||
return `${profileId}=OAuth${suffix ? ` ${suffix}` : ""}`;
|
||||
const base = `${profileId}=OAuth${suffix ? ` ${suffix}` : ""}`;
|
||||
return withUnusableSuffix(base, profileId);
|
||||
});
|
||||
const oauthCount = profiles.filter(
|
||||
(id) => store.profiles[id]?.type === "oauth",
|
||||
@@ -770,6 +793,39 @@ export async function modelsStatusCommand(
|
||||
(profile) => profile.type === "oauth" || profile.type === "token",
|
||||
);
|
||||
|
||||
const unusableProfiles = (() => {
|
||||
const now = Date.now();
|
||||
const out: Array<{
|
||||
profileId: string;
|
||||
provider?: string;
|
||||
kind: "cooldown" | "disabled";
|
||||
reason?: string;
|
||||
until: number;
|
||||
remainingMs: number;
|
||||
}> = [];
|
||||
for (const profileId of Object.keys(store.usageStats ?? {})) {
|
||||
const unusableUntil = resolveProfileUnusableUntilForDisplay(
|
||||
store,
|
||||
profileId,
|
||||
);
|
||||
if (!unusableUntil || now >= unusableUntil) continue;
|
||||
const stats = store.usageStats?.[profileId];
|
||||
const kind =
|
||||
typeof stats?.disabledUntil === "number" && now < stats.disabledUntil
|
||||
? "disabled"
|
||||
: "cooldown";
|
||||
out.push({
|
||||
profileId,
|
||||
provider: store.profiles[profileId]?.provider,
|
||||
kind,
|
||||
reason: stats?.disabledReason,
|
||||
until: unusableUntil,
|
||||
remainingMs: unusableUntil - now,
|
||||
});
|
||||
}
|
||||
return out.sort((a, b) => a.remainingMs - b.remainingMs);
|
||||
})();
|
||||
|
||||
const checkStatus = (() => {
|
||||
const hasExpiredOrMissing =
|
||||
oauthProfiles.some((profile) =>
|
||||
@@ -805,6 +861,7 @@ export async function modelsStatusCommand(
|
||||
providersWithOAuth: providersWithOauth,
|
||||
missingProvidersInUse,
|
||||
providers: providerAuth,
|
||||
unusableProfiles,
|
||||
oauth: {
|
||||
warnAfterMs: authHealth.warnAfterMs,
|
||||
profiles: authHealth.profiles,
|
||||
|
||||
Reference in New Issue
Block a user