#!/usr/bin/env bash set -euo pipefail # GyanDoot v2.0 — Cross-platform installer # Detects OS, architecture, and installs the gyandoot binary + camofox-browser. # For full documentation, visit: https://git.gyannetra.in/gnpl_admin/gyandoot-v2 GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' info() { echo -e "${BLUE}[INFO]${NC} $*"; } ok() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } err() { echo -e "${RED}[ERR]${NC} $*" >&2; exit 1; } detect_architecture() { local arch arch="$(uname -m)" case "$arch" in x86_64|amd64) echo "x86_64" ;; aarch64|arm64) echo "aarch64" ;; *) err "Unsupported architecture: $arch" ;; esac } detect_os() { local os os="$(uname -s)" case "$os" in Linux) echo "linux" ;; Darwin) echo "darwin" ;; CYGWIN*|MINGW*|MSYS*) echo "windows" ;; *) err "Unsupported OS: $os" ;; esac } # ── WSL Detection ────────────────────────────────────────────────────────── # Detect if we're running inside Windows Subsystem for Linux. # /proc/version contains "microsoft" or "wsl" (case-insensitive) on WSL. detect_wsl() { if [ -f /proc/version ] && grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then return 0 # true — we are in WSL fi return 1 } # ── Linux System Dependencies ────────────────────────────────────────────── # Camoufox (Playwright-based) needs ~30 system libraries on Linux/WSL. # Without these, the browser daemon crashes silently with # "Failed to connect: No such file or directory (os error 2)". # See: https://playwright.dev/docs/troubleshooting#system-dependencies CAMOUFOX_LINUX_DEPS=( libgtk-3-0 libnss3 libnspr4 libatk1.0-0t64 libatk-bridge2.0-0t64 libcups2t64 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2t64 libxcb-shm0 libx11-xcb1 libx11-6 libxcb1 libxext6 libxcursor1 libxi6 libfreetype6 libfontconfig1 libxshmfence1 libxrender1 libdbus-1-3 ) install_rust_if_needed() { if ! command -v cargo &>/dev/null; then info "Rust toolchain not found. Installing..." curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y source "$HOME/.cargo/env" ok "Rust installed" else ok "Rust already installed ($(cargo --version))" fi } install_node_if_needed() { if ! command -v node &>/dev/null; then info "Node.js not found. Installing via nvm..." curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash [ -s "$HOME/.nvm/nvm.sh" ] && \. "$HOME/.nvm/nvm.sh" nvm install 22 ok "Node.js installed ($(node --version))" else ok "Node.js already installed ($(node --version))" fi } # ── Linux System Deps Installer ──────────────────────────────────────────── install_linux_deps() { if [ "$(uname -s)" != "Linux" ]; then return 0 # Not Linux, skip fi # Check if we're in WSL (Windows Subsystem for Linux) if detect_wsl; then info "WSL detected. Checking Linux system dependencies..." fi # Check which packages are already installed local missing=() if command -v dpkg-query &>/dev/null; then for pkg in "${CAMOUFOX_LINUX_DEPS[@]}"; do if ! dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -q "install ok installed"; then # Handle libasound2 vs libasound2t64 and similar t64 variants # Try without 't64' suffix as fallback local alt_pkg="${pkg%t64}" if [ "$alt_pkg" != "$pkg" ] && dpkg-query -W -f='${Status}' "$alt_pkg" 2>/dev/null | grep -q "install ok installed"; then continue # alt version is already installed fi missing+=("$pkg") fi done else # Can't check, just try to install all missing=("${CAMOUFOX_LINUX_DEPS[@]}") fi if [ ${#missing[@]} -eq 0 ]; then info "All Linux system dependencies already installed" return 0 fi info "Installing ${#missing[@]} missing Linux system dependencies..." if command -v apt-get &>/dev/null; then if command -v sudo &>/dev/null; then sudo apt-get update -qq 2>/dev/null || true sudo apt-get install -y -qq "${missing[@]}" 2>/dev/null || { # If some packages failed, retry with auto-detected alternatives warn "Some packages had issues. Retrying with apt --fix..." sudo apt-get --fix-broken install -y -qq 2>/dev/null || true # Try installing without t64 variants as fallback local fallback=() for pkg in "${missing[@]}"; do local alt="${pkg%t64}" if [ "$alt" != "$pkg" ]; then fallback+=("$alt") fi done if [ ${#fallback[@]} -gt 0 ]; then sudo apt-get install -y -qq "${fallback[@]}" 2>/dev/null || true fi } else apt-get update -qq 2>/dev/null || true apt-get install -y -qq "${missing[@]}" 2>/dev/null || true fi ok "Linux system dependencies installed" elif command -v dnf &>/dev/null; then info "Detected dnf package manager" sudo dnf install -y nss nspr atk at-spi2-atk cups-libs libdrm libXcomposite \ libXdamage libXrandr mesa-libgbm pango alsa-lib libxkbcommon libxcb \ libX11-xcb libX11 libXext libXcursor libXfixes libXi gtk3 cairo-gobject 2>/dev/null || true ok "Linux system dependencies installed" elif command -v yum &>/dev/null; then info "Detected yum package manager" sudo yum install -y nss nspr atk at-spi2-atk cups-libs libdrm libXcomposite \ libXdamage libXrandr mesa-libgbm pango alsa-lib libxkbcommon 2>/dev/null || true ok "Linux system dependencies installed" else warn "No supported package manager (apt-get/dnf/yum). You may need to install system dependencies manually." warn "Required packages: ${CAMOUFOX_LINUX_DEPS[*]}" fi } install_camofox_browser() { info "Installing camofox-browser (anti-detection browser)..." # Step 1: Install Linux system dependencies (needed by Playwright/Camoufox) install_linux_deps # Step 2: Install the npm package if command -v npx &>/dev/null || command -v npm &>/dev/null; then info "Installing npm package..." npm install -g camoufox-browser@0.1.0 2>&1 | tail -3 info "Downloading Camoufox browser binary (this may take a while, ~662MB)..." info "Running: npx --yes camoufox-js@0.11.0 fetch" npx --yes camoufox-js@0.11.0 fetch 2>&1 # Verify the browser binary was actually downloaded # macOS: ~/Library/Caches/camoufox/ | Linux: ~/.cache/camoufox/ local cache_dir if [ "$(uname -s)" = "Darwin" ]; then cache_dir="$HOME/Library/Caches/camoufox" else cache_dir="$HOME/.cache/camoufox" fi if [ -f "$cache_dir/camoufox" ]; then ok "Camoufox browser binary downloaded ($cache_dir/)" elif [ -f "$cache_dir/version.json" ]; then ok "Camoufox browser cache found at $cache_dir/ (binary downloaded)" else warn "Browser binary not found at $cache_dir/. May need manual 'npx --yes camoufox-js@0.11.0 fetch'" fi ok "Camoufox browser npm package installed" elif command -v docker &>/dev/null; then info "Installing via Docker..." docker run -d --name camoufox -p 9377:9377 \ -e CAMOUFOX_WS_PORT=9377 \ --restart unless-stopped \ ghcr.io/nousresearch/camoufox-browser:latest 2>&1 | tail -3 ok "Camoufox browser installed (Docker)" else warn "Neither npm nor docker found. Install manually: npm install -g camoufox-browser@0.1.0" fi } build_release() { local root_dir root_dir="$(cd "$(dirname "$0")" && pwd)" info "Building GyanDoot v2.0 in release mode..." cd "$root_dir" if [ ! -f "$root_dir/Cargo.toml" ]; then local probe probe="$(pwd)" while [ "$probe" != "/" ]; do if [ -f "$probe/Cargo.toml" ] && grep -q '\[workspace\]' "$probe/Cargo.toml" 2>/dev/null; then root_dir="$probe" cd "$root_dir" info "Found workspace root at $root_dir" break fi probe="$(dirname "$probe")" done fi cargo build --release ok "Build complete ($(du -sh target/release/gyandoot | cut -f1))" } install_binary() { local root_dir root_dir="$(cd "$(dirname "$0")" && pwd)" local target_dir="$root_dir/target/release" local bin_name="gyandoot" if [ ! -f "$target_dir/$bin_name" ]; then err "Binary not found at $target_dir/$bin_name" fi # ── Detect best install location per platform ───────────────────── # Priority: # 1. ~/.cargo/bin/ — always in PATH for Rust users, no sudo # 2. /usr/local/bin/ — standard Unix location, may need sudo # 3. Windows: use a standard PATH location or just warn # local os os="$(uname -s)" local install_dir="" local installed_to="" # Detect WSL (Windows Subsystem for Linux) local is_wsl=false if [ -f /proc/version ] && grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then is_wsl=true fi case "$os" in Darwin|Linux) # Option A: ~/.cargo/bin/ (no sudo needed, always in PATH for Rust users) if [ -d "$HOME/.cargo/bin" ] && echo "$PATH" | grep -q "$HOME/.cargo/bin"; then install_dir="$HOME/.cargo/bin" cp "$target_dir/$bin_name" "$install_dir/$bin_name" chmod +x "$install_dir/$bin_name" installed_to="$install_dir/$bin_name" # Option B: /usr/local/bin/ (standard, may need sudo) elif [ -d "/usr/local/bin" ]; then if [ -w "/usr/local/bin" ]; then install_dir="/usr/local/bin" cp "$target_dir/$bin_name" "$install_dir/$bin_name" chmod +x "$install_dir/$bin_name" else info "Need sudo to install to /usr/local/bin" sudo cp "$target_dir/$bin_name" "/usr/local/bin/$bin_name" sudo chmod +x "/usr/local/bin/$bin_name" install_dir="/usr/local/bin" fi installed_to="$install_dir/$bin_name" # Option C: ~/.local/bin/ (XDG, common on modern Linux) elif [ -d "$HOME/.local/bin" ] && echo "$PATH" | grep -q "$HOME/.local/bin"; then install_dir="$HOME/.local/bin" cp "$target_dir/$bin_name" "$install_dir/$bin_name" chmod +x "$install_dir/$bin_name" installed_to="$install_dir/$bin_name" else # Fallback: just copy to ~/.cargo/bin and create it mkdir -p "$HOME/.cargo/bin" cp "$target_dir/$bin_name" "$HOME/.cargo/bin/$bin_name" chmod +x "$HOME/.cargo/bin/$bin_name" install_dir="$HOME/.cargo/bin" installed_to="$install_dir/$bin_name" if ! echo "$PATH" | grep -q "$HOME/.cargo/bin"; then warn "$HOME/.cargo/bin is not in your PATH." warn "Add this to your shell profile (~/.zshrc, ~/.bashrc, etc.):" echo " export PATH=\"\$HOME/.cargo/bin:\$PATH\"" fi fi ;; CYGWIN*|MINGW*|MSYS*) # Windows / Git Bash / MSYS2 install_dir="/usr/local/bin" cp "$target_dir/$bin_name" "$install_dir/$bin_name" chmod +x "$install_dir/$bin_name" installed_to="$install_dir/$bin_name" warn "Windows detected. Make sure '$install_dir' is in your PATH." ;; *) err "Unsupported OS: $os" ;; esac ok "Installed to $installed_to" # ── Verify it's usable via PATH ────────────────────────────────── local cmd_path cmd_path="$(command -v gyandoot 2>/dev/null || true)" if [ -z "$cmd_path" ]; then warn "'gyandoot' not found in PATH after install." warn "The install directory may not be in your PATH." warn "Try running with: $installed_to" if [ -n "$install_dir" ]; then case "$os" in Darwin) echo "" echo " Add to your ~/.zshrc:" echo " export PATH=\"$install_dir:\$PATH\"" echo " Then: source ~/.zshrc" ;; Linux) echo "" echo " Add to your ~/.bashrc:" echo " export PATH=\"$install_dir:\$PATH\"" echo " Then: source ~/.bashrc" ;; esac fi elif [ "$cmd_path" != "$installed_to" ]; then warn "There's ANOTHER 'gyandoot' at '$cmd_path' that takes priority over '$installed_to'." warn "That binary is from a PREVIOUS installation and may be outdated." warn "" warn " ▶ To use the NEW version, run: $installed_to" warn " ▶ Or remove the old one: sudo rm $cmd_path" warn " ▶ Then 'gyandoot run' will pick up the new one." else ok "PATH resolves to the newly installed binary." fi } print_post_install() { echo "" ok "GyanDoot v2.0 installed successfully!" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " Quick Start:" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " gyandoot --help Show all commands" echo " gyandoot init Create config" echo " gyandoot setup Interactive setup wizard" echo " gyandoot run Start interactive agent" echo "" echo " Browser Tools (anti-detection browsing):" echo " gyandoot browser health Check camofox status" echo " gyandoot browser search " echo " gyandoot browser browse " echo "" echo " Service Management:" echo " gyandoot service install Auto-start on boot" echo " gyandoot service remove Remove system service" echo " gyandoot service status Check service status" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if detect_wsl; then echo " ⚠️ WSL detected — Node.js must be installed INSIDE WSL." echo " (The Windows-side Node.js is not accessible from WSL.)" echo "" fi } prompt_service_install() { echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " Optional: Auto-start GyanDoot on Boot?" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " GyanDoot can run as a system service and" echo " start automatically when your computer boots." echo " This keeps the gateway server running in the background." echo "" echo " (You can also do this later with: gyandoot service install)" echo "" read -r -p " Install system service? [y/N]: " response } main() { local os arch os=$(detect_os) arch=$(detect_architecture) info "Detected: $os / $arch" if detect_wsl; then info "WSL (Windows Subsystem for Linux) detected — will install Linux dependencies" fi install_rust_if_needed install_node_if_needed install_camofox_browser build_release install_binary # Optional: install GyanDoot system service (auto-start on boot) prompt_service_install case "$response" in [yY]|[yY][eE][sS]) info "Installing GyanDoot system service..." gyandoot service install 2>&1 || warn "Service install failed (run 'gyandoot service install' later)" ;; *) info "Skipping service install (use 'gyandoot service install' later)" ;; esac print_post_install } main "$@"