From f3ea376b6b26695d58e340ee9ca33a3cc8e9f90d Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 9 Aug 2026 19:06:07 -0700 Subject: [PATCH] refactor(rate-limits): share initial account target policy (#13428) --- .../claude-rate-limit-target.test.ts | 179 ------------------ .../rate-limits/claude-rate-limit-target.ts | 68 +------ .../codex-rate-limit-target.test.ts | 179 ------------------ .../rate-limits/codex-rate-limit-target.ts | 67 +------ .../initial-account-rate-limit-target.test.ts | 163 ++++++++++++++++ .../initial-account-rate-limit-target.ts | 80 ++++++++ .../project-runtime-rate-limit-target.ts | 29 --- 7 files changed, 258 insertions(+), 507 deletions(-) delete mode 100644 src/main/rate-limits/claude-rate-limit-target.test.ts delete mode 100644 src/main/rate-limits/codex-rate-limit-target.test.ts create mode 100644 src/main/rate-limits/initial-account-rate-limit-target.test.ts create mode 100644 src/main/rate-limits/initial-account-rate-limit-target.ts delete mode 100644 src/main/rate-limits/project-runtime-rate-limit-target.ts diff --git a/src/main/rate-limits/claude-rate-limit-target.test.ts b/src/main/rate-limits/claude-rate-limit-target.test.ts deleted file mode 100644 index 969124655..000000000 --- a/src/main/rate-limits/claude-rate-limit-target.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { getDefaultSettings } from '../../shared/constants' -import type { GlobalSettings } from '../../shared/types' -import { getInitialClaudeRateLimitTarget } from './claude-rate-limit-target' - -function legacySettingsWithoutAccountRuntime(settings: GlobalSettings): GlobalSettings { - const next = { ...settings } as Partial - delete next.localAccountRuntime - return next as GlobalSettings -} - -describe('getInitialClaudeRateLimitTarget', () => { - it('uses the configured WSL account runtime before agent detection runtime', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - localAccountWslDistro: 'Fedora', - localAgentRuntime: 'host', - terminalWindowsWslDistro: 'Debian' - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Fedora' }) - }) - - it('uses the single selected WSL account distro when account runtime is WSL default', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - activeClaudeManagedAccountIdsByRuntime: { - host: 'host-account-1', - wsl: { Ubuntu: 'wsl-account-1' } - } - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('ignores stale terminal WSL distro when account runtime is WSL default', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - localAccountWslDistro: null, - terminalWindowsWslDistro: 'Debian', - activeClaudeManagedAccountIdsByRuntime: { - host: 'host-account-1', - wsl: {} - } - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: null }) - }) - - it('uses the global WSL project runtime default when account runtime is unset', () => { - expect( - getInitialClaudeRateLimitTarget( - legacySettingsWithoutAccountRuntime({ - ...getDefaultSettings('/tmp'), - localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' }, - localAgentRuntime: 'host', - terminalWindowsWslDistro: 'Debian' - }), - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('ignores stale legacy WSL agent and terminal settings when the project default is host', () => { - expect( - getInitialClaudeRateLimitTarget( - legacySettingsWithoutAccountRuntime({ - ...getDefaultSettings('/tmp'), - localWindowsRuntimeDefault: { kind: 'windows-host' }, - localAgentRuntime: 'wsl', - localAgentWslDistro: 'Ubuntu', - terminalWindowsShell: 'wsl.exe', - terminalWindowsWslDistro: 'Debian' - }), - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('uses a single WSL-only active account after restart', () => { - expect( - getInitialClaudeRateLimitTarget( - legacySettingsWithoutAccountRuntime({ - ...getDefaultSettings('/tmp'), - activeClaudeManagedAccountIdsByRuntime: { - host: null, - wsl: { Ubuntu: 'wsl-account-1' } - } - }) - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('auto follows the global WSL project runtime default', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'auto', - localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' } - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('auto resolves to host when the global project runtime is windows-host', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'auto', - localWindowsRuntimeDefault: { kind: 'windows-host' } - }, - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('does not let a stale WSL-only selection override an auto host target', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'auto', - localWindowsRuntimeDefault: { kind: 'windows-host' }, - activeClaudeManagedAccountIdsByRuntime: { - host: null, - wsl: { Ubuntu: 'wsl-account-1' } - } - }, - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('keeps explicit host runtime on host', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'host', - localAgentRuntime: 'host', - terminalWindowsShell: 'wsl.exe', - activeClaudeManagedAccountIdsByRuntime: { - host: null, - wsl: { Ubuntu: 'wsl-account-1' } - } - }, - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('ignores a stale explicit WSL runtime on non-Windows hosts', () => { - expect( - getInitialClaudeRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - localAccountWslDistro: 'Ubuntu' - }, - 'linux' - ) - ).toEqual({ runtime: 'host' }) - }) -}) diff --git a/src/main/rate-limits/claude-rate-limit-target.ts b/src/main/rate-limits/claude-rate-limit-target.ts index 199250cd3..dba2e3966 100644 --- a/src/main/rate-limits/claude-rate-limit-target.ts +++ b/src/main/rate-limits/claude-rate-limit-target.ts @@ -1,71 +1,21 @@ import type { GlobalSettings } from '../../shared/types' -import { resolveLocalAccountRuntimeTarget } from '../../shared/local-account-runtime' import { getClaudeWslSelectionKey, normalizeClaudeRuntimeSelection, type ClaudeAccountSelectionTarget } from '../claude-accounts/runtime-selection' -import { - getProjectRuntimeRateLimitTarget, - normalizeOptionalDistro -} from './project-runtime-rate-limit-target' - -function getSingleSelectedWslDistro(settings: GlobalSettings): string | null { - const selection = normalizeClaudeRuntimeSelection(settings) - const selectedWslEntries = Object.entries(selection.wsl).filter(([, accountId]) => - Boolean(accountId) - ) - if (selectedWslEntries.length !== 1) { - return null - } - const [distroKey] = selectedWslEntries[0] - return distroKey === getClaudeWslSelectionKey(null) ? null : distroKey -} +import { getInitialAccountRateLimitTarget } from './initial-account-rate-limit-target' export function getInitialClaudeRateLimitTarget( settings: GlobalSettings, platform: NodeJS.Platform = process.platform ): ClaudeAccountSelectionTarget { - if (settings.localAccountRuntime === 'host') { - return { runtime: 'host' } - } - if (settings.localAccountRuntime === 'wsl') { - if (platform !== 'win32') { - return { runtime: 'host' } - } - return { - runtime: 'wsl', - wslDistro: - normalizeOptionalDistro(settings.localAccountWslDistro) ?? - getSingleSelectedWslDistro(settings) - } - } - if (settings.localAccountRuntime === 'auto') { - const target = resolveLocalAccountRuntimeTarget(settings, platform) - return target.runtime === 'wsl' - ? { runtime: 'wsl', wslDistro: target.wslDistro } - : { runtime: 'host' } - } - - // Why: pre-setting profiles used account selection as their startup fallback. - const projectRuntimeTarget = getProjectRuntimeRateLimitTarget(settings, platform) - if (projectRuntimeTarget) { - return projectRuntimeTarget - } - - const selection = normalizeClaudeRuntimeSelection(settings) - if (!selection.host) { - const selectedWslEntries = Object.entries(selection.wsl).filter(([, accountId]) => - Boolean(accountId) - ) - if (selectedWslEntries.length === 1) { - const [distroKey] = selectedWslEntries[0] - return { - runtime: 'wsl', - wslDistro: distroKey === getClaudeWslSelectionKey(null) ? null : distroKey - } - } - } - - return { runtime: 'host' } + return getInitialAccountRateLimitTarget( + settings, + { + getWslSelectionKey: getClaudeWslSelectionKey, + normalizeRuntimeSelection: normalizeClaudeRuntimeSelection + }, + platform + ) } diff --git a/src/main/rate-limits/codex-rate-limit-target.test.ts b/src/main/rate-limits/codex-rate-limit-target.test.ts deleted file mode 100644 index b3b39f027..000000000 --- a/src/main/rate-limits/codex-rate-limit-target.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { getDefaultSettings } from '../../shared/constants' -import type { GlobalSettings } from '../../shared/types' -import { getInitialCodexRateLimitTarget } from './codex-rate-limit-target' - -function legacySettingsWithoutAccountRuntime(settings: GlobalSettings): GlobalSettings { - const next = { ...settings } as Partial - delete next.localAccountRuntime - return next as GlobalSettings -} - -describe('getInitialCodexRateLimitTarget', () => { - it('uses the configured WSL account runtime before agent detection runtime', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - localAccountWslDistro: 'Fedora', - localAgentRuntime: 'host', - terminalWindowsWslDistro: 'Debian' - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Fedora' }) - }) - - it('uses the single selected WSL account distro when account runtime is WSL default', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - activeCodexManagedAccountIdsByRuntime: { - host: 'host-account-1', - wsl: { Ubuntu: 'wsl-account-1' } - } - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('ignores stale terminal WSL distro when account runtime is WSL default', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - localAccountWslDistro: null, - terminalWindowsWslDistro: 'Debian', - activeCodexManagedAccountIdsByRuntime: { - host: 'host-account-1', - wsl: {} - } - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: null }) - }) - - it('uses the global WSL project runtime default when account runtime is unset', () => { - expect( - getInitialCodexRateLimitTarget( - legacySettingsWithoutAccountRuntime({ - ...getDefaultSettings('/tmp'), - localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' }, - localAgentRuntime: 'host', - terminalWindowsWslDistro: 'Debian' - }), - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('ignores stale legacy WSL agent and terminal settings when the project default is host', () => { - expect( - getInitialCodexRateLimitTarget( - legacySettingsWithoutAccountRuntime({ - ...getDefaultSettings('/tmp'), - localWindowsRuntimeDefault: { kind: 'windows-host' }, - localAgentRuntime: 'wsl', - localAgentWslDistro: 'Ubuntu', - terminalWindowsShell: 'wsl.exe', - terminalWindowsWslDistro: 'Debian' - }), - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('uses a single WSL-only active account after restart', () => { - expect( - getInitialCodexRateLimitTarget( - legacySettingsWithoutAccountRuntime({ - ...getDefaultSettings('/tmp'), - activeCodexManagedAccountIdsByRuntime: { - host: null, - wsl: { Ubuntu: 'wsl-account-1' } - } - }) - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('auto follows the global WSL project runtime default', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'auto', - localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' } - }, - 'win32' - ) - ).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' }) - }) - - it('auto resolves to host when the global project runtime is windows-host', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'auto', - localWindowsRuntimeDefault: { kind: 'windows-host' } - }, - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('does not let a stale WSL-only selection override an auto host target', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'auto', - localWindowsRuntimeDefault: { kind: 'windows-host' }, - activeCodexManagedAccountIdsByRuntime: { - host: null, - wsl: { Ubuntu: 'wsl-account-1' } - } - }, - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('keeps explicit host runtime on host', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'host', - localAgentRuntime: 'host', - terminalWindowsShell: 'wsl.exe', - activeCodexManagedAccountIdsByRuntime: { - host: null, - wsl: { Ubuntu: 'wsl-account-1' } - } - }, - 'win32' - ) - ).toEqual({ runtime: 'host' }) - }) - - it('ignores a stale explicit WSL runtime on non-Windows hosts', () => { - expect( - getInitialCodexRateLimitTarget( - { - ...getDefaultSettings('/tmp'), - localAccountRuntime: 'wsl', - localAccountWslDistro: 'Ubuntu' - }, - 'darwin' - ) - ).toEqual({ runtime: 'host' }) - }) -}) diff --git a/src/main/rate-limits/codex-rate-limit-target.ts b/src/main/rate-limits/codex-rate-limit-target.ts index 65c459ad7..61c116bab 100644 --- a/src/main/rate-limits/codex-rate-limit-target.ts +++ b/src/main/rate-limits/codex-rate-limit-target.ts @@ -1,73 +1,18 @@ import type { GlobalSettings } from '../../shared/types' -import { resolveLocalAccountRuntimeTarget } from '../../shared/local-account-runtime' import { getWslSelectionKey, normalizeCodexRuntimeSelection, type CodexAccountSelectionTarget } from '../codex-accounts/runtime-selection' -import { - getProjectRuntimeRateLimitTarget, - normalizeOptionalDistro -} from './project-runtime-rate-limit-target' - -function getSingleSelectedWslDistro(settings: GlobalSettings): string | null { - const selection = normalizeCodexRuntimeSelection(settings) - const selectedWslEntries = Object.entries(selection.wsl).filter(([, accountId]) => - Boolean(accountId) - ) - if (selectedWslEntries.length !== 1) { - return null - } - const [distroKey] = selectedWslEntries[0] - return distroKey === getWslSelectionKey(null) ? null : distroKey -} +import { getInitialAccountRateLimitTarget } from './initial-account-rate-limit-target' export function getInitialCodexRateLimitTarget( settings: GlobalSettings, platform: NodeJS.Platform = process.platform ): CodexAccountSelectionTarget { - if (settings.localAccountRuntime === 'host') { - return { runtime: 'host' } - } - if (settings.localAccountRuntime === 'wsl') { - if (platform !== 'win32') { - return { runtime: 'host' } - } - return { - runtime: 'wsl', - wslDistro: - normalizeOptionalDistro(settings.localAccountWslDistro) ?? - getSingleSelectedWslDistro(settings) - } - } - if (settings.localAccountRuntime === 'auto') { - const target = resolveLocalAccountRuntimeTarget(settings, platform) - return target.runtime === 'wsl' - ? { runtime: 'wsl', wslDistro: target.wslDistro } - : { runtime: 'host' } - } - - // Why: pre-setting profiles used account selection as their startup fallback. - const projectRuntimeTarget = getProjectRuntimeRateLimitTarget(settings, platform) - if (projectRuntimeTarget) { - return projectRuntimeTarget - } - - const selection = normalizeCodexRuntimeSelection(settings) - if (!selection.host) { - const selectedWslEntries = Object.entries(selection.wsl).filter(([, accountId]) => - Boolean(accountId) - ) - if (selectedWslEntries.length === 1) { - const [distroKey] = selectedWslEntries[0] - // Why: after restart there is no last-clicked switcher target, but a - // single WSL-only active account is the least surprising quota context. - return { - runtime: 'wsl', - wslDistro: distroKey === getWslSelectionKey(null) ? null : distroKey - } - } - } - - return { runtime: 'host' } + return getInitialAccountRateLimitTarget( + settings, + { getWslSelectionKey, normalizeRuntimeSelection: normalizeCodexRuntimeSelection }, + platform + ) } diff --git a/src/main/rate-limits/initial-account-rate-limit-target.test.ts b/src/main/rate-limits/initial-account-rate-limit-target.test.ts new file mode 100644 index 000000000..528a75279 --- /dev/null +++ b/src/main/rate-limits/initial-account-rate-limit-target.test.ts @@ -0,0 +1,163 @@ +import { describe, expect, it } from 'vitest' +import { getDefaultSettings } from '../../shared/constants' +import type { GlobalSettings } from '../../shared/types' +import { getInitialClaudeRateLimitTarget } from './claude-rate-limit-target' +import { getInitialCodexRateLimitTarget } from './codex-rate-limit-target' + +type RuntimeSelection = { + host: string | null + wsl: Record +} + +type Scenario = { + name: string + settings: Partial + expected: { runtime: 'host' } | { runtime: 'wsl'; wslDistro: string | null } + legacy?: boolean + platform?: NodeJS.Platform + selection?: RuntimeSelection +} + +const providers = [ + { + name: 'Codex', + getTarget: getInitialCodexRateLimitTarget, + selectionSettings: (selection: RuntimeSelection) => ({ + activeCodexManagedAccountIdsByRuntime: selection + }) + }, + { + name: 'Claude', + getTarget: getInitialClaudeRateLimitTarget, + selectionSettings: (selection: RuntimeSelection) => ({ + activeClaudeManagedAccountIdsByRuntime: selection + }) + } +] as const + +const scenarios: Scenario[] = [ + { + name: 'uses the trimmed configured WSL distro before the selected account', + settings: { + localAccountRuntime: 'wsl', + localAccountWslDistro: ' Fedora ', + localAgentRuntime: 'host', + terminalWindowsWslDistro: 'Debian' + }, + selection: { host: null, wsl: { Ubuntu: 'wsl-account-1' } }, + expected: { runtime: 'wsl', wslDistro: 'Fedora' } + }, + { + name: 'uses the single selected distro for an unpinned WSL runtime', + settings: { localAccountRuntime: 'wsl' }, + selection: { host: 'host-account-1', wsl: { Ubuntu: 'wsl-account-1' } }, + expected: { runtime: 'wsl', wslDistro: 'Ubuntu' } + }, + { + name: 'ignores a stale terminal distro for an unpinned WSL runtime', + settings: { + localAccountRuntime: 'wsl', + localAccountWslDistro: null, + terminalWindowsWslDistro: 'Debian' + }, + selection: { host: 'host-account-1', wsl: {} }, + expected: { runtime: 'wsl', wslDistro: null } + }, + { + name: 'keeps an explicit host runtime on host', + settings: { localAccountRuntime: 'host', terminalWindowsShell: 'wsl.exe' }, + selection: { host: null, wsl: { Ubuntu: 'wsl-account-1' } }, + expected: { runtime: 'host' } + }, + { + name: 'ignores an explicit WSL runtime on non-Windows hosts', + settings: { localAccountRuntime: 'wsl', localAccountWslDistro: 'Ubuntu' }, + platform: 'linux', + expected: { runtime: 'host' } + }, + { + name: 'auto follows the global WSL runtime before stale selections', + settings: { + localAccountRuntime: 'auto', + localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' } + }, + selection: { host: null, wsl: { Debian: 'wsl-account-1' } }, + expected: { runtime: 'wsl', wslDistro: 'Ubuntu' } + }, + { + name: 'auto keeps the global host runtime before stale selections', + settings: { + localAccountRuntime: 'auto', + localWindowsRuntimeDefault: { kind: 'windows-host' } + }, + selection: { host: null, wsl: { Ubuntu: 'wsl-account-1' } }, + expected: { runtime: 'host' } + }, + { + name: 'legacy settings use the global WSL runtime before selections', + settings: { localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' } }, + selection: { host: null, wsl: { Debian: 'wsl-account-1' } }, + legacy: true, + expected: { runtime: 'wsl', wslDistro: 'Ubuntu' } + }, + { + name: 'legacy settings ignore stale agent and terminal WSL values', + settings: { + localWindowsRuntimeDefault: { kind: 'windows-host' }, + localAgentRuntime: 'wsl', + localAgentWslDistro: 'Ubuntu', + terminalWindowsShell: 'wsl.exe', + terminalWindowsWslDistro: 'Debian' + }, + legacy: true, + expected: { runtime: 'host' } + }, + { + name: 'legacy settings preserve a single WSL selection on macOS', + settings: {}, + selection: { host: null, wsl: { Ubuntu: 'wsl-account-1' } }, + legacy: true, + platform: 'darwin', + expected: { runtime: 'wsl', wslDistro: 'Ubuntu' } + }, + { + name: 'legacy settings map the provider default WSL key to no distro', + settings: {}, + selection: { host: null, wsl: { __default__: 'wsl-account-1' } }, + legacy: true, + platform: 'linux', + expected: { runtime: 'wsl', wslDistro: null } + }, + { + name: 'legacy selection fallback requires no selected host account', + settings: {}, + selection: { host: 'host-account-1', wsl: { Ubuntu: 'wsl-account-1' } }, + legacy: true, + expected: { runtime: 'host' } + }, + { + name: 'legacy selection fallback requires exactly one selected WSL account', + settings: {}, + selection: { + host: null, + wsl: { Debian: 'wsl-account-1', Fedora: null, Ubuntu: 'wsl-account-2' } + }, + legacy: true, + expected: { runtime: 'host' } + } +] + +describe.each(providers)('$name initial rate-limit target', ({ getTarget, selectionSettings }) => { + it.each(scenarios)('$name', (scenario) => { + const settings = { + ...getDefaultSettings('/tmp'), + ...scenario.settings, + ...(scenario.selection ? selectionSettings(scenario.selection) : {}) + } + if (scenario.legacy) { + delete (settings as Partial).localAccountRuntime + } + + expect(getTarget(settings, scenario.platform ?? 'win32')).toEqual(scenario.expected) + }) +}) diff --git a/src/main/rate-limits/initial-account-rate-limit-target.ts b/src/main/rate-limits/initial-account-rate-limit-target.ts new file mode 100644 index 000000000..d60dab69d --- /dev/null +++ b/src/main/rate-limits/initial-account-rate-limit-target.ts @@ -0,0 +1,80 @@ +import { resolveLocalAccountRuntimeTarget } from '../../shared/local-account-runtime' +import type { GlobalSettings } from '../../shared/types' + +type AccountRuntimeSelection = { + host: string | null + wsl: Record +} + +type AccountRuntimeSelectionProvider = { + getWslSelectionKey: (wslDistro: string | null | undefined) => string + normalizeRuntimeSelection: (settings: GlobalSettings) => AccountRuntimeSelection +} + +type WslRateLimitRuntimeTarget = { runtime: 'wsl'; wslDistro: string | null } + +export type AccountRateLimitRuntimeTarget = { runtime: 'host' } | WslRateLimitRuntimeTarget + +export function getInitialAccountRateLimitTarget( + settings: GlobalSettings, + provider: AccountRuntimeSelectionProvider, + platform: NodeJS.Platform = process.platform +): AccountRateLimitRuntimeTarget { + if (settings.localAccountRuntime === 'host') { + return { runtime: 'host' } + } + + if (settings.localAccountRuntime === 'wsl') { + if (platform !== 'win32') { + return { runtime: 'host' } + } + const configuredTarget = resolveLocalAccountRuntimeTarget(settings, platform) + return { + runtime: 'wsl', + wslDistro: + configuredTarget.wslDistro ?? + getSingleSelectedWslTarget( + provider.normalizeRuntimeSelection(settings), + provider.getWslSelectionKey + )?.wslDistro ?? + null + } + } + + // Pre-setting profiles omit the policy and fall back to project/account selection. + const resolvedTarget = toRateLimitTarget(resolveLocalAccountRuntimeTarget(settings, platform)) + if (settings.localAccountRuntime === 'auto' || resolvedTarget.runtime === 'wsl') { + return resolvedTarget + } + + const selection = provider.normalizeRuntimeSelection(settings) + return selection.host + ? { runtime: 'host' } + : (getSingleSelectedWslTarget(selection, provider.getWslSelectionKey) ?? { runtime: 'host' }) +} + +function getSingleSelectedWslTarget( + selection: AccountRuntimeSelection, + getWslSelectionKey: AccountRuntimeSelectionProvider['getWslSelectionKey'] +): WslRateLimitRuntimeTarget | null { + const selectedWslEntries = Object.entries(selection.wsl).filter(([, accountId]) => + Boolean(accountId) + ) + if (selectedWslEntries.length !== 1) { + return null + } + + const [distroKey] = selectedWslEntries[0] + return { + runtime: 'wsl', + wslDistro: distroKey === getWslSelectionKey(null) ? null : distroKey + } +} + +function toRateLimitTarget( + target: ReturnType +): AccountRateLimitRuntimeTarget { + return target.runtime === 'wsl' + ? { runtime: 'wsl', wslDistro: target.wslDistro } + : { runtime: 'host' } +} diff --git a/src/main/rate-limits/project-runtime-rate-limit-target.ts b/src/main/rate-limits/project-runtime-rate-limit-target.ts deleted file mode 100644 index b6504235a..000000000 --- a/src/main/rate-limits/project-runtime-rate-limit-target.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { GlobalSettings } from '../../shared/types' -import { normalizeGlobalWindowsRuntimeDefault } from '../../shared/project-execution-runtime' - -export type AccountRateLimitRuntimeTarget = - | { runtime: 'host' } - | { runtime: 'wsl'; wslDistro: string | null } - -export function getProjectRuntimeRateLimitTarget( - settings: GlobalSettings, - platform: NodeJS.Platform -): AccountRateLimitRuntimeTarget | null { - if (platform !== 'win32') { - return null - } - - const runtimeDefault = normalizeGlobalWindowsRuntimeDefault(settings.localWindowsRuntimeDefault) - if (runtimeDefault.kind !== 'wsl') { - return null - } - - // Why: account quota polling has no project id, so its best default is the - // global project runtime instead of stale terminal/agent WSL settings. - return { runtime: 'wsl', wslDistro: runtimeDefault.distro } -} - -export function normalizeOptionalDistro(value: string | null | undefined): string | null { - const trimmed = value?.trim() - return trimmed ? trimmed : null -}