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 +}