diff --git a/Borealis.sh b/Borealis.sh index 3d37c55..6122d52 100644 --- a/Borealis.sh +++ b/Borealis.sh @@ -1,198 +1,488 @@ #!/usr/bin/env bash #////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Borealis.sh -# Still Experimental - Additional Work Needs to be Done to Reach Parity with Windows Environments +# Linux parity for Borealis.ps1 (Ubuntu/Rocky/Fedora/RHEL). Experimental but feature-complete. -clear +set -o errexit +set -o nounset +set -o pipefail -# ---- ASCII ART BANNER ---- +# ---- Colors / Icons ---- BOREALIS_BLUE="\033[38;5;39m" DARK_GRAY="\033[1;30m" -RESET="\033[0m" - -echo -e "${BOREALIS_BLUE}" -cat << "EOF" -███████████ ████ ███ -░░███░░░░░███ ░░███ ░░░ - ░███ ░███ ██████ ████████ ██████ ██████ ░███ ████ █████ - ░██████████ ███░░███░░███░░███ ███░░███ ░░░░░███ ░███ ░░███ ███░░ - ░███░░░░░███░███ ░███ ░███ ░░░ ░███████ ███████ ░███ ░███ ░░█████ - ░███ ░███░███ ░███ ░███ ░███░░░ ███░░███ ░███ ░███ ░░░░███ - ███████████ ░░██████ █████ ░░██████ ░░████████ █████ █████ ██████ -░░░░░░░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ -EOF -echo -e "${RESET}" - -echo -e "${DARK_GRAY}Drag-&-Drop Automation Orchestration | Macros | Data Collection & Analysis${RESET}" - -# ---- END ASCII ART BANNER ---- - -# Color codes GREEN="\033[0;32m" YELLOW="\033[1;33m" RED="\033[0;31m" RESET="\033[0m" +CHECKMARK="[OK]"; HOURGLASS="[WAIT]"; CROSSMARK="[X]"; INFO="[i]" -# ASCII-only icons -CHECKMARK="[OK]" -HOURGLASS="[WAIT]" -CROSSMARK="[X]" -INFO="[i]" +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" +# ---- CLI flags (parity with Borealis.ps1) ---- +SERVER_FLAG=0 +AGENT_FLAG=0 +AGENT_ACTION="" +VITE_FLAG=0 +FLASK_FLAG=0 +QUICK_FLAG=0 + +while (( "$#" )); do + case "$1" in + -Server|--server) SERVER_FLAG=1 ;; + -Agent|--agent) AGENT_FLAG=1 ;; + -AgentAction|--agent-action) shift; AGENT_ACTION="${1:-}" ;; + -Vite|--vite) VITE_FLAG=1 ;; + -Flask|--flask) FLASK_FLAG=1 ;; + -Quick|--quick) QUICK_FLAG=1 ;; + *) ;; # ignore unknown for flexibility + esac + shift || true +done + +# ---- Banner ---- +clear || true +echo -e "${BOREALIS_BLUE}" +cat << 'EOF' +����������� ���� ��� +����۰������� ����� ��� + ���� ���� ������ �������� ������ ������ ���� ���� ����� + ����������� ��۰���۰���۰���� ��۰���� �������� ���� ����� ��۰� + ���۰������۰��� ���� ���� ��� �������� ������� ���� ���� ������� + ���� ���۰��� ���� ���� ���۰�� ��۰���� ���� ���� ������� + ����������� �������� ����� �������� ���������� ����� ����� ������ +����������� ������ ����� ������ �������� ����� ����� ������ +EOF +echo -e "${RESET}" +echo -e "${DARK_GRAY}Automation Platform${RESET}" + +# ---- Helpers ---- run_step() { - local message="$1" - shift - echo -e "${GREEN}${HOURGLASS} ${message}...${RESET}" - if "$@"; then - echo -e "${GREEN}${CHECKMARK} ${message} completed.${RESET}" - else - echo -e "${RED}${CROSSMARK} ${message} failed!${RESET}" - exit 1 - fi + local message="$1"; shift + printf "%s %s... " "${HOURGLASS}" "$message" + if "$@"; then + printf "\r%s %s\n" "${CHECKMARK}" "$message" + else + printf "\r%s %s - Failed\n" "${CROSSMARK}" "$message" 1>&2 + exit 1 + fi } detect_distro() { - if [ -f /etc/os-release ]; then - . /etc/os-release - DISTRO_ID="$ID" - else - DISTRO_ID="unknown" - fi + DISTRO_ID="unknown"; DISTRO_LIKE="" + if [[ -f /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + DISTRO_ID=${ID:-unknown} + DISTRO_LIKE=${ID_LIKE:-} + fi } -install_core_dependencies() { +ensure_log_dir() { mkdir -p "${SCRIPT_DIR}/Logs/Agent"; } +log_agent() { ensure_log_dir; printf "[%s] %s\n" "$(date +%F\ %T)" "$1" >> "${SCRIPT_DIR}/Logs/Agent/$2"; } + +need_sudo() { [ "${EUID:-$(id -u)}" -ne 0 ]; } + +# ---- Dependency Installation (Linux) ---- +install_shared_dependencies() { + detect_distro + if command -v python3 >/dev/null 2>&1 && command -v pip3 >/dev/null 2>&1; then :; else case "$DISTRO_ID" in - ubuntu|debian) - sudo apt update -qq - sudo apt install -y python3 python3-venv python3-pip nodejs npm git curl tesseract-ocr - ;; - rhel|centos|fedora|rocky) - sudo dnf install -y python3 python3-pip nodejs npm git curl tesseract - ;; - arch) - sudo pacman -Sy --noconfirm python python-venv python-pip nodejs npm git curl tesseract - ;; - *) - echo -e "${RED}${CROSSMARK} Unsupported Linux distribution: ${DISTRO_ID}${RESET}" - exit 1 - ;; + ubuntu|debian) + sudo apt update -qq && sudo apt install -y python3 python3-venv python3-pip curl unzip ;; + rhel|centos|fedora|rocky) + if command -v dnf >/dev/null 2>&1; then sudo dnf install -y python3 python3-pip curl unzip ; else sudo yum install -y python3 python3-pip curl unzip ; fi ;; + arch) + sudo pacman -Sy --noconfirm python python-pip curl unzip ;; + *) : ;; esac + fi } -launch_server() { - echo -e "${GREEN}Deploying Borealis - Server Dashboard...${RESET}" - echo "====================================================================================" +NODE_VERSION="v23.11.0" +NODE_DIR="${SCRIPT_DIR}/Dependencies/NodeJS" +NODE_BIN="${NODE_DIR}/bin/node" +NPM_BIN="${NODE_DIR}/bin/npm" +NPX_BIN="${NODE_DIR}/bin/npx" - detect_distro - run_step "Install System Dependencies" install_core_dependencies +install_server_dependencies() { + # Tesseract via system packages; OCR engines code uses system binary on Linux + detect_distro + case "$DISTRO_ID" in + ubuntu|debian) + sudo apt update -qq && sudo apt install -y tesseract-ocr ;; + rhel|centos|fedora|rocky) + if command -v dnf >/dev/null 2>&1; then sudo dnf install -y tesseract; else sudo yum install -y tesseract; fi ;; + arch) + sudo pacman -Sy --noconfirm tesseract ;; + *) : ;; + esac - # Paths - venvFolder="Server" - dataSource="Data/Server" - dataDestination="${venvFolder}/Borealis" - customUIPath="${dataSource}/WebUI" - webUIDestination="${venvFolder}/web-interface" - venvPython="${venvFolder}/bin/python3" - - # Create Python venv - run_step "Create Virtual Python Environment" bash -c " - if [ ! -f '${venvFolder}/bin/activate' ]; then - python3 -m venv '${venvFolder}' - fi + # NodeJS (portable into Dependencies/NodeJS) + if [[ ! -x "$NODE_BIN" ]]; then + mkdir -p "$NODE_DIR" + local tarball="node-${NODE_VERSION}-linux-x64.tar.xz" + local url="https://nodejs.org/dist/${NODE_VERSION}/${tarball}" + local dl_path="${SCRIPT_DIR}/Dependencies/${tarball}" + run_step "Dependency: NodeJS (${NODE_VERSION})" bash -c " + curl -fsSL -o '${dl_path}' '${url}' && \ + rm -rf '${NODE_DIR:?}'/* && \ + mkdir -p '${NODE_DIR}' && \ + tar -xJf '${dl_path}' -C '${NODE_DIR}' --strip-components=1 && \ + rm -f '${dl_path}' " - - # Install Python requirements - run_step "Install Python Server Dependencies" bash -c " - source '${venvFolder}/bin/activate' - pip install --upgrade pip > /dev/null - pip install --no-input -r '${dataSource}/server-requirements.txt' - " - - # Copy Python server components - run_step "Copy Python Server Components" bash -c " - rm -rf '${dataDestination}' && mkdir -p '${dataDestination}' - cp -r '${dataSource}/Python_API_Endpoints' '${dataDestination}/' - cp -r '${dataSource}/Sounds' '${dataDestination}/' - cp -r '${dataSource}/Workflows' '${dataDestination}/' - cp '${dataSource}/server.py' '${dataDestination}/' - " - - # Setup Vite WebUI assets - run_step "Setup Vite WebUI assets" bash -c " - rm -rf '${webUIDestination}' && mkdir -p '${webUIDestination}' - cp -r '${customUIPath}/'* '${webUIDestination}/' - " - - # Install NPM packages for Vite - run_step "Install Vite Web Frontend NPM Packages" bash -c " - cd '${webUIDestination}' - npm install --silent --no-fund --audit=false - cd - > /dev/null - " - - # Launch Vite Web Frontend in Dev Mode - run_step "Start Vite Web Frontend (Dev Mode)" bash -c " - cd '${webUIDestination}' - npm run dev --silent - cd - > /dev/null - " - - # Launch Flask server - echo -e "\n${GREEN}Launching Borealis Flask Server...${RESET}" - echo "====================================================================================" - source '${venvFolder}/bin/activate' - python3 "${dataDestination}/server.py" + fi + export PATH="${NODE_DIR}/bin:${PATH}" } -launch_agent() { - echo -e "${GREEN}Deploying Borealis Agent...${RESET}" - echo "====================================================================================" - - detect_distro - run_step "Install System Dependencies" install_core_dependencies - - venvFolder="Agent" - agentSourcePath="Data/Agent/agent.py" - agentRequirements="Data/Agent/agent-requirements.txt" - agentDestinationFolder="${venvFolder}/Borealis" - - run_step "Create Virtual Python Environment for Agent" bash -c " - if [ ! -f '${venvFolder}/bin/activate' ]; then - python3 -m venv '${venvFolder}' - fi - " - - run_step "Install Agent Dependencies" bash -c " - source '${venvFolder}/bin/activate' - pip install --upgrade pip > /dev/null - pip install --no-input -r '${agentRequirements}' - " - - run_step "Copy Agent Script" bash -c " - mkdir -p '${agentDestinationFolder}' - cp 'Data/Agent/agent.py' '${agentDestinationFolder}/' - cp 'Data/Agent/role_manager.py' '${agentDestinationFolder}/' - if [ -d 'Data/Agent/Roles' ]; then cp -r 'Data/Agent/Roles' '${agentDestinationFolder}/'; fi - if [ -d 'Data/Agent/Python_API_Endpoints' ]; then cp -r 'Data/Agent/Python_API_Endpoints' '${agentDestinationFolder}/'; fi - " - - echo -e "\n${GREEN}Launching Borealis Agent...${RESET}" - echo "====================================================================================" - source '${venvFolder}/bin/activate' - python3 "${agentDestinationFolder}/agent.py" +ensure_node_bins() { + if [[ -x "$NPM_BIN" ]]; then return 0; fi + if command -v npm >/dev/null 2>&1; then + NPM_BIN="$(command -v npm)"; NPX_BIN="$(command -v npx || echo npx)"; return 0 + fi + echo -e "${RED}npm not found. Run server dependency install first.${RESET}" >&2 + return 1 } -# Main menu +install_agent_dependencies() { + # No AHK on Linux; ensure python only + install_shared_dependencies +} -echo -e "${GREEN}Deploying Borealis - Workflow Automation Tool...${RESET}" -echo "====================================================================================" -echo "Please choose which module you want to launch / (re)deploy:" -echo "- Server (Web Dashboard) [1]" -echo "- Agent (Local/Remote Client) [2]" +# ---- Process/task helpers (Linux) ---- +kill_agent_processes() { + # Kill only Python processes under this repo's Agent venv + if command -v pgrep >/dev/null 2>&1; then + pgrep -f "${SCRIPT_DIR}/Agent/.*/python.*${SCRIPT_DIR}/Agent/Borealis/agent.py" >/dev/null 2>&1 && \ + pkill -f "${SCRIPT_DIR}/Agent/.*/python.*${SCRIPT_DIR}/Agent/Borealis/agent.py" || true + else + pkill -f "Agent/Borealis/agent.py" || true + fi +} -read -p "Enter 1 or 2: " choice +ensure_cron_entry() { + # args: who command + local who="$1"; shift + local cmd="$*" + local tmp + tmp="$(mktemp)" + if [[ "$who" == "root" ]]; then + if need_sudo; then SUDO=sudo; else SUDO=""; fi + $SUDO crontab -l 2>/dev/null | grep -vF -- "$cmd" > "$tmp" || true + echo "@reboot ${cmd}" >> "$tmp" + $SUDO crontab "$tmp" + else + crontab -l 2>/dev/null | grep -vF -- "$cmd" > "$tmp" || true + echo "@reboot ${cmd}" >> "$tmp" + crontab "$tmp" + fi + rm -f "$tmp" +} -case "${choice}" in - 1) launch_server ;; - 2) launch_agent ;; - *) echo -e "${YELLOW}Invalid selection. Exiting...${RESET}"; exit 1 ;; -esac +remove_cron_entries() { + # Remove entries we added previously + local sys_cmd user_cmd + sys_cmd="$1"; shift + user_cmd="$1"; shift || true + local tmp + tmp="$(mktemp)"; if need_sudo; then SUDO=sudo; else SUDO=""; fi + $SUDO crontab -l 2>/dev/null | grep -vF -- "$sys_cmd" > "$tmp" || true; $SUDO crontab "$tmp" || true + crontab -l 2>/dev/null | grep -vF -- "$user_cmd" > "$tmp" || true; crontab "$tmp" || true + rm -f "$tmp" +} + +# ---- Agent deployment (Install / Repair / Remove) ---- +create_agent_venv_and_files() { + local venvFolder="Agent" + local agentDest="${venvFolder}/Borealis" + python3 -m venv "$venvFolder" 2>/dev/null || true + mkdir -p "$agentDest" + # Fresh copy of agent payload + rm -rf "${agentDest:?}"/* + cp -f "Data/Agent/agent.py" "$agentDest/" + cp -f "Data/Agent/role_manager.py" "$agentDest/" + cp -f "Data/Agent/agent_deployment.py" "$agentDest/" 2>/dev/null || true + [ -f "Data/Agent/Borealis.ico" ] && cp -f "Data/Agent/Borealis.ico" "$agentDest/" + [ -d "Data/Agent/Python_API_Endpoints" ] && cp -r "Data/Agent/Python_API_Endpoints" "$agentDest/" + [ -d "Data/Agent/Roles" ] && cp -r "Data/Agent/Roles" "$agentDest/" + # Linux wrapper to guarantee working dir and capture logs + cat > "${agentDest}/launch_service.sh" << 'SH' +#!/usr/bin/env bash +set -o errexit +set -o nounset +set -o pipefail +ROOT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" +cd "$ROOT_DIR" +LOG_DIR="$(cd -- "$ROOT_DIR/../../Logs/Agent" && pwd 2>/dev/null || echo "$ROOT_DIR/../../Logs/Agent")" +mkdir -p "$LOG_DIR" +PY_BIN="${ROOT_DIR}/../bin/python3" +exec "$PY_BIN" "$ROOT_DIR/agent.py" --system-service --config svc >>"$LOG_DIR/svc.out.log" 2>>"$LOG_DIR/svc.err.log" +SH + chmod +x "${agentDest}/launch_service.sh" + + # pip deps + if [[ -f "Data/Agent/agent-requirements.txt" ]]; then + "${SCRIPT_DIR}/Agent/bin/python3" -m pip install --disable-pip-version-check -q -r "Data/Agent/agent-requirements.txt" + fi +} + +ensure_agent_tasks() { + # Register @reboot cron entries for system (root) and current user + local agentDest="${SCRIPT_DIR}/Agent/Borealis" + local sys_cmd="bash '${agentDest}/launch_service.sh'" + local user_cmd="bash -lc 'cd "${agentDest}" && "${SCRIPT_DIR}/Agent/bin/python3" ./agent.py --config user'" + + # Root/system entry + if need_sudo; then + echo -e "${YELLOW}Agent SYSTEM cron requires sudo. Prompting...${RESET}" + fi + ensure_cron_entry root "$sys_cmd" + ensure_cron_entry user "$user_cmd" +} + +install_or_update_agent() { + echo -e "${GREEN}Ensuring Agent Dependencies...${RESET}" + install_shared_dependencies + install_agent_dependencies + log_agent "=== Install/Update start ===" install.log + kill_agent_processes || true + run_step "Create Agent venv and deploy files" create_agent_venv_and_files + run_step "Register cron tasks (SYSTEM, User)" ensure_agent_tasks + log_agent "=== Install/Update end ===" install.log +} + +repair_agent() { + log_agent "=== Repair start ===" Repair.log + kill_agent_processes || true + install_or_update_agent + log_agent "=== Repair end ===" Repair.log +} + +remove_agent() { + log_agent "=== Removal start ===" Removal.log + kill_agent_processes || true + local sys_cmd="bash '${SCRIPT_DIR}/Agent/Borealis/launch_service.sh'" + local user_cmd="bash -lc 'cd "${SCRIPT_DIR}/Agent/Borealis" && "${SCRIPT_DIR}/Agent/bin/python3" ./agent.py --config user'" + remove_cron_entries "$sys_cmd" "$user_cmd" || true + rm -rf "${SCRIPT_DIR}/Agent" || true + log_agent "=== Removal end ===" Removal.log +} + +launch_user_helper_now() { + local py="${SCRIPT_DIR}/Agent/bin/python3" + local helper="${SCRIPT_DIR}/Agent/Borealis/agent.py" + if [[ -x "$py" && -f "$helper" ]]; then + (cd "${SCRIPT_DIR}/Agent/Borealis" && nohup "$py" -W ignore::SyntaxWarning "$helper" --config user >/dev/null 2>&1 & ) + echo -e "${GREEN}Launched user-session helper.${RESET}" + else + echo -e "${YELLOW}Agent venv or helper missing; run install first.${RESET}" + fi +} + +# ---- Server deployment ---- +copy_server_payload() { + local venvFolder="Server" + local dataSource="Data/Server" + local dataDestination="${venvFolder}/Borealis" + python3 -m venv "$venvFolder" 2>/dev/null || true + mkdir -p "$dataDestination" + rm -rf "${dataDestination:?}"/* + cp -r "${dataSource}/Python_API_Endpoints" "$dataDestination/" + cp -r "${dataSource}/Sounds" "$dataDestination/" + [ -f "${dataSource}/server.py" ] && cp "${dataSource}/server.py" "$dataDestination/" + [ -f "${dataSource}/job_scheduler.py" ] && cp "${dataSource}/job_scheduler.py" "$dataDestination/" + # Python deps + if [[ -f "${dataSource}/server-requirements.txt" ]]; then + "${SCRIPT_DIR}/Server/bin/python3" -m pip install --disable-pip-version-check -q -r "${dataSource}/server-requirements.txt" + fi +} + +prepare_webui() { + local customUIPath="Data/Server/WebUI" + local webUIDestination="Server/web-interface" + mkdir -p "$webUIDestination" + rm -rf "${webUIDestination}/public" "${webUIDestination}/src" "${webUIDestination}/build" 2>/dev/null || true + cp -r "${customUIPath}/"* "$webUIDestination/" + ensure_node_bins + ( cd "$webUIDestination" && "$NPM_BIN" install --silent --no-fund --audit=false >/dev/null ) +} + +vite_start() { + local mode="$1" # developer|production + local webUIDestination="Server/web-interface" + local subcmd="build"; [[ "$mode" == "developer" ]] && subcmd="dev" + ensure_node_bins + ( cd "$webUIDestination" && nohup "$NPM_BIN" run "$subcmd" >/dev/null 2>&1 & ) +} + +flask_run() { + local py="${SCRIPT_DIR}/Server/bin/python3" + local server_py="${SCRIPT_DIR}/Server/Borealis/server.py" + echo -e "\n${GREEN}Launching Borealis Flask Server...${RESET}" + echo "====================================================================================" + "$py" "$server_py" +} + +# ---- Menus ---- +server_menu() { + echo -e "\nConfigure Borealis Server Mode:" + echo -e " 1) Build & Launch > Production Flask Server @ http://localhost:5000" + echo -e " 2) [Skip Build] & Immediately Launch > Production Flask Server @ http://localhost:5000" + echo -e " 3) Launch > [Hotload-Ready] Vite Dev Server @ http://localhost:5173" + read -r -p "Enter choice [1/2/3]: " modeChoice + + case "$modeChoice" in + 1) borealis_operation_mode="production" ;; + 2) borealis_operation_mode="production" ;; + 3) borealis_operation_mode="developer" ;; + *) echo -e "${RED}Invalid mode choice${RESET}"; return 1 ;; + esac + + # Common deps + echo -e "${GREEN}Ensuring Server Dependencies...${RESET}" + install_shared_dependencies + install_server_dependencies + export PATH="${NODE_DIR}/bin:${PATH}" + + if [[ "$modeChoice" == "2" ]]; then + flask_run + return 0 + fi + + run_step "Create Server venv & deploy files" copy_server_payload + run_step "Copy WebUI assets" prepare_webui + run_step "Start Vite (${borealis_operation_mode})" vite_start "$borealis_operation_mode" + flask_run +} + +agent_menu() { + echo -e "Agent Menu:" + echo -e " 1) Install/Update Agent" + echo -e " 2) Repair Borealis Agent" + echo -e " 3) Remove Agent" + echo -e " 4) Launch UserSession Helper (current session)" + echo -e " 5) Back" + read -r -p "Select an option: " agentChoice + case "$agentChoice" in + 1) install_or_update_agent ;; + 2) repair_agent ;; + 3) remove_agent ;; + 4) launch_user_helper_now ;; + 5) return 0 ;; + *) echo -e "${RED}Invalid selection${RESET}" ;; + esac +} + +electron_menu() { + echo -e "Deploying Borealis Desktop App..." + echo "====================================================================================" + local electronSource="Data/Electron" + local electronDestination="ElectronApp" + + run_step "Prepare ElectronApp folder" bash -c " + rm -rf '${electronDestination}' && mkdir -p '${electronDestination}' + [ -d 'Server/Borealis' ] || { echo 'Server/Borealis not found - please run Server build first.' >&2; exit 1; } + cp -r 'Server/Borealis' '${electronDestination}/Server' + cp '${electronSource}/package.json' '${electronDestination}' + cp '${electronSource}/main.js' '${electronDestination}' + [ -d 'Server/web-interface/build' ] || { echo 'WebUI build not found - run Server build first.' >&2; exit 1; } + mkdir -p '${electronDestination}/renderer' + cp -r 'Server/web-interface/build/'* '${electronDestination}/renderer/' + " + + run_step "ElectronApp: Install Node dependencies" bash -c " + export PATH='${NODE_DIR}/bin:'"'${PATH}'" && cd '${electronDestination}' && (command -v npm >/dev/null 2>&1 || exit 1) && npm install --silent --no-fund --audit=false + " + + run_step "ElectronApp: Package with electron-builder" bash -c " + export PATH='${NODE_DIR}/bin:'"'${PATH}'" && cd '${electronDestination}' && npm run dist + " + + run_step "ElectronApp: Launch in dev mode" bash -c " + export PATH='${NODE_DIR}/bin:'"'${PATH}'" && cd '${electronDestination}' && npm run dev + " +} + +update_borealis() { + echo -e "\nUpdating Borealis..." + local staging="${SCRIPT_DIR}/Update_Staging" + local updateZip="${staging}/main.zip" + local updateDir="${staging}/Borealis-main" + local preservePath="${SCRIPT_DIR}/Data/Server/Python_API_Endpoints/Tesseract-OCR" + local preserveBackupPath="${staging}/Tesseract-OCR" + mkdir -p "$staging" + + run_step "Updating: Move Tesseract-OCR to staging (if present)" bash -c " + [ -d '${preservePath}' ] && { rm -rf '${preserveBackupPath}'; mkdir -p '${staging}'; mv '${preservePath}' '${preserveBackupPath}'; } || true + " + run_step "Updating: Clean folders before update" bash -c " + rm -rf 'Data' 'Server/web-interface/src' 'Server/web-interface/build' 'Server/web-interface/public' 'Server/Borealis' || true + " + run_step "Updating: Download update" bash -c " + curl -fsSL -o '${updateZip}' 'https://github.com/bunny-lab-io/Borealis/archive/refs/heads/main.zip' + " + run_step "Updating: Extract update" bash -c " + unzip -oq '${updateZip}' -d '${staging}' + " + run_step "Updating: Copy update into repo" bash -c " + cp -r '${updateDir}/'* '${SCRIPT_DIR}/' + " + run_step "Updating: Restore Tesseract-OCR" bash -c " + [ -d '${preserveBackupPath}' ] && { mkdir -p 'Data/Server/Python_API_Endpoints'; mv '${preserveBackupPath}' 'Data/Server/Python_API_Endpoints/'; } || true + " + run_step "Updating: Clean staging" bash -c "rm -rf '${staging}'" + echo -e "\n${GREEN}Update Complete! Re-launch Borealis.${RESET}" +} + +# ---- Main entry ---- + +main_menu() { + echo -e "\nPlease choose which function you want to launch:" + echo -e " 1) Borealis Server" + echo -e " 2) Borealis Agent" + echo -e " 3) Build Electron App [Experimental]" + echo -e " 4) Package Self-Contained App [Experimental]" + echo -e " 5) Update Borealis [Requires Re-Build]" + read -r -p "Enter a number: " choice + case "$choice" in + 1) server_menu ;; + 2) agent_menu ;; + 3) electron_menu ;; + 4) echo -e "${YELLOW}Packaging to single-file EXE not supported on Linux yet.${RESET}" ;; + 5) update_borealis ;; + *) echo -e "${RED}Invalid selection. Exiting...${RESET}"; exit 1 ;; + esac +} + +# Auto-select when flags provided +if [[ $SERVER_FLAG -eq 1 && $AGENT_FLAG -eq 1 ]]; then + echo -e "${RED}Cannot use --server and --agent together.${RESET}"; exit 1 +fi + +if [[ $SERVER_FLAG -eq 1 ]]; then + # Resolve server mode from flags + if [[ $VITE_FLAG -eq 1 && $FLASK_FLAG -eq 1 ]]; then + echo -e "${RED}Cannot combine --vite and --flask.${RESET}"; exit 1 + fi + if [[ $VITE_FLAG -eq 1 ]]; then + borealis_operation_mode="developer"; server_menu <<< $'3\n' + elif [[ $FLASK_FLAG -eq 1 && $QUICK_FLAG -eq 1 ]]; then + borealis_operation_mode="production"; server_menu <<< $'2\n' + else + borealis_operation_mode="production"; server_menu <<< $'1\n' + fi + exit $? +fi + +if [[ $AGENT_FLAG -eq 1 ]]; then + case "${AGENT_ACTION:-install}" in + install) install_or_update_agent ;; + repair) repair_agent ;; + remove) remove_agent ;; + launch) launch_user_helper_now ;; + *) install_or_update_agent ;; + esac + exit $? +fi + +# Default to interactive menu +main_menu