feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel) - #55923
Merged
Merged
Conversation
`serve` (added in #54568) reused cmd_dashboard wholesale, so it still behaved like a dashboard: it ran a full vite build every launch, mounted and served the SPA whenever a stray web_dist/ existed, printed "Hermes Web UI →", and announced HERMES_DASHBOARD_READY. It's the headless JSON-RPC/WS backend the desktop app and remote clients run — pure socket clients that never load the browser SPA. Mark serve with headless_backend=True (resolved once in cmd_dashboard) and: - skip _build_web_ui entirely on the serve path - export HERMES_SERVE_HEADLESS=1 so mount_spa() disables the SPA even when a dist is present — only the JSON-RPC/WS/API surface is reachable - announce the bind ("Hermes backend listening on host:port") instead of a browser/auth-gated URL - print a neutral HERMES_BACKEND_READY sentinel; dashboard keeps the legacy one and the desktop port-discovery regex matches either - preserve serve across the named-profile re-exec so it can't rebuild as dashboard `hermes dashboard` is unchanged (builds + serves the browser UI). Backward compatible: old apps only ever spawn dashboard (legacy token + UI intact) and never invoke serve; the ready-file side channel is name-agnostic. The one behavior change is that a remote `hermes serve` no longer serves the browser dashboard as a side effect — that's `hermes dashboard`'s job. Tests: serve headless_backend contract, SPA-disabled-with-dist, the HERMES_BACKEND_READY desktop parse (17/17 node), and the existing serve/dashboard/web_server suites. AGENTS.md documents the behavior.
OutThisLife
force-pushed
the
bb/serve-headless-no-web-build
branch
from
June 30, 2026 22:58
096f624 to
f0f8c84
Compare
ethernet8023
approved these changes
Jun 30, 2026
ethernet8023
left a comment
Collaborator
There was a problem hiding this comment.
it looks good! 👍
as long as updating seems to work, let's ship 'er
teknium1
approved these changes
Jul 6, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Reviewed against current main (branch is up to date — author merged main in, MERGEABLE).
Verified:
- None of
headless_backend/HERMES_SERVE_HEADLESS/HERMES_BACKEND_READYexist on main — this is the missing half of the #54568 decoupling, not a duplicate. - The
headless_backendflag is resolved once incmd_dashboardand threaded through all three places it matters: the build gate, the named-profile re-exec (so a profileservedoesn't silently come back asdashboardand rebuild the UI), andstart_serverfor the banner/sentinel. mount_spa()honoringHERMES_SERVE_HEADLESS=1even when a built dist exists is the right strict-headless behavior — a strayweb_dist/from a priorhermes dashboardno longer leaks the SPA onto a backend bind.- Compat is covered on all four axes: old app + new CLI (old apps only spawn
dashboard, untouched), new app + old runtime (existingserve→dashboard --no-opencapability fallback), desktop port discovery (ready file is primary and name-agnostic; the stdout regex now acceptsHERMES_(BACKEND|DASHBOARD)_READYas fallback), and remoteserveusers (documented behavior change: the SPA side effect is gone, which is the point). - Tests pin the contract on both sides: parser flag (
serveTrue /dashboardFalse), SPA-disabled-with-dist (test_headless_serve_disables_spa_even_with_a_dist), and the Node ready-sentinel parse. - No prompt-cache, message-alternation, or core toolset-schema impact.
CI green across Python slices, TypeScript checks, desktop build, e2e, and supply-chain scan. Clean, well-scoped, and finishes what #54568 started. LGTM.
13 tasks
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…less-no-web-build feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel)
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…less-no-web-build feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel)
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…less-no-web-build feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel)
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…less-no-web-build feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
hermes serve(added in #54568) is the headless JSON-RPC/WS backend the desktop app and remote clients run — pure socket clients that never load the browser SPA. Butservereusedcmd_dashboardwholesale, so it still looked and behaved like a dashboard:_build_web_ui(vite) on every launch,web_dist/was lying around (from any priorhermes dashboard/hermes update/ manual build),Hermes Web UI → …and announcedHERMES_DASHBOARD_READY.That's the exact "is the dashboard a desktop prerequisite?" confusion #54568 set out to kill. This PR makes
servean honest backend;hermes dashboardis completely unchanged.What changed
servecarriesheadless_backend=True;cmd_dashboardresolves it once and threads it through.serve(hermes dashboardstill builds).cmd_dashboardexportsHERMES_SERVE_HEADLESS=1;mount_spa()honours it and takes the no-frontend 404 path even when a dist exists. Only the JSON-RPC/WS/API surface is reachable. Root returns:Headless backend (hermes serve): web UI disabled — use \hermes dashboard` for the browser UI.`Hermes backend listening on host:port) instead of a browser/auth-gated URL.HERMES_BACKEND_READY port=N;dashboardkeepsHERMES_DASHBOARD_READY. The desktop port-discovery regex now matchesHERMES_(BACKEND|DASHBOARD)_READY.servere-execs asserve(notdashboard), so it can't silently rebuild the UI.Compatibility
dashboard(legacy token + UI intact) and never invokeserve→ no break.serve→dashboard --no-opencapability fallback still applies.hermes serveno longer serves the browser dashboard as a side effect — that'shermes dashboard's job now (matches the documented decoupling intent).Tests
tests/hermes_cli/test_serve_command.py— pins theheadless_backendcontract (serveTrue,dashboardFalse).tests/hermes_cli/test_web_server.py::...::test_headless_serve_disables_spa_even_with_a_dist— SPA disabled even with a built dist present.apps/desktop/electron/backend-ready.test.cjs—HERMES_BACKEND_READYparses (17/17 node tests green).test_web_server338,test_dashboard_unified_launch,test_dashboard_lifecycle_flags,test_subcommands_batch,test_startup_plugin_gating,test_web_ui_build,test_web_server_boot_handshake).Docs
AGENTS.md— desktop section describes the strict-headlessserve(build + SPA both off viaHERMES_SERVE_HEADLESS).