(provider, reason = 'manual_rotation')
| 3693 | } |
| 3694 | |
| 3695 | function rotateAccount(provider, reason = 'manual_rotation') { |
| 3696 | const pool = buildAccountPool(provider); |
| 3697 | |
| 3698 | if (pool.accounts.length === 0) { |
| 3699 | return { success: false, error: 'No accounts in pool' }; |
| 3700 | } |
| 3701 | |
| 3702 | const now = Date.now(); |
| 3703 | const available = pool.accounts.filter(acc => |
| 3704 | acc.status === 'ready' || (acc.status === 'cooldown' && acc.cooldownUntil && acc.cooldownUntil < now) |
| 3705 | ); |
| 3706 | |
| 3707 | if (available.length === 0) { |
| 3708 | return { success: false, error: 'No available accounts (all in cooldown or expired)' }; |
| 3709 | } |
| 3710 | |
| 3711 | // Pick least recently used |
| 3712 | const next = available.sort((a, b) => a.lastUsed - b.lastUsed)[0]; |
| 3713 | const previousActive = pool.activeAccount; |
| 3714 | |
| 3715 | // Activate the new account |
| 3716 | const activePlugin = getActiveGooglePlugin(); |
| 3717 | const namespace = provider === 'google' |
| 3718 | ? ('google.antigravity') |
| 3719 | : provider; |
| 3720 | |
| 3721 | const profilePath = path.join(AUTH_PROFILES_DIR, namespace, `${next.name}.json`); |
| 3722 | if (!fs.existsSync(profilePath)) { |
| 3723 | return { success: false, error: 'Profile file not found' }; |
| 3724 | } |
| 3725 | |
| 3726 | const profileData = JSON.parse(fs.readFileSync(profilePath, 'utf8')); |
| 3727 | |
| 3728 | // Update auth.json |
| 3729 | const authCfg = loadAuthConfig() || {}; |
| 3730 | authCfg[provider] = profileData; |
| 3731 | if (provider === 'google') { |
| 3732 | authCfg[namespace] = profileData; |
| 3733 | } |
| 3734 | |
| 3735 | const cp = getConfigPath(); |
| 3736 | const ap = path.join(path.dirname(cp), 'auth.json'); |
| 3737 | atomicWriteFileSync(ap, JSON.stringify(authCfg, null, 2)); |
| 3738 | |
| 3739 | // Update studio config |
| 3740 | const studio = loadStudioConfig(); |
| 3741 | if (!studio.activeProfiles) studio.activeProfiles = {}; |
| 3742 | studio.activeProfiles[provider] = next.name; |
| 3743 | saveStudioConfig(studio); |
| 3744 | |
| 3745 | // Update metadata |
| 3746 | const metadata = loadPoolMetadata(); |
| 3747 | if (!metadata[namespace]) metadata[namespace] = {}; |
| 3748 | metadata[namespace][next.name] = { |
| 3749 | ...metadata[namespace][next.name], |
| 3750 | lastUsed: now, |
| 3751 | usageCount: (metadata[namespace][next.name]?.usageCount || 0) + 1 |
| 3752 | }; |
no test coverage detected