Add force rerun recovery and polish CLI startup

This commit is contained in:
rayd1o
2026-04-08 02:49:29 +08:00
parent 981617ee80
commit f5308340af
6 changed files with 546 additions and 137 deletions

293
planet.sh
View File

@@ -5,11 +5,30 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[38;5;203m'
GREEN='\033[38;5;114m'
YELLOW='\033[38;5;221m'
BLUE='\033[38;5;111m'
CYAN='\033[38;5;117m'
WHITE='\033[1;37m'
DIM='\033[2m'
NC='\033[0m'
WAIT_SPINNER_FRAMES=(
'⠉⠁'
'⠈⠉'
'⠀⠙'
'⠀⠸'
'⠀⢰'
'⠀⣠'
'⢀⣀'
'⣀⡀'
'⣄⠀'
'⡆⠀'
'⠇⠀'
'⠋⠀'
)
WAIT_SPINNER_STEP=0
WAIT_SPINNER_TICKS_PER_SECOND=8
BACKEND_MAX_RETRIES="${BACKEND_MAX_RETRIES:-3}"
BACKEND_HEALTH_CHECK_ATTEMPTS="${BACKEND_HEALTH_CHECK_ATTEMPTS:-10}"
@@ -37,6 +56,100 @@ compose_up() {
fi
}
render_wait_spinner() {
local message="$1"
local step="${2:-1}"
local frame_index=$(( (step - 1) % ${#WAIT_SPINNER_FRAMES[@]} ))
local frame="${WAIT_SPINNER_FRAMES[$frame_index]}"
printf "\r${DIM}·${NC} ${CYAN}%s${NC} ${WHITE}%s${NC}\033[K" "$frame" "$message"
}
animate_wait_spinner() {
local message="$1"
local duration="${2:-1}"
local ticks=$(( duration * WAIT_SPINNER_TICKS_PER_SECOND ))
local tick=0
if [ "$ticks" -lt 1 ]; then
ticks=1
fi
while [ "$tick" -lt "$ticks" ]; do
WAIT_SPINNER_STEP=$((WAIT_SPINNER_STEP + 1))
render_wait_spinner "$message" "$WAIT_SPINNER_STEP"
sleep 0.125
tick=$((tick + 1))
done
}
clear_wait_spinner() {
printf "\r\033[K"
}
finish_wait_spinner() {
log_line "done" "$GREEN" "$1"
}
log_line() {
local label="$1"
local color="$2"
local message="$3"
clear_wait_spinner
printf "${DIM}·${NC} ${color}%-4s${NC} ${WHITE}%s${NC}\n" "$label" "$message"
}
log_step() {
log_line "run" "$BLUE" "$1"
}
log_warn() {
log_line "warn" "$YELLOW" "$1"
}
log_error() {
log_line "fail" "$RED" "$1"
}
log_note() {
printf "${DIM} %s${NC}\n" "$1"
}
log_success() {
log_line "done" "$GREEN" "$1"
}
print_splash() {
clear_wait_spinner
printf "%b" "$CYAN"
cat <<'EOF'
___ _ _ _ _ _
|_ _|_ __ | |_ ___| | (_) __ _ ___ _ __ | |_
| || '_ \| __/ _ \ | | |/ _` |/ _ \ '_ \| __|
| || | | | || __/ | | | (_| | __/ | | | |_
|___|_| |_|\__\___|_|_|_|\__, |\___|_| |_|\__|
|___/
EOF
printf "%b\n" "$NC"
printf "%b" "$BLUE"
cat <<'EOF'
____ _ _
| _ \| | __ _ _ __ ___ | |_
| |_) | |/ _` | '_ \ / _ \| __|
| __/| | (_| | | | | __/| |_
|_| |_|\__,_|_| |_|\___| \__|
EOF
printf "%b\n" "$NC"
printf "%b" "$WHITE"
cat <<'EOF'
____ _
| _ \| | __ _ _ __
| |_) | |/ _` | '_ \
| __/| | (_| | | | |
|_| |_|\__,_|_| |_|
EOF
printf "%b\n\n" "$NC"
}
run_with_retry() {
local max_retries="$1"
local interval="$2"
@@ -52,12 +165,12 @@ run_with_retry() {
fi
if [ "$attempt" -eq "$max_retries" ]; then
echo -e "${RED}${failure_message}${NC}"
clear_wait_spinner
log_error "${failure_message}"
return 1
fi
echo -e "${YELLOW}⚠️ ${retry_label}${attempt}/${max_retries} 次失败,${interval} 秒后重试...${NC}"
sleep "$interval"
animate_wait_spinner "${retry_label}${attempt}/${max_retries} 次失败,${interval} 秒后重试" "$interval"
attempt=$((attempt + 1))
done
@@ -65,17 +178,17 @@ run_with_retry() {
}
ensure_uv_backend_deps() {
echo -e "${BLUE}📦 检查后端 uv 环境...${NC}"
log_step "检查后端 uv 环境"
if ! command -v uv >/dev/null 2>&1; then
echo -e "${RED}未找到 uv请先安装 uv 并加入 PATH${NC}"
log_error "未找到 uv请先安装 uv 并加入 PATH"
exit 1
fi
cd "$SCRIPT_DIR"
if [ ! -x "$SCRIPT_DIR/.venv/bin/python" ]; then
echo -e "${YELLOW}⚠️ 未检测到 .venv正在执行 uv sync...${NC}"
log_warn "未检测到 .venv正在执行 uv sync"
if ! run_with_retry \
"$DEPENDENCY_INSTALL_MAX_RETRIES" \
"$DEPENDENCY_INSTALL_RETRY_INTERVAL" \
@@ -87,23 +200,23 @@ ensure_uv_backend_deps() {
fi
if [ ! -x "$SCRIPT_DIR/.venv/bin/python" ]; then
echo -e "${RED}uv 环境初始化失败,未找到 .venv/bin/python${NC}"
log_error "uv 环境初始化失败,未找到 .venv/bin/python"
exit 1
fi
}
ensure_frontend_deps() {
echo -e "${BLUE}📦 检查前端依赖...${NC}"
log_step "检查前端依赖"
if ! command -v bun >/dev/null 2>&1; then
echo -e "${RED}未找到 bun请先安装或加载 bun 到 PATH${NC}"
log_error "未找到 bun请先安装或加载 bun 到 PATH"
exit 1
fi
cd "$SCRIPT_DIR/frontend"
if [ ! -x "$SCRIPT_DIR/frontend/node_modules/.bin/vite" ]; then
echo -e "${YELLOW}⚠️ 前端依赖缺失,正在执行 bun install...${NC}"
log_warn "前端依赖缺失,正在执行 bun install"
if ! run_with_retry \
"$DEPENDENCY_INSTALL_MAX_RETRIES" \
"$DEPENDENCY_INSTALL_RETRY_INTERVAL" \
@@ -115,7 +228,7 @@ ensure_frontend_deps() {
fi
if [ ! -x "$SCRIPT_DIR/frontend/node_modules/.bin/vite" ]; then
echo -e "${RED}前端依赖安装失败,未找到 vite${NC}"
log_error "前端依赖安装失败,未找到 vite"
exit 1
fi
}
@@ -129,14 +242,15 @@ wait_for_http() {
while [ "$attempt" -le "$attempts" ]; do
if curl -s "$url" > /dev/null 2>&1; then
finish_wait_spinner "${service_name}已就绪"
return 0
fi
echo -e "${YELLOW}等待${service_name}就绪...${NC}"
sleep "$interval"
animate_wait_spinner "等待${service_name}就绪" "$interval"
attempt=$((attempt + 1))
done
clear_wait_spinner
return 1
}
@@ -152,14 +266,15 @@ wait_for_container_health() {
status="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_name" 2>/dev/null || true)"
if [ "$status" = "healthy" ] || [ "$status" = "running" ]; then
finish_wait_spinner "${service_name}容器健康检查已通过"
return 0
fi
echo -e "${YELLOW}等待${service_name}容器健康检查通过...${NC}"
sleep "$interval"
animate_wait_spinner "等待${service_name}容器健康检查通过" "$interval"
attempt=$((attempt + 1))
done
clear_wait_spinner
return 1
}
@@ -173,15 +288,15 @@ wait_for_postgres_health() {
}
start_database_services() {
docker start planet_postgres planet_redis 2>/dev/null || compose_up up -d postgres redis
docker start planet_postgres planet_redis >/dev/null 2>&1 || compose_up up -d postgres redis >/dev/null 2>&1
}
restart_database_services() {
docker restart planet_postgres planet_redis 2>/dev/null || compose_up up -d postgres redis
docker restart planet_postgres planet_redis >/dev/null 2>&1 || compose_up up -d postgres redis >/dev/null 2>&1
}
start_postgres_service() {
docker start planet_postgres 2>/dev/null || compose_up up -d postgres
docker start planet_postgres >/dev/null 2>&1 || compose_up up -d postgres >/dev/null 2>&1
}
start_backend_with_retry() {
@@ -198,11 +313,12 @@ start_backend_with_retry() {
return 0
fi
echo -e "${YELLOW}⚠️ 后端第 ${retry}/${BACKEND_MAX_RETRIES} 次启动未就绪,准备重试...${NC}"
kill "$BACKEND_PID" 2>/dev/null || true
animate_wait_spinner "后端第 ${retry}/${BACKEND_MAX_RETRIES} 次启动未就绪,准备重试" "$BACKEND_HEALTH_CHECK_INTERVAL"
retry=$((retry + 1))
done
clear_wait_spinner
return 1
}
@@ -210,10 +326,10 @@ start_ai_provider_service() {
local ai_provider_port="${1:-$DEFAULT_AI_PROVIDER_PORT}"
local retry=1
echo -e "${BLUE}🧠 启动 AI Provider...${NC}"
log_step "启动 AI Provider"
while [ "$retry" -le "$AI_PROVIDER_START_MAX_RETRIES" ]; do
if docker start planet_aiprovider 2>/dev/null || compose_up up -d aiprovider; then
if docker start planet_aiprovider >/dev/null 2>&1 || compose_up up -d aiprovider >/dev/null 2>&1; then
if wait_for_container_health "planet_aiprovider" "$AI_PROVIDER_HEALTH_CHECK_ATTEMPTS" "$AI_PROVIDER_HEALTH_CHECK_INTERVAL" "AI Provider" &&
wait_for_http "http://localhost:${ai_provider_port}/health" "$AI_PROVIDER_HEALTH_CHECK_ATTEMPTS" "$AI_PROVIDER_HEALTH_CHECK_INTERVAL" "AI Provider"; then
return 0
@@ -221,18 +337,19 @@ start_ai_provider_service() {
fi
if [ "$retry" -eq "$AI_PROVIDER_START_MAX_RETRIES" ]; then
echo -e "${RED}❌ AI Provider 启动失败,已重试 ${AI_PROVIDER_START_MAX_RETRIES}${NC}"
clear_wait_spinner
log_error "AI Provider 启动失败,已重试 ${AI_PROVIDER_START_MAX_RETRIES}"
docker logs --tail 20 planet_aiprovider 2>/dev/null || true
exit 1
fi
echo -e "${YELLOW}⚠️ AI Provider 第 ${retry}/${AI_PROVIDER_START_MAX_RETRIES} 次启动后仍未健康,正在重启容器...${NC}"
docker restart planet_aiprovider 2>/dev/null || true
sleep "$AI_PROVIDER_RETRY_INTERVAL"
docker restart planet_aiprovider >/dev/null 2>&1 || true
animate_wait_spinner "AI Provider 第 ${retry}/${AI_PROVIDER_START_MAX_RETRIES} 次启动后仍未健康,正在重启容器" "$AI_PROVIDER_RETRY_INTERVAL"
retry=$((retry + 1))
done
echo -e "${RED}❌ AI Provider 启动失败${NC}"
clear_wait_spinner
log_error "AI Provider 启动失败"
docker logs --tail 20 planet_aiprovider 2>/dev/null || true
exit 1
}
@@ -252,11 +369,12 @@ start_frontend_with_retry() {
return 0
fi
echo -e "${YELLOW}⚠️ 前端第 ${retry}/${FRONTEND_MAX_RETRIES} 次启动未就绪,准备重试...${NC}"
kill "$FRONTEND_PID" 2>/dev/null || true
animate_wait_spinner "前端第 ${retry}/${FRONTEND_MAX_RETRIES} 次启动未就绪,准备重试" "$FRONTEND_HEALTH_CHECK_INTERVAL"
retry=$((retry + 1))
done
clear_wait_spinner
return 1
}
@@ -269,15 +387,15 @@ ensure_database_services_healthy() {
fi
if [ "$retry" -eq "$DATABASE_START_MAX_RETRIES" ]; then
echo -e "${RED}❌ 数据库启动失败,已重试 ${DATABASE_START_MAX_RETRIES}${NC}"
clear_wait_spinner
log_error "数据库启动失败,已重试 ${DATABASE_START_MAX_RETRIES}"
docker logs --tail 20 planet_postgres 2>/dev/null || true
docker logs --tail 20 planet_redis 2>/dev/null || true
exit 1
fi
echo -e "${YELLOW}⚠️ 数据库第 ${retry}/${DATABASE_START_MAX_RETRIES} 次启动后仍未健康,正在重启容器...${NC}"
docker restart planet_postgres planet_redis 2>/dev/null || true
sleep "$DATABASE_RETRY_INTERVAL"
docker restart planet_postgres planet_redis >/dev/null 2>&1 || true
animate_wait_spinner "数据库第 ${retry}/${DATABASE_START_MAX_RETRIES} 次启动后仍未健康,正在重启容器" "$DATABASE_RETRY_INTERVAL"
retry=$((retry + 1))
done
}
@@ -291,14 +409,14 @@ ensure_postgres_service_healthy() {
fi
if [ "$retry" -eq "$DATABASE_START_MAX_RETRIES" ]; then
echo -e "${RED}❌ PostgreSQL 启动失败,已重试 ${DATABASE_START_MAX_RETRIES}${NC}"
clear_wait_spinner
log_error "PostgreSQL 启动失败,已重试 ${DATABASE_START_MAX_RETRIES}"
docker logs --tail 20 planet_postgres 2>/dev/null || true
exit 1
fi
echo -e "${YELLOW}⚠️ PostgreSQL 第 ${retry}/${DATABASE_START_MAX_RETRIES} 次启动后仍未健康,正在重启容器...${NC}"
docker restart planet_postgres 2>/dev/null || true
sleep "$DATABASE_RETRY_INTERVAL"
docker restart planet_postgres >/dev/null 2>&1 || true
animate_wait_spinner "PostgreSQL 第 ${retry}/${DATABASE_START_MAX_RETRIES} 次启动后仍未健康,正在重启容器" "$DATABASE_RETRY_INTERVAL"
retry=$((retry + 1))
done
}
@@ -306,7 +424,7 @@ ensure_postgres_service_healthy() {
restart_database_service() {
local retry=1
echo -e "${BLUE}🗄️ 重启数据库...${NC}"
log_step "重启数据库"
while [ "$retry" -le "$DATABASE_START_MAX_RETRIES" ]; do
if restart_database_services && wait_for_database_health; then
@@ -315,14 +433,14 @@ restart_database_service() {
fi
if [ "$retry" -eq "$DATABASE_START_MAX_RETRIES" ]; then
echo -e "${RED}❌ 数据库重启失败,已重试 ${DATABASE_START_MAX_RETRIES}${NC}"
clear_wait_spinner
log_error "数据库重启失败,已重试 ${DATABASE_START_MAX_RETRIES}"
docker logs --tail 20 planet_postgres 2>/dev/null || true
docker logs --tail 20 planet_redis 2>/dev/null || true
exit 1
fi
echo -e "${YELLOW}⚠️ 数据库第 ${retry}/${DATABASE_START_MAX_RETRIES} 次重启后仍未健康,继续重试...${NC}"
sleep "$DATABASE_RETRY_INTERVAL"
animate_wait_spinner "数据库第 ${retry}/${DATABASE_START_MAX_RETRIES} 次重启后仍未健康,继续重试" "$DATABASE_RETRY_INTERVAL"
retry=$((retry + 1))
done
}
@@ -332,7 +450,7 @@ start_backend_service() {
local backend_port_requested="$2"
local ai_provider_port="${3:-$DEFAULT_AI_PROVIDER_PORT}"
echo -e "${BLUE}🗄️ 启动数据库...${NC}"
log_step "启动数据库"
ensure_database_services_healthy
sleep 3
@@ -342,10 +460,10 @@ start_backend_service() {
kill_port_if_requested "$backend_port" "后端"
fi
echo -e "${BLUE}🔧 启动后端...${NC}"
log_step "启动后端"
ensure_uv_backend_deps
if ! start_backend_with_retry "$backend_port"; then
echo -e "${RED}后端启动失败,已重试 ${BACKEND_MAX_RETRIES}${NC}"
log_error "后端启动失败,已重试 ${BACKEND_MAX_RETRIES}"
tail -10 /tmp/planet_backend.log
exit 1
fi
@@ -359,10 +477,10 @@ start_frontend_service() {
kill_port_if_requested "$frontend_port" "前端"
fi
echo -e "${BLUE}🌐 启动前端...${NC}"
log_step "启动前端"
ensure_frontend_deps
if ! start_frontend_with_retry "$frontend_port"; then
echo -e "${RED}前端启动失败,已重试 ${FRONTEND_MAX_RETRIES}${NC}"
log_error "前端启动失败,已重试 ${FRONTEND_MAX_RETRIES}"
tail -10 /tmp/planet_frontend.log
exit 1
fi
@@ -372,7 +490,7 @@ validate_port() {
local port="$1"
if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
echo -e "${RED}非法端口: ${port}${NC}"
log_error "非法端口: ${port}"
exit 1
fi
}
@@ -381,10 +499,10 @@ kill_port_if_requested() {
local port="$1"
local service_name="$2"
echo -e "${YELLOW}🧹 检测 ${service_name} 端口 ${port} 占用...${NC}"
log_warn "检测 ${service_name} 端口 ${port} 占用"
if command -v fuser >/dev/null 2>&1 && fuser "${port}/tcp" >/dev/null 2>&1; then
echo -e "${BLUE}🔌 发现端口 ${port} 占用,正在终止...${NC}"
log_step "发现端口 ${port} 占用,正在终止"
fuser -k "${port}/tcp" >/dev/null 2>&1 || true
sleep 1
return 0
@@ -394,14 +512,14 @@ kill_port_if_requested() {
local pids
pids="$(lsof -ti tcp:"${port}" 2>/dev/null || true)"
if [ -n "$pids" ]; then
echo -e "${BLUE}🔌 发现端口 ${port} 占用,正在终止...${NC}"
log_step "发现端口 ${port} 占用,正在终止"
kill $pids 2>/dev/null || true
sleep 1
return 0
fi
fi
echo -e "${GREEN}端口 ${port} 未被占用${NC}"
log_success "端口 ${port} 未被占用"
}
parse_service_args() {
@@ -447,7 +565,7 @@ parse_service_args() {
shift 1
;;
*)
echo -e "${RED}❌ 未知参数: $1${NC}"
log_error "未知参数: $1"
exit 1
;;
esac
@@ -462,9 +580,9 @@ cleanup_exit_containers() {
local exit_containers
exit_containers="$(docker ps -a --filter status=exited -q 2>/dev/null || true)"
if [ -n "$exit_containers" ]; then
echo -e "${BLUE}🗑️ 清理残留 Exit 容器...${NC}"
log_step "清理残留 Exit 容器"
echo "$exit_containers" | xargs -r docker rm -f >/dev/null 2>&1 || true
echo -e "${GREEN}✅ 残留容器已清理${NC}"
log_success "残留容器已清理"
fi
}
@@ -492,15 +610,15 @@ create_user() {
ensure_uv_backend_deps
echo -e "${BLUE}🗄️ 启动数据库...${NC}"
log_step "启动数据库"
ensure_postgres_service_healthy
sleep 2
echo -e "${BLUE}👤 创建用户${NC}"
log_step "创建用户"
read -r -p "用户名: " username
if [ -z "$username" ]; then
echo -e "${RED}❌ 用户名不能为空${NC}"
log_error "用户名不能为空"
exit 1
fi
@@ -516,17 +634,17 @@ create_user() {
echo ""
if [ -z "$password" ]; then
echo -e "${RED}❌ 密码不能为空${NC}"
log_error "密码不能为空"
exit 1
fi
if [ "${#password}" -lt 8 ]; then
echo -e "${RED}❌ 密码长度不能少于 8 位${NC}"
log_error "密码长度不能少于 8 位"
exit 1
fi
if [ "$password" != "$password_confirm" ]; then
echo -e "${RED}❌ 两次输入的密码不一致${NC}"
log_error "两次输入的密码不一致"
exit 1
fi
@@ -580,35 +698,36 @@ async def main():
asyncio.run(main())
PY
echo -e "${GREEN}✅ 用户创建成功${NC}"
echo " 用户名: ${username}"
echo " 角色: ${role}"
echo " 邮箱: ${email}"
log_success "用户创建成功"
log_note "用户名: ${username}"
log_note "角色: ${role}"
log_note "邮箱: ${email}"
}
start() {
parse_service_args "$@"
cleanup_exit_containers
echo -e "${BLUE}🚀 启动智能星球计划...${NC}"
print_splash
log_step "启动智能星球计划"
start_backend_service "$BACKEND_PORT" "$BACKEND_PORT_REQUESTED" "$AI_PROVIDER_PORT"
start_frontend_service "$FRONTEND_PORT" "$FRONTEND_PORT_REQUESTED"
echo ""
echo -e "${GREEN}✅ 启动完成!${NC}"
echo " 前端: http://localhost:${FRONTEND_PORT}"
echo " 后端: http://localhost:${BACKEND_PORT}"
echo " AI Provider: http://localhost:${AI_PROVIDER_PORT}"
log_success "启动完成"
log_note "前端: http://localhost:${FRONTEND_PORT}"
log_note "后端: http://localhost:${BACKEND_PORT}"
log_note "AI Provider: http://localhost:${AI_PROVIDER_PORT}"
}
stop() {
echo -e "${YELLOW}🛑 停止服务...${NC}"
log_warn "停止服务"
stop_backend_service
stop_ai_provider_service
stop_frontend_service
docker stop planet_postgres planet_redis 2>/dev/null || true
echo -e "${GREEN}✅ 已停止${NC}"
log_success "已停止"
}
restart() {
@@ -622,7 +741,7 @@ restart() {
return 0
fi
echo -e "${YELLOW}🔄 按需重启服务...${NC}"
log_warn "按需重启服务"
if [ "$DATABASE_REQUESTED" -eq 1 ]; then
restart_database_service
@@ -647,43 +766,43 @@ restart() {
fi
echo ""
echo -e "${GREEN}✅ 重启完成!${NC}"
log_success "重启完成"
if [ "$DATABASE_REQUESTED" -eq 1 ]; then
echo " 数据库: planet_postgres, planet_redis"
log_note "数据库: planet_postgres, planet_redis"
fi
if [ "$AI_PROVIDER_REQUESTED" -eq 1 ]; then
echo " AI Provider: http://localhost:${AI_PROVIDER_PORT}"
log_note "AI Provider: http://localhost:${AI_PROVIDER_PORT}"
fi
if [ "$BACKEND_PORT_REQUESTED" -eq 1 ]; then
echo " 后端: http://localhost:${BACKEND_PORT}"
log_note "后端: http://localhost:${BACKEND_PORT}"
fi
if [ "$FRONTEND_PORT_REQUESTED" -eq 1 ]; then
echo " 前端: http://localhost:${FRONTEND_PORT}"
log_note "前端: http://localhost:${FRONTEND_PORT}"
fi
}
health() {
echo "📊 容器状态:"
echo -e "${DIM}·${NC} ${BLUE}view${NC} ${WHITE}容器状态${NC}"
docker ps --filter "name=planet_" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "🔍 服务状态:"
echo -e "${DIM}·${NC} ${BLUE}view${NC} ${WHITE}服务状态${NC}"
if curl -s http://localhost:8000/health > /dev/null 2>&1; then
echo -e " 后端: ${GREEN}✅ 运行中${NC}"
echo -e "${DIM} 后端:${NC} ${GREEN}online${NC}"
else
echo -e " 后端: ${RED}❌ 未运行${NC}"
echo -e "${DIM} 后端:${NC} ${RED}offline${NC}"
fi
if curl -s "http://localhost:${DEFAULT_AI_PROVIDER_PORT}/health" > /dev/null 2>&1; then
echo -e " AI Provider: ${GREEN}✅ 运行中${NC}"
echo -e "${DIM} AI Provider:${NC} ${GREEN}online${NC}"
else
echo -e " AI Provider: ${RED}❌ 未运行${NC}"
echo -e "${DIM} AI Provider:${NC} ${RED}offline${NC}"
fi
if curl -s http://localhost:3000 > /dev/null 2>&1; then
echo -e " 前端: ${GREEN}✅ 运行中${NC}"
echo -e "${DIM} 前端:${NC} ${GREEN}online${NC}"
else
echo -e " 前端: ${RED}❌ 未运行${NC}"
echo -e "${DIM} 前端:${NC} ${RED}offline${NC}"
fi
}