joy-cli/scripts/manhuang/sync-codex-health.sh
feibisi 50f5089459 feat(manhuang): scout 号池 health 同步 + e2e --call 接 endpoints
sync-codex-health 拉 health/alive/endpoints(key 文件 600);
e2e --from-endpoints 真调中转;余额不足单独 break;refresh 不覆盖 call 结果。
2026-07-13 09:51:37 +08:00

107 lines
3.7 KiB
Bash
Executable file
Raw Permalink 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
# 从 scout 同步 Codex 号池元数据到 Mac 约定路径(供 chain_v2 / 面板)
#
# 同步:
# health-status.json — 额度统计(无密钥)
# alive-pool.json — 存活列表元数据
# api-endpoints.json — 中转端点(可能含 key → chmod 600勿提交 git
#
# 用法:
# bash scripts/manhuang/sync-codex-health.sh
# bash scripts/manhuang/sync-codex-health.sh --refresh-quick # scout 上先 --quick 扫描
# bash scripts/manhuang/sync-codex-health.sh --refresh-full # 全量(慢)
set -euo pipefail
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:${HOME}/bin:${PATH:-}"
DEST="${HOME}/data/codex-tokens"
HOST="${CODEX_HEALTH_SSH:-scout}"
REMOTE="${CODEX_HEALTH_REMOTE:-data/codex-tokens}"
REFRESH=""
for a in "$@"; do
case "$a" in
--refresh-quick) REFRESH=quick ;;
--refresh-full) REFRESH=full ;;
--refresh) REFRESH=quick ;;
esac
done
mkdir -p "$DEST"
if [[ -n "$REFRESH" ]]; then
echo "[sync-codex] scout token-health-check --${REFRESH}"
if [[ "$REFRESH" == "full" ]]; then
ssh -o BatchMode=yes -o ConnectTimeout=15 "$HOST" \
'python3 ~/scripts/ops/token-health-check.py --workers 10' || {
echo "[sync-codex] refresh full failed (continue pull)" >&2
}
else
ssh -o BatchMode=yes -o ConnectTimeout=15 "$HOST" \
'python3 ~/scripts/ops/token-health-check.py --quick --workers 8' || {
echo "[sync-codex] refresh quick failed (continue pull)" >&2
}
fi
fi
echo "[sync-codex] scp ${HOST}:${REMOTE}${DEST}"
scp -o BatchMode=yes -o ConnectTimeout=15 \
"${HOST}:${REMOTE}/health-status.json" \
"${HOST}:${REMOTE}/alive-pool.json" \
"${HOST}:${REMOTE}/api-endpoints.json" \
"$DEST/" 2>/dev/null || {
# 至少拉 health
scp -o BatchMode=yes -o ConnectTimeout=15 \
"${HOST}:${REMOTE}/health-status.json" "$DEST/" || {
echo "[sync-codex] FAIL: cannot pull health-status.json" >&2
exit 1
}
}
chmod 644 "$DEST/health-status.json" 2>/dev/null || true
chmod 644 "$DEST/alive-pool.json" 2>/dev/null || true
# endpoints 可能含 key
if [[ -f "$DEST/api-endpoints.json" ]]; then
chmod 600 "$DEST/api-endpoints.json"
fi
python3 - <<'PY'
import json
from pathlib import Path
base = Path.home() / "data" / "codex-tokens"
hs = base / "health-status.json"
h = json.loads(hs.read_text(encoding="utf-8"))
c = h.get("counts") or {}
total = h.get("total")
quota = c.get("has_quota")
exh = c.get("exhausted")
dead = c.get("dead")
ts = h.get("timestamp") or h.get("ts")
print(f"[sync-codex] health ts={ts}")
print(f"[sync-codex] total={total} has_quota={quota} exhausted={exh} dead={dead}")
alive = base / "alive-pool.json"
if alive.is_file():
try:
a = json.loads(alive.read_text(encoding="utf-8"))
n = len(a) if isinstance(a, list) else (a.get("count") if isinstance(a, dict) else "?")
print(f"[sync-codex] alive-pool n={n}")
except Exception as e:
print(f"[sync-codex] alive-pool read err: {e}")
ep = base / "api-endpoints.json"
if ep.is_file():
try:
e = json.loads(ep.read_text(encoding="utf-8"))
eps = e.get("endpoints") if isinstance(e, dict) else e
n = len(eps) if isinstance(eps, list) else 0
alive_n = sum(1 for x in (eps or []) if isinstance(x, dict) and str(x.get("status") or "") == "alive")
print(f"[sync-codex] endpoints n={n} alive={alive_n} (file mode 600)")
except Exception as e:
print(f"[sync-codex] endpoints err: {e}")
print(f"[sync-codex] done → {base}")
PY
# 刷新 chain breaks
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
if [[ -f "$ROOT/scripts/manhuang/refresh-chain-breaks.py" ]]; then
export PYTHONPATH="${ROOT}/src${PYTHONPATH:+:$PYTHONPATH}"
python3 "$ROOT/scripts/manhuang/refresh-chain-breaks.py" 2>/dev/null || true
fi