#!/usr/bin/env bash # bin/install — build + install Dispatch as a systemd service on Arch Linux. # # Usage: # bin/install # build + install + enable + start # bin/install --no-build # install only (skip the build step) # bin/install --uninstall # stop + disable + remove files # # No sudo prefix needed — the script uses sudo internally on the specific # lines that require root (writing to /usr/bin, /etc, systemctl). The build # step runs as the normal user so dist/ files are user-owned. # # What it installs: # /usr/bin/dispatch-server — backend binary # /usr/bin/dispatch — CLI binary # /usr/share/dispatch/web/ — frontend static files # /etc/dispatch/env — server config (EnvironmentFile) # /etc/systemd/system/dispatch.service — systemd unit # /var/lib/dispatch/ — data directory (SQLite DBs) # /var/log/dispatch/ — journal + trace logs # # After install: systemctl status dispatch # Logs: journalctl -u dispatch -f # Config: edit /etc/dispatch/env then `systemctl restart dispatch` set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="$(cd "$HERE/.." && pwd)" # Detect the real user (works whether or not the script is run with sudo) REAL_USER="${SUDO_USER:-$USER}" REAL_GROUP=$(id -gn "$REAL_USER" 2>/dev/null || echo "$REAL_USER") ACTION="install" NO_BUILD=0 for arg in "$@"; do case "$arg" in --no-build) NO_BUILD=1 ;; --uninstall) ACTION="uninstall" ;; *) echo "install: unknown flag: $arg" >&2; exit 1 ;; esac done # ─── Uninstall ─────────────────────────────────────────────────────────────── if [[ "$ACTION" == "uninstall" ]]; then echo "[uninstall] stopping service…" sudo systemctl stop dispatch 2>/dev/null || true sudo systemctl disable dispatch 2>/dev/null || true sudo rm -f /etc/systemd/system/dispatch.service sudo systemctl daemon-reload echo "[uninstall] removing files…" sudo rm -f /usr/bin/dispatch-server /usr/bin/dispatch sudo rm -rf /usr/share/dispatch/web # Keep config + data (user might want them) echo "[uninstall] done." echo " Kept: /etc/dispatch/env, /var/lib/dispatch/, /var/log/dispatch/" echo " Remove manually if desired: rm -rf /etc/dispatch /var/lib/dispatch /var/log/dispatch" exit 0 fi # ─── Build ─────────────────────────────────────────────────────────────────── if [[ "$NO_BUILD" -eq 0 ]]; then echo "[install] building…" "$ROOT/bin/build" fi # Verify outputs exist if [[ ! -f "$ROOT/dist/dispatch-server" ]]; then echo "install: dist/dispatch-server not found — run bin/build first" >&2 exit 1 fi # ─── Install binaries ──────────────────────────────────────────────────────── echo "[install] binaries → /usr/bin/" sudo install -Dm755 "$ROOT/dist/dispatch-server" /usr/bin/dispatch-server sudo install -Dm755 "$ROOT/dist/dispatch" /usr/bin/dispatch # ─── Install frontend ──────────────────────────────────────────────────────── if [[ -d "$ROOT/dist/web" ]]; then echo "[install] frontend → /usr/share/dispatch/web/" sudo mkdir -p /usr/share/dispatch/web sudo cp -r "$ROOT/dist/web/"* /usr/share/dispatch/web/ fi # ─── Install systemd service ───────────────────────────────────────────────── echo "[install] systemd service → /etc/systemd/system/" # Unmask if previously masked sudo rm -f /etc/systemd/system/dispatch.service 2>/dev/null || true # Write the service file directly with the user patched in cat << EOF | sudo tee /etc/systemd/system/dispatch.service > /dev/null [Unit] Description=Dispatch AI Agent Server After=network.target [Service] Type=simple User=$REAL_USER Group=$REAL_GROUP ExecStart=/usr/bin/dispatch-server EnvironmentFile=/etc/dispatch/env WorkingDirectory=/var/lib/dispatch Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOF sudo chmod 644 /etc/systemd/system/dispatch.service # ─── Config (don't overwrite if it exists) ─────────────────────────────────── if [[ ! -f /etc/dispatch/env ]]; then echo "[install] config → /etc/dispatch/env (run sudo bin/setup-env to populate)" sudo install -Dm600 "$ROOT/systemd/dispatch.env" /etc/dispatch/env else echo "[install] config /etc/dispatch/env already exists — keeping" fi # ─── Data + log directories ────────────────────────────────────────────────── sudo mkdir -p /var/lib/dispatch /var/log/dispatch sudo chown -R "$REAL_USER:$REAL_GROUP" /var/lib/dispatch /var/log/dispatch # ─── Enable + start ────────────────────────────────────────────────────────── sudo systemctl daemon-reload sudo systemctl enable dispatch if grep -q 'sk-\.\.\.' /etc/dispatch/env 2>/dev/null; then echo "" echo "[install] done — but DISPATCH_API_KEY is still the placeholder." echo " 1. Edit /etc/dispatch/env and set your API key" echo " 2. sudo systemctl start dispatch" echo " 3. journalctl -u dispatch -f" else echo "[install] starting service…" sudo systemctl restart dispatch echo "" echo "[install] done!" echo " Status: systemctl status dispatch" echo " Logs: journalctl -u dispatch -f" echo " Config: /etc/dispatch/env" echo " Frontend: http://localhost:24991" echo " API: http://localhost:24991/chat" fi