Files
omarchy/bin/omarchy-install-ai-hermes
T

163 lines
6.8 KiB
Bash
Executable File

#!/bin/bash
# omarchy:summary=Install the Hermes desktop app
# omarchy:requires-sudo=true
set -e
if (( EUID == 0 )); then
echo "Run this command as your desktop user, without sudo." >&2
exit 1
fi
echo "Installing Hermes Desktop..."
omarchy-pkg-add hermes-desktop
if [[ ! -r /usr/share/hermes-desktop/install.sh || ! -r /usr/share/hermes-desktop/runtime.patch ]] ||
! release_commit=$(jq -er 'select(.branch == "main") | .commit | select(test("^[0-9a-f]{40}$"))' /opt/hermes-desktop/resources/install-stamp.json 2>/dev/null); then
echo "The installed Hermes package cannot prepare in-app updates. Run 'omarchy update', then try again." >&2
exit 1
fi
# If Hermes was already installed for the terminal, the app supersedes it: one
# machine, one Hermes. This drops that copy so the terminal, the default agent
# and the app all end up on the app's installation.
omarchy-install-hermes-cli || true
# Keep the runtime at the root even when invoked from a Hermes profile.
HERMES_HOME=$(realpath -ms -- "${HERMES_HOME:-$HOME/.hermes}")
home_parent=$(dirname -- "$HERMES_HOME")
if [[ ${home_parent##*/} == [Pp][Rr][Oo][Ff][Ii][Ll][Ee][Ss] ]]; then
HERMES_HOME=$(dirname -- "$home_parent")
fi
export HERMES_HOME
runtime="$HERMES_HOME/hermes-agent"
native_app="$runtime/apps/desktop/release/linux-unpacked"
runtime_ready() {
[[ -f $runtime/.hermes-bootstrap-complete && -f $runtime/venv/bin/hermes && -x $runtime/venv/bin/hermes && -f $runtime/venv/bin/python && -x $runtime/venv/bin/python ]] &&
timeout 15 "$runtime/venv/bin/hermes" --version >/dev/null 2>&1
}
check_main() {
local main_commit
main_commit=$(git -C "$runtime" rev-parse --verify refs/heads/main 2>/dev/null || true)
if [[ -n $main_commit && $main_commit != "$release_commit" && $main_commit != "$(git -C "$runtime" rev-parse --verify refs/remotes/origin/main 2>/dev/null)" ]]; then
echo "Hermes main has local commits. Keep that work and prepare the desktop with 'hermes desktop --build-only'." >&2
return 1
fi
}
if ! runtime_ready; then
# The upstream installer can reset an existing checkout. Do not pin a newer
# or modified runtime back to the package release while repairing setup.
if [[ -e $runtime || -L $runtime ]]; then
if [[ $(git -C "$runtime" rev-parse HEAD 2>/dev/null) != "$release_commit" ]] ||
[[ -n $(git -C "$runtime" status --porcelain --untracked-files=all) ]]; then
echo "Hermes setup is incomplete at $runtime. Repair that installation before trying again; existing files have been kept." >&2
exit 1
fi
check_main
fi
# Upstream replaces these commands, including foreign files and symlinks.
# Keep their original bytes/links before handing the names to the desktop.
command_backup=""
for command in hermes hermes-agent hermes-acp; do
command_path="$HOME/.local/bin/$command"
if [[ -e $command_path || -L $command_path ]]; then
if [[ ! -f $command_path && ! -L $command_path ]]; then
echo "Cannot replace $command_path: move it aside before installing Hermes Desktop." >&2
exit 1
fi
if [[ -z $command_backup ]]; then
command_backup=$(mktemp -d "$HOME/.local/bin/.hermes-before-desktop.XXXXXX")
echo "Saving existing Hermes commands in $command_backup"
fi
cp -a -- "$command_path" "$command_backup/"
fi
done
echo "Setting up the Hermes runtime..."
bash /usr/share/hermes-desktop/install.sh --skip-setup --branch main --commit "$release_commit" --force-commit --dir "$runtime" --hermes-home "$HERMES_HOME"
if ! runtime_ready; then
echo "Hermes runtime setup did not complete. Re-run this command after resolving the installer error." >&2
exit 1
fi
fi
runtime_commit=$(git -C "$runtime" rev-parse HEAD)
if [[ $runtime_commit == "$release_commit" ]]; then
# The updater switches to main before checking for changes. Start main at
# the packaged release, with enough history for its first fast-forward.
check_main
if [[ $(git -C "$runtime" rev-parse --is-shallow-repository) == "true" ]]; then
git -C "$runtime" fetch --unshallow origin main
fi
git -C "$runtime" switch -C main "$release_commit"
if git -C "$runtime" apply --check /usr/share/hermes-desktop/runtime.patch >/dev/null 2>&1; then
git -C "$runtime" apply /usr/share/hermes-desktop/runtime.patch
elif ! git -C "$runtime" apply --reverse --check /usr/share/hermes-desktop/runtime.patch >/dev/null 2>&1; then
echo "The Hermes Linux runtime patch conflicts with local changes. Existing files have been kept." >&2
exit 1
fi
fi
if [[ -e $native_app || -L $native_app ]]; then
if [[ ! -f $native_app/Hermes || ! -x $native_app/Hermes || ! -f $native_app/resources/app.asar || ! -f $native_app/resources/install-stamp.json ]]; then
echo "The Hermes desktop app at $native_app is incomplete. Repair it with 'hermes desktop --build-only' before trying again." >&2
exit 1
fi
else
if [[ $runtime_commit != "$release_commit" ]]; then
echo "The Hermes runtime has moved beyond the packaged desktop release. Run 'hermes desktop --build-only', then try again." >&2
exit 1
fi
desktop_changes=$(git -C "$runtime" status --porcelain --untracked-files=all -- apps/desktop package.json package-lock.json)
if [[ -n $desktop_changes ]]; then
echo "Hermes desktop sources have local changes. Run 'hermes desktop --build-only', then try again; existing files have been kept." >&2
exit 1
fi
mkdir -p -- "${native_app%/*}"
staging=$(mktemp -d "${native_app%/*}/.linux-unpacked.XXXXXX")
trap 'rm -rf -- "$staging"' EXIT
cp -a /opt/hermes-desktop/. "$staging/"
chmod 0755 "$staging/chrome-sandbox"
mv -T --no-clobber -- "$staging" "$native_app"
if [[ -e $staging ]]; then
echo "A Hermes desktop app appeared during setup. It has been kept; please try again." >&2
exit 1
fi
trap - EXIT
# Record this matching prebuilt app using the CLI's own content hash, so
# subsequent menu launches do not rebuild an app that is already current.
env -u PYTHONPATH -u PYTHONHOME "$runtime/venv/bin/python" - "$runtime" <<'PY'
import sys
from pathlib import Path
sys.path.insert(0, sys.argv[1])
if Path(sys.argv[1], "hermes_cli/main_desktop.py").is_file():
from hermes_cli.main_desktop import _write_desktop_build_stamp
else:
from hermes_cli.main import _write_desktop_build_stamp
_write_desktop_build_stamp(Path(sys.argv[1]), source_mode=False)
PY
fi
echo "Opening Hermes Desktop..."
setsid uwsm-app -- /usr/bin/hermes-desktop >/dev/null 2>&1 &
# Only a running Hermes can be told which skin to show; a unit outlives this
# terminal and reports to the journal.
echo "Matching Hermes to the current theme once it is set up..."
systemctl --user stop omarchy-hermes-theme.service 2>/dev/null || true
systemd-run --user --quiet --collect --unit=omarchy-hermes-theme omarchy-theme-set-hermes --wait
echo ""
echo "Hermes Desktop has been installed."