From 8b8ede46edbad8ce80a8748b31859328cbfa1a91 Mon Sep 17 00:00:00 2001 From: ferdous Date: Fri, 9 Oct 2026 17:39:53 +0300 Subject: [PATCH] Add Ghost: runtime with optional Omarchy HUD (#390) * Add ghost: an owner-local AI persona with an Omarchy-native HUD Ghost (https://github.com/ferdousbhai/ghost, Apache-2.0) is a local AI persona for Omarchy: a daemon on 127.0.0.1, a Quickshell HUD summoned with Super+Ctrl+G, a `ghost` CLI, an opt-in Chromium relay, and a computer-use helper. The package builds from the v0.1.0 GitHub release's sanitized source archive plus its prebuilt Bun runtime archive; the `.omarchy/package.json` upstream block follows later releases the same way. check() runs the project's own test suite against the payload. Built locally with `makepkg --cleanbuild` from these exact inputs on Omarchy 4.0.0.alpha: ghost-0.1.0-1-x86_64.pkg.tar.zst. * ghost 0.1.1: arch=('any'), one runtime archive for every architecture The payload is Bun-target JavaScript with no native modules; verified on aarch64 under emulation (ferdousbhai/ghost#52). The runtime archive is now named linux-any and listed with the source tarball under sources.any. * ghost: 0.1.2 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_017TQbyws7icrhj7H7V7yi6v * ghost: 0.2.0 * ghost: 0.2.1 * ghost: 0.2.2 * ghost: 0.2.3 * ghost: 0.3.0 * ghost: 0.4.0 with independently installable runtime * ghost: 0.4.1 * ghost: 0.4.2 * ghost: 0.4.3 * ghost: 0.5.0 * ghost: 0.5.1 * ghost: 0.5.2 * ghost: 0.6.0 * ghost: 0.6.0-2, declare ripgrep for package checks check() in the 0.6.0 source runs packaging tests that need rg, and the clean build container does not have it, so every build since 0.5.0 stopped with "ripgrep is required by package checks but is not installed". Same version, so pkgrel goes to 2 (ghost e97eb09 carries the same checkdepend for the next release's render). Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Fable 5.1 --- pkgbuilds/ghost/.omarchy/package.json | 15 +++ pkgbuilds/ghost/PKGBUILD | 144 ++++++++++++++++++++++++++ pkgbuilds/ghost/ghost-runtime.install | 37 +++++++ pkgbuilds/ghost/ghost.install | 32 ++++++ 4 files changed, 228 insertions(+) create mode 100644 pkgbuilds/ghost/.omarchy/package.json create mode 100644 pkgbuilds/ghost/PKGBUILD create mode 100644 pkgbuilds/ghost/ghost-runtime.install create mode 100644 pkgbuilds/ghost/ghost.install diff --git a/pkgbuilds/ghost/.omarchy/package.json b/pkgbuilds/ghost/.omarchy/package.json new file mode 100644 index 00000000..718ca59b --- /dev/null +++ b/pkgbuilds/ghost/.omarchy/package.json @@ -0,0 +1,15 @@ +{ + "source": "local", + "release_ring": "fast", + "upstream": { + "github": "ferdousbhai/ghost", + "digests": true, + "latest_only": true, + "sources": { + "any": [ + "https://github.com/ferdousbhai/ghost/releases/download/{tag}/ghost-{pkgver}.tar.gz", + "https://github.com/ferdousbhai/ghost/releases/download/{tag}/ghost-runtime-{pkgver}-linux-any.tar.zst" + ] + } + } +} diff --git a/pkgbuilds/ghost/PKGBUILD b/pkgbuilds/ghost/PKGBUILD new file mode 100644 index 00000000..4093b540 --- /dev/null +++ b/pkgbuilds/ghost/PKGBUILD @@ -0,0 +1,144 @@ +# Maintainer: Ferdous Bhai + +pkgbase=ghost +pkgname=(ghost-runtime ghost) +pkgver=0.6.0 +pkgrel=2 +pkgdesc="Owner-local AI persona with a character, tools, and an Omarchy-native desktop shell" +arch=('any') +url="https://github.com/ferdousbhai/ghost" +license=('Apache-2.0') +_runtime_depends=( + 'at-spi2-core>=2.58' + 'bun>=1.3.14' + 'grim>=1.5' + 'hyprland>=0.56' + 'libnotify>=0.8' + 'qrencode' + 'systemd' + 'wl-clipboard' + 'wtype>=0.4' +) +_ui_depends=('qt6-5compat>=6.8' 'quickshell>=0.3' 'xdg-utils>=1.2') +depends=("${_runtime_depends[@]}" "${_ui_depends[@]}") +makedepends=( + 'bun>=1.4.0' + 'desktop-file-utils' +) +checkdepends=( + 'jq' + 'nodejs' + 'python-yaml' + 'ripgrep' +) +_runtime_optdepends=( + 'chromium: the browser the relay drives, and the browser-relay smoke test' + 'npm: npx-launched MCP servers' +) +options=('!debug' '!strip') + +# Omarchy's sync-upstream follows the GitHub release named in +# .omarchy/package.json and rewrites pkgver and both checksum arrays. +_release="https://github.com/ferdousbhai/ghost/releases/download/v${pkgver}" +# One runtime archive for every architecture: Bun-target JavaScript, no +# native modules (verified on aarch64, ghost#52). +_runtime="ghost-runtime-${pkgver}-linux-any.tar.zst" +source=( + "ghost-${pkgver}.tar.gz::${_release}/ghost-${pkgver}.tar.gz" + "${_runtime}::${_release}/${_runtime}" +) +noextract=("${_runtime}") +sha256sums=( + '681320615fa6192fe57f8fe6ab1e36ae04a7680d7b371721762bfdedf48ac985' + '1c9c66ca0299dbf9d15a37e541f61352658bcb9700a067eec1081a3bfc1549f5' +) + +_release_identity() { + local manifest="$1" + local line key value + declare -A fields=() + + [[ -f "$manifest" && ! -L "$manifest" ]] || return 1 + while IFS= read -r line || [[ -n "$line" ]]; do + [[ "$line" == *=* ]] || return 1 + key="${line%%=*}" + value="${line#*=}" + case "$key" in + format|version|source_commit|source_date_epoch) ;; + *) return 1 ;; + esac + [[ ! -v "fields[$key]" ]] || return 1 + fields["$key"]="$value" + done < "$manifest" + + [[ "${#fields[@]}" -eq 4 ]] || return 1 + [[ "${fields[format]}" == ghost-release-source/v1 ]] || return 1 + [[ "${fields[version]}" == "$pkgver" ]] || return 1 + [[ "${fields[source_commit]}" =~ ^[0-9a-f]{40}$ ]] || return 1 + [[ "${fields[source_date_epoch]}" =~ ^[0-9]+$ ]] || return 1 + printf '%s\t%s\n' \ + "${fields[source_commit]}" "${fields[source_date_epoch]}" +} + +prepare() { + local source_root="$srcdir/ghost-${pkgver}" + local runtime_parent="$srcdir/runtime" + local runtime_root="$runtime_parent/ghost-runtime-${pkgver}-linux-any" + local identity source_commit source_date_epoch + identity="$(_release_identity \ + "$source_root/RELEASE-SOURCE.MANIFEST")" || return 1 + IFS=$'\t' read -r source_commit source_date_epoch <<< "$identity" || return 1 + bash "$source_root/packaging/release/verify-release-source.sh" \ + "$source_root" "$pkgver" "$source_commit" "$source_date_epoch" || return 1 + export SOURCE_DATE_EPOCH="$source_date_epoch" + install -d -m755 "$runtime_parent" + find "$runtime_parent" -mindepth 1 -delete + bsdtar -xf "$srcdir/$_runtime" -C "$runtime_parent" || return 1 + bash "$srcdir/ghost-${pkgver}/packaging/release/verify-runtime-source.sh" \ + "$runtime_root" "$source_root" "$pkgver" any "$source_commit" \ + "$source_date_epoch" || return 1 +} + +build() { + # The Bun bundles were built from the frozen lock and verified in + # prepare(); there is no network build step. + : +} + +check() { + local source_root="$srcdir/ghost-${pkgver}" + local runtime_root="$srcdir/runtime/ghost-runtime-${pkgver}-linux-any" + local identity source_commit source_date_epoch + identity="$(_release_identity \ + "$source_root/RELEASE-SOURCE.MANIFEST")" || return 1 + IFS=$'\t' read -r source_commit source_date_epoch <<< "$identity" || return 1 + bash "$source_root/packaging/arch/test-check-dependencies.sh" || return 1 + bash "$source_root/packaging/release/verify-runtime-source.sh" \ + "$runtime_root" "$source_root" "$pkgver" any "$source_commit" \ + "$source_date_epoch" || return 1 + desktop-file-validate \ + "$source_root/packages/shell/contrib/ghost.desktop" || return 1 +} + +package_ghost-runtime() { + pkgdesc="Ghost daemon, API, CLI, and desktop/browser automation without the HUD" + depends=("${_runtime_depends[@]}") + optdepends=("${_runtime_optdepends[@]}") + conflicts=(ghost-runtime-dev) + install=ghost-runtime.install + local source_root="$srcdir/ghost-${pkgver}" + local runtime_root="$srcdir/runtime/ghost-runtime-${pkgver}-linux-any" + bash "$source_root/packaging/release/install-payload.sh" \ + "$source_root" "$runtime_root" "$pkgdir" "$pkgname" runtime +} + +package_ghost() { + pkgdesc="Ghost HUD and desktop integration for Omarchy" + depends=("ghost-runtime=$pkgver-$pkgrel" "${_ui_depends[@]}") + conflicts=(ghost-dev) + install=ghost.install + local source_root="$srcdir/ghost-${pkgver}" + local runtime_root="$srcdir/runtime/ghost-runtime-${pkgver}-linux-any" + bash "$source_root/packaging/release/install-payload.sh" \ + "$source_root" "$runtime_root" "$pkgdir" "$pkgname" ui +} diff --git a/pkgbuilds/ghost/ghost-runtime.install b/pkgbuilds/ghost/ghost-runtime.install new file mode 100644 index 00000000..fd963679 --- /dev/null +++ b/pkgbuilds/ghost/ghost-runtime.install @@ -0,0 +1,37 @@ +post_install() { + cat <<'TEXT' +Ghost runtime is installed without the Ghost HUD. Enable the daemon in the +current graphical session with: + + systemctl --user enable --now ghostd.service + ghost status + +The runtime keeps the CLI, authenticated API, desktop helper, and the relay +that the Ghost browser extension pairs with. The extension itself is a +separate install: https://github.com/ferdousbhai/ghost-chromium-extension +(pairing: `ghost browser allow `). An app can supply its own UI. +See /usr/share/doc/ghost/ARCH.md for UI installation and removal. + +Ghost homes, credentials, and state belong to you; package removal preserves them. +TEXT +} + +post_upgrade() { + cat <<'TEXT' +Ghost runtime was upgraded. Restart it in the graphical user's session: + + systemctl --user reenable --now ghostd.service + +Ghost homes, credentials, and state were preserved. +TEXT +} + +pre_remove() { + cat <<'TEXT' +Before removing the runtime, stop it from the graphical user's session: + + systemctl --user disable --now ghostd.service + +Removal preserves ghost homes, credentials, and state. +TEXT +} diff --git a/pkgbuilds/ghost/ghost.install b/pkgbuilds/ghost/ghost.install new file mode 100644 index 00000000..dd99e511 --- /dev/null +++ b/pkgbuilds/ghost/ghost.install @@ -0,0 +1,32 @@ +post_install() { + cat <<'TEXT' +Ghost's Omarchy UI is installed. Enable it in the graphical user's session: + + mkdir -p ~/.config/omarchy/plugins + ln -sfn /usr/share/ghost/plugin ~/.config/omarchy/plugins/ferdousbhai.ghost + omarchy-shell shell rescanPlugins && omarchy plugin enable ferdousbhai.ghost + +Summon it with `omarchy-shell shell toggle ferdousbhai.ghost`. +Shell snippets are under /usr/share/doc/ghost/shell-contrib/. +The runtime is managed separately; see /usr/share/doc/ghost/ARCH.md. +TEXT +} + +post_upgrade() { + cat <<'TEXT' +Ghost's Omarchy UI was upgraded. Reload its QML by restarting the shell: + + omarchy-restart-shell +TEXT +} + +pre_remove() { + cat <<'TEXT' +Before removing the UI, disable and unlink its plugin in the graphical session: + + omarchy plugin remove ferdousbhai.ghost --yes + omarchy-restart-shell + +Removing only the UI leaves Ghost runtime and all owner data available. +TEXT +}