sync-codex-health 拉 health/alive/endpoints(key 文件 600); e2e --from-endpoints 真调中转;余额不足单独 break;refresh 不覆盖 call 结果。
107 lines
3.7 KiB
Bash
Executable file
107 lines
3.7 KiB
Bash
Executable file
#!/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
|