#!/bin/sh # Agentica installer — one line, macOS / Linux / WSL. # # curl -fsSL https://agenticaai.vercel.app/install.sh | bash # # What it does: # • detects your OS (macOS, Linux, or Windows-via-WSL) and CPU (arm64 / x64) # • downloads the matching desktop build from GitHub Releases # • installs it (macOS → /Applications; Linux → ~/.local) and adds an `agentica` launcher # • on macOS, strips the Gatekeeper quarantine flag so it opens with no warning # # Overrides (env vars): # AGENTICA_VERSION=v0.1.0 pin a release (default: latest) # AGENTICA_INSTALL_DIR=/opt/... Linux install location (default: ~/.local/share/agentica) # AGENTICA_NO_MODIFY_PATH=1 don't touch your shell rc # set -eu REPO="Bowen-AI/agentica-releases" SITE="https://agenticaai.vercel.app" VERSION="${AGENTICA_VERSION:-latest}" RC_EDITED="" # shell rc the installer touched (for a clean uninstall) # ---- pretty output (only colorize a real terminal) ------------------------- if [ -t 1 ]; then B="$(printf '\033[1m')"; DIM="$(printf '\033[2m')"; R="$(printf '\033[0m')" GRN="$(printf '\033[32m')"; BLU="$(printf '\033[34m')"; YLW="$(printf '\033[33m')"; RED="$(printf '\033[31m')" else B=""; DIM=""; R=""; GRN=""; BLU=""; YLW=""; RED="" fi say() { printf '%s\n' "$*"; } step() { printf '%s==>%s %s\n' "$BLU$B" "$R" "$*"; } ok() { printf '%s ✓%s %s\n' "$GRN" "$R" "$*"; } warn() { printf '%s !%s %s\n' "$YLW" "$R" "$*"; } die() { printf '%s ✗ %s%s\n' "$RED$B" "$*" "$R" >&2; exit 1; } # ---- detect OS + arch ------------------------------------------------------ UNAME_S="$(uname -s)" UNAME_M="$(uname -m)" IS_WSL=0 case "$UNAME_S" in Darwin) OS="mac" ;; Linux) OS="linux" if grep -qiE 'microsoft|wsl' /proc/sys/kernel/osrelease 2>/dev/null \ || grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then IS_WSL=1; fi ;; *) die "Unsupported OS: $UNAME_S. Agentica ships for macOS and Linux (Windows via WSL)." ;; esac case "$UNAME_M" in arm64|aarch64) ARCH="arm64" ;; x86_64|amd64) ARCH="x64" ;; *) die "Unsupported CPU: $UNAME_M (need arm64 or x86_64)." ;; esac # Apple Silicon running this script under Rosetta reports x86_64 — correct it. if [ "$OS" = "mac" ] && [ "$ARCH" = "x64" ] && \ [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = "1" ]; then ARCH="arm64" fi # ---- map to a release asset ------------------------------------------------ if [ "$OS" = "mac" ]; then ASSET="Agentica-mac-${ARCH}.zip" else ASSET="Agentica-linux-${ARCH}.tar.gz" fi if [ -n "${AGENTICA_URL:-}" ]; then URL="$AGENTICA_URL" # explicit override (mirror / local file:// / testing) elif [ "$VERSION" = "latest" ]; then # Route through the site's download counter (increments KV, then redirects) URL="$SITE/api/dl/$ASSET" else URL="https://github.com/$REPO/releases/download/$VERSION/$ASSET" fi say "" say "${B} Agentica installer${R}" say "${DIM} ${OS} · ${ARCH}$( [ "$IS_WSL" = 1 ] && echo ' · WSL' ) · ${VERSION}${R}" say "" # ---- downloader ------------------------------------------------------------ if command -v curl >/dev/null 2>&1; then fetch() { curl -fL --progress-bar -o "$2" "$1"; } elif command -v wget >/dev/null 2>&1; then fetch() { wget -q --show-progress -O "$2" "$1"; } else die "Need curl or wget to download." fi TMP="$(mktemp -d "${TMPDIR:-/tmp}/agentica.XXXXXX")" cleanup() { rm -rf "$TMP"; } trap cleanup EXIT INT TERM PKG="$TMP/$ASSET" step "Downloading ${B}$ASSET${R}" say "${DIM} $URL${R}" if ! fetch "$URL" "$PKG"; then die "Download failed. No published build for ${OS}/${ARCH} yet? See ${SITE}" fi # A GitHub 404 page is small HTML, not a real archive — catch it early. # (Real builds are 100 MB+, so a 5 MB floor only ever trips on an error page.) if [ "$(wc -c < "$PKG")" -lt 5000000 ]; then die "Downloaded file is too small — the ${OS}/${ARCH} build may not be published yet. See ${SITE}" fi ok "Downloaded ($(du -h "$PKG" | cut -f1))" # ---- ensure a user bin dir on PATH ---------------------------------------- BIN_DIR="$HOME/.local/bin" mkdir -p "$BIN_DIR" add_to_path() { # $1 = dir to ensure on PATH; edits the right shell rc unless opted out. case ":$PATH:" in *":$1:"*) return 0 ;; esac [ "${AGENTICA_NO_MODIFY_PATH:-0}" = "1" ] && { warn "Add to PATH: export PATH=\"$1:\$PATH\""; return 0; } rc="" case "$(basename "${SHELL:-}")" in zsh) rc="$HOME/.zshrc" ;; # Linux bash reads .bashrc for interactive shells; macOS Terminal runs LOGIN # bash which reads .bash_profile, not .bashrc. bash) [ "$OS" = mac ] && rc="$HOME/.bash_profile" || rc="$HOME/.bashrc" ;; *) rc="$HOME/.profile" ;; esac rc="${rc:-$HOME/.profile}" # SHELL unset (e.g. curl|bash with no TTY) → safe default if [ -f "$rc" ] && grep -q 'agentica installer' "$rc" 2>/dev/null; then return 0; fi { printf '\n# >>> agentica installer >>>\n' printf 'export PATH="%s:$PATH"\n' "$1" printf '# <<< agentica installer <<<\n' } >> "$rc" RC_EDITED="$rc" ok "Added $1 to PATH in ${rc##*/} ${DIM}(restart your shell or: source $rc)${R}" } # Write a self-contained `agentica-uninstall` with the exact paths we installed, # so removal is one command and works offline. Paths are passed as arguments. write_uninstaller() { u="$BIN_DIR/agentica-uninstall" { echo '#!/bin/sh' echo '# Removes Agentica completely. Safe to run anytime.' echo 'set -u' echo 'echo "Removing Agentica…"' for p in "$@"; do printf 'rm -rf "%s"\n' "$p"; done if [ -n "$RC_EDITED" ]; then printf 't="%s.agx$$"; sed "/# >>> agentica installer >>>/,/# <<< agentica installer << "$t" 2>/dev/null && mv "$t" "%s"\n' "$RC_EDITED" "$RC_EDITED" "$RC_EDITED" fi echo 'echo "Done — Agentica removed."' printf 'rm -f "%s"\n' "$u" } > "$u" chmod +x "$u" } # =========================================================================== # macOS # =========================================================================== if [ "$OS" = "mac" ]; then step "Extracting" EX="$TMP/x"; mkdir -p "$EX" if command -v ditto >/dev/null 2>&1; then ditto -x -k "$PKG" "$EX"; else unzip -q "$PKG" -d "$EX"; fi APP_SRC="$(find "$EX" -maxdepth 2 -name 'Agentica.app' -print | head -n1)" [ -n "$APP_SRC" ] || die "Agentica.app not found inside the archive." if [ -w /Applications ]; then APPS="/Applications" else APPS="$HOME/Applications"; mkdir -p "$APPS" warn "/Applications not writable — installing to ~/Applications" fi APP="$APPS/Agentica.app" step "Installing to ${B}$APP${R}" rm -rf "$APP" ditto "$APP_SRC" "$APP" 2>/dev/null || cp -R "$APP_SRC" "$APP" # The auto-fix: drop the quarantine flag so Gatekeeper won't block an # unsigned app — the curl install opens with zero warnings. xattr -dr com.apple.quarantine "$APP" 2>/dev/null || true ok "Installed and unquarantined" # `agentica` terminal launcher printf '#!/bin/sh\nexec open -a "%s" "$@"\n' "$APP" > "$BIN_DIR/agentica" chmod +x "$BIN_DIR/agentica" add_to_path "$BIN_DIR" write_uninstaller "$APP" "$HOME/Applications/Agentica.app" "$BIN_DIR/agentica" \ "$HOME/Library/Application Support/Agentica" \ "$HOME/Library/Caches/ai.bowen.agentica" \ "$HOME/Library/Preferences/ai.bowen.agentica.plist" \ "$HOME/Library/Logs/Agentica" ok "Launcher: ${B}agentica${R} ${DIM}(or open it from Launchpad / Spotlight)${R}" # =========================================================================== # Linux (incl. WSL) # =========================================================================== else step "Extracting" EX="$TMP/x"; mkdir -p "$EX" tar -xzf "$PKG" -C "$EX" TOP="$(find "$EX" -mindepth 1 -maxdepth 1 -type d | head -n1)" [ -n "$TOP" ] && [ -x "$TOP/agentica" ] || die "Couldn't find the agentica binary inside the archive." INSTALL_DIR="${AGENTICA_INSTALL_DIR:-$HOME/.local/share/agentica}" step "Installing to ${B}$INSTALL_DIR${R}" rm -rf "$INSTALL_DIR" mkdir -p "$(dirname "$INSTALL_DIR")" mv "$TOP" "$INSTALL_DIR" chmod +x "$INSTALL_DIR/agentica" ok "Installed" # Launcher. Run with --no-sandbox so the unprivileged (no-setuid) extract # starts cleanly; pass extra args through. cat > "$BIN_DIR/agentica" </dev/null 2>&1 || true # Desktop-menu entry (best effort) — clickable in the app menu and file manager. APPDIR="$HOME/.local/share/applications"; mkdir -p "$APPDIR" cat > "$APPDIR/agentica.desktop" </dev/null || true command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$APPDIR" 2>/dev/null || true command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true add_to_path "$BIN_DIR" write_uninstaller "$INSTALL_DIR" "$BIN_DIR/agentica" \ "$HOME/.local/share/applications/agentica.desktop" \ "$HOME/.local/share/icons/hicolor/512x512/apps/agentica.png" \ "$HOME/.config/Agentica" ok "Launcher: ${B}agentica${R}" if [ "$IS_WSL" = 1 ]; then warn "WSL detected — the GUI needs WSLg (Windows 11) or an X server (VcXsrv) on the host." fi fi # ---- Ollama nudge ---------------------------------------------------------- say "" if command -v ollama >/dev/null 2>&1; then ok "Ollama found — you're ready to pull a model." else warn "Ollama not detected. Agentica's in-app setup can install it, or run:" if [ "$OS" = "mac" ]; then say "${DIM} brew install ollama # or download from https://ollama.com${R}" else say "${DIM} curl -fsSL https://ollama.com/install.sh | sh${R}" fi fi say "" say "${GRN}${B} Agentica is installed.${R}" say " Launch it: ${B}agentica${R}$( [ "$OS" = mac ] && echo " ${DIM}(or from Launchpad)${R}" )" say " Uninstall: ${B}agentica-uninstall${R} ${DIM}(or: curl -fsSL ${SITE}/uninstall.sh | bash)${R}" say " Guide: ${BLU}${SITE}${R}" say ""