Skip to content

feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel) - #55923

Merged
OutThisLife merged 2 commits into
mainfrom
bb/serve-headless-no-web-build
Jul 6, 2026
Merged

feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel)#55923
OutThisLife merged 2 commits into
mainfrom
bb/serve-headless-no-web-build

Conversation

@OutThisLife

@OutThisLife OutThisLife commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

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. But serve reused cmd_dashboard wholesale, so it still looked and behaved like a dashboard:

  • it ran a full _build_web_ui (vite) on every launch,
  • it mounted and served the dashboard SPA at the bind whenever a web_dist/ was lying around (from any prior hermes dashboard / hermes update / manual build),
  • it printed Hermes Web UI → … and announced HERMES_DASHBOARD_READY.

That's the exact "is the dashboard a desktop prerequisite?" confusion #54568 set out to kill. This PR makes serve an honest backend; hermes dashboard is completely unchanged.

What changed

serve carries headless_backend=True; cmd_dashboard resolves it once and threads it through.

  1. No web build — the build gate is skipped for headless serve (hermes dashboard still builds).
  2. No SPA mount (strict headless)cmd_dashboard exports HERMES_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.`
  3. Honest banner — headless announces the bind (Hermes backend listening on host:port) instead of a browser/auth-gated URL.
  4. Neutral ready sentinel — headless prints HERMES_BACKEND_READY port=N; dashboard keeps HERMES_DASHBOARD_READY. The desktop port-discovery regex now matches HERMES_(BACKEND|DASHBOARD)_READY.
  5. Profile re-exec preserved — a named-profile serve re-execs as serve (not dashboard), so it can't silently rebuild the UI.

Compatibility

  • Desktop spawn: unchanged argv; backend resolution mainly uses the name-agnostic ready file, with the stdout sentinel as fallback — both work.
  • Old app + new CLI: old apps only ever spawn dashboard (legacy token + UI intact) and never invoke serve → no break.
  • New app + old runtime: existing servedashboard --no-open capability fallback still applies.
  • One real behavior change: a remote hermes serve no longer serves the browser dashboard as a side effect — that's hermes dashboard's job now (matches the documented decoupling intent).
  • No prompt-cache / message-alternation / core toolset-schema impact.

Tests

  • tests/hermes_cli/test_serve_command.py — pins the headless_backend contract (serve True, dashboard False).
  • 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.cjsHERMES_BACKEND_READY parses (17/17 node tests green).
  • Green: serve/dashboard/web_server suites (test_web_server 338, 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-headless serve (build + SPA both off via HERMES_SERVE_HEADLESS).

@alt-glitch alt-glitch added type/feature New feature or request comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P3 Low — cosmetic, nice to have labels Jun 30, 2026
@OutThisLife OutThisLife changed the title feat(cli): hermes serve skips the web UI build (headless backend) feat(cli): make hermes serve a real headless backend (no web UI build/mount, neutral ready sentinel) Jun 30, 2026
`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
OutThisLife force-pushed the bb/serve-headless-no-web-build branch from 096f624 to f0f8c84 Compare June 30, 2026 22:58

@ethernet8023 ethernet8023 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks good! 👍
as long as updating seems to work, let's ship 'er

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against current main (branch is up to date — author merged main in, MERGEABLE).

Verified:

  • None of headless_backend / HERMES_SERVE_HEADLESS / HERMES_BACKEND_READY exist on main — this is the missing half of the #54568 decoupling, not a duplicate.
  • The headless_backend flag is resolved once in cmd_dashboard and threaded through all three places it matters: the build gate, the named-profile re-exec (so a profile serve doesn't silently come back as dashboard and rebuild the UI), and start_server for the banner/sentinel.
  • mount_spa() honoring HERMES_SERVE_HEADLESS=1 even when a built dist exists is the right strict-headless behavior — a stray web_dist/ from a prior hermes dashboard no 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 (existing servedashboard --no-open capability fallback), desktop port discovery (ready file is primary and name-agnostic; the stdout regex now accepts HERMES_(BACKEND|DASHBOARD)_READY as fallback), and remote serve users (documented behavior change: the SPA side effect is gone, which is the point).
  • Tests pin the contract on both sides: parser flag (serve True / dashboard False), 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.

@OutThisLife
OutThisLife merged commit 91c68bf into main Jul 6, 2026
36 checks passed
@OutThisLife
OutThisLife deleted the bb/serve-headless-no-web-build branch July 6, 2026 18:18
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants