mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(desktop): refresh profile rail after deletion (#49289)
This commit is contained in:
parent
c5e8a60b0a
commit
c3f06a8fda
3 changed files with 44 additions and 6 deletions
|
|
@ -19,7 +19,6 @@ import { Textarea } from '@/components/ui/textarea'
|
|||
import {
|
||||
createProfile,
|
||||
deleteProfile,
|
||||
getProfiles,
|
||||
getProfileSoul,
|
||||
type ProfileInfo,
|
||||
renameProfile,
|
||||
|
|
@ -31,7 +30,7 @@ import { profileColorSoft, resolveProfileColor } from '@/lib/profile-color'
|
|||
import { slug } from '@/lib/sanitize'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { notify, notifyError } from '@/store/notifications'
|
||||
import { $profileColors } from '@/store/profile'
|
||||
import { $profileColors, refreshProfiles } from '@/store/profile'
|
||||
|
||||
import { useRefreshHotkey } from '../hooks/use-refresh-hotkey'
|
||||
import {
|
||||
|
|
@ -72,7 +71,7 @@ export function ProfilesView({ onClose }: ProfilesViewProps) {
|
|||
|
||||
const refresh = useCallback(async () => {
|
||||
try {
|
||||
const { profiles: list } = await getProfiles()
|
||||
const list = await refreshProfiles()
|
||||
setProfiles(list)
|
||||
setSelectedName(current => {
|
||||
if (current && list.some(p => p.name === current)) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { atom } from 'nanostores'
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { HermesConnection } from '@/global'
|
||||
import type { ProfileInfo } from '@/types/hermes'
|
||||
|
||||
// Keep profile.ts's side-effecting imports inert: the gateway socket layer and
|
||||
// the REST query client must not run for real in a unit test.
|
||||
|
|
@ -17,9 +18,20 @@ vi.mock('@/hermes', () => ({
|
|||
vi.mock('@/lib/query-client', () => ({ queryClient: { invalidateQueries: vi.fn() } }))
|
||||
vi.mock('@/store/starmap', () => ({ resetStarmapGraph }))
|
||||
|
||||
const { $activeGatewayProfile, ensureGatewayProfile } = await import('./profile')
|
||||
const { $activeGatewayProfile, $profiles, ensureGatewayProfile, refreshProfiles } = await import('./profile')
|
||||
const { $connection } = await import('./session')
|
||||
const { queryClient } = await import('@/lib/query-client')
|
||||
const { getProfiles } = await import('@/hermes')
|
||||
|
||||
const profile = (name: string, isDefault = false): ProfileInfo => ({
|
||||
has_env: false,
|
||||
is_default: isDefault,
|
||||
model: null,
|
||||
name,
|
||||
path: `/tmp/hermes/${name}`,
|
||||
provider: null,
|
||||
skill_count: 0
|
||||
})
|
||||
|
||||
const remoteConn = (over: Partial<HermesConnection> = {}): HermesConnection =>
|
||||
({ baseUrl: 'https://hermes-roy.tail.ts.net', mode: 'remote', profile: 'vps-remote', ...over }) as HermesConnection
|
||||
|
|
@ -35,6 +47,7 @@ beforeEach(() => {
|
|||
$gateway.set({ id: 'live-socket' })
|
||||
$activeGatewayProfile.set('default')
|
||||
$connection.set(localConn())
|
||||
$profiles.set([])
|
||||
vi.stubGlobal('window', { hermesDesktop: { getConnection } })
|
||||
vi.mocked(queryClient.invalidateQueries).mockClear()
|
||||
resetStarmapGraph.mockClear()
|
||||
|
|
@ -101,3 +114,23 @@ describe('profile-scoped cache invalidation', () => {
|
|||
expect(resetStarmapGraph).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('refreshProfiles shared rail list (#49289)', () => {
|
||||
it('removes a deleted profile from the shared $profiles cache after Manage Profiles refreshes', async () => {
|
||||
$profiles.set([profile('default', true), profile('test1')])
|
||||
vi.mocked(getProfiles).mockResolvedValueOnce({ profiles: [profile('default', true)] })
|
||||
|
||||
await refreshProfiles()
|
||||
|
||||
expect($profiles.get().map(profile => profile.name)).toEqual(['default'])
|
||||
})
|
||||
|
||||
it('leaves the shared $profiles cache intact when the refresh fails', async () => {
|
||||
$profiles.set([profile('default', true), profile('test1')])
|
||||
vi.mocked(getProfiles).mockRejectedValueOnce(new Error('backend unavailable'))
|
||||
|
||||
await expect(refreshProfiles()).rejects.toThrow('backend unavailable')
|
||||
|
||||
expect($profiles.get().map(profile => profile.name)).toEqual(['default', 'test1'])
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ export function setActiveProfile(name: string): void {
|
|||
$activeProfile.set(name || 'default')
|
||||
}
|
||||
|
||||
export async function refreshProfiles(): Promise<ProfileInfo[]> {
|
||||
const { profiles } = await getProfiles()
|
||||
$profiles.set(profiles)
|
||||
|
||||
return profiles
|
||||
}
|
||||
|
||||
// ── Rail order ─────────────────────────────────────────────────────────────
|
||||
// User-defined order for the named (non-default) profile squares in the rail.
|
||||
// Names absent from the list fall back to alphabetical, appended at the tail —
|
||||
|
|
@ -111,8 +118,7 @@ export async function refreshActiveProfile(): Promise<void> {
|
|||
}
|
||||
|
||||
try {
|
||||
const { profiles } = await getProfiles()
|
||||
$profiles.set(profiles)
|
||||
await refreshProfiles()
|
||||
} catch {
|
||||
// Leave the cached list in place.
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue