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.
This commit is contained in:
@@ -1,73 +0,0 @@
|
|||||||
# Maintainer: David Heinemeier Hansson <david@hey.com>
|
|
||||||
|
|
||||||
# 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
|
|
||||||
}
|
|
||||||
@@ -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" "$@"
|
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# Maintainer: David Heinemeier Hansson <david@hey.com>
|
||||||
|
|
||||||
|
# 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
|
||||||
|
}
|
||||||
+4
-3
@@ -4,9 +4,10 @@ Type=Application
|
|||||||
Name=Hermes
|
Name=Hermes
|
||||||
GenericName=AI Agent
|
GenericName=AI Agent
|
||||||
Comment=The self-improving AI agent that grows with you
|
Comment=The self-improving AI agent that grows with you
|
||||||
Exec=xdg-terminal-exec --app-id=TUI.tile -e hermes
|
Exec=hermes-desktop %U
|
||||||
Icon=hermes
|
Icon=hermes-desktop
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=Utility;
|
Categories=Development;
|
||||||
Keywords=ai;agent;assistant;hermes;nous;
|
Keywords=ai;agent;assistant;hermes;nous;
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
|
StartupWMClass=Hermes
|
||||||
|
Before Width: | Height: | Size: 561 KiB After Width: | Height: | Size: 561 KiB |
@@ -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 "$@"
|
||||||
Reference in New Issue
Block a user