#!/bin/sh # Orquesta Agent Installer — standalone binary, no Node.js required set -e PURPLE="\033[35m" GREEN="\033[32m" RED="\033[31m" CYAN="\033[36m" DIM="\033[2m" BOLD="\033[1m" RESET="\033[0m" BASE_URL="https://getorquesta.com" # Parse arguments TOKEN="" CLI_MODE="" SERVICE_MODE=false shift_next=false for arg in "$@"; do case "$arg" in --token=*) TOKEN="${arg#*=}" ;; --service=*) TOKEN="${arg#*=}"; SERVICE_MODE=true ;; --mode=*) CLI_MODE="${arg#*=}" ;; --cli-preference=*) CLI_MODE="${arg#*=}" ;; --token) shift_next=token ;; --service) shift_next=service; SERVICE_MODE=true ;; --mode|--cli-preference) shift_next=mode ;; *) if [ "$shift_next" = "token" ]; then TOKEN="$arg"; shift_next=false elif [ "$shift_next" = "service" ]; then TOKEN="$arg"; shift_next=false elif [ "$shift_next" = "mode" ]; then CLI_MODE="$arg"; shift_next=false; fi ;; esac done # Build optional mode flag to append to agent invocations MODE_FLAG="" if [ -n "$CLI_MODE" ]; then MODE_FLAG="--mode $CLI_MODE" fi echo "" echo "${BOLD}${PURPLE} getorquesta${RESET}${BOLD}.com${RESET} — Agent Installer" echo "" # ── Detect platform ─────────────────────────────────── OS="$(uname -s)" ARCH="$(uname -m)" case "$OS" in Linux*) PLATFORM="linux" ;; Darwin*) PLATFORM="macos" ;; *) echo " ${RED}Unsupported OS: $OS${RESET}"; exit 1 ;; esac case "$ARCH" in x86_64|amd64) ARCH="x64" ;; aarch64|arm64) ARCH="arm64" ;; *) echo " ${RED}Unsupported architecture: $ARCH${RESET}"; exit 1 ;; esac BINARY_NAME="orquesta-agent-${PLATFORM}-${ARCH}" BINARY_URL="${BASE_URL}/agent/${BINARY_NAME}" FALLBACK_URL="${BASE_URL}/agent/orquesta-agent.mjs" MANIFEST_URL="${BASE_URL}/agent/manifest.json" echo " ${DIM}Platform: ${PLATFORM}/${ARCH}${RESET}" # ── Install location ────────────────────────────────── INSTALL_DIR="/usr/local/bin" if [ -w "$INSTALL_DIR" ]; then SUDO="" elif command -v sudo >/dev/null 2>&1; then SUDO="sudo" else INSTALL_DIR="$HOME/.local/bin" mkdir -p "$INSTALL_DIR" SUDO="" fi TARGET="$INSTALL_DIR/orquesta-agent" # ── Download ────────────────────────────────────────── download() { if command -v curl >/dev/null 2>&1; then curl -fsSL -o "$1" "$2" elif command -v wget >/dev/null 2>&1; then wget -q -O "$1" "$2" else echo " ${RED}Neither curl nor wget found.${RESET}"; exit 1 fi } # Try standalone binary first (no Node.js needed) echo " Downloading standalone binary..." TMP_FILE="$(mktemp)" if download "$TMP_FILE" "$BINARY_URL" 2>/dev/null; then $SUDO mv "$TMP_FILE" "$TARGET" $SUDO chmod +x "$TARGET" MODE="binary" else # Fallback: download JS bundle (needs Node.js) echo " ${DIM}Binary not available for ${PLATFORM}/${ARCH}, trying JS bundle...${RESET}" if ! command -v node >/dev/null 2>&1; then echo " ${RED}Node.js required for JS fallback. Install Node.js 20+:${RESET}" echo " curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -" echo " sudo apt-get install -y nodejs" rm -f "$TMP_FILE" exit 1 fi BUNDLE_PATH="$INSTALL_DIR/orquesta-agent-bundle.mjs" download "$TMP_FILE" "$FALLBACK_URL" $SUDO mv "$TMP_FILE" "$BUNDLE_PATH" # Create wrapper script $SUDO tee "$TARGET" > /dev/null << 'WRAPPER' #!/bin/sh exec node "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")/orquesta-agent-bundle.mjs" "$@" WRAPPER $SUDO chmod +x "$TARGET" MODE="js-bundle" fi rm -f "$TMP_FILE" # ── Verify checksum ─────────────────────────────────── INSTALLED_VERSION="$("$TARGET" --version 2>/dev/null | tr -d '"' || echo "unknown")" # Download manifest and verify SHA-256 (sha256sum on Linux, shasum on macOS) EXPECTED_HASH="" SHA_CMD="" if command -v sha256sum >/dev/null 2>&1; then SHA_CMD="sha256sum" elif command -v shasum >/dev/null 2>&1; then SHA_CMD="shasum -a 256" fi if [ -n "$SHA_CMD" ]; then MANIFEST="$(download /dev/stdout "$MANIFEST_URL" 2>/dev/null || echo "")" if [ -n "$MANIFEST" ]; then if [ "$MODE" = "binary" ]; then # Extract sha256 from the per-platform block: "-": { ..., "sha256": "..." } EXPECTED_HASH="$(echo "$MANIFEST" | sed -n '/"'"${PLATFORM}-${ARCH}"'"/,/}/p' | grep -o '"sha256": *"[a-f0-9]*"' | head -1 | grep -o '[a-f0-9]\{64\}')" ACTUAL_HASH="$($SHA_CMD "$TARGET" | cut -d' ' -f1)" else EXPECTED_HASH="$(echo "$MANIFEST" | grep -A1 '"bundle"' | grep -o '"sha256": *"[a-f0-9]*"' | grep -o '[a-f0-9]\{64\}')" BUNDLE_FILE="$INSTALL_DIR/orquesta-agent-bundle.mjs" ACTUAL_HASH="$($SHA_CMD "$BUNDLE_FILE" | cut -d' ' -f1)" fi if [ -n "$EXPECTED_HASH" ] && [ "$ACTUAL_HASH" = "$EXPECTED_HASH" ]; then echo " ${GREEN}SHA-256 verified${RESET}" elif [ -n "$EXPECTED_HASH" ]; then echo " ${RED}SHA-256 mismatch! Expected: $EXPECTED_HASH${RESET}" echo " ${RED} Got: $ACTUAL_HASH${RESET}" echo " ${RED}The binary may have been tampered with.${RESET}" exit 1 fi fi fi echo "" if [ "$MODE" = "binary" ]; then echo " ${GREEN}${BOLD}orquesta-agent@$INSTALLED_VERSION installed (standalone binary)${RESET}" else echo " ${GREEN}${BOLD}orquesta-agent@$INSTALLED_VERSION installed (JS bundle + Node.js)${RESET}" fi echo " ${DIM}Location: $TARGET${RESET}" echo " ${DIM}Verify: curl -s $MANIFEST_URL | jq .${RESET}" # ── Service mode (--service) ────────────────────────── if [ "$SERVICE_MODE" = true ] && [ -n "$TOKEN" ]; then echo "" echo " Setting up systemd service..." WORK_DIR="$(pwd)" CURRENT_USER="$(whoami)" $SUDO tee /etc/systemd/system/orquesta-agent.service > /dev/null << SVCEOF [Unit] Description=Orquesta Agent After=network.target [Service] Type=simple User=$CURRENT_USER WorkingDirectory=$WORK_DIR ExecStart=$TARGET --token $TOKEN --daemon $MODE_FLAG Restart=always RestartSec=10 Environment=NODE_ENV=production Environment=PATH=/usr/local/bin:/usr/bin:/bin:$HOME/.local/bin [Install] WantedBy=multi-user.target SVCEOF $SUDO systemctl daemon-reload $SUDO systemctl enable orquesta-agent $SUDO systemctl start orquesta-agent sleep 2 if systemctl is-active --quiet orquesta-agent 2>/dev/null; then echo " ${GREEN}Service started and enabled on boot.${RESET}" else echo " ${RED}Service may have failed. Check: sudo journalctl -u orquesta-agent${RESET}" fi echo "" echo " ${BOLD}Manage:${RESET}" echo " sudo systemctl status orquesta-agent" echo " sudo systemctl restart orquesta-agent" echo " sudo journalctl -u orquesta-agent -f" echo "" exit 0 fi # ── Auto-connect mode (--token) ─────────────────────── if [ -n "$TOKEN" ]; then echo "" echo " Starting agent..." echo "" if [ -n "$CLI_MODE" ]; then exec "$TARGET" --token "$TOKEN" --daemon --mode "$CLI_MODE" else exec "$TARGET" --token "$TOKEN" --daemon fi fi # ── No token — show instructions ────────────────────── echo "" echo " ${BOLD}Quick start:${RESET}" echo " orquesta-agent --token " echo "" echo " ${BOLD}Run as daemon (auto-restart):${RESET}" echo " orquesta-agent --token --daemon" echo "" echo " ${BOLD}Install as system service:${RESET}" echo " curl -fsSL $BASE_URL/install | sh -s -- --service " echo "" echo " ${BOLD}Web UI:${RESET}" echo " orquesta-agent ui" echo "" echo " ${DIM}Get your token at $BASE_URL${RESET}" echo ""