chore: bootstrap hermes-ui skeleton from 0.6.29 snapshot
Import the client, server, skills, tests, and build scripts as the starting point for the rebuild, then trim the surfaces this project does not ship: Electron desktop packaging, the marketing website, and the ESP32 firmware tree. CI keeps the web build, e2e, lockfile, release, and Docker workflows. Rename the package and CLI entry points to hermes-ui, drop the prepare hook so npm ci no longer triggers a build, and drop the removed tsconfig and nodemon references. Rewrite AGENTS.md, ARCHITECTURE.md, DEVELOPMENT.md, and the READMEs so the documented package map and hard rules match this repository. reference_code/ is ignored: it is a read-only snapshot, not an input to the build.
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
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
|
||||
targetProfile?: string
|
||||
unified?: 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user