mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 00:41:25 +00:00
chore: Enable "curly" rule to avoid single-statement if confusion/errors.
This commit is contained in:
@@ -10,6 +10,8 @@ export function resolveAuthProfileDisplayLabel(params: {
|
||||
const profile = store.profiles[profileId];
|
||||
const configEmail = cfg?.auth?.profiles?.[profileId]?.email?.trim();
|
||||
const email = configEmail || (profile && "email" in profile ? profile.email?.trim() : undefined);
|
||||
if (email) return `${profileId} (${email})`;
|
||||
if (email) {
|
||||
return `${profileId} (${email})`;
|
||||
}
|
||||
return profileId;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ export function formatAuthDoctorHint(params: {
|
||||
profileId?: string;
|
||||
}): string {
|
||||
const providerKey = normalizeProviderId(params.provider);
|
||||
if (providerKey !== "anthropic") return "";
|
||||
if (providerKey !== "anthropic") {
|
||||
return "";
|
||||
}
|
||||
|
||||
const legacyProfileId = params.profileId ?? "anthropic:default";
|
||||
const suggested = suggestOAuthProfileIdForLegacyDefault({
|
||||
@@ -21,7 +23,9 @@ export function formatAuthDoctorHint(params: {
|
||||
provider: providerKey,
|
||||
legacyProfileId,
|
||||
});
|
||||
if (!suggested || suggested === legacyProfileId) return "";
|
||||
if (!suggested || suggested === legacyProfileId) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const storeOauthProfiles = listProfilesForProvider(params.store, providerKey)
|
||||
.filter((id) => params.store.profiles[id]?.type === "oauth")
|
||||
|
||||
@@ -8,8 +8,12 @@ import {
|
||||
import type { AuthProfileCredential, AuthProfileStore, OAuthCredential } from "./types.js";
|
||||
|
||||
function shallowEqualOAuthCredentials(a: OAuthCredential | undefined, b: OAuthCredential): boolean {
|
||||
if (!a) return false;
|
||||
if (a.type !== "oauth") return false;
|
||||
if (!a) {
|
||||
return false;
|
||||
}
|
||||
if (a.type !== "oauth") {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
a.provider === b.provider &&
|
||||
a.access === b.access &&
|
||||
@@ -23,12 +27,18 @@ function shallowEqualOAuthCredentials(a: OAuthCredential | undefined, b: OAuthCr
|
||||
}
|
||||
|
||||
function isExternalProfileFresh(cred: AuthProfileCredential | undefined, now: number): boolean {
|
||||
if (!cred) return false;
|
||||
if (cred.type !== "oauth" && cred.type !== "token") return false;
|
||||
if (!cred) {
|
||||
return false;
|
||||
}
|
||||
if (cred.type !== "oauth" && cred.type !== "token") {
|
||||
return false;
|
||||
}
|
||||
if (cred.provider !== "qwen-portal") {
|
||||
return false;
|
||||
}
|
||||
if (typeof cred.expires !== "number") return true;
|
||||
if (typeof cred.expires !== "number") {
|
||||
return true;
|
||||
}
|
||||
return cred.expires > now + EXTERNAL_CLI_NEAR_EXPIRY_MS;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,21 @@ describe("resolveApiKeyForProfile fallback to main agent", () => {
|
||||
vi.unstubAllGlobals();
|
||||
|
||||
// Restore original environment
|
||||
if (previousStateDir === undefined) delete process.env.OPENCLAW_STATE_DIR;
|
||||
else process.env.OPENCLAW_STATE_DIR = previousStateDir;
|
||||
if (previousAgentDir === undefined) delete process.env.OPENCLAW_AGENT_DIR;
|
||||
else process.env.OPENCLAW_AGENT_DIR = previousAgentDir;
|
||||
if (previousPiAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR;
|
||||
else process.env.PI_CODING_AGENT_DIR = previousPiAgentDir;
|
||||
if (previousStateDir === undefined) {
|
||||
delete process.env.OPENCLAW_STATE_DIR;
|
||||
} else {
|
||||
process.env.OPENCLAW_STATE_DIR = previousStateDir;
|
||||
}
|
||||
if (previousAgentDir === undefined) {
|
||||
delete process.env.OPENCLAW_AGENT_DIR;
|
||||
} else {
|
||||
process.env.OPENCLAW_AGENT_DIR = previousAgentDir;
|
||||
}
|
||||
if (previousPiAgentDir === undefined) {
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
} else {
|
||||
process.env.PI_CODING_AGENT_DIR = previousPiAgentDir;
|
||||
}
|
||||
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
@@ -36,7 +36,9 @@ async function refreshOAuthTokenWithLock(params: {
|
||||
|
||||
const store = ensureAuthProfileStore(params.agentDir);
|
||||
const cred = store.profiles[params.profileId];
|
||||
if (!cred || cred.type !== "oauth") return null;
|
||||
if (!cred || cred.type !== "oauth") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Date.now() < cred.expires) {
|
||||
return {
|
||||
@@ -63,7 +65,9 @@ async function refreshOAuthTokenWithLock(params: {
|
||||
return { apiKey: newCredentials.access, newCredentials };
|
||||
})()
|
||||
: await getOAuthApiKey(cred.provider, oauthCreds);
|
||||
if (!result) return null;
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
store.profiles[params.profileId] = {
|
||||
...cred,
|
||||
...result.newCredentials,
|
||||
@@ -91,10 +95,16 @@ async function tryResolveOAuthProfile(params: {
|
||||
}): Promise<{ apiKey: string; provider: string; email?: string } | null> {
|
||||
const { cfg, store, profileId } = params;
|
||||
const cred = store.profiles[profileId];
|
||||
if (!cred || cred.type !== "oauth") return null;
|
||||
if (!cred || cred.type !== "oauth") {
|
||||
return null;
|
||||
}
|
||||
const profileConfig = cfg?.auth?.profiles?.[profileId];
|
||||
if (profileConfig && profileConfig.provider !== cred.provider) return null;
|
||||
if (profileConfig && profileConfig.mode !== cred.type) return null;
|
||||
if (profileConfig && profileConfig.provider !== cred.provider) {
|
||||
return null;
|
||||
}
|
||||
if (profileConfig && profileConfig.mode !== cred.type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Date.now() < cred.expires) {
|
||||
return {
|
||||
@@ -108,7 +118,9 @@ async function tryResolveOAuthProfile(params: {
|
||||
profileId,
|
||||
agentDir: params.agentDir,
|
||||
});
|
||||
if (!refreshed) return null;
|
||||
if (!refreshed) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
apiKey: refreshed.apiKey,
|
||||
provider: cred.provider,
|
||||
@@ -124,12 +136,18 @@ export async function resolveApiKeyForProfile(params: {
|
||||
}): Promise<{ apiKey: string; provider: string; email?: string } | null> {
|
||||
const { cfg, store, profileId } = params;
|
||||
const cred = store.profiles[profileId];
|
||||
if (!cred) return null;
|
||||
if (!cred) {
|
||||
return null;
|
||||
}
|
||||
const profileConfig = cfg?.auth?.profiles?.[profileId];
|
||||
if (profileConfig && profileConfig.provider !== cred.provider) return null;
|
||||
if (profileConfig && profileConfig.provider !== cred.provider) {
|
||||
return null;
|
||||
}
|
||||
if (profileConfig && profileConfig.mode !== cred.type) {
|
||||
// Compatibility: treat "oauth" config as compatible with stored token profiles.
|
||||
if (!(profileConfig.mode === "oauth" && cred.type === "token")) return null;
|
||||
if (!(profileConfig.mode === "oauth" && cred.type === "token")) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cred.type === "api_key") {
|
||||
@@ -137,7 +155,9 @@ export async function resolveApiKeyForProfile(params: {
|
||||
}
|
||||
if (cred.type === "token") {
|
||||
const token = cred.token?.trim();
|
||||
if (!token) return null;
|
||||
if (!token) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
typeof cred.expires === "number" &&
|
||||
Number.isFinite(cred.expires) &&
|
||||
@@ -161,7 +181,9 @@ export async function resolveApiKeyForProfile(params: {
|
||||
profileId,
|
||||
agentDir: params.agentDir,
|
||||
});
|
||||
if (!result) return null;
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
apiKey: result.apiKey,
|
||||
provider: cred.provider,
|
||||
@@ -191,7 +213,9 @@ export async function resolveApiKeyForProfile(params: {
|
||||
profileId: fallbackProfileId,
|
||||
agentDir: params.agentDir,
|
||||
});
|
||||
if (fallbackResolved) return fallbackResolved;
|
||||
if (fallbackResolved) {
|
||||
return fallbackResolved;
|
||||
}
|
||||
} catch {
|
||||
// keep original error
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ function resolveProfileUnusableUntil(stats: {
|
||||
const values = [stats.cooldownUntil, stats.disabledUntil]
|
||||
.filter((value): value is number => typeof value === "number")
|
||||
.filter((value) => Number.isFinite(value) && value > 0);
|
||||
if (values.length === 0) return null;
|
||||
if (values.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return Math.max(...values);
|
||||
}
|
||||
|
||||
@@ -26,17 +28,25 @@ export function resolveAuthProfileOrder(params: {
|
||||
const now = Date.now();
|
||||
const storedOrder = (() => {
|
||||
const order = store.order;
|
||||
if (!order) return undefined;
|
||||
if (!order) {
|
||||
return undefined;
|
||||
}
|
||||
for (const [key, value] of Object.entries(order)) {
|
||||
if (normalizeProviderId(key) === providerKey) return value;
|
||||
if (normalizeProviderId(key) === providerKey) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
const configuredOrder = (() => {
|
||||
const order = cfg?.auth?.order;
|
||||
if (!order) return undefined;
|
||||
if (!order) {
|
||||
return undefined;
|
||||
}
|
||||
for (const [key, value] of Object.entries(order)) {
|
||||
if (normalizeProviderId(key) === providerKey) return value;
|
||||
if (normalizeProviderId(key) === providerKey) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
@@ -49,12 +59,18 @@ export function resolveAuthProfileOrder(params: {
|
||||
const baseOrder =
|
||||
explicitOrder ??
|
||||
(explicitProfiles.length > 0 ? explicitProfiles : listProfilesForProvider(store, providerKey));
|
||||
if (baseOrder.length === 0) return [];
|
||||
if (baseOrder.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const filtered = baseOrder.filter((profileId) => {
|
||||
const cred = store.profiles[profileId];
|
||||
if (!cred) return false;
|
||||
if (normalizeProviderId(cred.provider) !== providerKey) return false;
|
||||
if (!cred) {
|
||||
return false;
|
||||
}
|
||||
if (normalizeProviderId(cred.provider) !== providerKey) {
|
||||
return false;
|
||||
}
|
||||
const profileConfig = cfg?.auth?.profiles?.[profileId];
|
||||
if (profileConfig) {
|
||||
if (normalizeProviderId(profileConfig.provider) !== providerKey) {
|
||||
@@ -62,12 +78,18 @@ export function resolveAuthProfileOrder(params: {
|
||||
}
|
||||
if (profileConfig.mode !== cred.type) {
|
||||
const oauthCompatible = profileConfig.mode === "oauth" && cred.type === "token";
|
||||
if (!oauthCompatible) return false;
|
||||
if (!oauthCompatible) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cred.type === "api_key") return Boolean(cred.key?.trim());
|
||||
if (cred.type === "api_key") {
|
||||
return Boolean(cred.key?.trim());
|
||||
}
|
||||
if (cred.type === "token") {
|
||||
if (!cred.token?.trim()) return false;
|
||||
if (!cred.token?.trim()) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeof cred.expires === "number" &&
|
||||
Number.isFinite(cred.expires) &&
|
||||
@@ -85,7 +107,9 @@ export function resolveAuthProfileOrder(params: {
|
||||
});
|
||||
const deduped: string[] = [];
|
||||
for (const entry of filtered) {
|
||||
if (!deduped.includes(entry)) deduped.push(entry);
|
||||
if (!deduped.includes(entry)) {
|
||||
deduped.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// If user specified explicit order (store override or config), respect it
|
||||
@@ -165,7 +189,9 @@ function orderProfilesByMode(order: string[], store: AuthProfileStore): string[]
|
||||
const sorted = scored
|
||||
.toSorted((a, b) => {
|
||||
// First by type (oauth > token > api_key)
|
||||
if (a.typeScore !== b.typeScore) return a.typeScore - b.typeScore;
|
||||
if (a.typeScore !== b.typeScore) {
|
||||
return a.typeScore - b.typeScore;
|
||||
}
|
||||
// Then by lastUsed (oldest first)
|
||||
return a.lastUsed - b.lastUsed;
|
||||
})
|
||||
|
||||
@@ -23,7 +23,9 @@ export function resolveAuthStorePathForDisplay(agentDir?: string): string {
|
||||
}
|
||||
|
||||
export function ensureAuthStoreFile(pathname: string) {
|
||||
if (fs.existsSync(pathname)) return;
|
||||
if (fs.existsSync(pathname)) {
|
||||
return;
|
||||
}
|
||||
const payload: AuthProfileStore = {
|
||||
version: AUTH_STORE_VERSION,
|
||||
profiles: {},
|
||||
|
||||
@@ -19,7 +19,9 @@ export async function setAuthProfileOrder(params: {
|
||||
|
||||
const deduped: string[] = [];
|
||||
for (const entry of sanitized) {
|
||||
if (!deduped.includes(entry)) deduped.push(entry);
|
||||
if (!deduped.includes(entry)) {
|
||||
deduped.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return await updateAuthProfileStoreWithLock({
|
||||
@@ -27,7 +29,9 @@ export async function setAuthProfileOrder(params: {
|
||||
updater: (store) => {
|
||||
store.order = store.order ?? {};
|
||||
if (deduped.length === 0) {
|
||||
if (!store.order[providerKey]) return false;
|
||||
if (!store.order[providerKey]) {
|
||||
return false;
|
||||
}
|
||||
delete store.order[providerKey];
|
||||
if (Object.keys(store.order).length === 0) {
|
||||
store.order = undefined;
|
||||
@@ -68,7 +72,9 @@ export async function markAuthProfileGood(params: {
|
||||
agentDir,
|
||||
updater: (freshStore) => {
|
||||
const profile = freshStore.profiles[profileId];
|
||||
if (!profile || profile.provider !== provider) return false;
|
||||
if (!profile || profile.provider !== provider) {
|
||||
return false;
|
||||
}
|
||||
freshStore.lastGood = { ...freshStore.lastGood, [provider]: profileId };
|
||||
return true;
|
||||
},
|
||||
@@ -78,7 +84,9 @@ export async function markAuthProfileGood(params: {
|
||||
return;
|
||||
}
|
||||
const profile = store.profiles[profileId];
|
||||
if (!profile || profile.provider !== provider) return;
|
||||
if (!profile || profile.provider !== provider) {
|
||||
return;
|
||||
}
|
||||
store.lastGood = { ...store.lastGood, [provider]: profileId };
|
||||
saveAuthProfileStore(store, agentDir);
|
||||
}
|
||||
|
||||
@@ -6,13 +6,17 @@ import type { AuthProfileIdRepairResult, AuthProfileStore } from "./types.js";
|
||||
|
||||
function getProfileSuffix(profileId: string): string {
|
||||
const idx = profileId.indexOf(":");
|
||||
if (idx < 0) return "";
|
||||
if (idx < 0) {
|
||||
return "";
|
||||
}
|
||||
return profileId.slice(idx + 1);
|
||||
}
|
||||
|
||||
function isEmailLike(value: string): boolean {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return false;
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
return trimmed.includes("@") && trimmed.includes(".");
|
||||
}
|
||||
|
||||
@@ -24,7 +28,9 @@ export function suggestOAuthProfileIdForLegacyDefault(params: {
|
||||
}): string | null {
|
||||
const providerKey = normalizeProviderId(params.provider);
|
||||
const legacySuffix = getProfileSuffix(params.legacyProfileId);
|
||||
if (legacySuffix !== "default") return null;
|
||||
if (legacySuffix !== "default") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const legacyCfg = params.cfg?.auth?.profiles?.[params.legacyProfileId];
|
||||
if (
|
||||
@@ -38,27 +44,39 @@ export function suggestOAuthProfileIdForLegacyDefault(params: {
|
||||
const oauthProfiles = listProfilesForProvider(params.store, providerKey).filter(
|
||||
(id) => params.store.profiles[id]?.type === "oauth",
|
||||
);
|
||||
if (oauthProfiles.length === 0) return null;
|
||||
if (oauthProfiles.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const configuredEmail = legacyCfg?.email?.trim();
|
||||
if (configuredEmail) {
|
||||
const byEmail = oauthProfiles.find((id) => {
|
||||
const cred = params.store.profiles[id];
|
||||
if (!cred || cred.type !== "oauth") return false;
|
||||
if (!cred || cred.type !== "oauth") {
|
||||
return false;
|
||||
}
|
||||
const email = cred.email?.trim();
|
||||
return email === configuredEmail || id === `${providerKey}:${configuredEmail}`;
|
||||
});
|
||||
if (byEmail) return byEmail;
|
||||
if (byEmail) {
|
||||
return byEmail;
|
||||
}
|
||||
}
|
||||
|
||||
const lastGood = params.store.lastGood?.[providerKey] ?? params.store.lastGood?.[params.provider];
|
||||
if (lastGood && oauthProfiles.includes(lastGood)) return lastGood;
|
||||
if (lastGood && oauthProfiles.includes(lastGood)) {
|
||||
return lastGood;
|
||||
}
|
||||
|
||||
const nonLegacy = oauthProfiles.filter((id) => id !== params.legacyProfileId);
|
||||
if (nonLegacy.length === 1) return nonLegacy[0] ?? null;
|
||||
if (nonLegacy.length === 1) {
|
||||
return nonLegacy[0] ?? null;
|
||||
}
|
||||
|
||||
const emailLike = nonLegacy.filter((id) => isEmailLike(getProfileSuffix(id)));
|
||||
if (emailLike.length === 1) return emailLike[0] ?? null;
|
||||
if (emailLike.length === 1) {
|
||||
return emailLike[0] ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -107,17 +125,25 @@ export function repairOAuthProfileIdMismatch(params: {
|
||||
const providerKey = normalizeProviderId(params.provider);
|
||||
const nextOrder = (() => {
|
||||
const order = params.cfg.auth?.order;
|
||||
if (!order) return undefined;
|
||||
if (!order) {
|
||||
return undefined;
|
||||
}
|
||||
const resolvedKey = Object.keys(order).find((key) => normalizeProviderId(key) === providerKey);
|
||||
if (!resolvedKey) return order;
|
||||
if (!resolvedKey) {
|
||||
return order;
|
||||
}
|
||||
const existing = order[resolvedKey];
|
||||
if (!Array.isArray(existing)) return order;
|
||||
if (!Array.isArray(existing)) {
|
||||
return order;
|
||||
}
|
||||
const replaced = existing
|
||||
.map((id) => (id === legacyProfileId ? toProfileId : id))
|
||||
.filter((id): id is string => typeof id === "string" && id.trim().length > 0);
|
||||
const deduped: string[] = [];
|
||||
for (const entry of replaced) {
|
||||
if (!deduped.includes(entry)) deduped.push(entry);
|
||||
if (!deduped.includes(entry)) {
|
||||
deduped.push(entry);
|
||||
}
|
||||
}
|
||||
return { ...order, [resolvedKey]: deduped };
|
||||
})();
|
||||
|
||||
@@ -53,8 +53,11 @@ describe("resolveSessionAuthProfileOverride", () => {
|
||||
expect(resolved).toBe("zai:work");
|
||||
expect(sessionEntry.authProfileOverride).toBe("zai:work");
|
||||
} finally {
|
||||
if (prevStateDir === undefined) delete process.env.OPENCLAW_STATE_DIR;
|
||||
else process.env.OPENCLAW_STATE_DIR = prevStateDir;
|
||||
if (prevStateDir === undefined) {
|
||||
delete process.env.OPENCLAW_STATE_DIR;
|
||||
} else {
|
||||
process.env.OPENCLAW_STATE_DIR = prevStateDir;
|
||||
}
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -13,7 +13,9 @@ function isProfileForProvider(params: {
|
||||
store: ReturnType<typeof ensureAuthProfileStore>;
|
||||
}): boolean {
|
||||
const entry = params.store.profiles[params.profileId];
|
||||
if (!entry?.provider) return false;
|
||||
if (!entry?.provider) {
|
||||
return false;
|
||||
}
|
||||
return normalizeProviderId(entry.provider) === normalizeProviderId(params.provider);
|
||||
}
|
||||
|
||||
@@ -56,7 +58,9 @@ export async function resolveSessionAuthProfileOverride(params: {
|
||||
storePath,
|
||||
isNewSession,
|
||||
} = params;
|
||||
if (!sessionEntry || !sessionStore || !sessionKey) return sessionEntry?.authProfileOverride;
|
||||
if (!sessionEntry || !sessionStore || !sessionKey) {
|
||||
return sessionEntry?.authProfileOverride;
|
||||
}
|
||||
|
||||
const store = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
|
||||
const order = resolveAuthProfileOrder({ cfg, store, provider });
|
||||
@@ -77,16 +81,22 @@ export async function resolveSessionAuthProfileOverride(params: {
|
||||
current = undefined;
|
||||
}
|
||||
|
||||
if (order.length === 0) return undefined;
|
||||
if (order.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const pickFirstAvailable = () =>
|
||||
order.find((profileId) => !isProfileInCooldown(store, profileId)) ?? order[0];
|
||||
const pickNextAvailable = (active: string) => {
|
||||
const startIndex = order.indexOf(active);
|
||||
if (startIndex < 0) return pickFirstAvailable();
|
||||
if (startIndex < 0) {
|
||||
return pickFirstAvailable();
|
||||
}
|
||||
for (let offset = 1; offset <= order.length; offset += 1) {
|
||||
const candidate = order[(startIndex + offset) % order.length];
|
||||
if (!isProfileInCooldown(store, candidate)) return candidate;
|
||||
if (!isProfileInCooldown(store, candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return order[startIndex] ?? order[0];
|
||||
};
|
||||
@@ -117,7 +127,9 @@ export async function resolveSessionAuthProfileOverride(params: {
|
||||
next = pickFirstAvailable();
|
||||
}
|
||||
|
||||
if (!next) return current;
|
||||
if (!next) {
|
||||
return current;
|
||||
}
|
||||
const shouldPersist =
|
||||
next !== sessionEntry.authProfileOverride ||
|
||||
sessionEntry.authProfileOverrideSource !== "auto" ||
|
||||
|
||||
@@ -48,12 +48,18 @@ export async function updateAuthProfileStoreWithLock(params: {
|
||||
}
|
||||
|
||||
function coerceLegacyStore(raw: unknown): LegacyAuthStore | null {
|
||||
if (!raw || typeof raw !== "object") return null;
|
||||
if (!raw || typeof raw !== "object") {
|
||||
return null;
|
||||
}
|
||||
const record = raw as Record<string, unknown>;
|
||||
if ("profiles" in record) return null;
|
||||
if ("profiles" in record) {
|
||||
return null;
|
||||
}
|
||||
const entries: LegacyAuthStore = {};
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
if (!value || typeof value !== "object") continue;
|
||||
if (!value || typeof value !== "object") {
|
||||
continue;
|
||||
}
|
||||
const typed = value as Partial<AuthProfileCredential>;
|
||||
if (typed.type !== "api_key" && typed.type !== "oauth" && typed.type !== "token") {
|
||||
continue;
|
||||
@@ -67,29 +73,41 @@ function coerceLegacyStore(raw: unknown): LegacyAuthStore | null {
|
||||
}
|
||||
|
||||
function coerceAuthStore(raw: unknown): AuthProfileStore | null {
|
||||
if (!raw || typeof raw !== "object") return null;
|
||||
if (!raw || typeof raw !== "object") {
|
||||
return null;
|
||||
}
|
||||
const record = raw as Record<string, unknown>;
|
||||
if (!record.profiles || typeof record.profiles !== "object") return null;
|
||||
if (!record.profiles || typeof record.profiles !== "object") {
|
||||
return null;
|
||||
}
|
||||
const profiles = record.profiles as Record<string, unknown>;
|
||||
const normalized: Record<string, AuthProfileCredential> = {};
|
||||
for (const [key, value] of Object.entries(profiles)) {
|
||||
if (!value || typeof value !== "object") continue;
|
||||
if (!value || typeof value !== "object") {
|
||||
continue;
|
||||
}
|
||||
const typed = value as Partial<AuthProfileCredential>;
|
||||
if (typed.type !== "api_key" && typed.type !== "oauth" && typed.type !== "token") {
|
||||
continue;
|
||||
}
|
||||
if (!typed.provider) continue;
|
||||
if (!typed.provider) {
|
||||
continue;
|
||||
}
|
||||
normalized[key] = typed as AuthProfileCredential;
|
||||
}
|
||||
const order =
|
||||
record.order && typeof record.order === "object"
|
||||
? Object.entries(record.order as Record<string, unknown>).reduce(
|
||||
(acc, [provider, value]) => {
|
||||
if (!Array.isArray(value)) return acc;
|
||||
if (!Array.isArray(value)) {
|
||||
return acc;
|
||||
}
|
||||
const list = value
|
||||
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
|
||||
.filter(Boolean);
|
||||
if (list.length === 0) return acc;
|
||||
if (list.length === 0) {
|
||||
return acc;
|
||||
}
|
||||
acc[provider] = list;
|
||||
return acc;
|
||||
},
|
||||
@@ -115,9 +133,15 @@ function mergeRecord<T>(
|
||||
base?: Record<string, T>,
|
||||
override?: Record<string, T>,
|
||||
): Record<string, T> | undefined {
|
||||
if (!base && !override) return undefined;
|
||||
if (!base) return { ...override };
|
||||
if (!override) return { ...base };
|
||||
if (!base && !override) {
|
||||
return undefined;
|
||||
}
|
||||
if (!base) {
|
||||
return { ...override };
|
||||
}
|
||||
if (!override) {
|
||||
return { ...base };
|
||||
}
|
||||
return { ...base, ...override };
|
||||
}
|
||||
|
||||
@@ -145,13 +169,19 @@ function mergeAuthProfileStores(
|
||||
function mergeOAuthFileIntoStore(store: AuthProfileStore): boolean {
|
||||
const oauthPath = resolveOAuthPath();
|
||||
const oauthRaw = loadJsonFile(oauthPath);
|
||||
if (!oauthRaw || typeof oauthRaw !== "object") return false;
|
||||
if (!oauthRaw || typeof oauthRaw !== "object") {
|
||||
return false;
|
||||
}
|
||||
const oauthEntries = oauthRaw as Record<string, OAuthCredentials>;
|
||||
let mutated = false;
|
||||
for (const [provider, creds] of Object.entries(oauthEntries)) {
|
||||
if (!creds || typeof creds !== "object") continue;
|
||||
if (!creds || typeof creds !== "object") {
|
||||
continue;
|
||||
}
|
||||
const profileId = `${provider}:default`;
|
||||
if (store.profiles[profileId]) continue;
|
||||
if (store.profiles[profileId]) {
|
||||
continue;
|
||||
}
|
||||
store.profiles[profileId] = {
|
||||
type: "oauth",
|
||||
provider,
|
||||
|
||||
@@ -7,7 +7,9 @@ function resolveProfileUnusableUntil(stats: ProfileUsageStats): number | null {
|
||||
const values = [stats.cooldownUntil, stats.disabledUntil]
|
||||
.filter((value): value is number => typeof value === "number")
|
||||
.filter((value) => Number.isFinite(value) && value > 0);
|
||||
if (values.length === 0) return null;
|
||||
if (values.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return Math.max(...values);
|
||||
}
|
||||
|
||||
@@ -16,7 +18,9 @@ function resolveProfileUnusableUntil(stats: ProfileUsageStats): number | null {
|
||||
*/
|
||||
export function isProfileInCooldown(store: AuthProfileStore, profileId: string): boolean {
|
||||
const stats = store.usageStats?.[profileId];
|
||||
if (!stats) return false;
|
||||
if (!stats) {
|
||||
return false;
|
||||
}
|
||||
const unusableUntil = resolveProfileUnusableUntil(stats);
|
||||
return unusableUntil ? Date.now() < unusableUntil : false;
|
||||
}
|
||||
@@ -34,7 +38,9 @@ export async function markAuthProfileUsed(params: {
|
||||
const updated = await updateAuthProfileStoreWithLock({
|
||||
agentDir,
|
||||
updater: (freshStore) => {
|
||||
if (!freshStore.profiles[profileId]) return false;
|
||||
if (!freshStore.profiles[profileId]) {
|
||||
return false;
|
||||
}
|
||||
freshStore.usageStats = freshStore.usageStats ?? {};
|
||||
freshStore.usageStats[profileId] = {
|
||||
...freshStore.usageStats[profileId],
|
||||
@@ -52,7 +58,9 @@ export async function markAuthProfileUsed(params: {
|
||||
store.usageStats = updated.usageStats;
|
||||
return;
|
||||
}
|
||||
if (!store.profiles[profileId]) return;
|
||||
if (!store.profiles[profileId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.usageStats = store.usageStats ?? {};
|
||||
store.usageStats[profileId] = {
|
||||
@@ -97,9 +105,13 @@ function resolveAuthCooldownConfig(params: {
|
||||
const cooldowns = params.cfg?.auth?.cooldowns;
|
||||
const billingOverride = (() => {
|
||||
const map = cooldowns?.billingBackoffHoursByProvider;
|
||||
if (!map) return undefined;
|
||||
if (!map) {
|
||||
return undefined;
|
||||
}
|
||||
for (const [key, value] of Object.entries(map)) {
|
||||
if (normalizeProviderId(key) === params.providerId) return value;
|
||||
if (normalizeProviderId(key) === params.providerId) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
})();
|
||||
@@ -139,7 +151,9 @@ export function resolveProfileUnusableUntilForDisplay(
|
||||
profileId: string,
|
||||
): number | null {
|
||||
const stats = store.usageStats?.[profileId];
|
||||
if (!stats) return null;
|
||||
if (!stats) {
|
||||
return null;
|
||||
}
|
||||
return resolveProfileUnusableUntil(stats);
|
||||
}
|
||||
|
||||
@@ -200,7 +214,9 @@ export async function markAuthProfileFailure(params: {
|
||||
agentDir,
|
||||
updater: (freshStore) => {
|
||||
const profile = freshStore.profiles[profileId];
|
||||
if (!profile) return false;
|
||||
if (!profile) {
|
||||
return false;
|
||||
}
|
||||
freshStore.usageStats = freshStore.usageStats ?? {};
|
||||
const existing = freshStore.usageStats[profileId] ?? {};
|
||||
|
||||
@@ -224,7 +240,9 @@ export async function markAuthProfileFailure(params: {
|
||||
store.usageStats = updated.usageStats;
|
||||
return;
|
||||
}
|
||||
if (!store.profiles[profileId]) return;
|
||||
if (!store.profiles[profileId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.usageStats = store.usageStats ?? {};
|
||||
const existing = store.usageStats[profileId] ?? {};
|
||||
@@ -275,7 +293,9 @@ export async function clearAuthProfileCooldown(params: {
|
||||
const updated = await updateAuthProfileStoreWithLock({
|
||||
agentDir,
|
||||
updater: (freshStore) => {
|
||||
if (!freshStore.usageStats?.[profileId]) return false;
|
||||
if (!freshStore.usageStats?.[profileId]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
freshStore.usageStats[profileId] = {
|
||||
...freshStore.usageStats[profileId],
|
||||
@@ -289,7 +309,9 @@ export async function clearAuthProfileCooldown(params: {
|
||||
store.usageStats = updated.usageStats;
|
||||
return;
|
||||
}
|
||||
if (!store.usageStats?.[profileId]) return;
|
||||
if (!store.usageStats?.[profileId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.usageStats[profileId] = {
|
||||
...store.usageStats[profileId],
|
||||
|
||||
Reference in New Issue
Block a user