fix(desktop): limit credential selector to API keys and cover target changes

This commit is contained in:
Teknium
2026-09-07 02:18:06 -07:00
parent 0eabb58e90
commit 6eb4c0803b
2 changed files with 31 additions and 5 deletions
@@ -9,13 +9,15 @@ import type { EnvVarInfo, OAuthProvider } from '@/types/hermes'
const listOAuthProviders = vi.fn()
const disconnectOAuthProvider = vi.fn()
const getEnvVars = vi.fn()
const setEnvVar = vi.fn()
const startManualProviderOAuth = vi.fn()
const startManualLocalEndpoint = vi.fn()
const onboarding = atom({ manual: false })
vi.mock('@/hermes', () => ({
disconnectOAuthProvider: (providerId: string) => disconnectOAuthProvider(providerId),
getEnvVars: () => getEnvVars(),
getEnvVars: (profile?: string) => getEnvVars(profile),
setEnvVar: (key: string, value: string, profile?: string) => setEnvVar(key, value, profile),
listOAuthProviders: () => listOAuthProviders()
}))
@@ -93,6 +95,33 @@ async function renderProvidersSettings() {
}
describe('ProvidersSettings', () => {
it('reads and saves API keys for the shared Settings target and reloads when it changes', async () => {
const { $settingsScopeOverride } = await import('@/store/settings-scope')
const { $activeGatewayProfile, $profiles } = await import('@/store/profile')
$activeGatewayProfile.set('profile-a')
$settingsScopeOverride.set('profile-b')
$profiles.set([{ name: 'profile-a' }, { name: 'profile-b' }] as never)
getEnvVars.mockResolvedValue({ WIDGET_API_KEY: keyVar({ provider: 'widget', provider_label: 'Widget' }) })
const { ProvidersSettings } = await import('./providers-settings')
try {
render(<ProvidersSettings onClose={vi.fn()} onViewChange={vi.fn()} view="keys" />)
await screen.findByText('Widget')
expect(getEnvVars).toHaveBeenLastCalledWith('profile-b')
expect(screen.getByText('Applies to')).toBeTruthy()
const input = document.querySelector('input[type="password"]')!
fireEvent.focus(input)
fireEvent.change(input, { target: { value: 'fixture-key' } })
fireEvent.click(screen.getByRole('button', { name: 'Save' }))
await waitFor(() => expect(setEnvVar).toHaveBeenCalledWith('WIDGET_API_KEY', 'fixture-key', 'profile-b'))
fireEvent.click(screen.getByRole('button', { name: 'profile-a' }))
await waitFor(() => expect(getEnvVars).toHaveBeenLastCalledWith(undefined))
} finally {
cleanup()
$settingsScopeOverride.set(null)
$activeGatewayProfile.set('default')
$profiles.set([])
}
})
it('disconnects a connected provider account and refreshes the accounts list', async () => {
await renderProvidersSettings()
@@ -349,9 +349,7 @@ export function ProvidersSettings({
view
}: ProvidersSettingsProps) {
const { t } = useI18n()
// Shared settings "Applies to" scope: read + edit the selected profile's
// env store instead of the active one. Undefined → active profile.
// See https://github.com/NousResearch/hermes-agent/issues/103993
// Match the shared Settings target for credential reads and writes.
const scopeProfile = useStore($settingsRequestProfile)
const { rowProps, vars } = useEnvCredentials(scopeProfile)
const [oauthProviders, setOauthProviders] = useState<OAuthProvider[]>([])
@@ -530,7 +528,6 @@ export function ProvidersSettings({
return (
<SettingsContent>
<SettingsProfileScope className="mb-5" />
<OAuthPicker
disconnecting={disconnecting}
onDisconnect={provider => void handleDisconnect(provider)}