#!/usr/bin/env bash # status.sh — 查看 moziplus v2 运行状态 set -euo pipefail TARGET_DIR="$HOME/.sanguo_projects/sanguo_moziplus_v2" PM2_NAME="sanguo-moziplus-v2" HEALTH_URL="http://localhost:8083/api/daemon/status" DETAILED=false usage() { echo "Usage: $0 [options]" echo "" echo "Options:" echo " --target=DIR 安装目录" echo " -v, --verbose 显示详细信息(项目列表、磁盘占用等)" echo " -h, --help 显示帮助" exit 0 } for arg in "$@"; do case "$arg" in --target=*) TARGET_DIR="${arg#*=}" ;; -v|--verbose) DETAILED=true ;; -h|--help) usage ;; esac done echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " moziplus v2 — Status" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # ── 版本 ── VERSION=$(cd "$TARGET_DIR" 2>/dev/null && grep -o 'version *= *["'\'''][^"'\''']*["'\''']' pyproject.toml 2>/dev/null | sed "s/version *= *['\"]//;s/['\"]$//" || echo 'unknown') echo " Version: v$VERSION" # ── 安装目录 ── if [ -d "$TARGET_DIR" ]; then echo " Install: $TARGET_DIR ✅" else echo " Install: $TARGET_DIR ❌ (not found)" exit 1 fi # ── PM2 状态 ── echo "" PM2_STATUS=$(pm2 jlist 2>/dev/null | python3 -c " import sys, json procs = json.load(sys.stdin) for p in procs: if p['name'] == '$PM2_NAME': s = p['pm2_env']['status'] pid = p['pid'] or '-' uptime = p['pm2_env'].get('pm_uptime', 0) restarts = p['pm2_env'].get('restart_time', 0) mem = p['monit'].get('memory', 0) mem_mb = round(mem / 1024 / 1024, 1) cpu = p['monit'].get('cpu', 0) print(f'{s}|{pid}|{uptime}|{restarts}|{mem_mb}|{cpu}') break else: print('not_found|||||') " 2>/dev/null || echo 'error|||||') IFS='|' read -r STATUS PID UPTIME RESTARTS MEM CPU <<< "$PM2_STATUS" if [ "$STATUS" = "online" ]; then # 计算 uptime if [ -n "$UPTIME" ] && [ "$UPTIME" != "0" ]; then UPTIME_FMT=$(python3 -c " from datetime import datetime, timezone delta = datetime.now(timezone.utc) - datetime.fromtimestamp($UPTIME/1000, tz=timezone.utc) days = delta.days hours, rem = divmod(delta.seconds, 3600) mins, _ = divmod(rem, 60) parts = [] if days: parts.append(f'{days}d') if hours: parts.append(f'{hours}h') parts.append(f'{mins}m') print(' '.join(parts)) " 2>/dev/null || echo '?') else UPTIME_FMT="just started" fi echo " PM2: online ✅" echo " PID: $PID" echo " Uptime: $UPTIME_FMT" echo " Restarts: $RESTARTS" echo " Memory: ${MEM}MB" echo " CPU: ${CPU}%" else echo " PM2: $STATUS ❌" fi # ── 健康检查 ── echo "" HEALTH=$(curl -sf "$HEALTH_URL" 2>/dev/null || echo '{"status":"unreachable"}') HEALTH_STATUS=$(echo "$HEALTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('status','unknown'))" 2>/dev/null || echo 'unknown') if [ "$HEALTH_STATUS" = "ok" ]; then echo " Health: ok ✅" else echo " Health: $HEALTH_STATUS ❌" fi # ── 数据统计 ── echo "" DATA_DIR="$TARGET_DIR/data" if [ -d "$DATA_DIR" ]; then # 项目数 PROJ_COUNT=$(find "$DATA_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ') DATA_SIZE=$(du -sh "$DATA_DIR" 2>/dev/null | cut -f1) echo " Projects: $PROJ_COUNT" echo " Data size: $DATA_SIZE" if [ "$DETAILED" = true ]; then echo "" echo " Project details:" for proj_dir in "$DATA_DIR"/*/; do proj_name=$(basename "$proj_dir") db_file="$proj_dir/blackboard.db" if [ -f "$db_file" ]; then TASK_COUNT=$(sqlite3 "$db_file" "SELECT COUNT(*) FROM tasks" 2>/dev/null || echo '?') ACTIVE_COUNT=$(sqlite3 "$db_file" "SELECT COUNT(*) FROM tasks WHERE status NOT IN ('done','cancelled','archived')" 2>/dev/null || echo '?') PROJ_SIZE=$(du -sh "$proj_dir" 2>/dev/null | cut -f1) printf " %-25s %s tasks (%s active) %s\n" "$proj_name" "$TASK_COUNT" "$ACTIVE_COUNT" "$PROJ_SIZE" else printf " %-25s (no db)\n" "$proj_name" fi done fi fi # ── 配置 ── if [ "$DETAILED" = true ]; then echo "" echo " Config:" echo " Port: $(grep -o 'port:.*' "$TARGET_DIR/config/default.yaml" 2>/dev/null | head -1 || echo '8083 (default)')" echo " Interval: $(grep -o 'interval:.*' "$TARGET_DIR/config/default.yaml" 2>/dev/null | head -1)" echo " Agents: $(grep 'agents:' -A 20 "$TARGET_DIR/config/default.yaml" 2>/dev/null | grep ' - ' | wc -l | tr -d ' ') configured" fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"