mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-12-16 02:05:48 -07:00
Merge branch 'main' of https://github.com/bunny-lab-io/Borealis
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@ Borealis-Server.exe
|
|||||||
# Production Deployment Folders
|
# Production Deployment Folders
|
||||||
/Agent/
|
/Agent/
|
||||||
/Server/
|
/Server/
|
||||||
|
/Engine/
|
||||||
/Certificates/
|
/Certificates/
|
||||||
/ElectronApp/
|
/ElectronApp/
|
||||||
/Logs/
|
/Logs/
|
||||||
|
|||||||
667
Borealis.sh
667
Borealis.sh
@@ -1,6 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Borealis.sh
|
#////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Borealis.sh
|
||||||
# Linux parity for Borealis.ps1 (Ubuntu/Rocky/Fedora/RHEL). Experimental but feature-complete.
|
# Linux parity for Borealis.ps1 (Engine focus). Aims to be OS-agnostic across Ubuntu/Debian, RHEL/Rocky/Fedora, Arch.
|
||||||
|
# - Installs system deps when needed (python3, venv, pip, curl, unzip, tesseract)
|
||||||
|
# - Bundles portable NodeJS into Dependencies/NodeJS to keep a known-good version (no root required)
|
||||||
|
# - Mirrors Windows flow: create Engine venv, stage Data/Engine, stage web-interface, Vite dev or prod build, Flask launch
|
||||||
|
# - Supports flags: --server/--agent (agent kept for compatibility), --vite/--flask, --quick, --engine-tests,
|
||||||
|
# --EngineProduction, --EngineDev (auto-select server mode), plus interactive menu
|
||||||
|
# NOTE: This script focuses on ENGINE parity. Agent paths remain but are not the goal here.
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
@@ -21,59 +27,30 @@ cd "$SCRIPT_DIR"
|
|||||||
# ---- CLI flags (parity with Borealis.ps1) ----
|
# ---- CLI flags (parity with Borealis.ps1) ----
|
||||||
SERVER_FLAG=0
|
SERVER_FLAG=0
|
||||||
AGENT_FLAG=0
|
AGENT_FLAG=0
|
||||||
AGENT_ACTION=""
|
|
||||||
VITE_FLAG=0
|
VITE_FLAG=0
|
||||||
FLASK_FLAG=0
|
FLASK_FLAG=0
|
||||||
QUICK_FLAG=0
|
QUICK_FLAG=0
|
||||||
ENGINE_TESTS_FLAG=0
|
ENGINE_TESTS_FLAG=0
|
||||||
|
ENGINE_PROD_FLAG=0
|
||||||
|
ENGINE_DEV_FLAG=0
|
||||||
|
INSTALLER_CODE=""
|
||||||
|
|
||||||
while (( "$#" )); do
|
while (( "$#" )); do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-Server|--server) SERVER_FLAG=1 ;;
|
-Server|--server) SERVER_FLAG=1 ;;
|
||||||
-Agent|--agent) AGENT_FLAG=1 ;;
|
-Agent|--agent) AGENT_FLAG=1 ;;
|
||||||
-AgentAction|--agent-action) shift; AGENT_ACTION="${1:-}" ;;
|
|
||||||
-Vite|--vite) VITE_FLAG=1 ;;
|
-Vite|--vite) VITE_FLAG=1 ;;
|
||||||
-Flask|--flask) FLASK_FLAG=1 ;;
|
-Flask|--flask) FLASK_FLAG=1 ;;
|
||||||
-Quick|--quick) QUICK_FLAG=1 ;;
|
-Quick|--quick) QUICK_FLAG=1 ;;
|
||||||
-EngineTests|--engine-tests) ENGINE_TESTS_FLAG=1 ;;
|
-EngineTests|--engine-tests) ENGINE_TESTS_FLAG=1 ;;
|
||||||
|
-EngineProduction|--engine-production) ENGINE_PROD_FLAG=1 ;;
|
||||||
|
-EngineDev|--engine-dev) ENGINE_DEV_FLAG=1 ;;
|
||||||
|
-InstallerCode|--installer-code) shift; INSTALLER_CODE="${1:-}" ;;
|
||||||
*) ;; # ignore unknown for flexibility
|
*) ;; # ignore unknown for flexibility
|
||||||
esac
|
esac
|
||||||
shift || true
|
shift || true
|
||||||
done
|
done
|
||||||
|
|
||||||
if (( ENGINE_TESTS_FLAG )); then
|
|
||||||
cd "$SCRIPT_DIR"
|
|
||||||
export BOREALIS_PROJECT_ROOT="${SCRIPT_DIR}"
|
|
||||||
|
|
||||||
if command -v python3 >/dev/null 2>&1; then
|
|
||||||
PYTHON_BIN="$(command -v python3)"
|
|
||||||
elif command -v python >/dev/null 2>&1; then
|
|
||||||
PYTHON_BIN="$(command -v python)"
|
|
||||||
else
|
|
||||||
echo -e "${RED}Python interpreter not found. Install Python 3 to run Engine tests.${RESET}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
"$PYTHON_BIN" -m pytest Data/Engine/Unit_Tests
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---- Banner ----
|
|
||||||
clear || true
|
|
||||||
echo -e "${BOREALIS_BLUE}"
|
|
||||||
cat << 'EOF'
|
|
||||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>
|
|
||||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>۰<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>
|
|
||||||
<20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>۰<EFBFBD><DBB0><EFBFBD>۰<EFBFBD><DBB0><EFBFBD>۰<EFBFBD><DBB0><EFBFBD><EFBFBD> <20><>۰<EFBFBD><DBB0><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><>۰<EFBFBD>
|
|
||||||
<20><><EFBFBD>۰<EFBFBD><DBB0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>۰<EFBFBD><DBB0><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
<20><><EFBFBD><EFBFBD> <20><><EFBFBD>۰<EFBFBD><DBB0><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>۰<EFBFBD><DBB0> <20><>۰<EFBFBD><DBB0><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
||||||
EOF
|
|
||||||
echo -e "${RESET}"
|
|
||||||
echo -e "${DARK_GRAY}Automation Platform${RESET}"
|
|
||||||
|
|
||||||
# ---- Helpers ----
|
# ---- Helpers ----
|
||||||
run_step() {
|
run_step() {
|
||||||
local message="$1"; shift
|
local message="$1"; shift
|
||||||
@@ -96,294 +73,280 @@ detect_distro() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_log_dir() { mkdir -p "${SCRIPT_DIR}/Agent/Logs"; }
|
|
||||||
log_agent() { ensure_log_dir; printf "[%s] %s\n" "$(date +%F\ %T)" "$1" >> "${SCRIPT_DIR}/Agent/Logs/$2"; }
|
|
||||||
|
|
||||||
need_sudo() { [ "${EUID:-$(id -u)}" -ne 0 ]; }
|
need_sudo() { [ "${EUID:-$(id -u)}" -ne 0 ]; }
|
||||||
|
|
||||||
|
ensure_engine_log_dir() {
|
||||||
|
mkdir -p "${SCRIPT_DIR}/Engine/Logs"
|
||||||
|
echo "${SCRIPT_DIR}/Engine/Logs"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_vite_log() {
|
||||||
|
local msg="$1"; local svc="${2:-vite-dev}"
|
||||||
|
local logdir; logdir=$(ensure_engine_log_dir)
|
||||||
|
printf "%s-%s-%s\n" "$(date +%FT%T)" "$svc" "$msg" >> "${logdir}/vite.log"
|
||||||
|
}
|
||||||
|
|
||||||
# ---- Dependency Installation (Linux) ----
|
# ---- Dependency Installation (Linux) ----
|
||||||
install_shared_dependencies() {
|
install_shared_dependencies() {
|
||||||
detect_distro
|
detect_distro
|
||||||
if command -v python3 >/dev/null 2>&1 && command -v pip3 >/dev/null 2>&1; then :; else
|
if command -v python3 >/dev/null 2>&1 && command -v pip3 >/dev/null 2>&1; then :; else
|
||||||
case "$DISTRO_ID" in
|
case "$DISTRO_ID" in
|
||||||
ubuntu|debian)
|
ubuntu|debian)
|
||||||
sudo apt update -qq && sudo apt install -y python3 python3-venv python3-pip curl unzip ;;
|
sudo apt update -qq && sudo apt install -y python3 python3-venv python3-pip curl unzip ca-certificates ;;
|
||||||
rhel|centos|fedora|rocky)
|
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 ;;
|
if command -v dnf >/dev/null 2>&1; then sudo dnf install -y python3 python3-pip python3-virtualenv curl unzip ca-certificates ; else sudo yum install -y python3 python3-pip python3-virtualenv curl unzip ca-certificates ; fi ;;
|
||||||
arch)
|
arch)
|
||||||
sudo pacman -Sy --noconfirm python python-pip curl unzip ;;
|
sudo pacman -Sy --noconfirm python python-pip python-virtualenv curl unzip ca-certificates ;;
|
||||||
*) : ;;
|
*) : ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_tesseract() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
NODE_VERSION="v23.11.0"
|
NODE_VERSION="v23.11.0"
|
||||||
NODE_DIR="${SCRIPT_DIR}/Dependencies/NodeJS"
|
NODE_DIR="${SCRIPT_DIR}/Dependencies/NodeJS"
|
||||||
NODE_BIN="${NODE_DIR}/bin/node"
|
NODE_BIN="${NODE_DIR}/bin/node"
|
||||||
NPM_BIN="${NODE_DIR}/bin/npm"
|
NPM_BIN="${NODE_DIR}/bin/npm"
|
||||||
NPX_BIN="${NODE_DIR}/bin/npx"
|
NPX_BIN="${NODE_DIR}/bin/npx"
|
||||||
|
|
||||||
install_server_dependencies() {
|
install_node_portable() {
|
||||||
# Tesseract via system packages; OCR engines code uses system binary on Linux
|
if [[ -x "$NPM_BIN" ]]; then return 0; fi
|
||||||
detect_distro
|
mkdir -p "$NODE_DIR"
|
||||||
case "$DISTRO_ID" in
|
local tarball="node-${NODE_VERSION}-linux-x64.tar.xz"
|
||||||
ubuntu|debian)
|
local url="https://nodejs.org/dist/${NODE_VERSION}/${tarball}"
|
||||||
sudo apt update -qq && sudo apt install -y tesseract-ocr ;;
|
local dl_path="${SCRIPT_DIR}/Dependencies/${tarball}"
|
||||||
rhel|centos|fedora|rocky)
|
write_vite_log "Downloading NodeJS ${NODE_VERSION} from ${url}" "bootstrap"
|
||||||
if command -v dnf >/dev/null 2>&1; then sudo dnf install -y tesseract; else sudo yum install -y tesseract; fi ;;
|
curl -fsSL -o "$dl_path" "$url"
|
||||||
arch)
|
rm -rf "${NODE_DIR:?}"/*
|
||||||
sudo pacman -Sy --noconfirm tesseract ;;
|
tar -xJf "$dl_path" -C "$NODE_DIR" --strip-components=1
|
||||||
*) : ;;
|
rm -f "$dl_path"
|
||||||
esac
|
|
||||||
|
|
||||||
# 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}'
|
|
||||||
"
|
|
||||||
fi
|
|
||||||
export PATH="${NODE_DIR}/bin:${PATH}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_node_bins() {
|
ensure_node_bins() {
|
||||||
if [[ -x "$NPM_BIN" ]]; then return 0; fi
|
if [[ -x "$NPM_BIN" ]]; then export PATH="${NODE_DIR}/bin:${PATH}"; return 0; fi
|
||||||
if command -v npm >/dev/null 2>&1; then
|
if command -v npm >/dev/null 2>&1; then
|
||||||
NPM_BIN="$(command -v npm)"; NPX_BIN="$(command -v npx || echo npx)"; return 0
|
NPM_BIN="$(command -v npm)"; NPX_BIN="$(command -v npx || echo npx)"; return 0
|
||||||
fi
|
fi
|
||||||
echo -e "${RED}npm not found. Run server dependency install first.${RESET}" >&2
|
echo -e "${YELLOW}npm not found on PATH; installing portable NodeJS...${RESET}"
|
||||||
return 1
|
install_node_portable
|
||||||
|
export PATH="${NODE_DIR}/bin:${PATH}"
|
||||||
}
|
}
|
||||||
|
|
||||||
install_agent_dependencies() {
|
install_server_dependencies() {
|
||||||
# No AHK on Linux; ensure python only
|
run_step "Dependency: Python (system)" install_shared_dependencies
|
||||||
install_shared_dependencies
|
run_step "Dependency: Tesseract-OCR (system)" install_tesseract
|
||||||
|
run_step "Dependency: NodeJS (portable)" install_node_portable
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- Process/task helpers (Linux) ----
|
# Prefer a resilient resolver for the Engine venv interpreter (some venvs only have 'python')
|
||||||
kill_agent_processes() {
|
engine_python_bin() {
|
||||||
# Kill only Python processes under this repo's Agent venv
|
if [[ -x "${SCRIPT_DIR}/Engine/bin/python3" ]]; then
|
||||||
if command -v pgrep >/dev/null 2>&1; then
|
echo "${SCRIPT_DIR}/Engine/bin/python3"
|
||||||
pgrep -f "${SCRIPT_DIR}/Agent/.*/python.*${SCRIPT_DIR}/Agent/Borealis/agent.py" >/dev/null 2>&1 && \
|
elif [[ -x "${SCRIPT_DIR}/Engine/bin/python" ]]; then
|
||||||
pkill -f "${SCRIPT_DIR}/Agent/.*/python.*${SCRIPT_DIR}/Agent/Borealis/agent.py" || true
|
echo "${SCRIPT_DIR}/Engine/bin/python"
|
||||||
else
|
else
|
||||||
pkill -f "Agent/Borealis/agent.py" || true
|
echo ""
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
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" && pwd 2>/dev/null || echo "$ROOT_DIR/../Logs")"
|
|
||||||
mkdir -p "$LOG_DIR"
|
|
||||||
PY_BIN="${ROOT_DIR}/../bin/python3"
|
|
||||||
exec "$PY_BIN" "$ROOT_DIR/agent.py" --system-service --config SYSTEM >>"$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 CURRENTUSER'"
|
|
||||||
|
|
||||||
# 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 CURRENTUSER'"
|
|
||||||
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 CURRENTUSER >/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
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---- Engine TLS material (parity with Ensure-EngineTlsMaterial) ----
|
||||||
ensure_engine_tls_material() {
|
ensure_engine_tls_material() {
|
||||||
local python_bin="${SCRIPT_DIR}/Server/bin/python3"
|
local py="$1" # engine venv python
|
||||||
local cert_dir="${SCRIPT_DIR}/Certificates/Server"
|
local cert_root_arg="$2" # optional path with pre-provided certs
|
||||||
mkdir -p "$cert_dir"
|
local effective_root=""
|
||||||
if [[ -x "$python_bin" ]]; then
|
|
||||||
"$python_bin" - <<'PY'
|
if [[ -x "$py" ]]; then
|
||||||
from Data.Engine.services.crypto import certificates
|
local code='from Data.Engine.security import certificates; certificates.ensure_certificate(); print(certificates.engine_certificates_root())'
|
||||||
certificates.ensure_certificate()
|
set +e
|
||||||
PY
|
effective_root="$("$py" -c "$code" 2>/dev/null | tail -n 1 | tr -d '\r' || true)"
|
||||||
|
set -e
|
||||||
fi
|
fi
|
||||||
export BOREALIS_CERT_DIR="$cert_dir"
|
|
||||||
export BOREALIS_TLS_CERT="${cert_dir}/borealis-server-cert.pem"
|
if [[ -z "$effective_root" && -n "${cert_root_arg}" ]]; then
|
||||||
export BOREALIS_TLS_KEY="${cert_dir}/borealis-server-key.pem"
|
if [[ -f "${cert_root_arg}/borealis-server-cert.pem" && -f "${cert_root_arg}/borealis-server-key.pem" ]]; then
|
||||||
|
effective_root="${cert_root_arg}"
|
||||||
|
else
|
||||||
|
write_vite_log "Provided certificate root '${cert_root_arg}' missing expected TLS material; using Engine runtime certificates instead." "tls"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$effective_root" ]]; then
|
||||||
|
effective_root="${SCRIPT_DIR}/Engine/Certificates"
|
||||||
|
mkdir -p "$effective_root"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export BOREALIS_CERT_DIR="$effective_root"
|
||||||
|
export BOREALIS_TLS_CERT="${effective_root}/borealis-server-cert.pem"
|
||||||
|
export BOREALIS_TLS_KEY="${effective_root}/borealis-server-key.pem"
|
||||||
|
export BOREALIS_TLS_BUNDLE="${effective_root}/borealis-server-bundle.pem"
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_engine_webui_source() {
|
# ---- Engine web interface staging (parity with Ensure-EngineWebInterface) ----
|
||||||
local engineSource="Engine/web-interface"
|
ensure_engine_web_interface() {
|
||||||
if [[ -d "${engineSource}/src" && -f "${engineSource}/package.json" ]]; then
|
local project_root="$1"
|
||||||
return 0
|
local dest="${project_root}/Engine/web-interface"
|
||||||
fi
|
local stage="${project_root}/Data/Engine/web-interface"
|
||||||
local stageSource="Data/Engine/web-interface"
|
[[ -d "$stage" ]] || { echo -e "${RED}Engine web interface source missing at '$stage'.${RESET}" >&2; return 1; }
|
||||||
if [[ ! -d "$stageSource" ]]; then
|
rm -rf "$dest" 2>/dev/null || true
|
||||||
echo "${RED}Engine web interface source '$stageSource' not found.${RESET}" >&2
|
mkdir -p "$dest"
|
||||||
return 1
|
cp -a "${stage}/." "$dest/"
|
||||||
fi
|
[[ -f "${dest}/package.json" ]] || { echo -e "${RED}Failed to stage Engine web interface into '$dest'.${RESET}" >&2; return 1; }
|
||||||
mkdir -p "$engineSource"
|
|
||||||
find "$engineSource" -mindepth 1 -maxdepth 1 \
|
|
||||||
! -name '.gitignore' ! -name 'README.md' -exec rm -rf {} +
|
|
||||||
cp -a "$stageSource/." "$engineSource/"
|
|
||||||
if [[ ! -f "${engineSource}/package.json" ]]; then
|
|
||||||
echo "${RED}Failed to stage Engine web interface into '$engineSource' from '$stageSource'.${RESET}" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare_webui() {
|
# ---- Engine build+launch flow ----
|
||||||
local customUIPath="Engine/web-interface"
|
create_engine_venv_and_stage_data() {
|
||||||
local webUIDestination="Server/web-interface"
|
local venv_dir="${SCRIPT_DIR}/Engine"
|
||||||
ensure_engine_webui_source || return 1
|
local engine_src="${SCRIPT_DIR}/Data/Engine"
|
||||||
mkdir -p "$webUIDestination"
|
local data_dest="${venv_dir}/Data/Engine"
|
||||||
rm -rf "${webUIDestination}/public" "${webUIDestination}/src" "${webUIDestination}/build" 2>/dev/null || true
|
|
||||||
cp -r "${customUIPath}/"* "$webUIDestination/"
|
[[ -d "$venv_dir" ]] || python3 -m venv "$venv_dir"
|
||||||
|
mkdir -p "${venv_dir}/Data"
|
||||||
|
|
||||||
|
rm -rf "$data_dest" 2>/dev/null || true
|
||||||
|
mkdir -p "$data_dest"
|
||||||
|
|
||||||
|
# Copy everything except Assemblies (handled separately)
|
||||||
|
shopt -s dotglob nullglob
|
||||||
|
for item in "${engine_src}"/*; do
|
||||||
|
base="$(basename "$item")"
|
||||||
|
if [[ "$base" == "Assemblies" ]]; then continue; fi
|
||||||
|
cp -a "$item" "$data_dest/"
|
||||||
|
done
|
||||||
|
shopt -u dotglob nullglob
|
||||||
|
|
||||||
|
# Assemblies runtime folder
|
||||||
|
[[ -d "${SCRIPT_DIR}/Engine/Assemblies" ]] || {
|
||||||
|
if [[ -d "${engine_src}/Assemblies" ]]; then
|
||||||
|
cp -a "${engine_src}/Assemblies" "${SCRIPT_DIR}/Engine/Assemblies"
|
||||||
|
else
|
||||||
|
mkdir -p "${SCRIPT_DIR}/Engine/Assemblies"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Auth_Tokens and database
|
||||||
|
mkdir -p "${SCRIPT_DIR}/Engine/Auth_Tokens"
|
||||||
|
# database.db will be created by the app if not present; ensure dir exists
|
||||||
|
}
|
||||||
|
|
||||||
|
install_engine_python_deps() {
|
||||||
|
local venv_py
|
||||||
|
venv_py="$(engine_python_bin)"
|
||||||
|
if [[ -z "$venv_py" ]]; then
|
||||||
|
# Try to create the venv if it doesn't exist yet
|
||||||
|
python3 -m venv "${SCRIPT_DIR}/Engine" || true
|
||||||
|
venv_py="$(engine_python_bin)"
|
||||||
|
fi
|
||||||
|
local engine_src="${SCRIPT_DIR}/Data/Engine"
|
||||||
|
local reqs=( "${engine_src}/engine-requirements.txt" "${engine_src}/requirements.txt" )
|
||||||
|
for r in "${reqs[@]}"; do
|
||||||
|
if [[ -f "$r" && -n "$venv_py" ]]; then
|
||||||
|
"$venv_py" -m pip install --disable-pip-version-check -q -r "$r"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
vite_web_frontend_install() {
|
||||||
|
local engine_ui_dest="${SCRIPT_DIR}/Engine/web-interface"
|
||||||
ensure_node_bins
|
ensure_node_bins
|
||||||
( cd "$webUIDestination" && "$NPM_BIN" install --silent --no-fund --audit=false >/dev/null )
|
( cd "$engine_ui_dest" && "$NPM_BIN" install --silent --no-fund --audit=false >/dev/null )
|
||||||
}
|
}
|
||||||
|
|
||||||
vite_start() {
|
vite_web_frontend_start() {
|
||||||
local mode="$1" # developer|production
|
local mode="$1" # developer|production
|
||||||
local webUIDestination="Server/web-interface"
|
local engine_ui_dest="${SCRIPT_DIR}/Engine/web-interface"
|
||||||
local subcmd="build"; [[ "$mode" == "developer" ]] && subcmd="dev"
|
|
||||||
ensure_node_bins
|
ensure_node_bins
|
||||||
ensure_engine_tls_material
|
ensure_engine_tls_material "$(engine_python_bin)" ""
|
||||||
( cd "$webUIDestination" && nohup "$NPM_BIN" run "$subcmd" >/dev/null 2>&1 & )
|
|
||||||
|
if [[ "$mode" == "developer" ]]; then
|
||||||
|
local logdir; logdir=$(ensure_engine_log_dir)
|
||||||
|
local stdout_log="${logdir}/vite-dev.stdout.log"
|
||||||
|
local stderr_log="${logdir}/vite-dev.stderr.log"
|
||||||
|
mv -f "$stdout_log" "${stdout_log}.$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
|
||||||
|
mv -f "$stderr_log" "${stderr_log}.$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
|
||||||
|
write_vite_log "Starting Vite dev server using TLS (cert=$BOREALIS_TLS_CERT bundle=$BOREALIS_TLS_BUNDLE)" "vite-dev"
|
||||||
|
(
|
||||||
|
cd "$engine_ui_dest"
|
||||||
|
PATH="${NODE_DIR}/bin:${PATH}" nohup "$NPM_BIN" run dev >"$stdout_log" 2>"$stderr_log" &
|
||||||
|
)
|
||||||
|
else
|
||||||
|
write_vite_log "Executing npm run build for production WebUI assets." "vite-build"
|
||||||
|
( cd "$engine_ui_dest" && "$NPM_BIN" run build )
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
flask_run() {
|
flask_engine_launch() {
|
||||||
local py="${SCRIPT_DIR}/Server/bin/python3"
|
local mode="$1" # production|developer
|
||||||
local server_py="${SCRIPT_DIR}/Server/Borealis/server.py"
|
pushd "${SCRIPT_DIR}/Engine" >/dev/null
|
||||||
echo -e "\n${GREEN}Launching Borealis Flask Server...${RESET}"
|
local py
|
||||||
|
py="$(engine_python_bin)"
|
||||||
|
if [[ -z "$py" ]]; then
|
||||||
|
python3 -m venv "${SCRIPT_DIR}/Engine" || true
|
||||||
|
py="$(engine_python_bin)"
|
||||||
|
fi
|
||||||
|
local prev_mode="${BOREALIS_ENGINE_MODE:-}"
|
||||||
|
local prev_port="${BOREALIS_ENGINE_PORT:-}"
|
||||||
|
local prev_root="${BOREALIS_PROJECT_ROOT:-}"
|
||||||
|
export BOREALIS_ENGINE_MODE="$mode"
|
||||||
|
export BOREALIS_ENGINE_PORT="5000"
|
||||||
|
export BOREALIS_PROJECT_ROOT="$SCRIPT_DIR"
|
||||||
|
echo -e "\n${GREEN}Launching Borealis Engine...${RESET}"
|
||||||
echo "===================================================================================="
|
echo "===================================================================================="
|
||||||
"$py" "$server_py"
|
echo "${HOURGLASS} Engine Socket Server Started..."
|
||||||
|
"$py" -m Data.Engine.bootstrapper || true
|
||||||
|
# restore env
|
||||||
|
if [[ -n "$prev_mode" ]]; then export BOREALIS_ENGINE_MODE="$prev_mode"; else unset BOREALIS_ENGINE_MODE; fi
|
||||||
|
if [[ -n "$prev_port" ]]; then export BOREALIS_ENGINE_PORT="$prev_port"; else unset BOREALIS_ENGINE_PORT; fi
|
||||||
|
if [[ -n "$prev_root" ]]; then export BOREALIS_PROJECT_ROOT="$prev_root"; else unset BOREALIS_PROJECT_ROOT; fi
|
||||||
|
popd >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---- Tests parity ----
|
||||||
|
if (( ENGINE_TESTS_FLAG )); then
|
||||||
|
export BOREALIS_PROJECT_ROOT="${SCRIPT_DIR}"
|
||||||
|
PYTHON_BIN="$(command -v python3 || command -v python || true)"
|
||||||
|
if [[ -z "${PYTHON_BIN}" ]]; then
|
||||||
|
echo -e "${RED}Python interpreter not found. Install Python 3 to run Engine tests.${RESET}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
"${PYTHON_BIN}" -m pytest 'Data/Engine/Unit_Tests'
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- Banner ----
|
||||||
|
clear || true
|
||||||
|
printf "%b" "${BOREALIS_BLUE}"
|
||||||
|
cat << 'EOF'
|
||||||
|
::::::::: :::::::: ::::::::: :::::::::: ::: ::: ::::::::::: ::::::::
|
||||||
|
:+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
|
||||||
|
+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+
|
||||||
|
+#++:++#+ +#+ +:+ +#++:++#: +#++:++# +#++:++#++: +#+ +#+ +#++:++#++
|
||||||
|
+#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+
|
||||||
|
#+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+#
|
||||||
|
######### ######## ### ### ########## ### ### ########## ########### ########
|
||||||
|
EOF
|
||||||
|
printf "%b" "${RESET}"
|
||||||
|
printf "%b\n" "${DARK_GRAY}Automation Platform${RESET}"
|
||||||
|
|
||||||
# ---- Menus ----
|
# ---- Menus ----
|
||||||
server_menu() {
|
server_menu() {
|
||||||
echo -e "\nConfigure Borealis Server Mode:"
|
echo -e "\nConfigure Borealis Engine Mode:"
|
||||||
echo -e " 1) Build & Launch > Production Flask Server @ http://localhost:5000"
|
echo -e " 1) Build & Launch > Production Flask Server @ https://localhost:5000"
|
||||||
echo -e " 2) [Skip Build] & Immediately Launch > Production Flask Server @ http://localhost:5000"
|
echo -e " 2) [Skip Build] & Immediately Launch > Production Flask Server @ https://localhost:5000"
|
||||||
echo -e " 3) Launch > [Hotload-Ready] Vite Dev Server @ http://localhost:5173"
|
echo -e " 3) Launch > [Hotload-Ready] Vite Dev Server @ https://localhost:5173"
|
||||||
read -r -p "Enter choice [1/2/3]: " modeChoice
|
read -r -p "Enter choice [1/2/3]: " modeChoice
|
||||||
|
|
||||||
case "$modeChoice" in
|
case "$modeChoice" in
|
||||||
@@ -393,150 +356,80 @@ server_menu() {
|
|||||||
*) echo -e "${RED}Invalid mode choice${RESET}"; return 1 ;;
|
*) echo -e "${RED}Invalid mode choice${RESET}"; return 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Common deps
|
echo -e "${GREEN}Ensuring Engine Dependencies Exist...${RESET}"
|
||||||
echo -e "${GREEN}Ensuring Server Dependencies...${RESET}"
|
|
||||||
install_shared_dependencies
|
|
||||||
install_server_dependencies
|
install_server_dependencies
|
||||||
export PATH="${NODE_DIR}/bin:${PATH}"
|
export PATH="${NODE_DIR}/bin:${PATH}"
|
||||||
|
|
||||||
if [[ "$modeChoice" == "2" ]]; then
|
if [[ "$modeChoice" == "2" ]]; then
|
||||||
flask_run
|
# Immediate launch of Flask without rebuild
|
||||||
|
flask_engine_launch "$borealis_operation_mode"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
run_step "Create Server venv & deploy files" copy_server_payload
|
run_step "Create Borealis Engine Virtual Python Environment & Stage Data" create_engine_venv_and_stage_data
|
||||||
run_step "Copy WebUI assets" prepare_webui
|
run_step "Install Engine Python Dependencies" install_engine_python_deps
|
||||||
run_step "Start Vite (${borealis_operation_mode})" vite_start "$borealis_operation_mode"
|
run_step "Copy Engine WebUI Files" ensure_engine_web_interface "$SCRIPT_DIR"
|
||||||
flask_run
|
run_step "Vite Web Frontend: Install NPM Packages" vite_web_frontend_install
|
||||||
|
run_step "Vite Web Frontend: Start (${borealis_operation_mode})" vite_web_frontend_start "$borealis_operation_mode"
|
||||||
|
flask_engine_launch "$borealis_operation_mode"
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
main_menu() {
|
||||||
echo -e "\nPlease choose which function you want to launch:"
|
echo -e "\nPlease choose which function you want to launch:"
|
||||||
echo -e " 1) Borealis Server"
|
echo -e " 1) Borealis Engine"
|
||||||
echo -e " 2) Borealis Agent"
|
echo -e " 2) Borealis Agent (not the focus on Linux)"
|
||||||
echo -e " 3) Build Electron App [Experimental]"
|
echo -e " 3) Exit"
|
||||||
echo -e " 4) Package Self-Contained App [Experimental]"
|
|
||||||
echo -e " 5) Update Borealis [Requires Re-Build]"
|
|
||||||
read -r -p "Enter a number: " choice
|
read -r -p "Enter a number: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1) server_menu ;;
|
1) server_menu ;;
|
||||||
2) agent_menu ;;
|
2) echo -e "${YELLOW}Agent management is out-of-scope for this parity pass. Use Windows for full Agent features.${RESET}" ;;
|
||||||
3) electron_menu ;;
|
3) exit 0 ;;
|
||||||
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 ;;
|
*) echo -e "${RED}Invalid selection. Exiting...${RESET}"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Auto-select when flags provided
|
# ---- Flag-driven auto-select (parity logic) ----
|
||||||
if [[ $SERVER_FLAG -eq 1 && $AGENT_FLAG -eq 1 ]]; then
|
if [[ $SERVER_FLAG -eq 1 && $AGENT_FLAG -eq 1 ]]; then
|
||||||
echo -e "${RED}Cannot use --server and --agent together.${RESET}"; exit 1
|
echo -e "${RED}Cannot use --server and --agent together.${RESET}"; exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Auto-select main menu option for Server when EngineProduction/EngineDev provided
|
||||||
|
if [[ $ENGINE_PROD_FLAG -eq 1 || $ENGINE_DEV_FLAG -eq 1 ]]; then
|
||||||
|
SERVER_FLAG=1
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $SERVER_FLAG -eq 1 ]]; then
|
if [[ $SERVER_FLAG -eq 1 ]]; then
|
||||||
# Resolve server mode from flags
|
|
||||||
if [[ $VITE_FLAG -eq 1 && $FLASK_FLAG -eq 1 ]]; then
|
if [[ $VITE_FLAG -eq 1 && $FLASK_FLAG -eq 1 ]]; then
|
||||||
echo -e "${RED}Cannot combine --vite and --flask.${RESET}"; exit 1
|
echo -e "${RED}Cannot combine --vite and --flask.${RESET}"; exit 1
|
||||||
fi
|
fi
|
||||||
if [[ $VITE_FLAG -eq 1 ]]; then
|
if [[ $ENGINE_PROD_FLAG -eq 1 && $ENGINE_DEV_FLAG -eq 1 ]]; then
|
||||||
borealis_operation_mode="developer"; server_menu <<< $'3\n'
|
echo -e "${RED}Cannot combine --EngineProduction and --EngineDev.${RESET}"; exit 1
|
||||||
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
|
fi
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $AGENT_FLAG -eq 1 ]]; then
|
if [[ $ENGINE_PROD_FLAG -eq 1 || $ENGINE_DEV_FLAG -eq 1 ]]; then
|
||||||
case "${AGENT_ACTION:-install}" in
|
# Map to menu choice automatically
|
||||||
install) install_or_update_agent ;;
|
if [[ $ENGINE_PROD_FLAG -eq 1 ]]; then
|
||||||
repair) repair_agent ;;
|
if [[ $QUICK_FLAG -eq 1 ]]; then
|
||||||
remove) remove_agent ;;
|
server_menu <<< $'2\n' # skip build, immediate launch
|
||||||
launch) launch_user_helper_now ;;
|
else
|
||||||
*) install_or_update_agent ;;
|
server_menu <<< $'1\n' # build & launch
|
||||||
esac
|
fi
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
if [[ $ENGINE_DEV_FLAG -eq 1 ]]; then
|
||||||
|
server_menu <<< $'3\n' # Vite dev
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Resolve server mode from flags (legacy compatibility)
|
||||||
|
if [[ $VITE_FLAG -eq 1 ]]; then
|
||||||
|
server_menu <<< $'3\n'
|
||||||
|
elif [[ $FLASK_FLAG -eq 1 && $QUICK_FLAG -eq 1 ]]; then
|
||||||
|
server_menu <<< $'2\n'
|
||||||
|
else
|
||||||
|
server_menu <<< $'1\n'
|
||||||
|
fi
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user