From a57f63914f2e8663821146a62c2045df01ea560b Mon Sep 17 00:00:00 2001 From: Omabot Date: Wed, 19 Aug 2026 02:42:15 -0700 Subject: [PATCH] Build Hermes Desktop from source instead of packaging the CLI Install > AI is for GUI apps, so the package becomes the desktop shell and the terminal agent moves to a lazy mise stub that omarchy installs itself. Nous builds Hermes Desktop for macOS and Windows only -- their download page offers a .dmg and an .exe and points Linux users at a terminal install -- so there is no vendor binary to repackage the way grok-bot repackages xAI's deb. Their electron-builder config does carry a Linux target though, untested by upstream but working: npm ci at the workspace root, npm run pack in apps/desktop, and electron-builder stages node-pty and get-windows for linux-x64 on its own. 337 MB unpacked, 129 MB packaged. The app is only a shell -- it runs `hermes serve` against a CLI it does not ship, and clones its own unpinned copy into ~/.hermes when it finds none. So /usr/bin/hermes-desktop makes sure the CLI is there before handing over, and says so plainly rather than bootstrapping a second Hermes where Omarchy's installer isn't around to do it properly. --- pkgbuilds/hermes-agent/PKGBUILD | 73 ------------ pkgbuilds/hermes-agent/hermes.sh | 46 ------- .../.omarchy/package.json | 0 pkgbuilds/hermes-desktop/PKGBUILD | 112 ++++++++++++++++++ .../hermes-desktop.desktop} | 7 +- .../hermes-desktop.png} | Bin pkgbuilds/hermes-desktop/hermes-desktop.sh | 19 +++ 7 files changed, 135 insertions(+), 122 deletions(-) delete mode 100644 pkgbuilds/hermes-agent/PKGBUILD delete mode 100755 pkgbuilds/hermes-agent/hermes.sh rename pkgbuilds/{hermes-agent => hermes-desktop}/.omarchy/package.json (100%) create mode 100644 pkgbuilds/hermes-desktop/PKGBUILD rename pkgbuilds/{hermes-agent/hermes.desktop => hermes-desktop/hermes-desktop.desktop} (70%) rename pkgbuilds/{hermes-agent/hermes.png => hermes-desktop/hermes-desktop.png} (100%) create mode 100644 pkgbuilds/hermes-desktop/hermes-desktop.sh diff --git a/pkgbuilds/hermes-agent/PKGBUILD b/pkgbuilds/hermes-agent/PKGBUILD deleted file mode 100644 index 0ddc19a..0000000 --- a/pkgbuilds/hermes-agent/PKGBUILD +++ /dev/null @@ -1,73 +0,0 @@ -# Maintainer: David Heinemeier Hansson - -# Hermes pins every one of its ~120 dependencies with == and declares -# Requires-Python >=3.11,<3.14, so it can neither be built against Arch's -# Python nor share the python-* packages, and Arch's next Python bump would -# break any venv this package built. So the package ships a wrapper rather than -# the application: mise gives Hermes a private, supported environment on first -# run and keeps it current, the arrangement omarchy-mise-install already makes -# for the other coding agents. pkgver therefore tracks the wrapper, not Hermes. - -pkgname=hermes-agent -pkgver=1.0.0 -pkgrel=1 -pkgdesc='The self-improving AI agent that grows with you' -arch=('any') -url='https://github.com/NousResearch/hermes-agent' -license=('MIT') - -depends=( - 'hicolor-icon-theme' - 'mise' - 'uv' # mise's pipx backend refuses to run without uv or pipx - 'xdg-terminal-exec' -) - -optdepends=( - 'chromium: browser automation tools' - 'ffmpeg: text-to-speech voice messages' - 'git: version control integration' - 'nodejs: browser-use CLI and MCP servers' - 'ripgrep: faster file search' -) - -makedepends=('imagemagick') -options=('!debug') - -source=('hermes.sh' - 'hermes.desktop' - 'hermes.png') -sha256sums=('39a13f25b2a1616b55da19400045630e6b0900140444a4457d7e0ff58208badb' - '0fee8e42dce35e7eee0e6b0ee296cc90ddf192f0f9b8297b7e5ea66800d94159' - 'd60d164e24fdcf6532133b8ea43c77a201e4b9e9dbc396187b58d51d8590ef52') - -build() { - # Upstream ships one 1024x1024 master. Menus and the dock ask for the small - # sizes, and letting them downscale a 1024px portrait themselves is what - # makes it a smudge at 48px. - local size - for size in 512 256 128 64 48; do - magick "$srcdir/hermes.png" -resize "${size}x${size}" "$srcdir/hermes-${size}.png" - done -} - -package() { - install -Dm755 "$srcdir/hermes.sh" "$pkgdir/usr/bin/hermes" - - # Hermes' other two entry points. The wrapper dispatches on the name it was - # invoked under, so these are symlinks rather than copies. - ln -s hermes "$pkgdir/usr/bin/hermes-agent" - ln -s hermes "$pkgdir/usr/bin/hermes-acp" - - install -Dm644 "$srcdir/hermes.desktop" \ - "$pkgdir/usr/share/applications/hermes.desktop" - - install -Dm644 "$srcdir/hermes.png" \ - "$pkgdir/usr/share/icons/hicolor/1024x1024/apps/hermes.png" - - local size - for size in 512 256 128 64 48; do - install -Dm644 "$srcdir/hermes-${size}.png" \ - "$pkgdir/usr/share/icons/hicolor/${size}x${size}/apps/hermes.png" - done -} diff --git a/pkgbuilds/hermes-agent/hermes.sh b/pkgbuilds/hermes-agent/hermes.sh deleted file mode 100755 index 601ddb1..0000000 --- a/pkgbuilds/hermes-agent/hermes.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# Hermes is a Python application that pins every one of its dependencies -# exactly and declares Requires-Python >=3.11,<3.14, so it cannot be built -# against Arch's Python or share the python-* packages. mise builds it a -# private environment instead, on first run and on demand. -# -# The interpreter pin below is not belt-and-braces. Given no compatible -# interpreter to hand, uv builds the venv against the system Python in -# violation of Hermes' own bound, reports success, and leaves the failure to -# surface later inside a dependency. - -set -euo pipefail - -readonly tool='pipx:hermes-agent[extras=all]' -readonly python="${HERMES_PYTHON:-3.13}" - -export UV_PYTHON="$python" - -# `mise up` -- which omarchy update runs -- reinstalls without this wrapper's -# UV_PYTHON, so an upgrade can quietly move Hermes onto the system Python. -# Check the interpreter that is actually there, not merely that something is. -hermes_installed_on_pinned_python() { - local prefix - prefix=$(mise where "$tool" 2>/dev/null) || return 1 - [[ -d "$prefix/hermes-agent/lib/python$python" ]] -} - -if ! hermes_installed_on_pinned_python; then - echo "Installing Hermes on Python $python (this takes a minute)..." >&2 - - # Hermes ships several times a week, so mise's release cooldown would hold a - # new version back for days. Same call omarchy-mise-install makes. - MISE_MINIMUM_RELEASE_AGE=0 mise use -g --quiet --force "$tool" || exit 1 -fi - -# Installed as /usr/bin/hermes, with hermes-agent and hermes-acp symlinked to -# it, so the name we were invoked under picks the entry point. Anything else -- -# someone running this file straight out of a checkout -- means hermes. -command=$(basename "$0") -case "$command" in -hermes | hermes-agent | hermes-acp) ;; -*) command='hermes' ;; -esac - -exec mise x "$tool" -- "$command" "$@" diff --git a/pkgbuilds/hermes-agent/.omarchy/package.json b/pkgbuilds/hermes-desktop/.omarchy/package.json similarity index 100% rename from pkgbuilds/hermes-agent/.omarchy/package.json rename to pkgbuilds/hermes-desktop/.omarchy/package.json diff --git a/pkgbuilds/hermes-desktop/PKGBUILD b/pkgbuilds/hermes-desktop/PKGBUILD new file mode 100644 index 0000000..dd80df3 --- /dev/null +++ b/pkgbuilds/hermes-desktop/PKGBUILD @@ -0,0 +1,112 @@ +# Maintainer: David Heinemeier Hansson + +# Nous builds Hermes Desktop for macOS and Windows only -- their download page +# offers a .dmg and an .exe and tells Linux users to install from a terminal -- +# so there is no vendor binary to repackage. Their electron-builder config does +# carry a Linux target, though, and it works; this builds it. +# +# The app is only a shell: it runs `hermes serve` against a Hermes CLI it does +# not ship, and clones its own copy with the upstream install script when it +# finds none. /usr/bin/hermes-desktop heads that off. See hermes-desktop.sh. + +pkgname=hermes-desktop +pkgver=2026.8.18 +pkgrel=1 +pkgdesc='Native desktop shell for Hermes Agent' +arch=('x86_64') +url='https://github.com/NousResearch/hermes-agent' +license=('MIT') + +depends=( + 'alsa-lib' + 'at-spi2-core' + 'cairo' + 'dbus' + 'expat' + 'gcc-libs' + 'gdk-pixbuf2' + 'glib2' + 'glibc' + 'gtk3' + 'hicolor-icon-theme' + 'libcups' + 'libdrm' + 'libglvnd' + 'libnotify' + 'libx11' + 'libxcb' + 'libxcomposite' + 'libxdamage' + 'libxext' + 'libxfixes' + 'libxkbcommon' + 'libxrandr' + 'mesa' + 'nspr' + 'nss' + 'pango' + 'systemd-libs' + 'xdg-utils' +) + +optdepends=('omarchy: installs the Hermes CLI the app needs on first launch') + +# The build runs the repo's own npm workspace install, which fetches Electron +# and rebuilds node-pty against it. +makedepends=('git' 'imagemagick' 'nodejs' 'npm' 'python') + +# Electron bundles prebuilt binaries that stripping corrupts. +options=('!strip' '!debug') + +_srcdir="hermes-agent-${pkgver}" +source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/refs/tags/v${pkgver}.tar.gz" + 'hermes-desktop.sh' + 'hermes-desktop.desktop' + 'hermes-desktop.png') +sha256sums=('1e3d39d3638ec15fa9d31af262568a953e9272090deb1c50c44cd401175f5b80' + '716978c836ad46aa4ad173366d3a5d322c174fadf2f8095e6ce81301e1cad048' + 'a71e5b3599515b97ea694d51771edede69107a221f301f91c088a5d81de7a8c0' + 'd60d164e24fdcf6532133b8ea43c77a201e4b9e9dbc396187b58d51d8590ef52') + +build() { + cd "${srcdir}/${_srcdir}" + + # The desktop workspace resolves against the repo root, so the install has to + # happen there rather than in apps/desktop. + npm ci + + cd apps/desktop + npm run pack +} + +package() { + cd "${srcdir}/${_srcdir}/apps/desktop/release/linux-unpacked" + + install -dm755 "${pkgdir}/opt/${pkgname}" + cp -a . "${pkgdir}/opt/${pkgname}/" + + install -Dm755 "${srcdir}/hermes-desktop.sh" "${pkgdir}/usr/bin/${pkgname}" + + install -Dm644 "${srcdir}/hermes-desktop.desktop" \ + "${pkgdir}/usr/share/applications/${pkgname}.desktop" + + install -Dm644 "${srcdir}/hermes-desktop.png" \ + "${pkgdir}/usr/share/icons/hicolor/1024x1024/apps/${pkgname}.png" + local size + for size in 512 256 128 64 48; do + magick "${srcdir}/hermes-desktop.png" -resize "${size}x${size}" "${srcdir}/icon-${size}.png" + install -Dm644 "${srcdir}/icon-${size}.png" \ + "${pkgdir}/usr/share/icons/hicolor/${size}x${size}/apps/${pkgname}.png" + done + + install -Dm644 "${pkgdir}/opt/${pkgname}/LICENSE.electron.txt" \ + "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE.electron.txt" + + # Chromium's setuid sandbox is only needed where unprivileged user namespaces + # are unavailable; where they work, setuid root is the worse of the two. + if ! { [[ -L /proc/self/ns/user ]] && unshare --user true; }; then + chmod 4755 "${pkgdir}/opt/${pkgname}/chrome-sandbox" + else + chmod 0755 "${pkgdir}/opt/${pkgname}/chrome-sandbox" + fi +} diff --git a/pkgbuilds/hermes-agent/hermes.desktop b/pkgbuilds/hermes-desktop/hermes-desktop.desktop similarity index 70% rename from pkgbuilds/hermes-agent/hermes.desktop rename to pkgbuilds/hermes-desktop/hermes-desktop.desktop index 2a4bdf2..61e375c 100644 --- a/pkgbuilds/hermes-agent/hermes.desktop +++ b/pkgbuilds/hermes-desktop/hermes-desktop.desktop @@ -4,9 +4,10 @@ Type=Application Name=Hermes GenericName=AI Agent Comment=The self-improving AI agent that grows with you -Exec=xdg-terminal-exec --app-id=TUI.tile -e hermes -Icon=hermes +Exec=hermes-desktop %U +Icon=hermes-desktop Terminal=false -Categories=Utility; +Categories=Development; Keywords=ai;agent;assistant;hermes;nous; StartupNotify=true +StartupWMClass=Hermes diff --git a/pkgbuilds/hermes-agent/hermes.png b/pkgbuilds/hermes-desktop/hermes-desktop.png similarity index 100% rename from pkgbuilds/hermes-agent/hermes.png rename to pkgbuilds/hermes-desktop/hermes-desktop.png diff --git a/pkgbuilds/hermes-desktop/hermes-desktop.sh b/pkgbuilds/hermes-desktop/hermes-desktop.sh new file mode 100644 index 0000000..958b537 --- /dev/null +++ b/pkgbuilds/hermes-desktop/hermes-desktop.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# The desktop app is only a shell: it runs `hermes serve` against a Hermes CLI +# it does not ship. Finding none, it clones its own copy with the upstream +# install script -- an unpinned checkout in ~/.hermes that no package manages. +# +# So make sure the CLI is there first. Omarchy installs it with its own script; +# elsewhere, say so rather than letting the app quietly bootstrap a second +# Hermes behind the user's back. +if ! command -v hermes >/dev/null 2>&1; then + if command -v omarchy-install-hermes-cli >/dev/null 2>&1; then + omarchy-install-hermes-cli --now + else + echo "The Hermes CLI is not installed, so Hermes Desktop will install its" >&2 + echo "own copy under ~/.hermes. Install the CLI first to avoid that." >&2 + fi +fi + +exec /opt/hermes-desktop/Hermes "$@"