# Maintainer: dispatch # # Split package: builds the base application once and produces three packages: # dispatch — application files + CLI wrappers + env configs # dispatch-systemd — systemd system units (run as a given user: dispatch-api@) # dispatch-s6 — s6 service definitions for dispatch-api / dispatch-frontend # # The Electron desktop wrapper lives in a separate PKGBUILD at packaging/electron/. # # Build with: # bin/build-pkg # makepkg -fd in packaging/ pkgbase=dispatch pkgname=('dispatch' 'dispatch-systemd' 'dispatch-s6' 'code-search') pkgver=0.0.1 pkgrel=1 arch=('x86_64') url='https://github.com/anomalyco/dispatch' license=('MIT') makedepends=('bun' 'go' 'git') # Pinned cs (code spelunker) commit (tag v3.1.0) built for the search_code tool. # Kept in lockstep with the Docker build (see Dockerfile / Dockerfile.dev). _cs_commit=697e0bf194bbc7a4a877e5170c70618989fc92e7 # All static files are read directly from ${_projectdir}/packaging/. We don't # use source=() because two of the s6 files share basenames (run, type), which # would collide inside ${srcdir}. source=() sha256sums=() _projectdir="${startdir}/.." _packagingdir="${_projectdir}/packaging" prepare() { mkdir -p "${srcdir}/dispatch-${pkgver}" # Copy project files into the src build directory (preserve symlinks) cp -a \ "${_projectdir}/packages" \ "${_projectdir}/package.json" \ "${_projectdir}/bun.lock" \ "${_projectdir}/tsconfig.base.json" \ "${srcdir}/dispatch-${pkgver}/" if [ -f "${_projectdir}/dispatch.toml" ]; then cp "${_projectdir}/dispatch.toml" "${srcdir}/dispatch-${pkgver}/" fi } build() { cd "${srcdir}/dispatch-${pkgver}" # Install all deps (including devDependencies for vite build) bun install --frozen-lockfile # Build the frontend with production API port VITE_API_URL="${VITE_API_URL:-http://localhost:18390}" \ bun run --cwd packages/frontend build # Slim node_modules for runtime rm -rf node_modules bun install --frozen-lockfile --production # --- Provide the patched `cs` code-search binary for the search_code tool --- # Building cs means a network clone + Go compile every run, which is wasted # work when a matching cs is already on the system: the code-search package # owns /usr/bin/cs and is pinned to the same _cs_commit. If it's already # installed, reuse that binary instead of recloning/recompiling. Force a # fresh build with DISPATCH_FORCE_CS_BUILD=1 (required when bumping # _cs_commit, since the installed binary won't reflect the new commit). if [ -z "${DISPATCH_FORCE_CS_BUILD:-}" ] && pacman -Qq code-search &>/dev/null \ && [ -x /usr/bin/cs ]; then echo "cs: code-search already installed — reusing /usr/bin/cs" \ "(set DISPATCH_FORCE_CS_BUILD=1 to rebuild)" cp /usr/bin/cs "${srcdir}/cs" else # Clone the pinned cs commit, apply the Luau declaration + fuzzy-distance # patches, and build a statically-linked binary. cs vendors its deps, so # `go build -mod=vendor` needs no network beyond the clone. Mirrors the # Docker cs-builder stage. # # rm -rf first so a rerun (makepkg -e, or two invocations without -C) # that reuses $srcdir doesn't abort on "destination path already exists". # This PKGBUILD intentionally avoids source=() (the s6 service files # share basenames and would collide in $srcdir), so we clone here rather # than via makepkg's VCS source handling. rm -rf "${srcdir}/cs-src" git clone https://github.com/boyter/cs.git "${srcdir}/cs-src" cd "${srcdir}/cs-src" git checkout "${_cs_commit}" git apply "${_projectdir}/docker/cs/luau-declarations.patch" git apply "${_projectdir}/docker/cs/fuzzy-distance.patch" CGO_ENABLED=0 GOFLAGS=-mod=vendor GOPATH="${srcdir}/gopath" GOCACHE="${srcdir}/gocache" \ go build -ldflags="-s -w" -o "${srcdir}/cs" . fi "${srcdir}/cs" --version } # ---------------------------------------------------------------------------- # dispatch — application files, CLI wrappers, env configs # ---------------------------------------------------------------------------- package_dispatch() { pkgdesc='AI Agent Dispatch — backend + frontend (application files)' depends=('bun') install=dispatch.install backup=( 'etc/dispatch/dispatch-api.conf' 'etc/dispatch/dispatch-frontend.conf' ) cd "${srcdir}/dispatch-${pkgver}" local optdir="${pkgdir}/opt/dispatch" # --- Application files --- # API package (full source) install -dm755 "${optdir}/packages/api" cp -a packages/api/. "${optdir}/packages/api/" # Core package (full source) install -dm755 "${optdir}/packages/core" cp -a packages/core/. "${optdir}/packages/core/" # Frontend: built dist + serve script + package.json (for workspace resolution) install -dm755 "${optdir}/packages/frontend/dist" cp -a packages/frontend/dist/. "${optdir}/packages/frontend/dist/" install -Dm644 packages/frontend/serve.ts "${optdir}/packages/frontend/serve.ts" install -Dm644 packages/frontend/package.json "${optdir}/packages/frontend/package.json" # Runtime node_modules (preserve symlinks for bun workspaces) # Exclude prebuilt ARM64 .node binaries — strip (x86_64 host) can't process them. install -dm755 "${optdir}/node_modules" cp -a node_modules/. "${optdir}/node_modules/" find "${optdir}/node_modules" -path '*/prebuilds/linux-arm64/*.node' -delete # Root manifest + lockfile install -Dm644 package.json "${optdir}/package.json" install -Dm644 bun.lock "${optdir}/bun.lock" # Optional default config if [ -f dispatch.toml ]; then install -Dm644 dispatch.toml "${optdir}/dispatch.toml" fi # --- Environment configs (preserved across upgrades via backup=()) --- install -Dm644 \ "${_packagingdir}/dispatch-api.conf" \ "${pkgdir}/etc/dispatch/dispatch-api.conf" install -Dm644 \ "${_packagingdir}/dispatch-frontend.conf" \ "${pkgdir}/etc/dispatch/dispatch-frontend.conf" # --- CLI wrappers --- install -Dm755 \ "${_packagingdir}/dispatch-api-wrapper.sh" \ "${pkgdir}/usr/bin/dispatch-api" install -Dm755 \ "${_packagingdir}/dispatch-frontend-wrapper.sh" \ "${pkgdir}/usr/bin/dispatch-frontend" # --- License --- if [ -f "${_projectdir}/LICENSE" ]; then install -Dm644 "${_projectdir}/LICENSE" \ "${pkgdir}/usr/share/licenses/dispatch/LICENSE" fi } # ---------------------------------------------------------------------------- # dispatch-systemd — systemd system unit templates (run as a chosen user) # # These are *system* units (managed by PID 1), not user units, so they do not # depend on a per-user `user@UID.service` manager. Each is a template keyed on # the username: `dispatch-api@tradam` runs as the `tradam` user via User=%i. # ---------------------------------------------------------------------------- package_dispatch-systemd() { pkgdesc='Systemd system unit templates for the Dispatch API and Frontend services (run as dispatch-api@)' depends=("dispatch=${pkgver}" 'systemd') conflicts=('dispatch-s6') install=dispatch-systemd.install install -Dm644 \ "${_packagingdir}/dispatch-api@.service" \ "${pkgdir}/usr/lib/systemd/system/dispatch-api@.service" install -Dm644 \ "${_packagingdir}/dispatch-frontend@.service" \ "${pkgdir}/usr/lib/systemd/system/dispatch-frontend@.service" } # ---------------------------------------------------------------------------- # dispatch-s6 — s6 service definitions (for Artix / standalone s6) # ---------------------------------------------------------------------------- package_dispatch-s6() { pkgdesc='s6-rc service definitions for the Dispatch API and Frontend services' depends=("dispatch=${pkgver}" 's6' 's6-rc') conflicts=('dispatch-systemd') install=dispatch-s6.install # s6-rc layout: each service is split into a `-srv` (the daemon) and a # `-log` (s6-log consumer), linked by producer-for/consumer-for/pipeline-name. # The pipeline-name lets `s6-rc -u change dispatch-api` bring both up together. # --- dispatch-api-srv --- install -Dm755 "${_packagingdir}/s6/dispatch-api-srv/run" \ "${pkgdir}/etc/s6/sv/dispatch-api-srv/run" install -Dm644 "${_packagingdir}/s6/dispatch-api-srv/type" \ "${pkgdir}/etc/s6/sv/dispatch-api-srv/type" install -Dm644 "${_packagingdir}/s6/dispatch-api-srv/producer-for" \ "${pkgdir}/etc/s6/sv/dispatch-api-srv/producer-for" # --- dispatch-api-log --- install -Dm755 "${_packagingdir}/s6/dispatch-api-log/run" \ "${pkgdir}/etc/s6/sv/dispatch-api-log/run" install -Dm644 "${_packagingdir}/s6/dispatch-api-log/type" \ "${pkgdir}/etc/s6/sv/dispatch-api-log/type" install -Dm644 "${_packagingdir}/s6/dispatch-api-log/consumer-for" \ "${pkgdir}/etc/s6/sv/dispatch-api-log/consumer-for" install -Dm644 "${_packagingdir}/s6/dispatch-api-log/pipeline-name" \ "${pkgdir}/etc/s6/sv/dispatch-api-log/pipeline-name" # notification-fd must match the `-d3` flag in run; without it s6-log # aborts with "invalid notification fd" and the API blocks on a full # stdout pipe before it can listen(). install -Dm644 "${_packagingdir}/s6/dispatch-api-log/notification-fd" \ "${pkgdir}/etc/s6/sv/dispatch-api-log/notification-fd" # --- dispatch-frontend-srv --- install -Dm755 "${_packagingdir}/s6/dispatch-frontend-srv/run" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-srv/run" install -Dm644 "${_packagingdir}/s6/dispatch-frontend-srv/type" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-srv/type" install -Dm644 "${_packagingdir}/s6/dispatch-frontend-srv/producer-for" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-srv/producer-for" # --- dispatch-frontend-log --- install -Dm755 "${_packagingdir}/s6/dispatch-frontend-log/run" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-log/run" install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/type" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-log/type" install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/consumer-for" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-log/consumer-for" install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/pipeline-name" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-log/pipeline-name" # notification-fd must match the `-d3` flag in run (see dispatch-api-log). install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/notification-fd" \ "${pkgdir}/etc/s6/sv/dispatch-frontend-log/notification-fd" } # ---------------------------------------------------------------------------- # code-search — patched `cs` (code spelunker) binary # # A standalone, relevance-ranked code search CLI (github.com/boyter/cs), built # from a pinned commit with an added Luau declaration table (see # docker/cs/luau-declarations.patch). Powers Dispatch's `search_code` agent # tool, but is a self-contained binary usable on its own. Built once in build() # and installed to /usr/bin/cs. # ---------------------------------------------------------------------------- package_code-search() { pkgdesc='code spelunker (cs) — fast, relevance-ranked code search CLI (patched for Luau)' depends=() # Both this package and the unrelated AUR `cs` (a colored ls) own /usr/bin/cs; # declare the conflict so pacman reports it cleanly instead of a raw file # collision. We do NOT `provides=('cs')` — this is a different program. conflicts=('cs') url='https://github.com/boyter/cs' install -Dm755 "${srcdir}/cs" "${pkgdir}/usr/bin/cs" if [ -f "${srcdir}/cs-src/LICENSE" ]; then install -Dm644 "${srcdir}/cs-src/LICENSE" \ "${pkgdir}/usr/share/licenses/code-search/LICENSE" fi }