#!/bin/bash # 智能家居代理系统启动脚本 (Linux/macOS) # Smart Home Agent System Startup Script (Linux/macOS) # # 架构设计: # - 环境检查模块:统一检查所有运行环境 # - 配置管理模块:集中处理配置文件读取 # - 服务管理模块:统一启动/停止服务 # - 工具函数模块:提供可复用的通用功能 # ======================================== # 初始化设置 # ======================================== set -e # 遇到错误立即退出 # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' GRAY='\033[0;90m' NC='\033[0m' # 全局变量 declare -A CONFIG declare -a PIDS PROJECT_ROOT="" # ======================================== # 工具函数模块 # ======================================== print_section() { echo echo -e "${CYAN}========================================${NC}" echo -e "${CYAN}$1${NC}" echo -e "${CYAN}========================================${NC}" echo } print_step() { echo -e "${YELLOW}$1${NC}" echo -e "${YELLOW}$2${NC}" echo } print_success() { echo -e "${GREEN}✓ $1${NC}" } print_error() { echo -e "${RED}✗ $1${NC}" } print_info() { echo -e "${GRAY} $1${NC}" } # ======================================== # 环境检查模块 - 统一检查所有必要的运行环境 # ======================================== check_command() { local cmd=$1 local name=$2 local hint=$3 if ! command -v "$cmd" &> /dev/null; then print_error "错误: 未找到 $name" print_error "Error: $name not found" echo -e "${YELLOW} $hint${NC}" return 1 fi local version=$("$cmd" --version 2>&1 | head -1) print_success "$name: $version" return 0 } check_environment() { print_step "检查运行环境..." "Checking runtime environment..." local all_passed=1 check_command "python3" "Python" "请安装 Python 3.8+ / Please install Python 3.8+" || all_passed=0 check_command "node" "Node.js" "请安装 Node.js 16+ / Please install Node.js 16+" || all_passed=0 check_command "pnpm" "pnpm" "请运行: npm install -g pnpm" || all_passed=0 check_command "uv" "uv" "请运行: pip install uv 或访问 https://docs.astral.sh/uv/" || all_passed=0 echo if [ $all_passed -eq 0 ]; then exit 1 fi } # ======================================== # 项目定位模块 - 自动定位项目根目录 # ======================================== find_project_root() { print_step "定位项目根目录..." "Locating project root directory..." local paths=("." ".." "../..") for path in "${paths[@]}"; do if [ -f "$path/config.yaml" ]; then cd "$path" || exit 1 PROJECT_ROOT=$(pwd) print_success "配置文件已找到: $PROJECT_ROOT" print_success "Configuration file found: $PROJECT_ROOT" return 0 fi done print_error "错误: 未找到配置文件 config.yaml" print_error "Error: Configuration file config.yaml not found" print_error "当前目录: $PWD" exit 1 } # ======================================== # 配置管理模块 - 集中处理配置读取和默认值 # ======================================== read_yaml_port() { local pattern=$1 local default=$2 local port=$(grep -E "$pattern" config.yaml | head -1 | sed 's/.*port: *\([0-9]*\).*/\1/') echo "${port:-$default}" } initialize_config() { print_step "读取配置文件..." "Reading configuration file..." # 定义配置映射:pattern|key|default local configs=( "backend:.*python:.*port:|BackendPort|3000" "frontend:.*dev_server:.*port:|FrontendPort|1420" "conductor:.*port:|ConductorPort|12000" "air_conditioner:.*port:|AirCondPort|12001" "air_cleaner:.*port:|AirCleanPort|12002" "bedside_lamp:.*port:|BedsideLampPort|12004" ) for config_line in "${configs[@]}"; do IFS='|' read -r pattern key default <<< "$config_line" CONFIG[$key]=$(read_yaml_port "$pattern" "$default") done print_success "配置读取完成" print_info "后端端口 / Backend Port: ${CONFIG[BackendPort]}" print_info "前端端口 / Frontend Port: ${CONFIG[FrontendPort]}" print_info "Conductor端口: ${CONFIG[ConductorPort]}" echo } # ======================================== # 目录准备模块 - 创建必要的目录 # ======================================== initialize_directories() { mkdir -p logs temp } # ======================================== # 环境准备模块 - Python虚拟环境和依赖 # ======================================== initialize_python_environment() { print_step "检查 Python 虚拟环境..." "Checking Python virtual environment..." if [ ! -d ".venv" ]; then print_error "虚拟环境不存在,正在创建..." print_error "Virtual environment not found, creating..." print_info "执行: uv venv" uv venv if [ $? -ne 0 ]; then print_error "虚拟环境创建失败!" print_error "Virtual environment creation failed!" exit 1 fi print_success "虚拟环境创建完成" else print_success "虚拟环境已存在" fi echo print_step "安装 Python 依赖..." "Installing Python dependencies..." print_info "执行: uv sync" uv sync if [ $? -ne 0 ]; then print_error "Python 依赖安装失败!" print_error "Python dependencies installation failed!" exit 1 fi print_success "Python 依赖已安装" } initialize_frontend_dependencies() { print_step "检查前端依赖..." "Checking frontend dependencies..." if [ ! -d "app/node_modules" ]; then print_error "前端依赖未安装,正在安装..." print_error "Frontend dependencies not installed, installing..." echo cd app || exit 1 print_info "执行: pnpm install" pnpm install if [ $? -ne 0 ]; then cd .. print_error "依赖安装失败!" print_error "Dependency installation failed!" exit 1 fi cd .. print_success "依赖安装完成" else print_success "前端依赖已安装" fi echo } # ======================================== # 服务启动模块 - 统一管理所有服务启动 # ======================================== start_service() { local index=$1 local name_cn=$2 local name_en=$3 local directory=$4 local command=$5 local port_key=$6 local delay=$7 local port=${CONFIG[$port_key]} echo -e "${CYAN}[$index/6] 启动$name_cn (端口 $port)...${NC}" echo -e "${CYAN}[$index/6] Starting $name_en (Port $port)...${NC}" cd "$PROJECT_ROOT/$directory" || exit 1 # 启动服务并重定向输出到日志 eval "$command" >> "$PROJECT_ROOT/logs/${name_en// /_}.log" 2>&1 & local pid=$! PIDS+=($pid) cd "$PROJECT_ROOT" || exit 1 sleep "$delay" print_success "$name_cn 已启动" print_success "$name_en started" echo } start_all_services() { print_section "正在启动 Moss AI 本地开发环境... Starting Moss AI Local Development Environment..." # 定义服务配置:index|name_cn|name_en|directory|command|port_key|delay local services=( "1|后端服务|Backend Service|app/backend-python|uv run .|BackendPort|3" "2|总管理代理|Conductor Agent|agents/conductor_agent|uv run .|ConductorPort|3" "3|空调代理|Air Conditioner Agent|agents/air_conditioner_agent|uv run .|AirCondPort|2" "4|空气净化器|Air Cleaner Agent|agents/air_cleaner_agent|uv run .|AirCleanPort|2" "5|床头灯代理|Bedside Lamp Agent|agents/bedside_lamp_agent|uv run .|BedsideLampPort|2" "6|前端开发服务器|Frontend Dev Server|app|pnpm dev|FrontendPort|3" ) for service_config in "${services[@]}"; do IFS='|' read -r idx name_cn name_en dir cmd port_key delay <<< "$service_config" start_service "$idx" "$name_cn" "$name_en" "$dir" "$cmd" "$port_key" "$delay" done print_info "所有服务窗口正在后台运行" print_info "All services are running in background" echo } # ======================================== # 信息显示模块 - 显示服务地址和使用说明 # ======================================== show_service_info() { print_section "所有服务已启动完成! All services started successfully!" echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ 服务地址 / Service URLs ║${NC}" echo -e "${CYAN}╠════════════════════════════════════════════════════════════╣${NC}" echo -e "${CYAN}║ ║${NC}" echo -e "${CYAN}║ 【前端应用 / Frontend】 ║${NC}" echo -e "${YELLOW}║ http://localhost:${CONFIG[FrontendPort]}${NC}" echo -e "${CYAN}║ ★ 请在浏览器中打开此地址使用应用 ║${NC}" echo -e "${CYAN}║ ║${NC}" echo -e "${CYAN}║ 【后端服务 / Backend】 ║${NC}" echo -e "${NC}║ http://localhost:${CONFIG[BackendPort]}${NC}" echo -e "${CYAN}║ ║${NC}" echo -e "${CYAN}║ 【智能代理 / Agents】 ║${NC}" echo -e "${NC}║ 总管理代理 / Conductor: http://localhost:${CONFIG[ConductorPort]}${NC}" echo -e "${NC}║ 空调代理 / Air Conditioner: http://localhost:${CONFIG[AirCondPort]}${NC}" echo -e "${NC}║ 空气净化器 / Air Cleaner: http://localhost:${CONFIG[AirCleanPort]}${NC}" echo -e "${NC}║ 床头灯 / Bedside Lamp: http://localhost:${CONFIG[BedsideLampPort]}${NC}" echo -e "${CYAN}║ ║${NC}" echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}" echo } # ======================================== # 服务停止模块 - 统一停止所有服务 # ======================================== stop_all_services() { echo print_step "正在停止所有服务..." "Stopping all services..." # 停止所有启动的进程 for pid in "${PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then print_info "停止进程 PID: $pid" kill "$pid" 2>/dev/null || true fi done # 停止端口占用的进程 local ports=( "${CONFIG[FrontendPort]}" "${CONFIG[BackendPort]}" "${CONFIG[ConductorPort]}" "${CONFIG[AirCondPort]}" "${CONFIG[AirCleanPort]}" "${CONFIG[BedsideLampPort]}" ) for port in "${ports[@]}"; do # Linux if command -v lsof &> /dev/null; then local pid=$(lsof -ti:"$port" 2>/dev/null || true) if [ -n "$pid" ]; then print_info "停止端口 $port 的进程 (PID: $pid)" kill "$pid" 2>/dev/null || true fi # macOS alternative elif command -v netstat &> /dev/null; then local pid=$(netstat -vanp tcp 2>/dev/null | grep "\.$port " | awk '{print $9}' | head -1) if [ -n "$pid" ]; then print_info "停止端口 $port 的进程 (PID: $pid)" kill "$pid" 2>/dev/null || true fi fi done print_success "所有服务已停止" print_success "All services stopped" } # ======================================== # 信号处理 - 捕获 Ctrl+C 等中断信号 # ======================================== cleanup() { stop_all_services exit 0 } trap cleanup SIGINT SIGTERM # ======================================== # 主流程 # ======================================== main() { clear print_section "智能家居代理系统启动脚本 Smart Home Agent System Startup Script" check_environment find_project_root initialize_config initialize_directories initialize_python_environment initialize_frontend_dependencies start_all_services show_service_info echo -e "${YELLOW}提示:按 Ctrl+C 停止所有服务并退出${NC}" echo -e "${YELLOW}Note: Press Ctrl+C to stop all services and exit${NC}" echo # 等待用户中断 wait } # 运行主程序 main