From 230e1d9962a7dc789c58e8113aa2f253f40cbc2b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 16 Feb 2026 22:51:42 +0000 Subject: [PATCH] refactor(auth): share profile id dedupe helper --- src/agents/auth-profiles.ts | 1 + src/agents/auth-profiles/order.ts | 9 ++------- src/agents/auth-profiles/profiles.ts | 12 +++++------- src/agents/auth-profiles/repair.ts | 9 ++------- src/infra/provider-usage.auth.ts | 10 ++-------- 5 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/agents/auth-profiles.ts b/src/agents/auth-profiles.ts index ae55d2cc5..fc731e87a 100644 --- a/src/agents/auth-profiles.ts +++ b/src/agents/auth-profiles.ts @@ -5,6 +5,7 @@ export { resolveApiKeyForProfile } from "./auth-profiles/oauth.js"; export { resolveAuthProfileOrder } from "./auth-profiles/order.js"; export { resolveAuthStorePathForDisplay } from "./auth-profiles/paths.js"; export { + dedupeProfileIds, listProfilesForProvider, markAuthProfileGood, setAuthProfileOrder, diff --git a/src/agents/auth-profiles/order.ts b/src/agents/auth-profiles/order.ts index 0301f4d6d..accdb97b9 100644 --- a/src/agents/auth-profiles/order.ts +++ b/src/agents/auth-profiles/order.ts @@ -1,7 +1,7 @@ import type { OpenClawConfig } from "../../config/config.js"; import type { AuthProfileStore } from "./types.js"; import { normalizeProviderId } from "../model-selection.js"; -import { listProfilesForProvider } from "./profiles.js"; +import { dedupeProfileIds, listProfilesForProvider } from "./profiles.js"; import { clearExpiredCooldowns, isProfileInCooldown } from "./usage.js"; function resolveProfileUnusableUntil(stats: { @@ -110,12 +110,7 @@ export function resolveAuthProfileOrder(params: { } return false; }); - const deduped: string[] = []; - for (const entry of filtered) { - if (!deduped.includes(entry)) { - deduped.push(entry); - } - } + const deduped = dedupeProfileIds(filtered); // If user specified explicit order (store override or config), respect it // exactly, but still apply cooldown sorting to avoid repeatedly selecting diff --git a/src/agents/auth-profiles/profiles.ts b/src/agents/auth-profiles/profiles.ts index 019a611f4..063e8438f 100644 --- a/src/agents/auth-profiles/profiles.ts +++ b/src/agents/auth-profiles/profiles.ts @@ -7,6 +7,10 @@ import { updateAuthProfileStoreWithLock, } from "./store.js"; +export function dedupeProfileIds(profileIds: string[]): string[] { + return [...new Set(profileIds)]; +} + export async function setAuthProfileOrder(params: { agentDir?: string; provider: string; @@ -17,13 +21,7 @@ export async function setAuthProfileOrder(params: { params.order && Array.isArray(params.order) ? params.order.map((entry) => String(entry).trim()).filter(Boolean) : []; - - const deduped: string[] = []; - for (const entry of sanitized) { - if (!deduped.includes(entry)) { - deduped.push(entry); - } - } + const deduped = dedupeProfileIds(sanitized); return await updateAuthProfileStoreWithLock({ agentDir: params.agentDir, diff --git a/src/agents/auth-profiles/repair.ts b/src/agents/auth-profiles/repair.ts index f2ccf2ec6..cbb34ea06 100644 --- a/src/agents/auth-profiles/repair.ts +++ b/src/agents/auth-profiles/repair.ts @@ -2,7 +2,7 @@ import type { OpenClawConfig } from "../../config/config.js"; import type { AuthProfileConfig } from "../../config/types.js"; import type { AuthProfileIdRepairResult, AuthProfileStore } from "./types.js"; import { normalizeProviderId } from "../model-selection.js"; -import { listProfilesForProvider } from "./profiles.js"; +import { dedupeProfileIds, listProfilesForProvider } from "./profiles.js"; function getProfileSuffix(profileId: string): string { const idx = profileId.indexOf(":"); @@ -139,12 +139,7 @@ export function repairOAuthProfileIdMismatch(params: { 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); - } - } + const deduped = dedupeProfileIds(replaced); return { ...order, [resolvedKey]: deduped }; })(); diff --git a/src/infra/provider-usage.auth.ts b/src/infra/provider-usage.auth.ts index 2c92c0938..75b1860f9 100644 --- a/src/infra/provider-usage.auth.ts +++ b/src/infra/provider-usage.auth.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import type { UsageProviderId } from "./provider-usage.types.js"; import { + dedupeProfileIds, ensureAuthProfileStore, listProfilesForProvider, resolveApiKeyForProfile, @@ -144,14 +145,7 @@ async function resolveOAuthToken(params: { store, provider: params.provider, }); - - const candidates = order; - const deduped: string[] = []; - for (const entry of candidates) { - if (!deduped.includes(entry)) { - deduped.push(entry); - } - } + const deduped = dedupeProfileIds(order); for (const profileId of deduped) { const cred = store.profiles[profileId];