fischer-agentkit/scripts/dev-start.sh

501 lines
18 KiB
Bash
Executable File
Raw 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
# =============================================================================
# Fischer AgentKit — 本地开发环境一键启动
# =============================================================================
#
# 启动:后端服务(+ 可选 Tauri 桌面客户端)
# 停止Ctrl+C 或运行 scripts/dev-stop.sh
#
# 用法:
# bash scripts/dev-start.sh # Web 模式agentkit gui
# bash scripts/dev-start.sh --tauri # Web + Tauri 桌面客户端
# bash scripts/dev-start.sh --serve # 仅后端 API
# bash scripts/dev-start.sh --init # 首次运行(初始化 DB
# bash scripts/dev-start.sh --help # 帮助
#
# 依赖:
# Python >= 3.11, Node.js >= 18, Redis, PostgreSQL (均自动检查)
# --tauri 需要Rust 工具链rustup / brew install rust
#
# 端口(与 .env.dev 保持一致):
# 18001 — AgentKit 后端 API
# 18002 — Web GUI前端 + 内置静态服务)
# 15173 — Vite 开发服务器(--tauri 模式)
# 15174 — Vite HMR websocket
# 6381 — Redisagentkit 专属容器,避免与 pms-redis:6379 / geo_redis:6380 冲突)
# 5435 — PostgreSQL+pgvectoragentkit 专属容器,避免与 geo_db:5433 冲突)
#
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_ROOT"
# ── 颜色 ────────────────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
# ── 帮助 ────────────────────────────────────────────────────────────────────
show_help() {
cat <<-'EOF'
Fischer AgentKit — 本地开发环境启动
用法: bash scripts/dev-start.sh [选项]
选项:
--tauri 启动 Tauri 桌面客户端Vite 热重载 + 原生窗口)
--serve 仅启动后端 API无前端
--init 首次运行,初始化数据库和测试用户
--help 显示此帮助
模式说明:
默认 Web 模式agentkit gui (前后端 + 内置静态服务)
--tauri Tauri 模式:后端 API + Vite (:15173) + Tauri 桌面窗口
端口映射:
18001 — 后端 API
18002 — Web GUI / 前端静态服务
15173 — Vite 开发服务器(--tauri 模式)
6381 — Redisagentkit 专属容器)
5435 — PostgreSQL+pgvectoragentkit 专属容器)
EOF
}
# ── 参数解析 ────────────────────────────────────────────────────────────────
MODE="gui"
INIT_DB=0
while [[ $# -gt 0 ]]; do
case $1 in
--tauri) MODE="tauri"; shift ;;
--serve) MODE="serve"; shift ;;
--init) INIT_DB=1; shift ;;
--help|-h) show_help; exit 0 ;;
*) shift ;;
esac
done
# ── 日志函数 ────────────────────────────────────────────────────────────────
ok() { echo -e " ${GREEN}OK${NC} $*"; }
info() { echo -e " ${CYAN}${NC} $*"; }
warn() { echo -e " ${YELLOW}!${NC} $*"; }
fail() { echo -e " ${RED}FAIL${NC} $*" >&2; }
section() {
echo ""
echo -e "${CYAN}────────────────────────────────────────${NC}"
echo -e "${CYAN} $*${NC}"
echo -e "${CYAN}────────────────────────────────────────${NC}"
}
# ── 进度状态机 ──────────────────────────────────────────────────────────────
# 0=未开始, 1=进行中, 2=成功, 3=失败
S_DEPS=0 S_ENV=0 S_REDIS=0 S_PG=0 S_BACKEND=0 S_FRONTEND=0 S_TAURI=0
set_status() {
local step=$1 val=$2
case $step in
deps) S_DEPS=$val ;;
env) S_ENV=$val ;;
redis) S_REDIS=$val ;;
pg) S_PG=$val ;;
backend) S_BACKEND=$val ;;
frontend) S_FRONTEND=$val ;;
tauri) S_TAURI=$val ;;
esac
}
print_status() {
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${CYAN} 启动状态总览${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "$([[ $S_DEPS -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_DEPS -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 依赖检查"
echo -e "$([[ $S_ENV -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_ENV -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 环境配置"
echo -e "$([[ $S_REDIS -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_REDIS -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") Redis (:6381)"
echo -e "$([[ $S_PG -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_PG -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") PostgreSQL (:5435)"
echo -e "$([[ $S_BACKEND -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_BACKEND -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 后端服务 (:18001)"
if [[ $MODE == "gui" || $MODE == "tauri" ]]; then
echo -e "$([[ $S_FRONTEND -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_FRONTEND -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") 前端服务 (:18002)"
fi
if [[ $MODE == "tauri" ]]; then
echo -e "$([[ $S_TAURI -eq 2 ]] && echo " ${GREEN}OK${NC}" || [[ $S_TAURI -eq 3 ]] && echo " ${RED}FAIL${NC}" || echo " ${YELLOW}..${NC}") Tauri 客户端"
fi
}
# ── 前置检查 ────────────────────────────────────────────────────────────────
check_deps() {
section "检查依赖"
set_status deps 1
for cmd in python3 node npm; do
if ! command -v "$cmd" &>/dev/null; then
fail "缺少依赖: $cmd"
set_status deps 3
return 1
fi
done
ok "Python $(python3 --version 2>&1 | awk '{print $2}')"
ok "Node $(node --version 2>&1)"
ok "npm $(npm --version 2>&1)"
# Python 版本 >= 3.11
if ! python3 -c 'import sys; sys.exit(0 if (sys.version_info.major, sys.version_info.minor) >= (3, 11) else 1)'; then
fail "Python 版本需 >= 3.11"
set_status deps 3
return 1
fi
# Tauri 模式需要 Rust
if [[ $MODE == "tauri" ]]; then
if ! command -v rustc &>/dev/null; then
warn "未检测到 RustTauri 需要):请运行 brew install rust 或 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
else
ok "Rust $(rustc --version 2>&1 | awk '{print $2}')"
fi
fi
set_status deps 2
}
check_env() {
section "检查环境配置"
set_status env 1
if [[ ! -f .env ]]; then
warn "未找到 .env将使用环境变量默认值"
if [[ -f .env.example ]]; then
cp .env.example .env
ok "已从 .env.example 生成 .env"
fi
else
ok ".env 存在"
fi
set_status env 2
}
check_redis() {
section "检查 Redis"
set_status redis 1
# agentkit 专属容器映射到 6381避免与 pms-redis(6379) / geo_redis(6380) 冲突
if redis-cli -h 127.0.0.1 -p 6381 ping 2>/dev/null | grep -q PONG; then
ok "Redis 运行中 (127.0.0.1:6381, agentkit 专属容器)"
set_status redis 2
return 0
fi
warn "Redis 未运行 (6381),启动 agentkit 专属 Docker 容器..."
if docker compose -f docker-compose.yaml -f docker-compose.dev.yml up -d redis &>/dev/null; then
sleep 2
if redis-cli -h 127.0.0.1 -p 6381 ping 2>/dev/null | grep -q PONG; then
ok "Redis Docker 容器启动成功 (127.0.0.1:6381)"
set_status redis 2
return 0
fi
fi
fail "Redis 启动失败(请确保 Docker 运行中)"
set_status redis 3
return 1
}
check_postgres() {
section "检查 PostgreSQL"
set_status pg 1
# agentkit 专属容器映射到 5435避免与 geo_db(5433) 冲突
if PGPASSWORD=agentkit psql -h 127.0.0.1 -p 5435 -U agentkit -d agentkit -c "SELECT 1" &>/dev/null; then
ok "PostgreSQL 运行中 (127.0.0.1:5435, agentkit 专属容器)"
set_status pg 2
return 0
fi
warn "PostgreSQL 未运行 (5435),启动 agentkit 专属 Docker 容器..."
if docker compose -f docker-compose.yaml -f docker-compose.dev.yml up -d postgres &>/dev/null; then
sleep 3
if PGPASSWORD=agentkit psql -h 127.0.0.1 -p 5435 -U agentkit -d agentkit -c "SELECT 1" &>/dev/null; then
ok "PostgreSQL Docker 容器启动成功 (127.0.0.1:5435)"
set_status pg 2
return 0
fi
fi
warn "PostgreSQL 启动失败bitable 等功能可能受限,继续启动后端..."
set_status pg 2 # 不阻塞,继续
return 0
}
# ── 安装依赖 ────────────────────────────────────────────────────────────────
install_deps() {
section "安装依赖"
info "后端 Python 依赖..."
# 虚拟环境
if [[ ! -d .venv ]]; then
python3 -m venv .venv
ok "虚拟环境 .venv 创建完成"
fi
source .venv/bin/activate
pip install -q -U pip
pip install -e ".[dev]" -q
ok "后端依赖安装完成"
# 前端 npm 依赖
local FE_DIR="$PROJECT_ROOT/src/agentkit/server/frontend"
if [[ ! -d "$FE_DIR/node_modules" ]]; then
info "前端 npm 依赖..."
cd "$FE_DIR"
npm install
cd "$PROJECT_ROOT"
ok "前端依赖安装完成"
else
ok "前端 node_modules 已存在"
fi
}
# ── 启动后端 ────────────────────────────────────────────────────────────────
start_backend() {
section "启动后端服务"
set_status backend 1
info "启动后端 API (:18001)..."
source .venv/bin/activate
agentkit serve --port 18001 &
BACKEND_PID=$!
# 等待健康检查就绪(最多 60 秒)
info "等待后端就绪..."
local attempt=0
while [[ $attempt -lt 60 ]]; do
if curl -sf http://127.0.0.1:18001/api/v1/health &>/dev/null; then
ok "后端 API 就绪 (http://127.0.0.1:18001, PID $BACKEND_PID)"
set_status backend 2
return 0
fi
# 检查进程是否还活着
if ! kill -0 $BACKEND_PID 2>/dev/null; then
fail "后端进程意外退出"
set_status backend 3
return 1
fi
sleep 1
((attempt++))
[[ $((attempt % 10)) -eq 0 ]] && info "等待中... (${attempt}s)"
done
fail "后端启动超时60s 内未响应健康检查)"
kill $BACKEND_PID 2>/dev/null || true
set_status backend 3
return 1
}
# ── 启动 Web GUI ────────────────────────────────────────────────────────────
start_gui() {
section "启动 Web GUI"
set_status frontend 1
info "启动 Web GUI (:18002)..."
source .venv/bin/activate
agentkit gui --port 18002 &
GUI_PID=$!
# 等待就绪
info "等待 Web GUI 就绪..."
local attempt=0
while [[ $attempt -lt 60 ]]; do
if curl -sf http://127.0.0.1:18002/api/v1/health &>/dev/null; then
ok "Web GUI 就绪 (http://127.0.0.1:18002, PID $GUI_PID)"
set_status frontend 2
return 0
fi
if ! kill -0 $GUI_PID 2>/dev/null; then
fail "Web GUI 进程意外退出"
set_status frontend 3
return 1
fi
sleep 1
((attempt++))
[[ $((attempt % 10)) -eq 0 ]] && info "等待中... (${attempt}s)"
done
fail "Web GUI 启动超时"
kill $GUI_PID 2>/dev/null || true
set_status frontend 3
return 1
}
# ── 启动 Tauri ─────────────────────────────────────────────────────────────
start_tauri() {
section "启动 Tauri 桌面客户端"
set_status tauri 1
local FE_DIR="$PROJECT_ROOT/src/agentkit/server/frontend"
if ! command -v tauri &>/dev/null; then
warn "Tauri CLI 未安装,跳过桌面客户端"
info "安装方式npm install -g @tauri-apps/cli"
set_status tauri 2
return 0
fi
info "启动 Tauri 桌面客户端..."
info " (Vite → :15173, 后端 API → :18001)"
cd "$FE_DIR"
npm run tauri dev &
TAURI_PID=$!
info "Tauri 启动中(首次运行需要编译 Rust可能需要几分钟..."
ok "Tauri 进程已启动 (PID $TAURI_PID)"
info "桌面窗口将自动打开,如未打开请手动查看终端输出"
set_status tauri 2
cd "$PROJECT_ROOT"
}
# ── 数据库初始化 ────────────────────────────────────────────────────────────
init_db() {
section "初始化数据库"
info "创建测试用户..."
local SETUP_SCRIPT="$PROJECT_ROOT/src/agentkit/server/frontend/e2e/setup-test-user.py"
if [[ -f "$SETUP_SCRIPT" ]]; then
source .venv/bin/activate
if python3 "$SETUP_SCRIPT" 2>/dev/null; then
ok "测试用户创建完成"
else
warn "测试用户创建失败(可能已存在或数据库未就绪)"
fi
fi
}
# ── 停止服务 ────────────────────────────────────────────────────────────────
stop_services() {
echo ""
info "正在停止所有服务..."
for port in 18001 18002 15173 15174; do
local pid=$(lsof -ti :$port 2>/dev/null || true)
if [[ -n "$pid" ]]; then
kill $pid 2>/dev/null && ok "端口 $port 已停止" || true
fi
done
# Vite 进程
pkill -f "vite" 2>/dev/null && ok "Vite 进程已停止" || true
echo ""
ok "所有服务已停止。感谢使用!"
}
# ── 主流程 ─────────────────────────────────────────────────────────────────
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo -e "${CYAN} Fischer AgentKit — 本地开发环境启动${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
# 检查依赖
if ! check_deps; then
echo ""
fail "依赖检查失败,请先安装缺失的依赖"
exit 1
fi
check_env
# 初始化数据库
[[ $INIT_DB -eq 1 ]] && init_db
# 安装依赖
install_deps
# 检查中间件
check_redis || true
check_postgres || true
echo ""
# ── 启动服务 ────────────────────────────────────────────────────────────────
FAILED=0
case $MODE in
serve)
if ! start_backend; then FAILED=1; fi
;;
gui)
if ! start_backend; then FAILED=1; fi
if [[ $FAILED -eq 0 ]] && ! start_gui; then FAILED=1; fi
;;
tauri)
if ! start_backend; then FAILED=1; fi
if [[ $FAILED -eq 0 ]]; then
start_tauri
fi
;;
esac
# ── 状态总览 + 启动完成 ──────────────────────────────────────────────────────
print_status
echo ""
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
if [[ $FAILED -eq 0 ]]; then
echo -e "${GREEN} 所有服务启动成功!${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
if [[ $MODE == "gui" ]]; then
echo -e " Web GUI: ${GREEN}http://localhost:18002${NC}"
echo " (在浏览器中打开,或直接在 http://localhost:18002 访问)"
elif [[ $MODE == "tauri" ]]; then
echo -e " 后端 API: ${GREEN}http://localhost:18001${NC}"
echo -e " Vite 热重载: ${GREEN}http://localhost:15173${NC}"
echo " Tauri 桌面窗口应已自动打开"
elif [[ $MODE == "serve" ]]; then
echo -e " 后端 API: ${GREEN}http://localhost:18001${NC}"
fi
echo ""
echo -e " ${YELLOW}按 Ctrl+C 停止所有服务${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
else
echo -e "${RED} 服务启动失败,请查看上方错误信息${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════${NC}"
echo ""
echo -e " 诊断命令:"
echo -e " 查看日志: ${CYAN}curl http://127.0.0.1:18001/api/v1/health${NC}"
echo -e " 停止服务: ${CYAN}bash scripts/dev-stop.sh${NC}"
fi
echo ""
# 注册退出钩子
trap stop_services EXIT INT TERM
# 保持脚本运行
if [[ $MODE == "tauri" ]]; then
# Tauri 模式:等待 Tauri 进程
wait
elif [[ $MODE == "gui" || $MODE == "serve" ]]; then
# 等待后端进程
wait $BACKEND_PID 2>/dev/null || true
fi