diff --git a/pkgbuilds/cua-driver-bin/.omarchy/package.json b/pkgbuilds/cua-driver-bin/.omarchy/package.json new file mode 100644 index 00000000..bbe9ae19 --- /dev/null +++ b/pkgbuilds/cua-driver-bin/.omarchy/package.json @@ -0,0 +1,5 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h" +} diff --git a/pkgbuilds/cua-driver-bin/.omarchy/upstream.sh b/pkgbuilds/cua-driver-bin/.omarchy/upstream.sh new file mode 100644 index 00000000..2833d64e --- /dev/null +++ b/pkgbuilds/cua-driver-bin/.omarchy/upstream.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# cua-driver ships from the trycua/cua monorepo, whose single release feed +# interleaves many products (cua-driver-rs-v*, fleet-v*, sandbox-v*, and +# nightly-* builds). The declarative github provider reads a feed as one +# product and stops on the first foreign tag, so this hook selects the newest +# stable cua-driver-rs release itself and reads its checksums.txt manifest. +set -euo pipefail + +REPO="trycua/cua" +TAG_PREFIX="cua-driver-rs-v" + +releases=$(curl -fsSL "https://api.github.com/repos/$REPO/releases?per_page=100") + +min_age="${MIN_RELEASE_AGE_SECONDS:-0}" +now=$(date +%s) +candidates=0 +best_pkgver="" best_tag="" best_published="" +while IFS=$'\t' read -r tag published_at; do + [[ "$tag" == "$TAG_PREFIX"* ]] || continue + pkgver=${tag#"$TAG_PREFIX"} + # Upstream marks every driver release "prerelease" so the monorepo's + # "latest" can point at another product; stability lives in the tag shape + # instead. Stable driver versions are plain dotted numbers -- nightlies + # carry a nightly- tag prefix and a -nightly.N version suffix, and both + # fall out here. + [[ "$pkgver" =~ ^[0-9]+(\.[0-9]+)*$ ]] || continue + + # Strict ISO 8601 before GNU date sees it, matching bin/sync-upstream's + # backstop: date alone also accepts relative expressions, which would let a + # malformed feed fabricate an age instead of failing closed. + if [[ ! "$published_at" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:?[0-9]{2})$ ]] \ + || ! published_epoch=$(date --date="$published_at" +%s 2>/dev/null); then + echo "$REPO release $tag has an invalid published_at: ${published_at:-}" >&2 + exit 1 + fi + candidates=$((candidates + 1)) + + if (( now - published_epoch < min_age )); then + if [[ "${BYPASS_MIN_RELEASE_AGE:-}" == "1" ]]; then + echo "Bypassing release-age gate for $REPO $tag" >&2 + else + continue + fi + fi + + if [[ -z "$best_pkgver" ]] || [[ "$(vercmp "$pkgver" "$best_pkgver")" -gt 0 ]]; then + best_pkgver=$pkgver + best_tag=$tag + best_published=$published_at + fi +done < <(jq -r '.[] | select(.draft | not) | [.tag_name // "", .published_at // ""] | @tsv' <<<"$releases") + +# A feed page with no stable driver release at all is an anomaly worth a loud +# error; every candidate merely being inside the quarantine window is not. +if (( candidates == 0 )); then + echo "no stable $TAG_PREFIX releases in the feed for $REPO" >&2 + exit 1 +fi +if [[ -z "$best_tag" ]]; then + echo "every recent $TAG_PREFIX release is still inside the release-age quarantine; skipping" >&2 + echo '{}' + exit 0 +fi + +current=$(grep -m1 '^pkgver=' PKGBUILD | cut -d= -f2- | tr -d "\"'") +if [[ -n "$current" ]] && [[ "$(vercmp "$best_pkgver" "$current")" -le 0 ]]; then + echo '{}' + exit 0 +fi + +checksums=$(curl -fsSL "https://github.com/$REPO/releases/download/$best_tag/checksums.txt") + +sums_json='{}' +for arch in x86_64 aarch64; do + case "$arch" in + x86_64) platform="linux-x86_64" ;; + aarch64) platform="linux-arm64" ;; + esac + asset="cua-driver-rs-${best_pkgver}-${platform}.tar.gz" + sum=$(awk -v f="$asset" '$2 == f { print $1; exit }' <<<"$checksums") + if [[ ! "$sum" =~ ^[0-9a-f]{64}$ ]]; then + echo "no valid checksum for $asset in $REPO $best_tag checksums.txt" >&2 + exit 1 + fi + sums_json=$(jq -c --arg arch "$arch" --arg sum "$sum" '.[$arch] = [$sum]' <<<"$sums_json") +done + +jq -n --arg pkgver "$best_pkgver" --arg published_at "$best_published" \ + --argjson sums "$sums_json" \ + '{pkgver: $pkgver, published_at: $published_at, sha256sums: $sums}' diff --git a/pkgbuilds/cua-driver-bin/LICENSE b/pkgbuilds/cua-driver-bin/LICENSE new file mode 100644 index 00000000..b8b198ce --- /dev/null +++ b/pkgbuilds/cua-driver-bin/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Cua AI, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pkgbuilds/cua-driver-bin/PKGBUILD b/pkgbuilds/cua-driver-bin/PKGBUILD new file mode 100644 index 00000000..6af53aa6 --- /dev/null +++ b/pkgbuilds/cua-driver-bin/PKGBUILD @@ -0,0 +1,113 @@ +# Maintainer: Spencer Bull + +# cua-driver ships prebuilt from the trycua/cua monorepo release feed, so this +# repackages the vendor tarball. Upstream keeps the CLI, its cursor-theme +# compiler, and the SDK artifacts together in one directory and exposes the +# CLI through a symlink -- the binary resolves its helpers as siblings of +# /proc/self/exe -- so the whole tree lands under /usr/lib/cua-driver with a +# /usr/bin symlink. .omarchy/upstream.sh rewrites the version and checksums +# below from the release feed. +# +# The binary carries its own updater: `cua-driver update --apply` pipes the +# vendor installer into bash, which would install a second copy under +# ~/.cua-driver and link it into ~/.local/bin, stepping around pacman and +# this repository's release gate. Upstream offers no switch for that path, +# and a /usr/bin wrapper would not cover it either, since the MCP +# configurations the binary generates record the resolved executable. So +# prepare() rewrites the installer URL inside the binary to point at pm.sh, +# a stand-in that declines and names pacman instead. + +pkgname=cua-driver-bin +pkgver=0.23.2 +pkgrel=1 +pkgdesc="Computer-use driver for native GUI apps: accessibility-tree snapshots and input injection" +arch=('x86_64' 'aarch64') +url="https://github.com/trycua/cua" +license=('MIT') +# at-spi2-core carries the AT-SPI accessibility bus the driver reads GUI +# trees through; the X libraries are linked, not dlopen'd. +depends=( + 'at-spi2-core' + 'gcc-libs' + 'glibc' + 'libx11' + 'libxcb' + 'libxext' + 'libxi' + 'libxkbcommon' +) +provides=("cua-driver=${pkgver}") +conflicts=('cua-driver') +# Prebuilt Rust binaries ship byte-exact apart from the installer rewrite +# below: their build ids are what upstream symbolication matches. +options=('!strip' '!debug') +source=('LICENSE' 'pm.sh') +source_x86_64=("https://github.com/trycua/cua/releases/download/cua-driver-rs-v${pkgver}/cua-driver-rs-${pkgver}-linux-x86_64.tar.gz") +source_aarch64=("https://github.com/trycua/cua/releases/download/cua-driver-rs-v${pkgver}/cua-driver-rs-${pkgver}-linux-arm64.tar.gz") +sha256sums=('c0779290c1d4783169aa3dbfb55feb505e563ef8a004bbf55298ceffcfbda8d9' + 'c76e251c3ed424200eac52bec35ba534336307fabd83a175ab0b47e2084ab0d8') +sha256sums_x86_64=('478e010d2b0426de9d8a07eb839802daa92137b33cb8affb93b991e91d76ce7e') +sha256sums_aarch64=('3ad2d6c7ca7356a08e534baa2f4ca84ad02cebb2d27caef66cb268b6df71210d') + +case "${CARCH}" in + x86_64) _platform="linux-x86_64" ;; + aarch64) _platform="linux-arm64" ;; +esac +_vendor_tree="cua-driver-rs-${pkgver}-${_platform}" + +# Rust strings carry their length out of band, so the replacement has to be +# exactly as long as the original: 32 bytes, which is what fixes the +# stand-in's short name and location. +_vendor_installer='https://cua.ai/driver/install.sh' +_pacman_installer='file:///usr/lib/cua-driver/pm.sh' + +prepare() { + cd "${srcdir}/${_vendor_tree}" + + if (( ${#_vendor_installer} != ${#_pacman_installer} )); then + echo "installer URLs must be the same length to rewrite in place" >&2 + return 1 + fi + + # The URL appears twice: once in the updater and once in the printed + # reinstall one-liner. Any other count means upstream moved the updater + # and this rewrite needs another look, so the build stops rather than + # shipping a live self-updater. + local found + found=$(grep -obUaF "${_vendor_installer}" cua-driver | wc -l) + if (( found != 2 )); then + echo "expected the vendor installer URL twice in cua-driver, found ${found}" >&2 + return 1 + fi + + local size_before size_after + size_before=$(stat -c %s cua-driver) + sed -i "s|${_vendor_installer//./\\.}|${_pacman_installer}|g" cua-driver + size_after=$(stat -c %s cua-driver) + + if (( size_before != size_after )) \ + || grep -qUaF "${_vendor_installer}" cua-driver \ + || (( $(grep -obUaF "${_pacman_installer}" cua-driver | wc -l) != 2 )); then + echo "installer URL rewrite did not land cleanly in cua-driver" >&2 + return 1 + fi +} + +package() { + cd "${srcdir}/${_vendor_tree}" + + # The vendor tree stays together: cua-driver execs cua-cursor-theme as a + # sibling of the resolved binary, and the SDK library, node runtime, ABI + # header, and the GNOME wayland-helper extension are versioned with it. + install -d "${pkgdir}/usr/lib/cua-driver" + cp -a . "${pkgdir}/usr/lib/cua-driver/" + chmod -R a+rX "${pkgdir}/usr/lib/cua-driver" + + # The path prepare() wrote into the binary. + install -Dm755 "${srcdir}/pm.sh" "${pkgdir}/usr/lib/cua-driver/pm.sh" + + install -d "${pkgdir}/usr/bin" + ln -s ../lib/cua-driver/cua-driver "${pkgdir}/usr/bin/cua-driver" + + install -Dm644 "${srcdir}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" +} diff --git a/pkgbuilds/cua-driver-bin/pm.sh b/pkgbuilds/cua-driver-bin/pm.sh new file mode 100644 index 00000000..e60fd6ce --- /dev/null +++ b/pkgbuilds/cua-driver-bin/pm.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Stands in for the vendor installer. cua-driver-bin points the binary's +# `update --apply` here instead of https://cua.ai/driver/install.sh, which +# would otherwise install a second copy under ~/.cua-driver and link it into +# ~/.local/bin, stepping around pacman and the repository's release gate. The +# exit status is what `cua-driver update --apply` reports. +{ + echo "cua-driver is installed by pacman (cua-driver-bin), so the vendor installer is disabled." + echo "Upgrade it with pacman instead:" + echo " sudo pacman -Syu cua-driver-bin" +} >&2 +exit 1