fix(accounts): align auto runtime consumers

This commit is contained in:
Brennan Benson
2026-07-20 17:26:35 -07:00
parent 182a3e9b63
commit 9658fb25ff
13 changed files with 158 additions and 37 deletions
@@ -3537,6 +3537,72 @@ describe('ClaudeRuntimeAuthService', () => {
})
})
it('uses the global WSL runtime for untargeted Claude preparation under auto', async () => {
setPlatform('win32')
vi.doMock('../wsl', () => ({
getDefaultWslDistro: () => 'Ubuntu',
getWslHome: () => null,
toWindowsWslPath: (value: string) => value
}))
const ubuntuAuthPath = createManagedClaudeAuth(
testState.userDataDir,
'ubuntu-account',
createClaudeCredentialsJson('ubuntu@example.com', 'ubuntu-token')
)
const settings = createSettings({
localAccountRuntime: 'auto',
localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' },
claudeManagedAccounts: [
createClaudeAccount('ubuntu-account', ubuntuAuthPath, {
managedAuthRuntime: 'wsl',
wslDistro: 'Ubuntu',
wslLinuxAuthPath: '/home/alice/.local/share/orca/claude-accounts/ubuntu/auth'
})
],
activeClaudeManagedAccountId: null,
activeClaudeManagedAccountIdsByRuntime: {
host: null,
wsl: { Ubuntu: 'ubuntu-account' }
}
})
const store = createStore(settings)
const { ClaudeRuntimeAuthService } = await import('./runtime-auth-service')
const service = new ClaudeRuntimeAuthService(store as never)
const preparation = await service.prepareForClaudeLaunch()
expect(preparation).toMatchObject({
runtime: 'wsl',
wslDistro: 'Ubuntu',
provenance: 'managed:ubuntu-account:wsl:Ubuntu'
})
})
it('ignores a persisted WSL account-runtime pin on non-Windows hosts', async () => {
setPlatform('darwin')
vi.doMock('../wsl', () => ({
getDefaultWslDistro: () => 'Ubuntu',
getWslHome: () => null,
toWindowsWslPath: (value: string) => value
}))
const settings = createSettings({
localAccountRuntime: 'wsl',
localAccountWslDistro: 'Ubuntu'
})
const store = createStore(settings)
const { ClaudeRuntimeAuthService } = await import('./runtime-auth-service')
const service = new ClaudeRuntimeAuthService(store as never)
const preparation = await service.prepareForClaudeLaunch()
expect(preparation).toMatchObject({
runtime: 'host',
wslDistro: null,
provenance: 'system',
stripAuthEnv: false
})
})
it('keeps untargeted Claude preparation on host when account runtime is host', async () => {
setPlatform('win32')
vi.doMock('../wsl', () => ({
@@ -684,11 +684,9 @@ export class ClaudeRuntimeAuthService {
private getDefaultAccountSelectionTarget(
settings = this.store.getSettings()
): ClaudeAccountSelectionTarget {
// Why: auth defaults follow the resolved account runtime ('auto' tracks the
// global WSL project default), not legacy terminal WSL settings that can
// outlive the Terminal UI.
// Why: Windows auth follows the resolved account runtime; stale cross-platform WSL pins must stay local-host.
const resolved = resolveLocalAccountRuntimeTarget(settings)
if (resolved.runtime === 'wsl') {
if (process.platform === 'win32' && resolved.runtime === 'wsl') {
return { runtime: 'wsl', wslDistro: resolved.wslDistro }
}
return { runtime: 'host' }
+2
View File
@@ -618,6 +618,8 @@ describe('Store', () => {
expect(settings.terminalScrollSensitivity).toBe(1.15)
expect(settings.terminalFastScrollSensitivity).toBe(5)
expect(settings.terminalTuiScrollSensitivity).toBe(1)
expect(settings.localAccountRuntime).toBe('auto')
expect(settings.localAccountRuntimeDefaultedToAutoForAllUsers).toBe(true)
expect(settings.terminalTuiScrollSensitivityDefaultedToOne).toBe(true)
expect(settings.terminalUseSeparateLightTheme).toBe(true)
expect(settings.rightSidebarOpenByDefault).toBe(true)
+1 -3
View File
@@ -2959,9 +2959,7 @@ export class Store {
) {
this.loadNeedsSave = true
}
// Why (#9537): the legacy hard 'host' default pinned account detection to
// Windows even for WSL projects. Flip the untouched default to 'auto' once
// so it follows localWindowsRuntimeDefault; an explicit 'wsl' pin survives.
// Why (#9537): migrate the indistinguishable legacy host default once so WSL-default users follow their runtime.
const localAccountRuntimeAlreadyMigrated =
parsed.settings?.localAccountRuntimeDefaultedToAutoForAllUsers === true
const migratedLocalAccountRuntime: GlobalSettings['localAccountRuntime'] =
@@ -103,7 +103,7 @@ describe('getInitialClaudeRateLimitTarget', () => {
).toEqual({ runtime: 'wsl', wslDistro: 'Ubuntu' })
})
it("auto follows the global WSL project runtime default", () => {
it('auto follows the global WSL project runtime default', () => {
expect(
getInitialClaudeRateLimitTarget(
{
@@ -129,6 +129,23 @@ describe('getInitialClaudeRateLimitTarget', () => {
).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(
@@ -1,4 +1,5 @@
import type { GlobalSettings } from '../../shared/types'
import { resolveLocalAccountRuntimeTarget } from '../../shared/local-account-runtime'
import {
getClaudeWslSelectionKey,
normalizeClaudeRuntimeSelection,
@@ -36,9 +37,14 @@ export function getInitialClaudeRateLimitTarget(
getSingleSelectedWslDistro(settings)
}
}
if (settings.localAccountRuntime === 'auto') {
const target = resolveLocalAccountRuntimeTarget(settings, platform)
return target.runtime === 'wsl'
? { runtime: 'wsl', wslDistro: target.wslDistro }
: { runtime: 'host' }
}
// 'auto' (the default) and legacy-unset settings fall through here so account
// detection follows the global WSL project runtime instead of pinning host.
// Why: pre-setting profiles used account selection as their startup fallback.
const projectRuntimeTarget = getProjectRuntimeRateLimitTarget(settings, platform)
if (projectRuntimeTarget) {
return projectRuntimeTarget
@@ -129,6 +129,23 @@ describe('getInitialCodexRateLimitTarget', () => {
).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(
@@ -1,4 +1,5 @@
import type { GlobalSettings } from '../../shared/types'
import { resolveLocalAccountRuntimeTarget } from '../../shared/local-account-runtime'
import {
getWslSelectionKey,
normalizeCodexRuntimeSelection,
@@ -36,9 +37,14 @@ export function getInitialCodexRateLimitTarget(
getSingleSelectedWslDistro(settings)
}
}
if (settings.localAccountRuntime === 'auto') {
const target = resolveLocalAccountRuntimeTarget(settings, platform)
return target.runtime === 'wsl'
? { runtime: 'wsl', wslDistro: target.wslDistro }
: { runtime: 'host' }
}
// 'auto' (the default) and legacy-unset settings fall through here so account
// detection follows the global WSL project runtime instead of pinning host.
// Why: pre-setting profiles used account selection as their startup fallback.
const projectRuntimeTarget = getProjectRuntimeRateLimitTarget(settings, platform)
if (projectRuntimeTarget) {
return projectRuntimeTarget
@@ -10,6 +10,7 @@ import type {
GlobalSettings
} from '../../../../shared/types'
import { resolveLocalAccountRuntimeTarget } from '../../../../shared/local-account-runtime'
import { getRendererAppPlatform } from '../../lib/renderer-app-platform'
import { Badge } from '../ui/badge'
import { Button } from '../ui/button'
import { Input } from '../ui/input'
@@ -267,11 +268,8 @@ function getSelectedAccountRuntime(
wslDistros: string[],
wslCapabilitiesLoading: boolean
): LocalAccountRuntime {
// Why: 'auto' (the default) follows the global WSL project runtime, so resolve
// it to a concrete host/wsl position for the toggle instead of only honoring an
// explicit 'wsl' pin. The renderer is sandboxed, so derive platform from the UA.
const platform = navigator.userAgent.includes('Windows') ? 'win32' : 'linux'
const resolvedRuntime = resolveLocalAccountRuntimeTarget(settings, platform)
// Why: the two-option control displays the concrete target behind the persisted auto policy.
const resolvedRuntime = resolveLocalAccountRuntimeTarget(settings, getRendererAppPlatform())
if (wslSupportedPlatform && resolvedRuntime.runtime === 'wsl') {
if (!wslAvailable && !wslCapabilitiesLoading) {
return {
@@ -46,6 +46,7 @@ import type {
RateLimitWindow
} from '../../../../shared/rate-limit-types'
import { resolveLocalAccountRuntimeTarget } from '../../../../shared/local-account-runtime'
import { getRendererAppPlatform } from '../../lib/renderer-app-platform'
import {
ProviderIcon,
ProviderPanel,
@@ -196,24 +197,24 @@ function toCodexStatusRuntimeTarget(
export function getStatusBarPreferredWslDistro(
settings: GlobalSettings | null | undefined,
wslDistros: string[]
wslDistros: string[],
platform: NodeJS.Platform = getRendererAppPlatform()
): string | null {
const configuredDistro = settings?.localAccountWslDistro?.trim() || null
if (configuredDistro) {
return configuredDistro
if (settings) {
const target = resolveLocalAccountRuntimeTarget(settings, platform)
if (target.runtime === 'wsl' && target.wslDistro) {
return target.wslDistro
}
}
return wslDistros.length === 1 ? wslDistros[0] : null
}
function shouldIncludeSettingsWslRuntime(settings: GlobalSettings | null | undefined): boolean {
// Why: 'auto' (the default) surfaces the WSL group when the global project
// runtime is WSL, matching where account detection now reads from. The renderer
// is sandboxed without process.platform, so derive it from the user agent.
if (!settings) {
return false
}
const platform = navigator.userAgent.includes('Windows') ? 'win32' : 'linux'
return resolveLocalAccountRuntimeTarget(settings, platform).runtime === 'wsl'
// Why: the fallback group must match the concrete runtime used for account polling.
return resolveLocalAccountRuntimeTarget(settings, getRendererAppPlatform()).runtime === 'wsl'
}
function getSingleConcreteCodexWslDistro(state: CodexRateLimitAccountsState): string | null {
@@ -116,10 +116,12 @@ describe('status bar runtime switch groups', () => {
expect(
getStatusBarPreferredWslDistro(
{
localAccountRuntime: 'wsl',
localAccountWslDistro: null,
terminalWindowsWslDistro: 'Debian'
} as GlobalSettings,
['Ubuntu']
['Ubuntu'],
'win32'
)
).toBe('Ubuntu')
})
@@ -128,14 +130,30 @@ describe('status bar runtime switch groups', () => {
expect(
getStatusBarPreferredWslDistro(
{
localAccountRuntime: 'wsl',
localAccountWslDistro: 'Fedora',
terminalWindowsWslDistro: 'Debian'
} as GlobalSettings,
['Ubuntu']
['Ubuntu'],
'win32'
)
).toBe('Fedora')
})
it('uses the auto runtime distro instead of a stale account-runtime distro', () => {
expect(
getStatusBarPreferredWslDistro(
{
localAccountRuntime: 'auto',
localAccountWslDistro: 'Fedora',
localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' }
} as GlobalSettings,
['Fedora', 'Ubuntu'],
'win32'
)
).toBe('Ubuntu')
})
it('labels the host account group with the active remote server name', () => {
const state: CodexRateLimitAccountsState = {
accounts: [],
+1 -7
View File
@@ -11,13 +11,7 @@ type LocalAccountRuntimeSettings = Pick<
'localAccountRuntime' | 'localAccountWslDistro' | 'localWindowsRuntimeDefault'
>
/**
* Resolves where provider accounts are inspected (Windows host vs a WSL distro).
*
* `'host'`/`'wsl'` are explicit pins; `'auto'` (the default) follows the global
* Windows runtime default so a WSL project surfaces WSL accounts instead of the
* Windows-host ones. Non-Windows platforms have no WSL, so always resolve host.
*/
/** Resolves the persisted account policy to a concrete host or WSL target. */
export function resolveLocalAccountRuntimeTarget(
settings: LocalAccountRuntimeSettings,
platform: NodeJS.Platform = process.platform
+2 -2
View File
@@ -2653,10 +2653,10 @@ export type GlobalSettings = {
terminalWindowsShell: string
/** Pins the WSL distro for terminals/agent scans instead of WSL's current global default. */
terminalWindowsWslDistro?: string | null
/** Account/auth location independent from the terminal shell (e.g. WSL terminals but Windows-scoped accounts). 'auto' follows the global Windows runtime default; 'host'/'wsl' pin it explicitly. */
/** Account/auth location; auto follows the global Windows runtime while host/wsl pin it. */
localAccountRuntime: 'auto' | 'host' | 'wsl'
localAccountWslDistro?: string | null
/** One-shot guard: flips the legacy hard 'host' default to 'auto' once so account detection follows the WSL project runtime; preserves explicit 'wsl'. */
/** One-shot guard for migrating the legacy host default to auto. */
localAccountRuntimeDefaultedToAutoForAllUsers?: boolean
/** Independent from the terminal shell so users can inspect Windows vs WSL agent PATH state without changing it. */
localAgentRuntime?: 'host' | 'wsl'