mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
feat: 增强 OAuth 标识展示与 Codex 调试日志
- 优化 OAuth badge 支持 account_id/account_user_id,tooltip 展示完整身份信息 - 重构池管理额度进度条布局,倒计时独立展示并增加样式区分 - Codex 插件增加 token 解析快照日志,便于排查 OAuth 透传问题 Closes #245 Co-Authored-By: kayphoon <109347466+Kayphoon@users.noreply.github.com>
This commit is contained in:
@@ -53,6 +53,65 @@ def _build_short_header_id(seed: str) -> str:
|
||||
return hashlib.sha256(seed.encode()).hexdigest()[:16]
|
||||
|
||||
|
||||
def _normalize_codex_debug_organizations(value: Any) -> list[dict[str, Any]]:
|
||||
if not isinstance(value, list):
|
||||
return []
|
||||
|
||||
items: list[dict[str, Any]] = []
|
||||
for raw in value:
|
||||
if not isinstance(raw, Mapping):
|
||||
continue
|
||||
|
||||
normalized: dict[str, Any] = {}
|
||||
org_id = str(raw.get("id") or "").strip()
|
||||
if org_id:
|
||||
normalized["id"] = org_id
|
||||
|
||||
title = str(raw.get("title") or "").strip()
|
||||
if title:
|
||||
normalized["title"] = title
|
||||
|
||||
role = str(raw.get("role") or "").strip()
|
||||
if role:
|
||||
normalized["role"] = role
|
||||
|
||||
if "is_default" in raw:
|
||||
normalized["is_default"] = bool(raw.get("is_default"))
|
||||
|
||||
if normalized:
|
||||
items.append(normalized)
|
||||
|
||||
return items
|
||||
|
||||
|
||||
def _build_safe_codex_debug_snapshot(values: Mapping[str, Any] | None) -> dict[str, Any]:
|
||||
if not isinstance(values, Mapping):
|
||||
return {}
|
||||
|
||||
snapshot: dict[str, Any] = {}
|
||||
for field in (
|
||||
"email",
|
||||
"account_id",
|
||||
"account_user_id",
|
||||
"plan_type",
|
||||
"user_id",
|
||||
"account_name",
|
||||
):
|
||||
raw = values.get(field)
|
||||
if isinstance(raw, str):
|
||||
normalized = raw.strip()
|
||||
if normalized:
|
||||
snapshot[field] = normalized
|
||||
elif raw is not None:
|
||||
snapshot[field] = raw
|
||||
|
||||
organizations = _normalize_codex_debug_organizations(values.get("organizations"))
|
||||
if organizations:
|
||||
snapshot["organizations"] = organizations
|
||||
|
||||
return snapshot
|
||||
|
||||
|
||||
def _build_codex_headers(
|
||||
request_body: Any,
|
||||
original_headers: Mapping[str, Any] | None,
|
||||
@@ -213,24 +272,40 @@ async def enrich_codex(
|
||||
if direct_email and not auth_config.get("email"):
|
||||
auth_config["email"] = direct_email
|
||||
|
||||
direct_snapshot = _build_safe_codex_debug_snapshot(
|
||||
{
|
||||
"email": direct_email,
|
||||
"account_id": direct_account_id,
|
||||
"account_user_id": direct_account_user_id,
|
||||
"plan_type": direct_plan_type,
|
||||
"user_id": direct_user_id,
|
||||
}
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
"Codex enrich_auth_config: id_token_present={} access_token_present={} token_keys={}",
|
||||
bool(token_response.get("id_token") or token_response.get("idToken")),
|
||||
bool(token_response.get("access_token") or token_response.get("accessToken")),
|
||||
list(token_response.keys()),
|
||||
)
|
||||
logger.debug("Codex direct token response values: {}", direct_snapshot)
|
||||
|
||||
token_candidates = [
|
||||
token_response.get("id_token"),
|
||||
token_response.get("idToken"),
|
||||
token_response.get("access_token"),
|
||||
token_response.get("accessToken"),
|
||||
("id_token", token_response.get("id_token")),
|
||||
("idToken", token_response.get("idToken")),
|
||||
("access_token", token_response.get("access_token")),
|
||||
("accessToken", token_response.get("accessToken")),
|
||||
]
|
||||
for token_payload in token_candidates:
|
||||
for source_name, token_payload in token_candidates:
|
||||
codex_info = parse_codex_id_token(token_payload)
|
||||
if not codex_info:
|
||||
continue
|
||||
logger.debug("Codex parsed token fields: {}", list(codex_info.keys()))
|
||||
logger.debug(
|
||||
"Codex parsed token values: source={} fields={} values={}",
|
||||
source_name,
|
||||
list(codex_info.keys()),
|
||||
_build_safe_codex_debug_snapshot(codex_info),
|
||||
)
|
||||
for key, value in codex_info.items():
|
||||
if not auth_config.get(key):
|
||||
auth_config[key] = value
|
||||
@@ -243,9 +318,19 @@ async def enrich_codex(
|
||||
proxy_config=proxy_config,
|
||||
timeout_seconds=10.0,
|
||||
)
|
||||
logger.debug(
|
||||
"Codex account_name lookup: account_id={} resolved_account_name={}",
|
||||
account_id,
|
||||
account_name,
|
||||
)
|
||||
if account_name:
|
||||
auth_config["account_name"] = account_name
|
||||
|
||||
logger.debug(
|
||||
"Codex enrich_auth_config final metadata: {}",
|
||||
_build_safe_codex_debug_snapshot(auth_config),
|
||||
)
|
||||
|
||||
return auth_config
|
||||
|
||||
|
||||
@@ -274,9 +359,7 @@ def register_all() -> None:
|
||||
# Provider Format Capability:默认 body_rules
|
||||
from src.core.api_format.metadata import CODEX_DEFAULT_BODY_RULES
|
||||
|
||||
register_provider_default_body_rules(
|
||||
"codex", "openai:cli", CODEX_DEFAULT_BODY_RULES
|
||||
)
|
||||
register_provider_default_body_rules("codex", "openai:cli", CODEX_DEFAULT_BODY_RULES)
|
||||
|
||||
# Export: Codex uses the default export builder (strip null + temp fields)
|
||||
# No need to register a custom one — the default in export.py suffices.
|
||||
|
||||
Reference in New Issue
Block a user