diff --git a/pkgbuilds/hermes-desktop/.omarchy/package.json b/pkgbuilds/hermes-desktop/.omarchy/package.json new file mode 100644 index 0000000..db153c3 --- /dev/null +++ b/pkgbuilds/hermes-desktop/.omarchy/package.json @@ -0,0 +1,4 @@ +{ + "source": "local", + "release_ring": "fast" +} diff --git a/pkgbuilds/hermes-desktop/PKGBUILD b/pkgbuilds/hermes-desktop/PKGBUILD new file mode 100644 index 0000000..2bde6be --- /dev/null +++ b/pkgbuilds/hermes-desktop/PKGBUILD @@ -0,0 +1,134 @@ +# 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 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=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' + 'curl' + 'gcc-libs' + 'gdk-pixbuf2' + 'git' + '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') + +# 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" + 'hermes-desktop.sh' + 'hermes-desktop.desktop' + 'hermes-desktop.png') +sha256sums=('1e3d39d3638ec15fa9d31af262568a953e9272090deb1c50c44cd401175f5b80' + '6c6b59bd168d6ae34b7e9fcfcabc26ced8bccdd837bc2d09e687839ded6a9452' + '3ef685bfcf366776b025d26c37d32854d8d4aa2023b2bd07c8e08b001ef1e8c4' + '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 -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 "${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 +} diff --git a/pkgbuilds/hermes-desktop/hermes-desktop.desktop b/pkgbuilds/hermes-desktop/hermes-desktop.desktop new file mode 100644 index 0000000..8186b65 --- /dev/null +++ b/pkgbuilds/hermes-desktop/hermes-desktop.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Hermes +GenericName=AI Agent +Comment=The self-improving AI agent that grows with you +Exec=hermes-desktop %U +Icon=hermes-desktop +Terminal=false +Categories=Development; +MimeType=x-scheme-handler/hermes; +Keywords=ai;agent;assistant;hermes;nous; +StartupNotify=true +StartupWMClass=Hermes diff --git a/pkgbuilds/hermes-desktop/hermes-desktop.png b/pkgbuilds/hermes-desktop/hermes-desktop.png new file mode 100644 index 0000000..539fdf9 Binary files /dev/null and b/pkgbuilds/hermes-desktop/hermes-desktop.png differ diff --git a/pkgbuilds/hermes-desktop/hermes-desktop.sh b/pkgbuilds/hermes-desktop/hermes-desktop.sh new file mode 100644 index 0000000..1849f85 --- /dev/null +++ b/pkgbuilds/hermes-desktop/hermes-desktop.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -euo pipefail + +# Hermes Desktop is a shell around a Hermes runtime, and it only works against +# one built from its own commit. A CLI from PyPI is always a different release +# -- PyPI trails the tags -- and the mismatch fails the app's readiness probe +# with 401 Unauthorized. So keep it away from whatever `hermes` is on PATH, +# which on Omarchy is the mise CLI installed for the terminal agent, and let +# the app provision and manage its own runtime under ~/.hermes. That is the +# arrangement upstream ships, and the only one that starts. +export HERMES_DESKTOP_IGNORE_EXISTING=1 + +# Reconcile every launch rather than trusting whatever installed us. A plain +# `pacman -S hermes-desktop`, or an install interrupted partway, leaves any +# Hermes the terminal agent had built still sitting there, and by then the +# menu entry that would have tidied it up is disabled because we are present. +if command -v omarchy-install-hermes-cli >/dev/null 2>&1; then + omarchy-install-hermes-cli >/dev/null 2>&1 || true +fi + +# Chromium's own Ozone detection falls back to XWayland often enough to matter, +# and the result is a blurry window on every scaled display. Ask for Wayland +# directly, unless the user has already picked a platform themselves. +platform_flags=() +if [[ -n "${WAYLAND_DISPLAY:-}" || ${XDG_SESSION_TYPE:-} == wayland ]]; then + platform_flags=(--ozone-platform=wayland) + + for flag in "$@"; do + case "$flag" in + --ozone-platform=* | --ozone-platform-hint=*) platform_flags=() ;; + esac + done +fi + +exec /opt/hermes-desktop/Hermes "${platform_flags[@]}" "$@"