mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-12 13:52:15 +00:00
* feat(desktop): CLI/dashboard parity — skills hub browser, MCP test/toggle/catalog, maintenance ops, log filters
Brings desktop GUI to parity with hermes skills/mcp/doctor/backup/debug-share/
curator/memory CLI commands and the dashboard's System + Skills-hub pages:
- Skills page: new Browse Hub tab (search official/GitHub/community sources,
preview SKILL.md, security scan verdicts, install/update with live action log)
- MCP settings: connection test (tool listing), per-server enable/disable
toggle, and a Catalog tab installing Nous-approved MCP servers with env prompts
- Command Center: new Maintenance section (doctor, security audit, backup,
debug share links, curator status/pause/run, memory file status + reset)
- Command Center system logs: file (agent/errors/gateway/desktop), level, and
substring filters instead of a fixed agent.log tail
- hermes.ts API client + types for all the above; en/zh locale strings (ja and
zh-hant inherit via defineLocale)
* feat(desktop): backend model catalogs in toolset config — hermes tools parity
Completes the `hermes tools` parity gap: after picking an image/video
generation backend the CLI runs a model picker (e.g. FAL's multi-model
catalog with speed/strengths/price); the desktop toolset drawer now has the
same flow as a radio-card list.
- web_server: GET /api/tools/toolsets/{name}/models (catalog + current +
default for the active or named provider row) and PUT .../model
(validated write to image_gen.model / video_gen.model), reusing the CLI's
plugin catalog helpers so GUI and `hermes tools` stay in lockstep
- desktop: ModelCatalogPicker in ToolsetConfigPanel — per-model cards with
speed/strengths/price, in-use + default badges, disabled until the
backend is the active one; provider selection now mirrors is_active
locally so the catalog unlocks without a refetch
- tests: 3 backend endpoint tests (catalog shape invariants, persist +
validation), 2 component tests, 2 API-contract tests; en/zh strings
140 lines
3.9 KiB
TypeScript
140 lines
3.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import {
|
|
getCuratorStatus,
|
|
getMcpCatalog,
|
|
getMemoryStatus,
|
|
getSkillHubSources,
|
|
getToolsetModels,
|
|
installSkillFromHub,
|
|
resetMemory,
|
|
runDebugShare,
|
|
searchSkillsHub,
|
|
selectToolsetModel,
|
|
setCuratorPaused,
|
|
setMcpServerEnabled,
|
|
testMcpServer
|
|
} from './hermes'
|
|
|
|
describe('Hermes REST parity helpers (hub / mcp / maintenance)', () => {
|
|
let api: ReturnType<typeof vi.fn>
|
|
|
|
beforeEach(() => {
|
|
api = vi.fn().mockResolvedValue({})
|
|
Object.defineProperty(window, 'hermesDesktop', {
|
|
configurable: true,
|
|
value: { api }
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks()
|
|
Reflect.deleteProperty(window, 'hermesDesktop')
|
|
})
|
|
|
|
it('loads hub sources with a network-tolerant timeout', async () => {
|
|
await getSkillHubSources()
|
|
|
|
expect(api).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/skills/hub/sources', timeoutMs: 45_000 }))
|
|
})
|
|
|
|
it('encodes hub search params', async () => {
|
|
await searchSkillsHub('gif search', 'official', 5)
|
|
|
|
expect(api).toHaveBeenCalledWith(
|
|
expect.objectContaining({ path: '/api/skills/hub/search?q=gif+search&source=official&limit=5' })
|
|
)
|
|
})
|
|
|
|
it('installs a hub skill by identifier', async () => {
|
|
await installSkillFromHub('official/gifs/gif-search')
|
|
|
|
expect(api).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
path: '/api/skills/hub/install',
|
|
method: 'POST',
|
|
body: { identifier: 'official/gifs/gif-search' }
|
|
})
|
|
)
|
|
})
|
|
|
|
it('tests an MCP server with a boot-tolerant timeout and encoded name', async () => {
|
|
await testMcpServer('file system')
|
|
|
|
expect(api).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
path: '/api/mcp/servers/file%20system/test',
|
|
method: 'POST',
|
|
timeoutMs: 60_000
|
|
})
|
|
)
|
|
})
|
|
|
|
it('toggles MCP server enablement', async () => {
|
|
await setMcpServerEnabled('filesystem', false)
|
|
|
|
expect(api).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
path: '/api/mcp/servers/filesystem/enabled',
|
|
method: 'PUT',
|
|
body: { enabled: false }
|
|
})
|
|
)
|
|
})
|
|
|
|
it('reads the MCP catalog', async () => {
|
|
await getMcpCatalog()
|
|
|
|
expect(api).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/mcp/catalog' }))
|
|
})
|
|
|
|
it('reads memory status and resets a specific target', async () => {
|
|
await getMemoryStatus()
|
|
await resetMemory('user')
|
|
|
|
expect(api).toHaveBeenNthCalledWith(1, expect.objectContaining({ path: '/api/memory' }))
|
|
expect(api).toHaveBeenNthCalledWith(
|
|
2,
|
|
expect.objectContaining({ path: '/api/memory/reset', method: 'POST', body: { target: 'user' } })
|
|
)
|
|
})
|
|
|
|
it('manages the curator', async () => {
|
|
await getCuratorStatus()
|
|
await setCuratorPaused(true)
|
|
|
|
expect(api).toHaveBeenNthCalledWith(1, expect.objectContaining({ path: '/api/curator' }))
|
|
expect(api).toHaveBeenNthCalledWith(
|
|
2,
|
|
expect.objectContaining({ path: '/api/curator/paused', method: 'PUT', body: { paused: true } })
|
|
)
|
|
})
|
|
|
|
it('runs debug share synchronously with an upload-tolerant timeout', async () => {
|
|
await runDebugShare()
|
|
|
|
expect(api).toHaveBeenCalledWith(
|
|
expect.objectContaining({ path: '/api/ops/debug-share', method: 'POST', timeoutMs: 120_000 })
|
|
)
|
|
})
|
|
|
|
it('reads a backend model catalog scoped to a provider row', async () => {
|
|
await getToolsetModels('image_gen', 'FAL.ai')
|
|
|
|
expect(api).toHaveBeenCalledWith(
|
|
expect.objectContaining({ path: '/api/tools/toolsets/image_gen/models?provider=FAL.ai' })
|
|
)
|
|
})
|
|
|
|
it('persists a backend model selection', async () => {
|
|
await selectToolsetModel('image_gen', 'z-image-turbo', 'FAL.ai')
|
|
|
|
expect(api).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
path: '/api/tools/toolsets/image_gen/model',
|
|
method: 'PUT',
|
|
body: { model: 'z-image-turbo', provider: 'FAL.ai' }
|
|
})
|
|
)
|
|
})
|
|
})
|