refactor(settings): one settings host-scope type and label (#27629)

Settings had two identical provider-scope types, a host-choice type that
copied RuntimeClientTarget, a scope-to-environment-id getter that copied
runtimeTargetEnvironmentId, and the remote-server/local label written
three times. Keep one of each. getProviderAccountScope now reads the
default host through defaultScopeHost, the sanctioned reader; output is
unchanged (trimmed id, blank means this computer).

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
OrcaWin
2026-10-11 17:50:13 -07:00
committed by GitHub
co-authored by m4air Claude Opus 5.5
parent c5224f70d8
commit 9d305f3ce4
10 changed files with 73 additions and 83 deletions
@@ -41,8 +41,8 @@ import {
} from './provider-account-visibility'
import { GrokAccountsSection } from './GrokAccountsSection'
import { AntigravityAccountsSection } from './AntigravityAccountsSection'
import type { RuntimeClientTarget } from '@/runtime/runtime-rpc-client'
import { getSettingsHostScopeEnvironmentId } from './settings-host-scope'
import type { RuntimeClientTarget } from '@/runtime/runtime-client-target'
import { runtimeTargetEnvironmentId } from '@/runtime/runtime-client-target'
import { CursorAccountsSection } from './CursorAccountsSection'
import { ZcodePlanAccountsSection } from './ZcodePlanAccountsSection'
import type {
@@ -112,7 +112,7 @@ export function AccountsPane({
// below goes to the host chosen in the page, never to the focused one.
const accountOwner = hostScope.target
const isRemoteAccountScope = accountOwner.kind === 'environment'
const scopeEnvironmentId = getSettingsHostScopeEnvironmentId(hostScope)
const scopeEnvironmentId = runtimeTargetEnvironmentId(hostScope.target)
// Why: a select/remove can outlive a host change; its result
// belongs to the owner it was sent to, not the one now shown.
const accountOwnerKey = getProviderAccountsOwnerKey(accountOwner)
@@ -51,7 +51,8 @@ import {
import { AgentAvailabilityControl, type AgentCatalogRowProps } from './AgentCatalogRow'
import { AgentDefaultSetting } from './AgentDefaultSetting'
import { AgentDetectionCatalog } from './AgentDetectionCatalog'
import { getSettingsHostScopeEnvironmentId, type SettingsHostScope } from './settings-host-scope'
import type { SettingsHostScope } from './settings-host-scope'
import { runtimeTargetEnvironmentId } from '@/runtime/runtime-client-target'
export {
buildAgentAvailabilitySettingsUpdate,
@@ -158,7 +159,7 @@ export function AgentsPane({
wslDistros,
wslCapabilitiesLoading
}: AgentsPaneProps): React.JSX.Element {
const activeServerEnvironmentId = getSettingsHostScopeEnvironmentId(hostScope)
const activeServerEnvironmentId = runtimeTargetEnvironmentId(hostScope.target)
const agentDetectionTarget = useMemo<AgentDetectionTarget>(
() =>
activeServerEnvironmentId
@@ -1,12 +1,12 @@
import { ServerCog } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { useAppStore } from '@/store'
import type { ProviderAccountScope, ProviderRateLimitScope } from './provider-account-scope'
import type { ProviderHostScope } from './provider-account-scope'
import { translate } from '@/i18n/i18n'
type ProviderHostScopeControlProps = {
labelPrefix: string
scope: ProviderAccountScope | ProviderRateLimitScope
scope: ProviderHostScope
className?: string
}
@@ -9,10 +9,10 @@ import { translate } from '@/i18n/i18n'
import { getLocalExecutionHostLabel } from '../../../../shared/execution-host'
import type { PublicKnownRuntimeEnvironment } from '../../../../shared/runtime-environments'
import {
getSettingsHostScopeEnvironmentId,
type SettingsHostChoice,
type SettingsHostScope
} from './settings-host-scope'
runtimeTargetEnvironmentId,
type RuntimeClientTarget
} from '@/runtime/runtime-client-target'
import type { SettingsHostScope } from './settings-host-scope'
const LOCAL_VALUE = 'local'
const ENVIRONMENT_VALUE_PREFIX = 'environment:'
@@ -20,7 +20,7 @@ const ENVIRONMENT_VALUE_PREFIX = 'environment:'
type SettingsHostScopePickerProps = {
scope: SettingsHostScope
environments: readonly PublicKnownRuntimeEnvironment[]
onChoose: (choice: SettingsHostChoice) => void
onChoose: (choice: RuntimeClientTarget) => void
}
export function shouldShowSettingsHostScopePicker(
@@ -35,7 +35,7 @@ export function SettingsHostScopePicker({
environments,
onChoose
}: SettingsHostScopePickerProps): React.JSX.Element {
const scopeEnvironmentId = getSettingsHostScopeEnvironmentId(scope)
const scopeEnvironmentId = runtimeTargetEnvironmentId(scope.target)
const value = scopeEnvironmentId
? `${ENVIRONMENT_VALUE_PREFIX}${scopeEnvironmentId}`
: LOCAL_VALUE
@@ -10,11 +10,13 @@ const LOCAL_HOST_LABEL = getExecutionHostLabel('local')
describe('getProviderAccountScope', () => {
it('describes provider accounts as client-owned without an active runtime', () => {
expect(getProviderAccountScope({ activeRuntimeEnvironmentId: null })).toEqual({
label: LOCAL_HOST_LABEL,
description:
'Credentials and account checks for this provider are owned by this desktop client. To use a server, change Settings > Remote Orca Servers > Advanced > Default host for new projects.'
})
for (const activeRuntimeEnvironmentId of [null, ' ']) {
expect(getProviderAccountScope({ activeRuntimeEnvironmentId })).toEqual({
label: LOCAL_HOST_LABEL,
description:
'Credentials and account checks for this provider are owned by this desktop client. To use a server, change Settings > Remote Orca Servers > Advanced > Default host for new projects.'
})
}
})
it('describes provider accounts as remote-server-owned with an active runtime', () => {
@@ -1,31 +1,38 @@
import { translate } from '@/i18n/i18n'
import { getLocalExecutionHostLabel } from '../../../../shared/execution-host'
import type { GlobalSettings } from '../../../../shared/global-settings-types'
import { defaultScopeHost } from '@/lib/default-creation-host'
import type { RuntimeClientTarget } from '@/runtime/runtime-client-target'
export type ProviderAccountScope = {
/** Which host a provider card or budget belongs to, and how to change it. */
export type ProviderHostScope = {
label: string
description: string
}
export type ProviderRateLimitScope = {
label: string
description: string
function remoteServerScopeLabel(name: string): string {
return translate(
'auto.components.settings.providerAccountScope.remoteServer',
'Remote server: {{value0}}',
{ value0: name }
)
}
function providerHostScopeLabel(target: RuntimeClientTarget): string {
return target.kind === 'environment'
? remoteServerScopeLabel(target.environmentId)
: getLocalExecutionHostLabel()
}
/** Accounts-pane scope while a remote server owns the roster (#8186). */
export function getRemoteAccountsPaneScope(serverName: string | null): ProviderAccountScope {
export function getRemoteAccountsPaneScope(serverName: string | null): ProviderHostScope {
const name = serverName?.trim()
return {
// Why: the saved-server list is still empty on first paint, and the generic
// fallback is already a noun phrase — interpolating it would render
// "Remote server: the remote server".
label: name
? translate(
'auto.components.settings.providerAccountScope.remoteServer',
'Remote server: {{value0}}',
{ value0: name }
)
? remoteServerScopeLabel(name)
: translate(
'auto.components.settings.AccountsPane.accountScopeRemoteServerUnnamed',
'Remote server'
@@ -39,55 +46,40 @@ export function getRemoteAccountsPaneScope(serverName: string | null): ProviderA
export function getProviderAccountScope(
settings: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null | undefined
): ProviderAccountScope {
const runtimeId = settings?.activeRuntimeEnvironmentId?.trim()
if (runtimeId) {
return {
label: translate(
'auto.components.settings.providerAccountScope.remoteServer',
'Remote server: {{value0}}',
{ value0: runtimeId }
),
description: translate(
'auto.components.settings.providerAccountScope.remoteServerCredentials',
'Credentials and account checks for this provider are owned by this remote server. To use another host, change Settings > Remote Orca Servers > Advanced > Default host for new projects.'
)
}
}
): ProviderHostScope {
const target = defaultScopeHost(settings)
return {
label: getLocalExecutionHostLabel(),
description: translate(
'auto.components.settings.providerAccountScope.localCredentials',
'Credentials and account checks for this provider are owned by this desktop client. To use a server, change Settings > Remote Orca Servers > Advanced > Default host for new projects.'
)
label: providerHostScopeLabel(target),
description:
target.kind === 'environment'
? translate(
'auto.components.settings.providerAccountScope.remoteServerCredentials',
'Credentials and account checks for this provider are owned by this remote server. To use another host, change Settings > Remote Orca Servers > Advanced > Default host for new projects.'
)
: translate(
'auto.components.settings.providerAccountScope.localCredentials',
'Credentials and account checks for this provider are owned by this desktop client. To use a server, change Settings > Remote Orca Servers > Advanced > Default host for new projects.'
)
}
}
export function getProviderRateLimitScope(
target: RuntimeClientTarget,
providerLabel: string
): ProviderRateLimitScope {
if (target.kind === 'environment') {
const runtimeId = target.environmentId
return {
label: translate(
'auto.components.settings.providerAccountScope.remoteServer',
'Remote server: {{value0}}',
{ value0: runtimeId }
),
description: translate(
'auto.components.settings.providerAccountScope.remoteServerRateLimit',
'{{value0}} API budget is fetched from the CLI on this remote server. Choose another host in the Host menu at the top of Settings to see its budget.',
{ value0: providerLabel }
)
}
}
): ProviderHostScope {
return {
label: getLocalExecutionHostLabel(),
description: translate(
'auto.components.settings.providerAccountScope.localRateLimit',
'{{value0}} API budget is fetched from the CLI on this desktop client. Choose a server in the Host menu at the top of Settings to see its budget.',
{ value0: providerLabel }
)
label: providerHostScopeLabel(target),
description:
target.kind === 'environment'
? translate(
'auto.components.settings.providerAccountScope.remoteServerRateLimit',
'{{value0}} API budget is fetched from the CLI on this remote server. Choose another host in the Host menu at the top of Settings to see its budget.',
{ value0: providerLabel }
)
: translate(
'auto.components.settings.providerAccountScope.localRateLimit',
'{{value0}} API budget is fetched from the CLI on this desktop client. Choose a server in the Host menu at the top of Settings to see its budget.',
{ value0: providerLabel }
)
}
}
@@ -7,8 +7,6 @@ export type SettingsHostScope = {
available: boolean
}
export type SettingsHostChoice = { kind: 'local' } | { kind: 'environment'; environmentId: string }
export const LOCAL_SETTINGS_HOST_SCOPE: SettingsHostScope = {
target: { kind: 'local' },
available: true
@@ -16,7 +14,7 @@ export const LOCAL_SETTINGS_HOST_SCOPE: SettingsHostScope = {
export function resolveSettingsHostScope(args: {
/** Null until the user picks a host in the page. */
choice: SettingsHostChoice | null
choice: RuntimeClientTarget | null
/** The "default host for new projects" setting; only the initial default, never a later override. */
defaultEnvironmentId: string | null | undefined
savedEnvironmentIds: readonly string[]
@@ -37,7 +35,3 @@ export function resolveSettingsHostScope(args: {
available: !args.catalogHydrated || args.savedEnvironmentIds.includes(environmentId)
}
}
export function getSettingsHostScopeEnvironmentId(scope: SettingsHostScope): string | null {
return scope.target.kind === 'environment' ? scope.target.environmentId : null
}
@@ -10,7 +10,7 @@ import { useAppStore } from '@/store'
import { IntegrationCardDetails, IntegrationCardShell } from './integration-card-shell'
import { useIntegrationSubordinateRowClass } from './integration-card-presentation'
import { LinearAgentSkillInstallCta } from './linear-agent-skill-install-cta'
import { getProviderAccountScope } from './provider-account-scope'
import { getProviderAccountScope, type ProviderHostScope } from './provider-account-scope'
import { ProviderHostScopeControl } from './ProviderHostScopeControl'
import { LINEAR_INTEGRATION_SECTION_ID } from './task-provider-integration-section-ids'
import { translate } from '@/i18n/i18n'
@@ -236,7 +236,7 @@ export function LinearIntegrationCard(): React.JSX.Element {
)
}
function ProviderAccountScopeRow({ scope }: { scope: ReturnType<typeof getProviderAccountScope> }) {
function ProviderAccountScopeRow({ scope }: { scope: ProviderHostScope }) {
const subordinateRowClass = useIntegrationSubordinateRowClass('text-xs')
return (
@@ -31,7 +31,8 @@ import {
buildSettingsProjectList,
removeSettingsProjectFromAllHosts
} from './settings-project-list'
import { resolveSettingsHostScope, type SettingsHostChoice } from './settings-host-scope'
import { resolveSettingsHostScope } from './settings-host-scope'
import type { RuntimeClientTarget } from '@/runtime/runtime-client-target'
import { defaultScopeHost } from '@/lib/default-creation-host'
export function useSettingsStoreModel() {
@@ -148,7 +149,7 @@ export function useSettingsStoreModel() {
const confirm = useConfirmationDialog()
// Why: session-only (deliberately not persisted) unlock — Option-click the Experimental page title reveals the hidden group.
const [hiddenExperimentalUnlocked, setHiddenExperimentalUnlocked] = useState(false)
const [settingsHostChoice, setSettingsHostChoice] = useState<SettingsHostChoice | null>(null)
const [settingsHostChoice, setSettingsHostChoice] = useState<RuntimeClientTarget | null>(null)
const defaultScope = defaultScopeHost(settings)
const defaultRuntimeEnvironmentId =
defaultScope.kind === 'environment' ? defaultScope.environmentId : null
@@ -10,13 +10,13 @@ import { getRepoHostIdentity } from '../../store/slices/repo-host-identity'
import { getSettingsEntryHostSelection, getSettingsProjectHostRepo } from './settings-project-list'
import type { SettingsStoreModel } from './use-settings-store-model'
import type { SettingsNavigationModel } from './use-settings-navigation-model'
import { getSettingsHostScopeEnvironmentId } from './settings-host-scope'
import { runtimeTargetEnvironmentId } from '@/runtime/runtime-client-target'
export function useSettingsTerminalModel(
model: SettingsStoreModel,
navigation: SettingsNavigationModel
) {
const scopeEnvironmentId = getSettingsHostScopeEnvironmentId(model.settingsHostScope)
const scopeEnvironmentId = runtimeTargetEnvironmentId(model.settingsHostScope.target)
const windowsTerminalCapabilityOwnerKey = useWindowsTerminalCapabilityOwnerKey(scopeEnvironmentId)
const runtimeTarget = model.settingsHostScope.target
const capabilityLoadTarget = useMemo(