Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ jobs:
with:
persist-credentials: false

- name: Test Hermes installation and launch
run: python3 test/hermes-desktop-test.py

# An Arch container for vercmp: version ordering has to be decided by
# the same comparator pacman uses on users' machines.
- name: Run self-tests
Expand Down
109 changes: 33 additions & 76 deletions pkgbuilds/hermes-desktop/PKGBUILD
Original file line number Diff line number Diff line change
@@ -1,38 +1,24 @@
# 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 only runs against a Hermes runtime built from its own commit, so it
# provisions one itself under ~/.hermes on first launch and the package stays
# on the newest tag. Pairing it with the mise CLI instead was tried and does
# not work: PyPI trails the tags, and the version gap fails the readiness probe
# with a 401. Pinning back to the tag behind PyPI's release does not rescue it
# either -- v2026.7.20's desktop hangs after "backend is ready" without ever
# opening a window, against its own matching runtime.
#
# 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=2
pkgdesc='Native desktop shell for Hermes Agent'
pkgver=2026.8.31
pkgrel=1
pkgdesc='Installer and launcher for the self-updating Hermes desktop'
arch=('x86_64')
url='https://github.com/NousResearch/hermes-agent'
license=('MIT')

# Hermes builds its desktop under the user's runtime so both the app and CLI
# can update it. The compiler and Electron libraries must survive installation.
depends=(
'alsa-lib'
'at-spi2-core'
'bash'
'cairo'
'curl'
'dbus'
'expat'
'curl'
'gcc-libs'
'gcc'
'gdk-pixbuf2'
'git'
'glib2'
Expand All @@ -52,65 +38,49 @@ depends=(
'libxfixes'
'libxkbcommon'
'libxrandr'
'make'
'mesa'
'nodejs'
'npm'
'nspr'
'nss'
'pango'
'python'
'systemd-libs'
'util-linux'
'xdg-terminal-exec'
'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')
makedepends=('imagemagick')

# The tag's commit. apps/desktop/scripts/write-build-stamp.mjs pins the app's
# first-launch bootstrap to a Hermes commit, and resolves it from $GITHUB_SHA
# before falling back to `git rev-parse`. That fallback is wrong here: makepkg
# builds inside this repository, so git ascends out of srcdir and stamps the
# app with an omarchy-pkgs commit that means nothing upstream.
_commit=e624e9fde561e1add9388384012b295fde669ade

_srcdir="hermes-agent-${pkgver}"
source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/refs/tags/v${pkgver}.tar.gz"
# Snapshot the bootstrap installer, including upstream's Linux sandbox fixes.
# Only installation uses it; updates belong to the native runtime on main.
_commit=245e48008fa814b3251f50755eb656bd9fb86cb1
source=("install-${_commit}.sh::https://raw.githubusercontent.com/NousResearch/hermes-agent/${_commit}/scripts/install.sh"
"LICENSE-${_commit}::https://raw.githubusercontent.com/NousResearch/hermes-agent/${_commit}/LICENSE"
'hermes-desktop.sh'
'hermes-desktop.desktop'
'hermes-desktop.png')
sha256sums=('1e3d39d3638ec15fa9d31af262568a953e9272090deb1c50c44cd401175f5b80'
'f5833b969ce451aadee9f08d92db55cce3c7c9213175080590bf37444854d676'
'3ef685bfcf366776b025d26c37d32854d8d4aa2023b2bd07c8e08b001ef1e8c4'
sha256sums=('5854b15670b51a8daae8f59ddfa917062de9f74be261eb73b4b8d719710f8968'
'821556e6336796450ab852d375117b48a4887e71d255794fd6318d99982a5ab6'
'27040084e691421dc1748e7646ae7ee79d5777e8ee1e1ec7581a68432d4a29c4'
'c37d4cfa4801eccbd769fddaebb2115d54faa49a5b7bc0f305f563e3cefd9bd8'
'd60d164e24fdcf6532133b8ea43c77a201e4b9e9dbc396187b58d51d8590ef52')

build() {
cd "${srcdir}/${_srcdir}"

export GITHUB_SHA="${_commit}"
export GITHUB_REF_NAME="v${pkgver}"

# 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 -Dm644 "${srcdir}/install-${_commit}.sh" \
"${pkgdir}/usr/share/${pkgname}/install.sh"
install -Dm644 "${srcdir}/LICENSE-${_commit}" \
"${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
install -Dm755 "${srcdir}/hermes-desktop.sh" "${pkgdir}/usr/bin/${pkgname}"

# Native registration uses hermes.desktop but omits URI handling. Keep the
# original handler ID hidden, and let the native entry replace the visible one.
install -Dm644 "${srcdir}/hermes-desktop.desktop" \
"${pkgdir}/usr/share/applications/${pkgname}.desktop"

"${pkgdir}/usr/share/applications/hermes-desktop.desktop"
printf 'NoDisplay=true\n' >>"${pkgdir}/usr/share/applications/hermes-desktop.desktop"
sed '/^MimeType=/d' "${srcdir}/hermes-desktop.desktop" >"${pkgdir}/usr/share/applications/hermes.desktop"
install -Dm644 "${srcdir}/hermes-desktop.png" \
"${pkgdir}/usr/share/icons/hicolor/1024x1024/apps/${pkgname}.png"
local size
Expand All @@ -119,17 +89,4 @@ package() {
install -Dm644 "${srcdir}/icon-${size}.png" \
"${pkgdir}/usr/share/icons/hicolor/${size}x${size}/apps/${pkgname}.png"
done

install -Dm644 "${srcdir}/${_srcdir}/LICENSE" \
"${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
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
}
2 changes: 1 addition & 1 deletion pkgbuilds/hermes-desktop/hermes-desktop.desktop
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Type=Application
Name=Hermes
GenericName=AI Agent
Comment=The self-improving AI agent that grows with you
Exec=hermes-desktop %U
Exec=/usr/bin/hermes-desktop %U
Icon=hermes-desktop
Terminal=false
Categories=Development;
Expand Down
Loading
Loading