#!/usr/bin/env python3
"""
Fetch Codex/WHAM usage-related ChatGPT backend endpoints with the local Codex auth.
The script reads ~/.codex/auth.json by default, extracts the bearer token, then calls:
- https://chatgpt.com/backend-api/wham/usage
- https://chatgpt.com/backend-api/wham/rate-limit-reset-credits
- https://chatgpt.com/backend-api/wham/referrals/eligibility_rules?referral_key=codex_referral_persistent_invite
It never prints the token. By default it prints a Chinese human-readable summary.
Use --json to print raw data.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import urllib.error
import urllib.request
from dataclasses import dataclass
from pathlib import Path
from typing import Any
def load_json(path: Path) -> dict[str, Any]:
try:
with path.open("r", encoding="utf-8") as f:
data = json.load(f)
except FileNotFoundError:
raise SystemExit(f"auth file not found: {path}") from None
except json.JSONDecodeError as exc:
raise SystemExit(f"auth file is not valid JSON: {path}: {exc}") from None
if not isinstance(data, dict):
raise SystemExit(f"auth file root must be a JSON object: {path}")
return data
def first_string(data: dict[str, Any], paths: list[tuple[str, ...]]) -> tuple[str, str] | tuple[None, None]:
for path in paths:
current: Any = data
for key in path:
if not isinstance(current, dict) or key not in current:
current = None
break
current = current[key]
if isinstance(current, str) and current.strip():
return current.strip(), ".".join(path)
return None, None
if not token or not source:
raise SystemExit(
"could not find a bearer token in auth.json. "
"Tried tokens.access_token/accessToken and tokens.id_token/idToken. "
"Run `codex login` again, or pass a different file with --auth."
)
def decode_body(raw: bytes, content_type: str | None) -> Any:
text = raw.decode("utf-8", errors="replace")
if content_type and "json" in content_type.lower():
try:
return json.loads(text)
except json.JSONDecodeError:
return text
stripped = text.strip()
if stripped.startswith("{") or stripped.startswith("["):
try:
return json.loads(stripped)
except json.JSONDecodeError:
pass
return text
def label_for_key(key: str) -> str:
if key in FIELD_LABELS:
return FIELD_LABELS[key]
return key.replace("_", " ")
def format_scalar(value: Any) -> str:
if value is True:
return "是"
if value is False:
return "否"
if value is None:
return "无"
if isinstance(value, (int, float)):
return str(value)
if isinstance(value, str):
return value if value else "空字符串"
return str(value)
endpoints: list[str] = []
if not args.only_extra_endpoints:
endpoints.extend(DEFAULT_ENDPOINTS)
if args.endpoints:
endpoints.extend(args.endpoints)
if not endpoints:
raise SystemExit("no endpoints to fetch")
results = {
"auth_path": os.fspath(auth_path),
"token_source": auth.token_source,
"account_id_present": bool(auth.account_id),
"results": [request_json(url, auth, args.timeout) for url in endpoints],
}
if args.json:
json.dump(results, sys.stdout, ensure_ascii=False, indent=2)
sys.stdout.write("\n")
else:
sys.stdout.write(format_text_report(results))
return 0
if __name__ == "__main__":
raise SystemExit(main())