setup.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. #!/usr/bin/env bash
  2. # ============================================================
  3. # LambdAgent PaaS 一键安装启动脚本
  4. # 用法:
  5. # chmod +x setup.sh
  6. # ./setup.sh # 安装并启动(自动识别 发行包/源码 两种模式)
  7. # ./setup.sh --build # 构建普通 wheel 包到 dist/(含 .py 源码)
  8. # ./setup.sh --build-secure # 用 Nuitka 编译成 .so wheel 到 dist-secure/(防逆向,仍需 Python)
  9. # ./setup.sh --build-binary # 用 Nuitka 打成单文件二进制 dist-binary/(防逆向 + 免装 Python)
  10. # ./setup.sh --package # 打出「不含源码」的发行包 release/(优先用编译版)
  11. # ./setup.sh --docker # 使用 Docker 启动
  12. # ./setup.sh --help # 查看帮助
  13. #
  14. # 安装模式(install_deps 自动判定):
  15. # 发行模式 dist/*.whl 存在 → 从预编译 wheel 安装(终端用户拿到的包用这种)
  16. # 开发模式 无 wheel 但有源码目录 → editable 安装(开发者本机)
  17. # ============================================================
  18. set -e
  19. # ── 颜色输出 ──────────────────────────────────────────────
  20. RED='\033[0;31m'
  21. GREEN='\033[0;32m'
  22. YELLOW='\033[1;33m'
  23. BLUE='\033[0;34m'
  24. NC='\033[0m' # No Color
  25. info() { echo -e "${BLUE}[INFO]${NC} $1"; }
  26. ok() { echo -e "${GREEN}[OK]${NC} $1"; }
  27. warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
  28. fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; }
  29. # ── 项目根目录 ────────────────────────────────────────────
  30. PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
  31. cd "$PROJECT_DIR"
  32. # ── 帮助信息 ──────────────────────────────────────────────
  33. show_help() {
  34. echo ""
  35. echo "LambdAgent PaaS 一键安装启动脚本"
  36. echo ""
  37. echo "用法:"
  38. echo " ./setup.sh 安装依赖并启动服务(自动识别发行/源码模式)"
  39. echo " ./setup.sh --build 构建普通 wheel 包 → dist/*.whl(仍含 .py 源码)"
  40. echo " ./setup.sh --build-secure Nuitka 编译成 .so wheel → dist-secure/(防逆向,仍需 Python)"
  41. echo " ./setup.sh --build-binary Nuitka 打成单文件二进制 → dist-binary/(防逆向 + 免装 Python)"
  42. echo " ./setup.sh --package 打出不含源码的发行包 → release/(有编译版则优先用)"
  43. echo " ./setup.sh --docker 使用 Docker Compose 启动"
  44. echo " ./setup.sh --install 仅安装依赖,不启动服务"
  45. echo " ./setup.sh --launch 安装并一键启动 Agent 对话"
  46. echo " ./setup.sh --help 显示此帮助信息"
  47. echo ""
  48. echo "发布给终端用户的流程(开发者执行):"
  49. echo " ./setup.sh --build # 在源码树里先构建 wheel"
  50. echo " ./setup.sh --package # 组装 release/,里面只有 wheel + 启动脚本,无源码"
  51. echo " 然后把 release/ 压缩发给用户;用户解压后跑 ./setup.sh 即可"
  52. echo ""
  53. echo "环境要求:"
  54. echo " Python 方式: Python 3.10+"
  55. echo " Docker 方式: Docker + Docker Compose"
  56. echo ""
  57. }
  58. # ── 检查 Python ───────────────────────────────────────────
  59. check_python() {
  60. if command -v python3 &>/dev/null; then
  61. PYTHON=python3
  62. elif command -v python &>/dev/null; then
  63. PYTHON=python
  64. else
  65. fail "未找到 Python。请安装 Python 3.10+\n macOS: brew install python@3.12\n Ubuntu: sudo apt install python3 python3-venv python3-pip"
  66. fi
  67. # 检查版本
  68. PY_VERSION=$($PYTHON -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
  69. PY_MAJOR=$($PYTHON -c 'import sys; print(sys.version_info.major)')
  70. PY_MINOR=$($PYTHON -c 'import sys; print(sys.version_info.minor)')
  71. if [ "$PY_MAJOR" -lt 3 ] || { [ "$PY_MAJOR" -eq 3 ] && [ "$PY_MINOR" -lt 10 ]; }; then
  72. fail "Python 版本过低: $PY_VERSION(需要 3.10+)"
  73. fi
  74. ok "Python $PY_VERSION"
  75. }
  76. # ── 检查 Docker ───────────────────────────────────────────
  77. check_docker() {
  78. if ! command -v docker &>/dev/null; then
  79. fail "未找到 Docker。请先安装 Docker Desktop\n https://www.docker.com/products/docker-desktop/"
  80. fi
  81. if ! docker info &>/dev/null 2>&1; then
  82. fail "Docker 未运行。请启动 Docker Desktop"
  83. fi
  84. if ! command -v docker-compose &>/dev/null && ! docker compose version &>/dev/null 2>&1; then
  85. fail "未找到 Docker Compose"
  86. fi
  87. ok "Docker $(docker --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')"
  88. }
  89. # ── 配置 .env 文件 ────────────────────────────────────────
  90. setup_env() {
  91. if [ ! -f .env ]; then
  92. if [ -f .env.example ]; then
  93. cp .env.example .env
  94. warn ".env 文件已从 .env.example 创建"
  95. echo ""
  96. echo " ┌─────────────────────────────────────────────┐"
  97. echo " │ 请编辑 .env 文件,填入你的 LLM API Key: │"
  98. echo " │ │"
  99. echo " │ 至少填写以下其中一个: │"
  100. echo " │ DASHSCOPE_API_KEY=sk-xxx │"
  101. echo " │ OPENAI_API_KEY=sk-xxx │"
  102. echo " │ ANTHROPIC_API_KEY=sk-xxx │"
  103. echo " │ DEEPSEEK_API_KEY=sk-xxx │"
  104. echo " │ │"
  105. echo " │ 编辑命令: nano .env 或 vim .env │"
  106. echo " └─────────────────────────────────────────────┘"
  107. echo ""
  108. read -p "是否现在编辑 .env 文件?(y/N) " -r
  109. if [[ $REPLY =~ ^[Yy]$ ]]; then
  110. if command -v nano &>/dev/null; then
  111. nano .env
  112. elif command -v vim &>/dev/null; then
  113. vim .env
  114. else
  115. info "请手动编辑 .env 文件后重新运行此脚本"
  116. exit 0
  117. fi
  118. fi
  119. fi
  120. else
  121. ok ".env 文件已存在"
  122. fi
  123. }
  124. # ── 构建发行 wheel 包 ─────────────────────────────────────
  125. # 把 lambdagent / agentpaas 两个子包构建成 .whl 放进 dist/。
  126. # wheel 是「打包后的安装件」——发给用户的不再是整个源码树(含 .git、
  127. # 测试、商业计划书等),而是这两个 whl。注意:普通 wheel 内部仍是 .py
  128. # 源文件,它解决的是「别再把整个仓库 editable 送人」,不是代码混淆;
  129. # 真要防逆向需要 Nuitka 编译,见 docs/DESKTOP_MANUAL_zh.md「源码保护」一节。
  130. build_wheels() {
  131. check_python
  132. if [ ! -d lambdagent ] || [ ! -d agentpaas ]; then
  133. fail "未找到源码目录(lambdagent/ agentpaas/)。--build 只能在源码树里执行。"
  134. fi
  135. # 构建 wheel 走 PEP 517 构建隔离,不依赖 .venv —— 直接用 $PYTHON 即可。
  136. info "构建 wheel 包 → dist/ ..."
  137. mkdir -p dist
  138. rm -f dist/lambdagent-*.whl dist/agentpaas-*.whl
  139. $PYTHON -m pip wheel --no-deps ./lambdagent -w dist/ --quiet
  140. $PYTHON -m pip wheel --no-deps ./agentpaas -w dist/ --quiet
  141. echo ""
  142. ok "wheel 构建完成:"
  143. ls -1 dist/lambdagent-*.whl dist/agentpaas-*.whl 2>/dev/null | sed 's/^/ /'
  144. echo ""
  145. echo " 下一步: ./setup.sh --package 组装不含源码的发行包"
  146. echo ""
  147. }
  148. # ── 组装「不含源码」的发行包 ──────────────────────────────
  149. # 产出 release/:只含 wheel + 启动脚本 + 前端静态产物 + 配置样例 + 文档,
  150. # 不含 lambdagent/src、agentpaas/src、tests、.git、商业计划书等。
  151. # 把 release/ 压缩后发给终端用户即可。
  152. package_release() {
  153. # 优先用编译版(dist-secure/,.so wheel,防逆向);没有则退回普通 dist/
  154. local WHEEL_SRC=dist
  155. if ls dist-secure/lambdagent-*.whl >/dev/null 2>&1 && ls dist-secure/agentpaas-*.whl >/dev/null 2>&1; then
  156. WHEEL_SRC=dist-secure
  157. info "检测到编译版 wheel,发行包将使用 dist-secure/(.so,防逆向)。"
  158. elif ! ls dist/lambdagent-*.whl >/dev/null 2>&1 || ! ls dist/agentpaas-*.whl >/dev/null 2>&1; then
  159. warn "未找到 wheel,先执行普通构建(如需防逆向请改用 ./setup.sh --build-secure)..."
  160. build_wheels
  161. fi
  162. # 从 agentpaas/pyproject.toml 取版本号(兼容无源码时回退到 wheel 文件名)
  163. local VER
  164. if [ -f agentpaas/pyproject.toml ]; then
  165. VER=$(grep -m1 '^version' agentpaas/pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')
  166. else
  167. VER=$(ls dist/agentpaas-*.whl 2>/dev/null | sed -E 's/.*agentpaas-([0-9][^-]*)-.*/\1/' | head -1)
  168. fi
  169. [ -z "$VER" ] && VER="dev"
  170. info "组装发行包 → release/ ..."
  171. rm -rf release
  172. mkdir -p release/dist
  173. cp "$WHEEL_SRC"/lambdagent-*.whl "$WHEEL_SRC"/agentpaas-*.whl release/dist/
  174. cp setup.sh release/
  175. chmod +x release/setup.sh
  176. # 前端静态产物(已编译,无源码)
  177. [ -d webui-dist ] && cp -R webui-dist release/
  178. # 用户需要的配置样例 / 编排文件 / 文档(均非核心源码)
  179. [ -f .env.example ] && cp .env.example release/
  180. [ -f docker-compose.yml ] && cp docker-compose.yml release/
  181. [ -f Dockerfile ] && cp Dockerfile release/ 2>/dev/null || true
  182. [ -d agentexample ] && cp -R agentexample release/
  183. mkdir -p release/docs
  184. [ -f docs/DESKTOP_MANUAL_zh.md ] && cp docs/DESKTOP_MANUAL_zh.md release/docs/
  185. [ -f QUICK_START.md ] && cp QUICK_START.md release/
  186. [ -f README.md ] && cp README.md release/
  187. [ -f LICENSE ] && cp LICENSE release/
  188. # 清掉 macOS 垃圾文件
  189. find release -name '.DS_Store' -delete 2>/dev/null || true
  190. echo ""
  191. ok "发行包已生成: release/"
  192. echo " 内容(不含任何 src 源码 / .git / 内部文档):"
  193. (cd release && find . -maxdepth 2 -type f | sed 's/^/ /' | head -30)
  194. echo ""
  195. echo " 压缩发布: zip -r lambdagent-paas-${VER}.zip release/"
  196. echo " 用户使用: 解压 → cd release → ./setup.sh"
  197. echo ""
  198. }
  199. # ── 创建虚拟环境并安装依赖 ────────────────────────────────
  200. # 自动判定两种模式:
  201. # 发行模式: 同目录 dist/*.whl 存在 → 从预编译 wheel 安装(终端用户)
  202. # 开发模式: 无 wheel 但有源码目录 → editable 安装(开发者本机)
  203. install_deps() {
  204. info "创建 Python 虚拟环境..."
  205. if [ ! -d .venv ]; then
  206. $PYTHON -m venv .venv
  207. ok "虚拟环境已创建: .venv/"
  208. else
  209. ok "虚拟环境已存在: .venv/"
  210. fi
  211. # 激活虚拟环境
  212. source .venv/bin/activate
  213. pip install --quiet --upgrade pip >/dev/null 2>&1
  214. if ls dist/lambdagent-*.whl >/dev/null 2>&1 && ls dist/agentpaas-*.whl >/dev/null 2>&1; then
  215. # ── 发行模式: 从 wheel 安装 ──
  216. info "检测到预编译 wheel,按【发行模式】安装..."
  217. # 先装 lambdagent(agentpaas 依赖它),再装 agentpaas;
  218. # 第三方依赖(fastapi 等)由 pip 从 PyPI 拉取。
  219. pip install --quiet --upgrade dist/lambdagent-*.whl
  220. ok "lambdagent 已从 wheel 安装"
  221. pip install --quiet --upgrade dist/agentpaas-*.whl
  222. ok "agentpaas 已从 wheel 安装"
  223. elif [ -d lambdagent ] && [ -d agentpaas ]; then
  224. # ── 开发模式: editable 源码安装 ──
  225. info "未检测到 wheel,按【开发模式】editable 安装源码..."
  226. # 修复历史 bug: 原为 `pip install -e ".[all]"`(root 无 pyproject,
  227. # 实际安装失败),lambdagent 真实路径是 ./lambdagent。
  228. pip install -e "./lambdagent[all]" --quiet 2>&1 | tail -1
  229. ok "lambdagent 已安装(editable)"
  230. pip install -e "./agentpaas[dev]" --quiet 2>&1 | tail -1
  231. ok "agentpaas 已安装(editable)"
  232. else
  233. fail "既无 dist/*.whl 也无源码目录(lambdagent/ agentpaas/),无法安装。"
  234. fi
  235. echo ""
  236. ok "所有依赖安装完成!"
  237. echo ""
  238. echo " 激活虚拟环境: source .venv/bin/activate"
  239. echo ""
  240. }
  241. # ── Python 方式启动 ───────────────────────────────────────
  242. start_python() {
  243. source .venv/bin/activate
  244. # 加载 .env
  245. if [ -f .env ]; then
  246. set -a
  247. source .env
  248. set +a
  249. fi
  250. info "启动 AgentPaaS 服务(开发模式)..."
  251. echo ""
  252. echo " ┌─────────────────────────────────────────────┐"
  253. echo " │ 服务地址: http://localhost:${AGENTPAAS_PORT:-8000} │"
  254. echo " │ API 文档: http://localhost:${AGENTPAAS_PORT:-8000}/docs │"
  255. echo " │ 按 Ctrl+C 停止服务 │"
  256. echo " └─────────────────────────────────────────────┘"
  257. echo ""
  258. python -m agentpaas serve --dev
  259. }
  260. # ── Docker 方式启动 ───────────────────────────────────────
  261. start_docker() {
  262. check_docker
  263. setup_env
  264. info "使用 Docker Compose 启动..."
  265. # 使用根目录的 docker-compose
  266. if docker compose version &>/dev/null 2>&1; then
  267. COMPOSE_CMD="docker compose"
  268. else
  269. COMPOSE_CMD="docker-compose"
  270. fi
  271. $COMPOSE_CMD -f docker-compose.yml up --build
  272. }
  273. # ── 一键启动 Agent 对话 ───────────────────────────────────
  274. launch_agent() {
  275. source .venv/bin/activate
  276. # 加载 .env
  277. if [ -f .env ]; then
  278. set -a
  279. source .env
  280. set +a
  281. fi
  282. CONFIG_FILE="${1:-agent-config.yml}"
  283. if [ ! -f "$CONFIG_FILE" ]; then
  284. fail "Agent 配置文件不存在: $CONFIG_FILE\n 请创建配置文件或指定路径: ./setup.sh --launch your-config.yml"
  285. fi
  286. info "一键启动 Agent: $CONFIG_FILE"
  287. python -m agentpaas launch "$CONFIG_FILE"
  288. }
  289. # ── 主流程 ────────────────────────────────────────────────
  290. main() {
  291. echo ""
  292. echo "================================================"
  293. echo " LambdAgent PaaS 一键安装启动"
  294. echo "================================================"
  295. echo ""
  296. case "${1:-}" in
  297. --help|-h)
  298. show_help
  299. exit 0
  300. ;;
  301. --build)
  302. build_wheels
  303. ;;
  304. --build-secure)
  305. check_python
  306. bash scripts/build_secure.sh "$PYTHON"
  307. ;;
  308. --build-binary)
  309. check_python
  310. bash scripts/build_binary.sh "$PYTHON"
  311. ;;
  312. --package)
  313. package_release
  314. ;;
  315. --docker)
  316. start_docker
  317. ;;
  318. --install)
  319. check_python
  320. setup_env
  321. install_deps
  322. echo "安装完成! 启动服务请运行:"
  323. echo " source .venv/bin/activate"
  324. echo " python -m agentpaas serve --dev"
  325. ;;
  326. --launch)
  327. check_python
  328. setup_env
  329. install_deps
  330. launch_agent "${2:-}"
  331. ;;
  332. *)
  333. check_python
  334. setup_env
  335. install_deps
  336. start_python
  337. ;;
  338. esac
  339. }
  340. main "$@"