Files
lingxi-ai/packages/client/src/api/hermes/profiles.ts
T
yi 7d10320a82
Build / build (push) Has been cancelled
NPM Lockfile Check / npm ci --ignore-scripts (push) Has been cancelled
Playwright / e2e (push) Has been cancelled
feat: 灵犀 Studio Web UI 定制版
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 11:29:11 +08:00

229 lines
6.3 KiB
TypeScript

import { request, getBaseUrlValue, getApiKey } from '../client'
export interface HermesProfile {
name: string
active: boolean
model: string
gatewayStatus?: string
alias: string
avatar?: ProfileAvatar | null
}
export interface HermesProfileDetail {
name: string
path: string
model: string
provider: string
skills: number
hasEnv: boolean
hasSoulMd: boolean
avatar?: ProfileAvatar | null
}
export interface ProfileAvatar {
type: 'generated' | 'image'
seed?: string
dataUrl?: string
updatedAt?: number
}
export interface ProfileRuntimeStatus {
profile: string
bridge: {
running: boolean
profile: string
mode?: string
reachable?: boolean
error?: string
}
gateway: {
profile: string
running: boolean
pid?: number
port?: number
host?: string
url?: string
error?: string
diagnostics?: {
health_url?: string
reason?: string
health_ok?: boolean
}
}
}
export interface ProfileRuntimeStatusesResponse {
profiles: ProfileRuntimeStatus[]
refreshing?: boolean
}
export async function fetchProfiles(): Promise<HermesProfile[]> {
const res = await request<{ profiles: HermesProfile[] }>('/api/hermes/profiles')
return res.profiles
}
export async function fetchProfileDetail(name: string): Promise<HermesProfileDetail> {
const res = await request<{ profile: HermesProfileDetail }>(`/api/hermes/profiles/${encodeURIComponent(name)}`)
return res.profile
}
export async function fetchProfileRuntimeStatus(name: string): Promise<ProfileRuntimeStatus> {
return request<ProfileRuntimeStatus>(`/api/hermes/profiles/${encodeURIComponent(name)}/runtime-status`)
}
export async function fetchProfileRuntimeStatusesWithMeta(options: { refresh?: boolean } = {}): Promise<ProfileRuntimeStatusesResponse> {
const query = options.refresh === false ? '?refresh=0' : ''
return request<ProfileRuntimeStatusesResponse>(`/api/hermes/profiles/runtime-statuses${query}`)
}
export async function fetchProfileRuntimeStatuses(): Promise<ProfileRuntimeStatus[]> {
const res = await fetchProfileRuntimeStatusesWithMeta()
return res.profiles
}
export async function updateProfileAvatar(name: string, avatar: ProfileAvatar): Promise<ProfileAvatar> {
const res = await request<{ avatar: ProfileAvatar }>(`/api/hermes/profiles/${encodeURIComponent(name)}/avatar`, {
method: 'PUT',
body: JSON.stringify(avatar),
})
return res.avatar
}
export async function deleteProfileAvatar(name: string): Promise<void> {
await request(`/api/hermes/profiles/${encodeURIComponent(name)}/avatar`, { method: 'DELETE' })
}
export async function restartProfileGateway(name: string): Promise<ProfileRuntimeStatus['gateway']> {
const res = await request<{ success: boolean; gateway: ProfileRuntimeStatus['gateway'] }>(
`/api/hermes/profiles/${encodeURIComponent(name)}/gateway/restart`,
{ method: 'POST' },
)
return res.gateway
}
export async function restartProfileRuntime(name: string): Promise<ProfileRuntimeStatus> {
const res = await request<{ success: boolean; status: ProfileRuntimeStatus }>(
`/api/hermes/profiles/${encodeURIComponent(name)}/restart`,
{ method: 'POST' },
)
return res.status
}
export interface CreateProfileResult {
success: boolean
/** clone=true 时被清理的独占平台凭据 KEY 名 */
strippedCredentials?: string[]
/** clone=true 时被禁用的独占平台名 */
disabledPlatforms?: string[]
/** clone=true 时在 config.yaml 中被清理的内嵌凭据字段路径 */
strippedConfigCredentials?: string[]
}
export async function createProfile(name: string, clone?: boolean): Promise<CreateProfileResult & { error?: string }> {
try {
const res = await request<{
success: boolean
strippedCredentials?: string[]
disabledPlatforms?: string[]
strippedConfigCredentials?: string[]
error?: string
}>('/api/hermes/profiles', {
method: 'POST',
body: JSON.stringify({ name, clone }),
})
return {
success: !!res.success,
strippedCredentials: res.strippedCredentials,
disabledPlatforms: res.disabledPlatforms,
strippedConfigCredentials: res.strippedConfigCredentials,
error: res.error,
}
} catch (err: any) {
return { success: false, error: err.message || 'Unknown error' }
}
}
export async function deleteProfile(name: string): Promise<boolean> {
try {
await request(`/api/hermes/profiles/${encodeURIComponent(name)}`, { method: 'DELETE' })
return true
} catch {
return false
}
}
export async function renameProfile(name: string, newName: string): Promise<boolean> {
try {
await request(`/api/hermes/profiles/${encodeURIComponent(name)}/rename`, {
method: 'POST',
body: JSON.stringify({ new_name: newName }),
})
return true
} catch {
return false
}
}
export async function switchProfile(name: string): Promise<boolean> {
return !!name
}
export async function switchHermesProfile(name: string): Promise<boolean> {
try {
await request('/api/hermes/profiles/active', {
method: 'PUT',
body: JSON.stringify({ name }),
})
return true
} catch {
return false
}
}
export async function exportProfile(name: string): Promise<boolean> {
try {
const baseUrl = getBaseUrlValue()
const token = getApiKey()
const headers: Record<string, string> = {}
if (token) headers['Authorization'] = `Bearer ${token}`
const res = await fetch(`${baseUrl}/api/hermes/profiles/${encodeURIComponent(name)}/export`, {
method: 'POST',
headers,
})
if (!res.ok) throw new Error()
const blob = await res.blob()
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `hermes-profile-${name}.tar.gz`
a.click()
URL.revokeObjectURL(url)
return true
} catch {
return false
}
}
export async function importProfile(file: File): Promise<boolean> {
try {
const baseUrl = getBaseUrlValue()
const token = getApiKey()
const headers: Record<string, string> = {}
if (token) headers['Authorization'] = `Bearer ${token}`
const formData = new FormData()
formData.append('file', file)
const res = await fetch(`${baseUrl}/api/hermes/profiles/import`, {
method: 'POST',
headers,
body: formData,
})
return res.ok
} catch {
return false
}
}