#!/usr/bin/env bash # ZoarkBot Linux Installer # Usage: curl -sSL https://get.zoarkai.org | bash # bash install-linux.sh [LICENSE_KEY] set -euo pipefail INSTALL_DIR="${ZOARKBOT_INSTALL_DIR:-$HOME/.zoarkbot}" BIN_DIR="${ZOARKBOT_BIN_DIR:-$HOME/.local/bin}" LICENSE_SERVER="${ZOARKBOT_LICENSE_SERVER:-https://license.zoarkai.org}" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m' info() { echo -e "${CYAN}[zoarkbot]${NC} $*"; } ok() { echo -e "${GREEN}[✓]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } die() { echo -e "${RED}[✗]${NC} $*" >&2; exit 1; } echo -e "\n${BOLD} ZoarkBot Installer${NC} · https://app.zoarkai.org\n" # ── Root/sudo detection: use sudo only when needed AND available ────────────── # Fixes installs on servers/containers that run as root (no sudo) or lack sudo. if [[ "$(id -u)" -eq 0 ]]; then SUDO="" elif command -v sudo >/dev/null 2>&1; then SUDO="sudo" else SUDO="" # not root and no sudo — privileged steps will be attempted directly and may warn fi # ── Auto-install Node.js 22 if missing or too old ───────────────────────────── _install_node_linux() { info "Installing Node.js 22 via NodeSource..." if [[ -z "$SUDO" && "$(id -u)" -ne 0 ]]; then die "Node.js 22+ is required but installing it needs root. Install Node.js 22+ from https://nodejs.org (or install 'sudo'), then re-run." fi # -E (preserve env) only applies to sudo; drop it when running directly as root. local SUDO_E="$SUDO" [[ -n "$SUDO" ]] && SUDO_E="$SUDO -E" if command -v apt-get >/dev/null 2>&1; then curl -fsSL https://deb.nodesource.com/setup_22.x | $SUDO_E bash - \ && $SUDO apt-get install -y nodejs elif command -v dnf >/dev/null 2>&1; then curl -fsSL https://rpm.nodesource.com/setup_22.x | $SUDO bash - \ && $SUDO dnf install -y nodejs else die "Cannot auto-install Node.js. Install Node.js 22+ from https://nodejs.org then re-run." fi } # ── Node.js check ───────────────────────────────────────────────────────────── if ! command -v node >/dev/null 2>&1; then warn "Node.js not found — installing Node.js 22..." _install_node_linux fi NODE_MAJOR=$(node -e "process.stdout.write(process.versions.node.split('.')[0])") if [[ "$NODE_MAJOR" -lt 22 ]]; then warn "Node.js $(node --version) found — need 22+. Upgrading..." _install_node_linux fi ok "Node.js $(node --version)" # ── License key ──────────────────────────────────────────────────────────────── LICENSE_KEY="${1:-${ZOARKBOT_LICENSE_KEY:-}}" if [[ -z "$LICENSE_KEY" ]]; then echo -n " License key (from app.zoarkai.org): " read -r LICENSE_KEY fi LICENSE_KEY="${LICENSE_KEY^^}" if [[ ! "$LICENSE_KEY" =~ ^ZOARKBOT-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$ ]]; then die "Invalid key format. Expected: ZOARKBOT-XXXX-XXXX-XXXX-XXXX" fi # ── Get signed download URL ──────────────────────────────────────────────────── info "Validating license..." HTTP_RESP=$(curl -sf --max-time 15 \ "${LICENSE_SERVER}/v1/account/download-url?os=linux&license_key=${LICENSE_KEY}") \ || die "Cannot reach license server. Check your connection." DOWNLOAD_URL=$(echo "$HTTP_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['url'])" 2>/dev/null) \ || die "License validation failed. Verify your key at app.zoarkai.org" # Validate URL is HTTPS before following it if [[ "$DOWNLOAD_URL" != https://* ]]; then die "License server returned an unsafe download URL. Aborting." fi ok "License valid" # ── Download ────────────────────────────────────────────────────────────────── info "Downloading ZoarkBot for Linux..." TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT curl -fL --proto '=https' --tlsv1.2 --progress-bar -o "$TMP/zoarkbot.tar.gz" "$DOWNLOAD_URL" \ || die "Download failed. Please try again." # Verify tarball is a valid gzip archive before extracting if ! python3 -c "import gzip,sys; gzip.open('$TMP/zoarkbot.tar.gz').read(1)" 2>/dev/null; then die "Downloaded file is corrupt or not a valid archive." fi # ── Install ─────────────────────────────────────────────────────────────────── info "Installing to ${INSTALL_DIR}..." rm -rf "${INSTALL_DIR}/dist" "${INSTALL_DIR}/node_modules" # clean old version mkdir -p "$INSTALL_DIR" tar -xzf "$TMP/zoarkbot.tar.gz" -C "$INSTALL_DIR" # ── Enhanced browser UI ───────────────────────────────────────────────────────── # The stock bundle ships a plain control UI. Overlay the enhanced dashboard # (black theme, accent colors, voice input/output) by wrapping the bundle's OWN # assets — so it stays correct regardless of the bundle's asset hashes. _install_enhanced_ui() { local UI_DIR="${INSTALL_DIR}/dist/control-ui" local STOCK="${UI_DIR}/index.html" [[ -f "$STOCK" ]] || { warn "control-ui not found — skipping UI enhancement"; return 0; } local TPL="$TMP/enhanced-ui.template.html" curl -fsSL --max-time 15 "https://get.zoarkai.org/enhanced-ui.template.html" -o "$TPL" 2>/dev/null \ || { warn "Could not fetch enhanced UI template — keeping stock UI"; return 0; } # Discover the bundle's real asset filenames from the stock index.html local JS CSS JS=$(grep -oE 'assets/index-[A-Za-z0-9_-]+\.js' "$STOCK" | head -1 | sed 's#assets/##') CSS=$(grep -oE 'assets/index-[A-Za-z0-9_-]+\.css' "$STOCK" | head -1 | sed 's#assets/##') if [[ -z "$JS" || -z "$CSS" ]]; then warn "Could not read UI asset names — keeping stock UI"; return 0; fi cp "$STOCK" "${STOCK}.stock.bak" 2>/dev/null || true sed -e "s/__ZK_JS__/${JS}/g" -e "s/__ZK_CSS__/${CSS}/g" "$TPL" > "$STOCK" ok "Enhanced browser UI installed (voice, themes, black mode)" } _install_enhanced_ui # ── Wrapper ─────────────────────────────────────────────────────────────────── mkdir -p "$BIN_DIR" cat > "$BIN_DIR/zoarkbot" </dev/null; then printf '\nexport PATH="%s:$PATH"\n' "$BIN_DIR" >> "$RC" break fi done export PATH="${BIN_DIR}:$PATH" # ── Activate ────────────────────────────────────────────────────────────────── info "Activating license on this machine..." node "${INSTALL_DIR}/zoarkbot.mjs" license activate "$LICENSE_KEY" \ || warn "Auto-activation failed. Run: zoarkbot license activate ${LICENSE_KEY}" ok "ZoarkBot installed successfully!" echo "" echo -e " ${BOLD}zoarkbot gateway${NC} — start the AI gateway" echo -e " ${BOLD}zoarkbot mc${NC} — open Mission Control" echo "" warn "Reload shell or run: export PATH=\"${BIN_DIR}:\$PATH\""