feat(dashboard): check-before-update flow on the System page (#38205)

The dashboard's update button ran 'hermes update' immediately with no
preview. Now the System page shows whether an update is available and
asks the user to confirm before applying it.

- New GET /api/hermes/update/check: reports install method, current
  version, and commits-behind (via banner.check_for_updates, 6h-cached;
  ?force=1 busts the cache). Soft-fails to behind=null on network error;
  marks docker/nix/homebrew as can_apply=false with the out-of-band cmd.
- System page: update-status badge on the Hermes version row (latest /
  N behind), a Check-for-updates button, and an Update-now button that
  opens a ConfirmDialog showing the commit count before POST /api/hermes/
  update fires. Cached status loads with the rest of the page.
- Docs + 5 endpoint tests (git/up-to-date/docker/soft-failure + auth gate).
This commit is contained in:
Teknium 2026-06-03 05:57:15 -07:00 committed by GitHub
parent ba57ebec33
commit c5d199eada
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 296 additions and 3 deletions

View file

@ -1165,6 +1165,72 @@ async def update_hermes():
}
@app.get("/api/hermes/update/check")
async def check_hermes_update(force: bool = False):
"""Report whether a Hermes update is available, without applying it.
Powers the dashboard's "check before you update" flow: the System page
shows the commit-behind count and asks the user to confirm before
``POST /api/hermes/update`` actually runs ``hermes update``.
Returns:
install_method: 'git' | 'pip' | 'docker' | 'nixos' | 'homebrew' | ...
current_version: installed Hermes version string
behind: commits behind upstream (>=1), 0 if up to date,
-1 if behind by an unknown count (nix/pypi), or null if the
check could not run (offline, no remote, etc.)
update_available: convenience bool (behind is non-zero and not null)
can_apply: True when the dashboard's update button can apply it
in place (git/pip); False for docker/nix/homebrew where the
user must update out-of-band
update_command: the recommended command for this install method
message: human-readable guidance for non-applyable methods
"""
install_method = detect_install_method(PROJECT_ROOT)
update_command = recommended_update_command_for_method(install_method)
payload: Dict[str, Any] = {
"install_method": install_method,
"current_version": __version__,
"behind": None,
"update_available": False,
"can_apply": install_method in ("git", "pip"),
"update_command": update_command,
"message": None,
}
if install_method == "docker":
payload["message"] = format_docker_update_message()
return payload
# banner.check_for_updates() handles git / pypi / nix-revision paths and
# caches the result for 6h. ``force`` busts the cache so the "Check now"
# button reflects reality immediately.
try:
from hermes_cli.banner import check_for_updates
if force:
try:
(get_hermes_home() / ".update_check").unlink()
except OSError:
pass
behind = await asyncio.to_thread(check_for_updates)
except Exception:
_log.exception("Update check failed")
behind = None
payload["behind"] = behind
if behind is None:
payload["message"] = "Couldn't reach the update source — try again later."
elif behind == 0:
payload["message"] = "You're on the latest version."
else:
payload["update_available"] = True
return payload
@app.post("/api/audio/transcribe")
async def transcribe_audio_upload(payload: AudioTranscriptionRequest):
data_url = (payload.data_url or "").strip()