The web bridge (previous commit) made the renderer boot in a browser; this makes an instance actually serve it, end to end, plus the Phase 1 UI gating. Web bridge + renderer: - petOverlay/terminal/updates/uninstall/themes become OPTIONAL bridge members, absent on web rather than stubbed inert — every caller already ?.-guards, so features hide through existing branches (typecheck-verified) - Gateway settings nav entry absent on web (connection IS the serving origin); About hides the Updates section behind a capability check on window.hermesDesktop?.updates (version + release notes stay) - notify requests Notification permission at point of first use - getVersion reports the renderer package version via a vite define Serving (web_server.py): - APP_DIST/mount_app: /app/assets static mount, /app 307-canonicalized to /app/ (relative asset URLs), index served with the same bootstrap-global injection contract as the dashboard SPA (token in loopback mode, cookie + ws-ticket in gated mode); same traversal guard, same auth-gate coverage - Mounts ONLY when HERMES_APP_DIST is explicitly set: no path fallback, so a regular desktop build leaving a dist at apps/desktop/dist can't silently enable /app. Env set but dist missing logs a WARNING (a Docker image with a broken frontend build should not surface as a mystery 404) Build + image: - apps/desktop build:web script: renderer-only build (typecheck + vite), no Electron packaging steps - Dockerfile: desktop manifest joins the cached npm-install layer (ELECTRON_SKIP_BINARY_DOWNLOAD=1 keeps the ~100MB binary out), build:web appended to the frontend-build layer, HERMES_APP_DIST set at runtime - .dockerignore: apps/desktop source enters the build context; node_modules/ dist/build/release re-excluded after the negation (last-match-wins would otherwise re-include them — a stale local dist would clobber the image-built one via COPY . ., and a Windows node_modules would clobber the image's Linux natives) Verified: build:web dist served at /app/ from a real gateway (token injected server-side, host:'web', gateway WS connected, zero console errors); mount contract curl-verified in both directions (no env var = /app falls through to the dashboard SPA even with a dist present on disk). Image build itself pending a docker-capable machine. No-op for every install that doesn't set HERMES_APP_DIST; zero behavior change for Electron users. |
||
|---|---|---|
| .. | ||
| assets | ||
| electron | ||
| pr-assets | ||
| public | ||
| scripts | ||
| src | ||
| .prettierrc | ||
| components.json | ||
| DESIGN.md | ||
| eslint.config.mjs | ||
| index.html | ||
| package.json | ||
| preview-demo.html | ||
| README.md | ||
| tsconfig.electron.json | ||
| tsconfig.json | ||
| vite.config.ts | ||
Hermes Desktop ☤
The native desktop app for Hermes Agent — the self-improving AI agent from Nous Research. Same agent, same skills, same memory as the CLI and gateway, in a polished native window — chat with streaming tool output, side-by-side previews, a file browser, voice, and settings, no terminal required. Available for macOS, Windows, and Linux.
| Chat with the full agent | Streaming responses, live tool activity, structured tool summaries, and the same conversation history as every other Hermes surface. |
| Side-by-side previews | Render web pages, files, and tool outputs in a right-hand pane while you keep chatting. |
| File browser | Explore and preview the working directory without leaving the app. |
| Voice | Talk to Hermes and hear it back. |
| Settings & onboarding | Manage providers, models, tools, and credentials from a real UI. First-run setup gets you to your first message in seconds. |
| Stays current | Built-in updates pull the latest agent and rebuild the app in place. |
Install
Install with Hermes (recommended)
Already have the Hermes CLI? Just run:
hermes desktop
It builds and launches the GUI against your existing install — same config, keys, sessions, and skills. On first launch Hermes walks you through picking a provider and model; nothing else to configure.
Prebuilt installers
Prebuilt installers are built and distributed via the Hermes Desktop website..
Updating
The app checks for updates in the background and offers a one-click update when one is ready. You can also update any time from the CLI:
hermes update
Requirements
The installer handles everything for you (Python 3.11+, a portable Git, ripgrep).
Development
Want to hack on the app itself? Install workspace deps from the repo root once, then run the dev server from this directory:
npm install # from repo root — links apps/desktop, web, apps/shared
cd apps/desktop
npm run dev # Vite renderer + Electron, which boots the Python backend
Point the app at a specific source checkout, or sandbox it away from your real config:
# throwaway HERMES_HOME, separate Electron userData, distinct app name to avoid the single-instance lock
../scripts/dev-sandbox.sh npm run dev
HERMES_DESKTOP_HERMES_ROOT=/path/to/clone npm run dev
HERMES_HOME=/tmp/throwaway npm run dev
npm run dev:fake-boot # exercise the startup overlay with deterministic delays
Building installers
npm run dist:mac # DMG + zip
npm run dist:win # NSIS + MSI
npm run dist:linux # AppImage + deb + rpm
npm run pack # unpacked app under release/ (no installer)
Installers are built and uploaded to GitHub Releases manually. macOS/Windows signing & notarization happen automatically when the relevant credentials are present in the environment (CSC_LINK / CSC_KEY_PASSWORD / APPLE_* for macOS, WIN_CSC_* for Windows).
How it works
The packaged app ships the Electron shell and a native React chat surface. On first launch it can install the Hermes Agent runtime into HERMES_HOME (~/.hermes, or %LOCALAPPDATA%\hermes on Windows) — the same layout a CLI install uses, so the two are interchangeable. Backend resolution first honours HERMES_DESKTOP_HERMES_ROOT, then a completed managed install, then a probed hermes on PATH (unless HERMES_DESKTOP_IGNORE_EXISTING=1 is set), and finally an explicit HERMES_DESKTOP_HERMES command override for packagers/troubleshooting. The renderer (React, in src/) talks to a headless backend the app launches for you — a hermes serve process that serves the tui_gateway JSON-RPC/WebSocket API — through the framework-agnostic client in apps/shared (the same client the web dashboard consumes), and reuses the agent runtime rather than embedding hermes --tui. The app is self-contained: it runs its own hermes serve backend and never opens or requires the web dashboard UI. (For backward compatibility, a runtime that predates the serve command automatically falls back to a headless dashboard --no-open — see electron/backend-command.ts — so mid-upgrade installs never break.) The install, backend-resolution, and self-update logic all live in electron/main.ts.
Verification
Run before opening a PR (lint may surface pre-existing warnings but must exit cleanly):
npm run fix
npm run typecheck
npm run lint
npm run test:desktop:all
Troubleshooting
Boot logs land in HERMES_HOME/logs/desktop.log (includes backend output and recent Python tracebacks) — check it first if the app reports a boot failure.
macOS / Linux:
# Force a clean first-launch setup
rm "$HOME/.hermes/hermes-agent/.hermes-bootstrap-complete"
# Rebuild a broken Python venv
rm -rf "$HOME/.hermes/hermes-agent/venv"
# Reset a stuck macOS microphone prompt (macOS only)
tccutil reset Microphone com.nousresearch.hermes
Windows (PowerShell):
# Force a clean first-launch setup
Remove-Item "$env:LOCALAPPDATA\hermes\hermes-agent\.hermes-bootstrap-complete"
# Rebuild a broken Python venv
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\hermes\hermes-agent\venv"
The default Hermes home on Windows is
%LOCALAPPDATA%\hermes. Set theHERMES_HOMEenv var if you've relocated it.
Community
- 💬 Discord
- 📖 Documentation
- 🐛 Issues
License
MIT — see LICENSE.
Built by Nous Research.