mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
`hermes serve` is newer than the desktop binary's release cadence, so a new app launched against an un-upgraded managed install / PATH `hermes` would crash on an unknown subcommand and brick the user mid-upgrade. Detect whether the resolved runtime registers `serve` (fast source read of its dashboard.py, with a one-time CLI probe fallback) and rewrite the backend argv to the legacy `dashboard --no-open` only when it does not. Happy path (current runtimes) pays nothing and still spawns `serve`. - electron/backend-command.cjs: pure serve/dashboard argv helpers + serve- source detection (unit-tested in backend-command.test.cjs) - main.cjs: backendSupportsServe() cache + getBackendArgsForRuntime() guard at both backend spawn sites; expose `root` from the Windows venv unwrap so the fast source check covers Windows too - docs: note the backward-compat fallback in README, desktop.md, AGENTS.md
51 lines
2 KiB
JavaScript
51 lines
2 KiB
JavaScript
'use strict'
|
|
|
|
// Backend subcommand routing for the desktop-managed Hermes process.
|
|
//
|
|
// The desktop app launches its own headless backend via `hermes serve` — it
|
|
// must NEVER depend on or launch the browser `dashboard`. But `serve` is a
|
|
// newer subcommand: a runtime that predates it (an older managed install the
|
|
// app hasn't updated yet, or an older `hermes` resolved from PATH) only knows
|
|
// `dashboard --no-open`. To avoid bricking those users mid-upgrade we detect
|
|
// whether the resolved runtime understands `serve` and, only when it does not,
|
|
// fall back to the legacy `dashboard --no-open` invocation. Both produce the
|
|
// exact same headless gateway; `serve` is just the decoupled name.
|
|
//
|
|
// These helpers are pure so they can be unit-tested without Electron.
|
|
|
|
/**
|
|
* Build the canonical headless backend argv (always `serve`).
|
|
* @param {string} [profile] optional Hermes profile to pin via `--profile`.
|
|
*/
|
|
function serveBackendArgs(profile) {
|
|
const head = profile ? ['--profile', profile] : []
|
|
return [...head, 'serve', '--host', '127.0.0.1', '--port', '0']
|
|
}
|
|
|
|
/**
|
|
* Rewrite a resolved backend argv from `serve` to the legacy
|
|
* `dashboard --no-open` form, preserving every other argument (incl. a leading
|
|
* `-m hermes_cli.main` and any `--profile <name>`). Returns a copy; if there is
|
|
* no `serve` token the argv is returned unchanged.
|
|
*/
|
|
function dashboardFallbackArgs(args) {
|
|
const i = args.indexOf('serve')
|
|
if (i === -1) return args.slice()
|
|
return [...args.slice(0, i), 'dashboard', '--no-open', ...args.slice(i + 1)]
|
|
}
|
|
|
|
/**
|
|
* True when a runtime's `hermes_cli/subcommands/dashboard.py` source registers
|
|
* the `serve` subcommand. Matches `add_parser("serve"` / `add_parser('serve'`
|
|
* specifically so the substring "server" (e.g. "start_server", "web server")
|
|
* never produces a false positive.
|
|
*/
|
|
function sourceDeclaresServe(dashboardPySource) {
|
|
return /add_parser\(\s*["']serve["']/.test(String(dashboardPySource || ''))
|
|
}
|
|
|
|
module.exports = {
|
|
serveBackendArgs,
|
|
dashboardFallbackArgs,
|
|
sourceDeclaresServe,
|
|
}
|