Files
2026-07-13 13:20:22 +08:00

448 lines
16 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 "$@"