Files
Hermes-ui/packages/client/src/stores/hermes/profiles.ts
T
ekko 3ba76ad19b feat: add History page for browsing Hermes sessions (v0.5.5) (#370)
Features:
- Add dedicated History page for browsing Hermes session history
- Independent session state (does not interfere with active chat)
- Auto-select first CLI session on page load
- Filter out api_server and cron sources

Components:
- New HistoryView.vue with isolated state management
- New HistoryMessageList.vue with session prop support
- Filters empty content and tool messages without toolName

Backend:
- Add GET /api/hermes/sessions/hermes endpoint (excludes api_server)
- Add GET /api/hermes/sessions/hermes/:id endpoint (404s for api_server)
- Add fetchHermesSessions() and fetchHermesSession() API functions

Cleanup:
- Remove localStorage session caching
- Simplify profile switching cache management
- Clean up废弃 cache cleanup calls

i18n:
- Add "History" translation to all 8 locales
- Add v0.5.5 changelog entries in all languages
- 🎉 Happy Labor Day!

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:27:43 +08:00

116 lines
3.3 KiB
TypeScript

import { defineStore } from 'pinia'
import { ref } from 'vue'
import * as profilesApi from '@/api/hermes/profiles'
import type { HermesProfile, HermesProfileDetail } from '@/api/hermes/profiles'
const ACTIVE_PROFILE_STORAGE_KEY = 'hermes_active_profile_name'
export const useProfilesStore = defineStore('profiles', () => {
const profiles = ref<HermesProfile[]>([])
// 初始化时同步读 localStorage,确保其他 store(如 chat)在启动时能拿到 profile name
const activeProfileName = ref<string | null>(localStorage.getItem(ACTIVE_PROFILE_STORAGE_KEY))
const activeProfile = ref<HermesProfile | null>(null)
const detailMap = ref<Record<string, HermesProfileDetail>>({})
const loading = ref(false)
const switching = ref(false)
async function fetchProfiles() {
loading.value = true
try {
profiles.value = await profilesApi.fetchProfiles()
activeProfile.value = profiles.value.find(p => p.active) ?? null
// 同步缓存 profile name,供其他 store 启动时读取
if (activeProfile.value) {
activeProfileName.value = activeProfile.value.name
localStorage.setItem(ACTIVE_PROFILE_STORAGE_KEY, activeProfile.value.name)
}
// 清理所有会话缓存(不再使用 localStorage 缓存)
clearAllSessionCaches()
} catch (err) {
console.error('Failed to fetch profiles:', err)
} finally {
loading.value = false
}
}
async function fetchProfileDetail(name: string) {
if (detailMap.value[name]) return detailMap.value[name]
try {
const detail = await profilesApi.fetchProfileDetail(name)
detailMap.value[name] = detail
return detail
} catch {
return null
}
}
async function createProfile(name: string, clone?: boolean) {
const res = await profilesApi.createProfile(name, clone)
if (res.success) await fetchProfiles()
return res
}
async function deleteProfile(name: string) {
const ok = await profilesApi.deleteProfile(name)
if (ok) {
delete detailMap.value[name]
await fetchProfiles()
}
return ok
}
// 清理所有 profile 的会话缓存
function clearAllSessionCaches() {
// 注意:不再清理任何缓存,因为已经不再使用 localStorage 缓存会话数据
// 所有会话数据都从服务器实时获取
}
async function renameProfile(name: string, newName: string) {
const ok = await profilesApi.renameProfile(name, newName)
if (ok) {
delete detailMap.value[name]
await fetchProfiles()
}
return ok
}
async function switchProfile(name: string) {
switching.value = true
try {
const ok = await profilesApi.switchProfile(name)
if (ok) await fetchProfiles()
return ok
} finally {
switching.value = false
}
}
async function exportProfile(name: string) {
return profilesApi.exportProfile(name)
}
async function importProfile(file: File) {
const ok = await profilesApi.importProfile(file)
if (ok) await fetchProfiles()
return ok
}
return {
profiles,
activeProfile,
activeProfileName,
detailMap,
loading,
switching,
fetchProfiles,
fetchProfileDetail,
createProfile,
deleteProfile,
renameProfile,
switchProfile,
exportProfile,
importProfile,
clearAllSessionCaches,
}
})