start.sh 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #!/usr/bin/env bash
  2. # ============================================================
  3. # LambdaAgentPaaS — Mac/Linux 一键启动脚本
  4. #
  5. # 用法:
  6. # ./deploy/start.sh # 正常启动(首次会自动初始化)
  7. # ./deploy/start.sh --build # 强制重新构建镜像
  8. # ./deploy/start.sh --reset # 清除所有数据重新初始化(谨慎!)
  9. #
  10. # 要求: Docker Desktop 已安装并运行
  11. # ============================================================
  12. set -euo pipefail
  13. # ── 路径常量 ─────────────────────────────────────────────────
  14. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  15. PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
  16. DATA_DIR="${HOME}/.agentpaas/data"
  17. ENV_FILE="${HOME}/.agentpaas/.env"
  18. APP_PORT="${AGENTPAAS_PORT:-8000}"
  19. APP_URL="http://localhost:${APP_PORT}"
  20. # ── 颜色输出 ─────────────────────────────────────────────────
  21. RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
  22. BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
  23. info() { echo -e "${BLUE}ℹ ${NC}$*"; }
  24. success() { echo -e "${GREEN}✅ $*${NC}"; }
  25. warn() { echo -e "${YELLOW}⚠️ $*${NC}"; }
  26. error() { echo -e "${RED}❌ $*${NC}" >&2; }
  27. header() { echo -e "\n${BOLD}${CYAN}$*${NC}\n"; }
  28. # ── 参数解析 ─────────────────────────────────────────────────
  29. BUILD_FLAG=""
  30. RESET_FLAG=""
  31. for arg in "$@"; do
  32. case $arg in
  33. --build) BUILD_FLAG="--build" ;;
  34. --reset) RESET_FLAG="yes" ;;
  35. esac
  36. done
  37. # ── 检查 Docker ──────────────────────────────────────────────
  38. check_docker() {
  39. if ! command -v docker &>/dev/null; then
  40. error "未找到 Docker。请先安装 Docker Desktop:"
  41. echo " https://docs.docker.com/desktop/install/mac-install/"
  42. echo ""
  43. if [[ "$OSTYPE" == "darwin"* ]]; then
  44. read -p "是否现在打开下载页面? (y/N) " yn
  45. [[ "$yn" =~ ^[Yy]$ ]] && open "https://docs.docker.com/desktop/install/mac-install/"
  46. fi
  47. exit 1
  48. fi
  49. if ! docker info &>/dev/null 2>&1; then
  50. info "Docker Desktop 未运行,正在启动..."
  51. if [[ "$OSTYPE" == "darwin"* ]]; then
  52. open -a "Docker Desktop" 2>/dev/null || open -a "Docker" 2>/dev/null || true
  53. fi
  54. echo -n "等待 Docker 就绪"
  55. local wait=0
  56. until docker info &>/dev/null 2>&1; do
  57. echo -n "."
  58. sleep 2
  59. ((wait+=2))
  60. if (( wait > 60 )); then
  61. echo ""
  62. error "Docker 启动超时(60s)。请手动启动 Docker Desktop 后重试。"
  63. exit 1
  64. fi
  65. done
  66. echo ""
  67. success "Docker 已就绪"
  68. fi
  69. }
  70. # ── 检查 docker compose ──────────────────────────────────────
  71. check_compose() {
  72. if docker compose version &>/dev/null 2>&1; then
  73. COMPOSE_CMD="docker compose"
  74. elif command -v docker-compose &>/dev/null; then
  75. COMPOSE_CMD="docker-compose"
  76. else
  77. error "未找到 docker compose。请升级 Docker Desktop 到最新版本。"
  78. exit 1
  79. fi
  80. }
  81. # ── 初始化数据目录 ────────────────────────────────────────────
  82. init_data_dir() {
  83. mkdir -p "${DATA_DIR}/instances"
  84. mkdir -p "${DATA_DIR}/knowledge_bases"
  85. mkdir -p "${DATA_DIR}/logs"
  86. # 限制 data 目录权限(避免被其他用户读取)
  87. chmod 700 "${HOME}/.agentpaas" 2>/dev/null || true
  88. }
  89. # ── 初始化 .env 配置文件 ─────────────────────────────────────
  90. init_env() {
  91. if [[ -f "$ENV_FILE" && -z "$RESET_FLAG" ]]; then
  92. return 0 # 已存在且不是 reset 模式
  93. fi
  94. header "🔧 首次配置初始化"
  95. mkdir -p "$(dirname "$ENV_FILE")"
  96. # 复制模板
  97. cp "${PROJECT_DIR}/.env.template" "$ENV_FILE"
  98. # 自动生成 MASTER_KEY(安全随机 64 字节 hex)
  99. if command -v python3 &>/dev/null; then
  100. MASTER_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
  101. else
  102. MASTER_KEY=$(openssl rand -hex 32)
  103. fi
  104. # 写入 DATA_DIR 和 MASTER_KEY
  105. sed -i.bak "s|^AGENTPAAS_DATA_DIR=.*|AGENTPAAS_DATA_DIR=${DATA_DIR}|" "$ENV_FILE"
  106. sed -i.bak "s|^AGENTPAAS_MASTER_KEY=.*|AGENTPAAS_MASTER_KEY=${MASTER_KEY}|" "$ENV_FILE"
  107. rm -f "${ENV_FILE}.bak"
  108. success "配置文件已创建: $ENV_FILE"
  109. echo ""
  110. warn "请编辑 ${ENV_FILE} 填写至少一个 LLM API Key:"
  111. echo " DASHSCOPE_API_KEY= (阿里云百炼,国内推荐)"
  112. echo " ANTHROPIC_API_KEY= (Claude)"
  113. echo " OPENAI_API_KEY= (GPT)"
  114. echo ""
  115. echo "编辑完成后重新运行此脚本。"
  116. echo ""
  117. read -p "是否现在打开配置文件编辑? (Y/n) " yn
  118. yn="${yn:-Y}"
  119. if [[ "$yn" =~ ^[Yy]$ ]]; then
  120. # 尝试用系统默认编辑器打开
  121. ${EDITOR:-$(command -v nano || command -v vi || echo "open")} "$ENV_FILE"
  122. fi
  123. }
  124. # ── 检查是否配置了至少一个 API Key ───────────────────────────
  125. check_api_keys() {
  126. local has_key=false
  127. local key_vars=(DASHSCOPE_API_KEY OPENAI_API_KEY ANTHROPIC_API_KEY
  128. DEEPSEEK_API_KEY ZHIPU_API_KEY MOONSHOT_API_KEY)
  129. for var in "${key_vars[@]}"; do
  130. val=$(grep "^${var}=" "$ENV_FILE" 2>/dev/null | cut -d= -f2 | tr -d ' \r\n')
  131. if [[ -n "$val" ]]; then
  132. has_key=true
  133. break
  134. fi
  135. done
  136. if [[ "$has_key" == "false" ]]; then
  137. warn "未检测到任何 LLM API Key。"
  138. warn "Agent 功能将不可用,直到在 ${ENV_FILE} 中配置至少一个 API Key。"
  139. echo ""
  140. read -p "继续启动? (y/N) " yn
  141. [[ "$yn" =~ ^[Yy]$ ]] || exit 0
  142. fi
  143. }
  144. # ── 启动服务 ─────────────────────────────────────────────────
  145. start_services() {
  146. header "🚀 启动 LambdaAgentPaaS"
  147. cd "$PROJECT_DIR"
  148. # 将 DATA_DIR 导出供 docker-compose.yml 使用
  149. export AGENTPAAS_DATA_DIR="$DATA_DIR"
  150. # 加载 .env 中的其他变量
  151. set -a
  152. source "$ENV_FILE"
  153. set +a
  154. if [[ -n "$RESET_FLAG" ]]; then
  155. info "清除旧容器和镜像..."
  156. $COMPOSE_CMD down --remove-orphans 2>/dev/null || true
  157. docker rmi lambdagentpaas:latest 2>/dev/null || true
  158. fi
  159. info "构建并启动容器(首次构建约 3-5 分钟)..."
  160. $COMPOSE_CMD up -d ${BUILD_FLAG}
  161. }
  162. # ── 等待健康检查 ─────────────────────────────────────────────
  163. wait_for_health() {
  164. echo -n "等待服务就绪"
  165. local wait=0
  166. until curl -sf "${APP_URL}/health" &>/dev/null; do
  167. echo -n "."
  168. sleep 2
  169. ((wait+=2))
  170. if (( wait > 120 )); then
  171. echo ""
  172. error "服务启动超时(120s)"
  173. echo ""
  174. echo "查看日志:"
  175. echo " docker logs lambdagentpaas"
  176. exit 1
  177. fi
  178. done
  179. echo ""
  180. }
  181. # ── 打印成功信息 ─────────────────────────────────────────────
  182. print_success() {
  183. echo ""
  184. echo -e "${GREEN}╔══════════════════════════════════════════════╗${NC}"
  185. echo -e "${GREEN}║ 🎉 LambdaAgentPaaS 已成功启动! ║${NC}"
  186. echo -e "${GREEN}╠══════════════════════════════════════════════╣${NC}"
  187. echo -e "${GREEN}║ Web 界面: ${BOLD}${APP_URL}${NC}${GREEN} ║${NC}"
  188. echo -e "${GREEN}║ API 文档: ${APP_URL}/docs ║${NC}"
  189. echo -e "${GREEN}╠══════════════════════════════════════════════╣${NC}"
  190. echo -e "${GREEN}║ 数据目录: ${DATA_DIR}${NC}"
  191. echo -e "${GREEN}║ 配置文件: ${ENV_FILE}${NC}"
  192. echo -e "${GREEN}╠══════════════════════════════════════════════╣${NC}"
  193. echo -e "${GREEN}║ 停止服务: ./deploy/stop.sh ║${NC}"
  194. echo -e "${GREEN}║ 查看日志: docker logs -f lambdagentpaas ║${NC}"
  195. echo -e "${GREEN}╚══════════════════════════════════════════════╝${NC}"
  196. echo ""
  197. }
  198. # ── 主流程 ───────────────────────────────────────────────────
  199. main() {
  200. header "LambdaAgentPaaS 启动器"
  201. check_docker
  202. check_compose
  203. init_data_dir
  204. init_env
  205. check_api_keys
  206. start_services
  207. wait_for_health
  208. print_success
  209. # 自动打开浏览器
  210. if [[ "$OSTYPE" == "darwin"* ]]; then
  211. open "$APP_URL"
  212. elif command -v xdg-open &>/dev/null; then
  213. xdg-open "$APP_URL"
  214. fi
  215. }
  216. main "$@"