From 19c95d0ff73a20398273161fef581b5995a78df5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 7 Jan 2026 01:06:51 +0100 Subject: [PATCH] fix(auth): serialize profile stats updates --- src/agents/auth-profiles.ts | 40 ++++++++++++++------------------ src/agents/pi-embedded-runner.ts | 9 ++++--- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/agents/auth-profiles.ts b/src/agents/auth-profiles.ts index 8b746707a..460faef50 100644 --- a/src/agents/auth-profiles.ts +++ b/src/agents/auth-profiles.ts @@ -107,16 +107,16 @@ function syncAuthProfileStore( target.usageStats = source.usageStats; } -function updateAuthProfileStoreWithLock(params: { +async function updateAuthProfileStoreWithLock(params: { agentDir?: string; updater: (store: AuthProfileStore) => boolean; -}): AuthProfileStore | null { +}): Promise { const authPath = resolveAuthStorePath(params.agentDir); ensureAuthStoreFile(authPath); - let release: (() => void) | undefined; + let release: (() => Promise) | undefined; try { - release = lockfile.lockSync(authPath, AUTH_STORE_LOCK_OPTIONS); + release = await lockfile.lock(authPath, AUTH_STORE_LOCK_OPTIONS); const store = ensureAuthProfileStore(params.agentDir); const shouldSave = params.updater(store); if (shouldSave) { @@ -128,13 +128,9 @@ function updateAuthProfileStoreWithLock(params: { } finally { if (release) { try { - release(); + await release(); } catch { - try { - lockfile.unlockSync(authPath); - } catch { - // ignore unlock errors - } + // ignore unlock errors } } } @@ -403,13 +399,13 @@ export function isProfileInCooldown( * Mark a profile as successfully used. Resets error count and updates lastUsed. * Uses store lock to avoid overwriting concurrent usage updates. */ -export function markAuthProfileUsed(params: { +export async function markAuthProfileUsed(params: { store: AuthProfileStore; profileId: string; agentDir?: string; -}): void { +}): Promise { const { store, profileId, agentDir } = params; - const updated = updateAuthProfileStoreWithLock({ + const updated = await updateAuthProfileStoreWithLock({ agentDir, updater: (freshStore) => { if (!freshStore.profiles[profileId]) return false; @@ -452,13 +448,13 @@ export function calculateAuthProfileCooldownMs(errorCount: number): number { * Cooldown times: 1min, 5min, 25min, max 1 hour. * Uses store lock to avoid overwriting concurrent usage updates. */ -export function markAuthProfileCooldown(params: { +export async function markAuthProfileCooldown(params: { store: AuthProfileStore; profileId: string; agentDir?: string; -}): void { +}): Promise { const { store, profileId, agentDir } = params; - const updated = updateAuthProfileStoreWithLock({ + const updated = await updateAuthProfileStoreWithLock({ agentDir, updater: (freshStore) => { if (!freshStore.profiles[profileId]) return false; @@ -503,13 +499,13 @@ export function markAuthProfileCooldown(params: { * Clear cooldown for a profile (e.g., manual reset). * Uses store lock to avoid overwriting concurrent usage updates. */ -export function clearAuthProfileCooldown(params: { +export async function clearAuthProfileCooldown(params: { store: AuthProfileStore; profileId: string; agentDir?: string; -}): void { +}): Promise { const { store, profileId, agentDir } = params; - const updated = updateAuthProfileStoreWithLock({ + const updated = await updateAuthProfileStoreWithLock({ agentDir, updater: (freshStore) => { if (!freshStore.usageStats?.[profileId]) return false; @@ -693,14 +689,14 @@ export async function resolveApiKeyForProfile(params: { } } -export function markAuthProfileGood(params: { +export async function markAuthProfileGood(params: { store: AuthProfileStore; provider: string; profileId: string; agentDir?: string; -}): void { +}): Promise { const { store, provider, profileId, agentDir } = params; - const updated = updateAuthProfileStoreWithLock({ + const updated = await updateAuthProfileStoreWithLock({ agentDir, updater: (freshStore) => { const profile = freshStore.profiles[profileId]; diff --git a/src/agents/pi-embedded-runner.ts b/src/agents/pi-embedded-runner.ts index af60309b1..c4113c774 100644 --- a/src/agents/pi-embedded-runner.ts +++ b/src/agents/pi-embedded-runner.ts @@ -1000,7 +1000,7 @@ export async function runEmbeddedPiAgent(params: { if (shouldRotate) { // Mark current profile for cooldown before rotating if (lastProfileId) { - markAuthProfileCooldown({ + await markAuthProfileCooldown({ store: authStore, profileId: lastProfileId, }); @@ -1091,13 +1091,16 @@ export async function runEmbeddedPiAgent(params: { `embedded run done: runId=${params.runId} sessionId=${params.sessionId} durationMs=${Date.now() - started} aborted=${aborted}`, ); if (lastProfileId) { - markAuthProfileGood({ + await markAuthProfileGood({ store: authStore, provider, profileId: lastProfileId, }); // Track usage for round-robin rotation - markAuthProfileUsed({ store: authStore, profileId: lastProfileId }); + await markAuthProfileUsed({ + store: authStore, + profileId: lastProfileId, + }); } return { payloads: payloads.length ? payloads : undefined,