#!/usr/bin/env bash # ============================================================================ # AionUi — Ubuntu / Debian 一鍵自動化安裝腳本 # ============================================================================ # 功能: # 1. 自動偵測系統架構 (amd64 / arm64) # 2. 從 GitHub Release 下載指定版本的 .deb 套件(預設 latest) # 3. 安裝 .deb + 自動修復依賴 # 4. 安裝 Xvfb 等 headless 運行所需套件 # 5. 建立服務管理腳本 (/opt/AionUi/start-aionui.sh) # 6. (可選) 建立 systemd service # 7. (可選) 建立桌面捷徑 # # 用法: # curl -fsSL https://raw.githubusercontent.com/iOfficeAI/AionUi/main/scripts/install-ubuntu.sh | bash # # 或指定版本: # AIONUI_VERSION=1.8.25 bash install-ubuntu.sh # # 僅安裝桌面版(跳過 headless 設定): # AIONUI_MODE=desktop bash install-ubuntu.sh # ============================================================================ set -euo pipefail # ─── 顏色定義 ─────────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # No Color # ─── 輔助函式 ─────────────────────────────────────────────────────────────── info() { echo -e "${BLUE}[INFO]${NC} $*"; } success() { echo -e "${GREEN}[✓]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } error() { echo -e "${RED}[✗]${NC} $*" >&2; } die() { error "$*"; exit 1; } banner() { echo -e "${CYAN}${BOLD}" echo " ╔══════════════════════════════════════════════╗" echo " ║ AionUi Installer for Ubuntu ║" echo " ╚══════════════════════════════════════════════╝" echo -e "${NC}" } # ─── 前置檢查 ─────────────────────────────────────────────────────────────── check_prerequisites() { # 必須是 Linux [[ "$(uname -s)" == "Linux" ]] || die "此腳本僅支援 Linux 系統" # 必須有 apt (Debian/Ubuntu 系列) command -v apt-get &>/dev/null || die "此腳本需要 apt-get (Debian/Ubuntu 系列)" # 建議以 root 或 sudo 執行 if [[ $EUID -ne 0 ]]; then if command -v sudo &>/dev/null; then SUDO="sudo" warn "非 root 使用者,將使用 sudo 執行安裝" else die "請以 root 身份執行,或安裝 sudo" fi else SUDO="" fi } # ─── 偵測架構 ─────────────────────────────────────────────────────────────── detect_arch() { local machine machine="$(uname -m)" case "$machine" in x86_64|amd64) DEB_ARCH="amd64" ;; aarch64|arm64) DEB_ARCH="arm64" ;; *) die "不支援的架構: $machine(僅支援 x86_64 / aarch64)" ;; esac info "偵測到系統架構: ${BOLD}$machine${NC} → 套件架構: ${BOLD}$DEB_ARCH${NC}" } # ─── 取得版本號 ────────────────────────────────────────────────────────────── resolve_version() { if [[ -n "${AIONUI_VERSION:-}" ]]; then VERSION="$AIONUI_VERSION" info "使用指定版本: ${BOLD}v$VERSION${NC}" else info "正在查詢最新版本..." # 透過 GitHub API 取得 latest release tag if command -v curl &>/dev/null; then VERSION=$(curl -fsSL "https://api.github.com/repos/iOfficeAI/AionUi/releases/latest" \ | grep '"tag_name"' | head -1 | sed 's/.*"v\([^"]*\)".*/\1/') elif command -v wget &>/dev/null; then VERSION=$(wget -qO- "https://api.github.com/repos/iOfficeAI/AionUi/releases/latest" \ | grep '"tag_name"' | head -1 | sed 's/.*"v\([^"]*\)".*/\1/') else die "需要 curl 或 wget 來下載,請先安裝: sudo apt-get install -y curl" fi if [[ -z "$VERSION" ]]; then die "無法取得最新版本號,請手動指定: AIONUI_VERSION=1.8.25 bash $0" fi info "最新版本: ${BOLD}v$VERSION${NC}" fi DEB_FILENAME="AionUi-${VERSION}-linux-${DEB_ARCH}.deb" DOWNLOAD_URL="https://github.com/iOfficeAI/AionUi/releases/download/v${VERSION}/${DEB_FILENAME}" } # ─── 下載 .deb 套件 ────────────────────────────────────────────────────────── download_deb() { local tmpdir tmpdir="$(mktemp -d)" DEB_PATH="${tmpdir}/${DEB_FILENAME}" info "下載 ${BOLD}$DEB_FILENAME${NC} ..." info "網址: $DOWNLOAD_URL" if command -v curl &>/dev/null; then curl -fSL --progress-bar -o "$DEB_PATH" "$DOWNLOAD_URL" || die "下載失敗" elif command -v wget &>/dev/null; then wget --show-progress -q -O "$DEB_PATH" "$DOWNLOAD_URL" || die "下載失敗" fi local size size=$(du -h "$DEB_PATH" | cut -f1) success "下載完成 ($size)" } # ─── 安裝 .deb + 修復依賴 ──────────────────────────────────────────────────── install_deb() { info "安裝 AionUi .deb 套件..." # dpkg 安裝(可能會缺依賴) $SUDO dpkg -i "$DEB_PATH" 2>/dev/null || true # 自動修復缺失的依賴 info "修復依賴套件..." $SUDO apt-get install -f -y success "AionUi v${VERSION} 安裝完成" # 驗證安裝 if command -v AionUi &>/dev/null || [[ -x /usr/bin/AionUi ]]; then success "AionUi 已安裝至 $(which AionUi 2>/dev/null || echo '/usr/bin/AionUi')" else warn "安裝可能不完整,找不到 AionUi 執行檔" fi # 清理暫存 rm -rf "$(dirname "$DEB_PATH")" } # ─── 安裝 Headless 依賴 ────────────────────────────────────────────────────── install_headless_deps() { info "安裝 headless 運行所需套件 (Xvfb 等)..." $SUDO apt-get update -qq $SUDO apt-get install -y --no-install-recommends \ xvfb \ libxkbcommon-x11-0 \ libgtk-3-0 \ libnotify4 \ libnss3 \ libxss1 \ libasound2 \ libgbm1 \ libicu-dev \ 2>/dev/null || warn "部分套件可能已安裝或不可用" success "Headless 依賴安裝完成" } # ─── 建立服務管理腳本 ───────────────────────────────────────────────────────── create_service_script() { local script_dir="/opt/AionUi" local script_path="${script_dir}/start-aionui.sh" info "建立服務管理腳本: $script_path" $SUDO mkdir -p "$script_dir" $SUDO tee "$script_path" > /dev/null << 'SCRIPT_EOF' #!/bin/bash # ============================================================================ # AionUi WebUI Headless 服務管理腳本 # 用法: ./start-aionui.sh [start|stop|restart|status|logs] # ============================================================================ PIDFILE="/var/run/aionui.pid" LOGFILE="/var/log/aionui.log" WORKDIR="${AIONUI_WORKDIR:-$HOME}" start() { if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then echo "⚡ AionUi 已在執行中 (PID: $(cat "$PIDFILE"))" return 1 fi echo "🚀 正在啟動 AionUi WebUI..." cd "$WORKDIR" || exit 1 nohup xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" \ /usr/bin/AionUi --webui --remote --no-sandbox \ > "$LOGFILE" 2>&1 & echo $! > "$PIDFILE" sleep 3 if kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then echo "✅ AionUi 啟動成功 (PID: $(cat "$PIDFILE"))" local ip ip=$(hostname -I 2>/dev/null | awk '{print $1}') echo "🌐 WebUI: http://${ip:-localhost}:25808" else echo "❌ AionUi 啟動失敗,請查看日誌: $LOGFILE" rm -f "$PIDFILE" return 1 fi } stop() { if [ ! -f "$PIDFILE" ]; then echo "⚠️ AionUi 未在執行" return 1 fi local pid pid=$(cat "$PIDFILE") echo "🛑 正在停止 AionUi (PID: $pid)..." kill "$pid" 2>/dev/null sleep 2 kill -9 "$pid" 2>/dev/null pkill -f "AionUi --webui" 2>/dev/null rm -f "$PIDFILE" echo "✅ AionUi 已停止" } restart() { stop 2>/dev/null sleep 1 start } status() { if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then echo "✅ AionUi 執行中 (PID: $(cat "$PIDFILE"))" ss -tlnp 2>/dev/null | grep 25808 || netstat -tlnp 2>/dev/null | grep 25808 || true else echo "⚠️ AionUi 未在執行" rm -f "$PIDFILE" 2>/dev/null fi } logs() { if [ -f "$LOGFILE" ]; then tail -f "$LOGFILE" else echo "日誌檔案不存在: $LOGFILE" fi } case "${1:-}" in start) start ;; stop) stop ;; restart) restart ;; status) status ;; logs) logs ;; "") echo "用法: $0 {start|stop|restart|status|logs}" echo "" echo "環境變數:" echo " AIONUI_WORKDIR - AionUi 工作目錄 (預設: \$HOME)" ;; *) echo "用法: $0 {start|stop|restart|status|logs}" exit 1 ;; esac SCRIPT_EOF $SUDO chmod +x "$script_path" success "服務管理腳本已建立: $script_path" } # ─── 建立 systemd service (可選) ───────────────────────────────────────────── create_systemd_service() { # 若系統不支援 systemd 則跳過 if ! command -v systemctl &>/dev/null; then info "系統不支援 systemd,跳過 service 建立" return fi local service_path="/etc/systemd/system/aionui.service" info "建立 systemd 服務: $service_path" $SUDO tee "$service_path" > /dev/null << 'SERVICE_EOF' [Unit] Description=AionUi AI Agent Desktop App (WebUI Mode) Documentation=https://github.com/iOfficeAI/AionUi After=network-online.target Wants=network-online.target [Service] Type=simple User=root WorkingDirectory=/root ExecStart=/usr/bin/xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" /usr/bin/AionUi --webui --remote --no-sandbox Restart=on-failure RestartSec=10 StandardOutput=journal StandardError=journal # 安全性設定 NoNewPrivileges=false ProtectSystem=false [Install] WantedBy=multi-user.target SERVICE_EOF $SUDO systemctl daemon-reload success "systemd 服務已建立" info "使用方式:" echo " sudo systemctl start aionui # 啟動" echo " sudo systemctl stop aionui # 停止" echo " sudo systemctl enable aionui # 開機自動啟動" echo " sudo systemctl status aionui # 查看狀態" echo " journalctl -u aionui -f # 查看日誌" } # ─── 建立桌面捷徑 ───────────────────────────────────────────────────────────── create_desktop_entry() { local desktop_dir="${HOME}/.local/share/applications" local desktop_file="${desktop_dir}/aionui.desktop" mkdir -p "$desktop_dir" cat > "$desktop_file" << 'DESKTOP_EOF' [Desktop Entry] Name=AionUi Comment=AI Agent Cowork Platform Exec=/usr/bin/AionUi --no-sandbox %U Icon=AionUi Terminal=false Type=Application Categories=Office;Utility;Development; MimeType=x-scheme-handler/aionui; StartupWMClass=AionUi DESKTOP_EOF success "桌面捷徑已建立: $desktop_file" } # ─── 顯示安裝摘要 ───────────────────────────────────────────────────────────── print_summary() { echo "" echo -e "${GREEN}${BOLD}══════════════════════════════════════════════════${NC}" echo -e "${GREEN}${BOLD} 🎉 AionUi v${VERSION} 安裝完成!${NC}" echo -e "${GREEN}${BOLD}══════════════════════════════════════════════════${NC}" echo "" echo -e " ${BOLD}📍 執行檔位置:${NC} /usr/bin/AionUi" echo -e " ${BOLD}📍 管理腳本:${NC} /opt/AionUi/start-aionui.sh" echo "" if [[ "${MODE}" == "headless" ]]; then echo -e " ${BOLD}🖥️ Headless 模式使用方式:${NC}" echo "" echo " # 使用管理腳本" echo " /opt/AionUi/start-aionui.sh start" echo " /opt/AionUi/start-aionui.sh status" echo " /opt/AionUi/start-aionui.sh stop" echo "" if command -v systemctl &>/dev/null; then echo " # 或使用 systemd" echo " sudo systemctl start aionui" echo " sudo systemctl enable aionui # 開機自啟" echo "" fi echo " # WebUI 預設監聽 http://localhost:25808" echo "" else echo -e " ${BOLD}🖥️ 桌面模式使用方式:${NC}" echo "" echo " # 直接啟動(桌面環境)" echo " AionUi --no-sandbox" echo "" echo " # 或從應用程式選單尋找 AionUi" echo "" fi echo -e " ${BOLD}📖 文件:${NC} https://github.com/iOfficeAI/AionUi" echo -e " ${BOLD}🐛 回報:${NC} https://github.com/iOfficeAI/AionUi/issues" echo "" if [[ "${MODE}" == "headless" ]]; then echo -e " ${YELLOW}💡 提示:${NC}" echo " • 設定工作目錄: export AIONUI_WORKDIR=/path/to/workspace" echo " • 遠端存取方式: SSH 隧道 / ngrok / 直接開放 25808 端口" echo " • 詳細指南: docs/guides/deploy-server.md" echo "" fi } # ─── 主流程 ─────────────────────────────────────────────────────────────────── main() { banner # 安裝模式:headless (預設) 或 desktop MODE="${AIONUI_MODE:-headless}" info "安裝模式: ${BOLD}$MODE${NC}" # Step 1: 前置檢查 check_prerequisites # Step 2: 偵測架構 detect_arch # Step 3: 取得版本號 resolve_version # Step 4: 下載 download_deb # Step 5: 安裝 install_deb # Step 6: 根據模式安裝額外元件 if [[ "$MODE" == "headless" ]]; then install_headless_deps create_service_script create_systemd_service fi # Step 7: 桌面捷徑(兩種模式都建立) create_desktop_entry # 完成! print_summary } # 執行 main "$@"