diff options
| author | Adam Malczewski <[email protected]> | 2026-05-22 16:19:35 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-22 16:19:35 +0900 |
| commit | 45a4890031192f4e7409443f98e824dad17ba175 (patch) | |
| tree | a28e1b0618f682476b0b57ba70e8ba26a1939a49 /packaging | |
| parent | 9ecaabd87c0e51b8a7408dabb0133a9344586859 (diff) | |
| download | dispatch-45a4890031192f4e7409443f98e824dad17ba175.tar.gz dispatch-45a4890031192f4e7409443f98e824dad17ba175.zip | |
feat: Arch Linux packaging with Electron frontend, systemd backend service, and Windows exe build
- Add Electron wrapper (main.cjs, preload.cjs) for desktop frontend
- Add systemd service unit, env config, and sysusers for backend API
- Add PKGBUILD and .install for Arch package (makepkg -si)
- Add desktop entry, SVG/PNG icon, and wrapper scripts
- Add bin/build-pkg, bin/install-pkg, bin/windows-pkg scripts
- Make API port configurable via PORT env var (default 3000, prod 18390)
- Add electron-builder config for Windows exe cross-compilation
- Set vite base to './' for Electron file:// loading
Diffstat (limited to 'packaging')
| -rw-r--r-- | packaging/PKGBUILD | 131 | ||||
| -rwxr-xr-x | packaging/dispatch-api-wrapper.sh | 32 | ||||
| -rw-r--r-- | packaging/dispatch-api.conf | 31 | ||||
| -rw-r--r-- | packaging/dispatch-api.service | 33 | ||||
| -rwxr-xr-x | packaging/dispatch-electron-wrapper.sh | 2 | ||||
| -rw-r--r-- | packaging/dispatch.desktop | 9 | ||||
| -rw-r--r-- | packaging/dispatch.install | 16 | ||||
| -rw-r--r-- | packaging/dispatch.png | bin | 0 -> 10192 bytes | |||
| -rw-r--r-- | packaging/dispatch.svg | 24 |
9 files changed, 278 insertions, 0 deletions
diff --git a/packaging/PKGBUILD b/packaging/PKGBUILD new file mode 100644 index 0000000..57e7e28 --- /dev/null +++ b/packaging/PKGBUILD @@ -0,0 +1,131 @@ +# Maintainer: dispatch +pkgname=dispatch +pkgver=0.0.1 +pkgrel=1 +pkgdesc='AI Agent Dispatch Interface' +arch=('x86_64') +url='https://github.com/anomalyco/dispatch' +license=('MIT') +depends=('electron' 'bun') +makedepends=() +install=dispatch.install +backup=('etc/dispatch/dispatch-api.conf') +source=( + 'dispatch-api.service' + 'dispatch-api.conf' + 'dispatch-electron-wrapper.sh' + 'dispatch-api-wrapper.sh' + 'dispatch.desktop' + 'dispatch.svg' +) +sha256sums=('SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP') + +_projectdir="${startdir}/.." + +prepare() { + mkdir -p "${srcdir}/dispatch-${pkgver}" + # Copy project files into the src build directory (use -a to preserve symlinks) + cp -a \ + "${_projectdir}/packages" \ + "${_projectdir}/package.json" \ + "${_projectdir}/bun.lock" \ + "${_projectdir}/tsconfig.base.json" \ + "${srcdir}/dispatch-${pkgver}/" + + # Copy dispatch.toml if it exists + if [ -f "${_projectdir}/dispatch.toml" ]; then + cp "${_projectdir}/dispatch.toml" "${srcdir}/dispatch-${pkgver}/" + fi +} + +build() { + cd "${srcdir}/dispatch-${pkgver}" + + # Install all deps (including devDependencies needed for vite build) + bun install --frozen-lockfile + + # Build the frontend with production API port + VITE_API_URL="http://localhost:18390" bun run --cwd packages/frontend build + + # Reinstall with production only to slim down node_modules for packaging + rm -rf node_modules + bun install --frozen-lockfile --production +} + +package() { + 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 only + install -dm755 "${optdir}/packages/frontend/dist" + cp -a packages/frontend/dist/. "${optdir}/packages/frontend/dist/" + + # Frontend: electron launcher files + install -dm755 "${optdir}/packages/frontend/electron" + cp -a packages/frontend/electron/. "${optdir}/packages/frontend/electron/" + + # Frontend: package.json (needed for workspace resolution) + install -Dm644 packages/frontend/package.json "${optdir}/packages/frontend/package.json" + + # Runtime node_modules (preserve symlinks for bun workspaces) + install -dm755 "${optdir}/node_modules" + cp -a node_modules/. "${optdir}/node_modules/" + + # Root manifest and lockfile + install -Dm644 package.json "${optdir}/package.json" + install -Dm644 bun.lock "${optdir}/bun.lock" + + # Default config (if present) + if [ -f dispatch.toml ]; then + install -Dm644 dispatch.toml "${optdir}/dispatch.toml" + fi + + # --- systemd service --- + install -Dm644 \ + "${srcdir}/dispatch-api.service" \ + "${pkgdir}/usr/lib/systemd/system/dispatch-api.service" + + # --- Environment config (preserved across upgrades via backup=()) --- + install -Dm644 \ + "${srcdir}/dispatch-api.conf" \ + "${pkgdir}/etc/dispatch/dispatch-api.conf" + + # --- Wrapper scripts --- + install -Dm755 \ + "${srcdir}/dispatch-electron-wrapper.sh" \ + "${pkgdir}/usr/bin/dispatch" + + install -Dm755 \ + "${srcdir}/dispatch-api-wrapper.sh" \ + "${pkgdir}/usr/bin/dispatch-api" + + # --- Desktop integration --- + install -Dm644 \ + "${srcdir}/dispatch.desktop" \ + "${pkgdir}/usr/share/applications/dispatch.desktop" + + install -Dm644 \ + "${srcdir}/dispatch.svg" \ + "${pkgdir}/usr/share/icons/hicolor/scalable/apps/dispatch.svg" + + # --- sysusers: create the 'dispatch' system user --- + install -Dm644 /dev/null "${pkgdir}/usr/lib/sysusers.d/dispatch.conf" + echo 'u dispatch - "Dispatch API" /var/lib/dispatch' > "${pkgdir}/usr/lib/sysusers.d/dispatch.conf" + + # --- License --- + if [ -f "${_projectdir}/LICENSE" ]; then + install -Dm644 "${_projectdir}/LICENSE" \ + "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" + fi +} diff --git a/packaging/dispatch-api-wrapper.sh b/packaging/dispatch-api-wrapper.sh new file mode 100755 index 0000000..d8c656b --- /dev/null +++ b/packaging/dispatch-api-wrapper.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# dispatch-api-wrapper.sh +# Wrapper script for running the Dispatch API outside of systemd. +# Install to /usr/bin/dispatch-api (chmod 755). +# +# Usage: +# dispatch-api # runs the server, inheriting the current environment +# dispatch-api --help # passes flags through to bun + +set -euo pipefail + +DISPATCH_CONF="/etc/dispatch/dispatch-api.conf" +DISPATCH_DIR="/opt/dispatch" +BUN="/usr/bin/bun" +ENTRY_POINT="packages/api/src/index.ts" + +# Source the environment file if it exists +if [[ -f "$DISPATCH_CONF" ]]; then + # Export every variable defined in the conf file so child processes see them. + # Lines starting with '#' and blank lines are skipped automatically by bash's + # 'source' builtin. + set -o allexport + # shellcheck source=/etc/dispatch/dispatch-api.conf + source "$DISPATCH_CONF" + set +o allexport +fi + +# Change to the application directory +cd "$DISPATCH_DIR" + +# Hand off to bun — exec replaces this process so signals are forwarded correctly +exec "$BUN" "$ENTRY_POINT" "$@" diff --git a/packaging/dispatch-api.conf b/packaging/dispatch-api.conf new file mode 100644 index 0000000..9824e16 --- /dev/null +++ b/packaging/dispatch-api.conf @@ -0,0 +1,31 @@ +# /etc/dispatch/dispatch-api.conf +# Environment configuration for the Dispatch API service. +# This file is sourced by systemd (via EnvironmentFile=) and by the +# dispatch-api-wrapper.sh script for manual invocation. +# +# Lines beginning with '#' are comments and are ignored. +# Syntax: KEY=value (no spaces around '=', no 'export' keyword needed for systemd) + +# --------------------------------------------------------------------------- +# LLM / AI provider +# --------------------------------------------------------------------------- + +# API key used by the backend to call the OpenCode (or compatible) LLM API. +OPENCODE_API_KEY= + +# --------------------------------------------------------------------------- +# Runtime environment +# --------------------------------------------------------------------------- + +# Set to "production" for live deployments. +NODE_ENV=production + +# --------------------------------------------------------------------------- +# Server +# --------------------------------------------------------------------------- + +# Port the API listens on (default: 3000). +PORT=18390 + +# Hostname/address to bind to (default: 0.0.0.0). +# HOST=0.0.0.0 diff --git a/packaging/dispatch-api.service b/packaging/dispatch-api.service new file mode 100644 index 0000000..ccbb727 --- /dev/null +++ b/packaging/dispatch-api.service @@ -0,0 +1,33 @@ +[Unit] +Description=Dispatch API Backend +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=dispatch +Group=dispatch +WorkingDirectory=/opt/dispatch +ExecStart=/usr/bin/bun packages/api/src/index.ts +EnvironmentFile=-/etc/dispatch/dispatch-api.conf +Restart=on-failure +RestartSec=5 +StandardOutput=journal +StandardError=journal + +# Data directory: systemd creates /var/lib/dispatch owned by dispatch:dispatch +StateDirectory=dispatch + +# Tell the app to store its SQLite DB under /var/lib/dispatch/ +Environment=XDG_DATA_HOME=/var/lib + +# Security hardening +ProtectSystem=strict +ProtectHome=true +NoNewPrivileges=true +PrivateTmp=true +PrivateDevices=true +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX + +[Install] +WantedBy=multi-user.target diff --git a/packaging/dispatch-electron-wrapper.sh b/packaging/dispatch-electron-wrapper.sh new file mode 100755 index 0000000..df24713 --- /dev/null +++ b/packaging/dispatch-electron-wrapper.sh @@ -0,0 +1,2 @@ +#!/bin/bash +exec /usr/bin/electron --name="Dispatch" /opt/dispatch/packages/frontend/electron/main.cjs "$@" diff --git a/packaging/dispatch.desktop b/packaging/dispatch.desktop new file mode 100644 index 0000000..0ed44af --- /dev/null +++ b/packaging/dispatch.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=Dispatch +Comment=AI Agent Dispatch Interface +Exec=/usr/bin/dispatch %U +Icon=dispatch +Terminal=false +Type=Application +Categories=Development;Utility; +StartupWMClass=Dispatch diff --git a/packaging/dispatch.install b/packaging/dispatch.install new file mode 100644 index 0000000..e6db694 --- /dev/null +++ b/packaging/dispatch.install @@ -0,0 +1,16 @@ +post_install() { + echo "" + echo "==> Dispatch has been installed." + echo " Edit /etc/dispatch/dispatch-api.conf to configure the API." + echo " Then enable and start the service:" + echo " systemctl enable --now dispatch-api" + echo "" +} + +post_upgrade() { + echo "" + echo "==> Dispatch has been upgraded." + echo " You may need to restart the service:" + echo " systemctl restart dispatch-api" + echo "" +} diff --git a/packaging/dispatch.png b/packaging/dispatch.png Binary files differnew file mode 100644 index 0000000..84d743a --- /dev/null +++ b/packaging/dispatch.png diff --git a/packaging/dispatch.svg b/packaging/dispatch.svg new file mode 100644 index 0000000..2aae97f --- /dev/null +++ b/packaging/dispatch.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64"> + <defs> + <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%"> + <stop offset="0%" style="stop-color:#5b6af0"/> + <stop offset="100%" style="stop-color:#9b59f7"/> + </linearGradient> + <linearGradient id="arrow" x1="0%" y1="0%" x2="100%" y2="100%"> + <stop offset="0%" style="stop-color:#ffffff;stop-opacity:1"/> + <stop offset="100%" style="stop-color:#e0d8ff;stop-opacity:1"/> + </linearGradient> + </defs> + + <!-- Rounded square background --> + <rect x="2" y="2" width="60" height="60" rx="14" ry="14" fill="url(#bg)"/> + + <!-- Send/dispatch arrow icon: a paper-plane style arrow pointing upper-right --> + <!-- Main arrow body --> + <polygon points="10,50 54,10 38,32" fill="url(#arrow)" opacity="0.95"/> + <!-- Arrow tail fill --> + <polygon points="10,50 28,36 22,54" fill="url(#arrow)" opacity="0.75"/> + <!-- Subtle highlight line --> + <line x1="54" y1="10" x2="28" y2="36" stroke="white" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/> +</svg> |
