fix(desktop): show 'hermes update' guidance for CLI installs instead of dead-end error

A user who installed via the CLI (irm|iex / install.sh) then ran
`hermes desktop` has no staged hermes-setup.exe, so clicking Update
in-app hit resolveUpdaterBinary()=null and showed a misleading error
('re-run the Hermes installer') with a Try-again button that could
never succeed — a dead loop for a perfectly valid install.

Treat the no-updater case as an intentional outcome, not a failure:
- main.cjs applyUpdates returns { ok:true, manual:true, command:'hermes update' }
  (no throw, no 'error' stage) when no updater binary exists.
- New 'manual' update stage + apply-state.command thread the command to the UI.
- updates-overlay ManualView: a polished terminal-native card with the
  exact command and a copy button, framed as the correct path for a CLI
  user rather than an error.

GUI-installer users are unaffected — hermes-setup.exe present => seamless
auto-update runs as before. Zero new process orchestration; can't fail
the update demo.
This commit is contained in:
emozilla
2026-05-29 01:53:43 -04:00
parent 006136c4ab
commit ce31ec09b9
4 changed files with 125 additions and 14 deletions
+15 -5
View File
@@ -1061,11 +1061,21 @@ async function applyUpdates(opts = {}) {
try {
const updater = resolveUpdaterBinary()
if (!updater) {
const message =
'The Hermes updater was not found. Re-run the Hermes installer to ' +
'enable in-app updates, or run `hermes update` from a terminal.'
emitUpdateProgress({ stage: 'error', error: 'no-updater', message })
throw new Error(message)
// No staged updater binary — this is a CLI-installed user (they ran
// `hermes desktop`, never the Tauri installer that self-copies
// hermes-setup.exe into HERMES_HOME). They DO have a working `hermes`
// on PATH / in the venv, so the correct path is the one-liner in their
// native medium: `hermes update`. We surface that as an intentional
// "manual update" outcome — NOT an error with a dead retry button.
//
// We resolve the most copy-pasteable command we can: prefer the bare
// `hermes update` (works if hermes is on PATH, which install.sh/ps1
// both arrange), and include the resolved checkout dir as context.
const command = 'hermes update'
const updateRoot = resolveUpdateRoot()
rememberLog(`[updates] no staged updater; surfacing manual \`${command}\` for CLI install at ${updateRoot}`)
emitUpdateProgress({ stage: 'manual', message: command, percent: null })
return { ok: true, manual: true, command, hermesRoot: updateRoot }
}
emitUpdateProgress({ stage: 'restart', message: 'Handing off to the Hermes updater…', percent: 100 })