refactor(hermes_cli): doctor — _login_row show_error, packed DOCTOR_CHECKS table, dump helper ternaries
This commit is contained in:
@@ -79,14 +79,10 @@ def _check_auth_providers(should_fix: bool) -> Finding:
|
||||
from hermes_cli.auth import get_nous_auth_status_local, get_codex_auth_status, get_minimax_oauth_auth_status
|
||||
# Read-only display: refresh-free snapshot — doctor must never trigger an OAuth refresh.
|
||||
_login_row("Nous Portal auth", get_nous_auth_status_local())
|
||||
codex_status = get_codex_auth_status()
|
||||
if not _login_row("OpenAI Codex auth", codex_status):
|
||||
if codex_status.get("error"):
|
||||
check_info(codex_status["error"])
|
||||
# Native OAuth uses Hermes' own device-code flow — the Codex CLI only imports existing tokens from
|
||||
# ~/.codex/auth.json. Hint sits under the Codex row so it doesn't read as another provider's remedy.
|
||||
if not _safe_which("codex"):
|
||||
check_info("codex CLI not installed (optional — only required to import tokens from an existing Codex CLI login)")
|
||||
# Native OAuth uses Hermes' own device-code flow — the Codex CLI only imports existing tokens from
|
||||
# ~/.codex/auth.json. Hint sits under the Codex row so it doesn't read as another provider's remedy.
|
||||
if not _login_row("OpenAI Codex auth", get_codex_auth_status(), show_error=True) and not _safe_which("codex"):
|
||||
check_info("codex CLI not installed (optional — only required to import tokens from an existing Codex CLI login)")
|
||||
minimax_status = get_minimax_oauth_auth_status()
|
||||
_login_row("MiniMax OAuth", minimax_status, f"(logged in, region={minimax_status.get('region', 'global')})")
|
||||
except Exception as e:
|
||||
@@ -94,16 +90,18 @@ def _check_auth_providers(should_fix: bool) -> Finding:
|
||||
# xAI OAuth — separate try/except so an import failure cannot disrupt the already-printed rows above.
|
||||
try:
|
||||
from hermes_cli.auth import get_xai_oauth_auth_status
|
||||
xai_oauth_status = get_xai_oauth_auth_status() or {}
|
||||
if not _login_row("xAI OAuth", xai_oauth_status) and xai_oauth_status.get("error"):
|
||||
check_info(xai_oauth_status["error"])
|
||||
_login_row("xAI OAuth", get_xai_oauth_auth_status() or {}, show_error=True)
|
||||
except Exception:
|
||||
pass
|
||||
return f
|
||||
|
||||
|
||||
def _login_row(label: str, status: dict, ok_detail: str = "(logged in)") -> bool:
|
||||
return check_bool(status.get("logged_in"), (label, ok_detail), (label, "(not logged in)"))
|
||||
def _login_row(label: str, status: dict, ok_detail: str = "(logged in)", show_error: bool = False) -> bool:
|
||||
"""ok/warn row for an OAuth status dict; with show_error, its ``error`` hint prints under a not-logged-in row."""
|
||||
logged_in = check_bool(status.get("logged_in"), (label, ok_detail), (label, "(not logged in)"))
|
||||
if not logged_in and show_error and status.get("error"):
|
||||
check_info(status["error"])
|
||||
return logged_in
|
||||
|
||||
|
||||
def _check_api_connectivity(should_fix: bool) -> Finding:
|
||||
@@ -124,29 +122,17 @@ def _check_api_connectivity(should_fix: bool) -> Finding:
|
||||
|
||||
# Ordered (section title, check). None title = check prints its own header (or none); order is user-visible.
|
||||
DOCTOR_CHECKS = (
|
||||
('Security Advisories', _check_security_advisories),
|
||||
('MCP Server Security', _check_mcp_security),
|
||||
('Python Environment', _check_python_environment),
|
||||
('SSL / CA Certificates', _check_certificates),
|
||||
('Required Packages', _check_required_packages),
|
||||
('Configuration Files', _check_env_file),
|
||||
(None, _check_config_file),
|
||||
(None, _check_config_drift),
|
||||
('xAI Model Retirement (May 15, 2026)', _check_xai_retirement),
|
||||
('Auth Providers', _check_auth_providers),
|
||||
('Directory Structure', _check_directory_structure),
|
||||
(None, _check_state_db),
|
||||
(None, _check_gateway_supervision),
|
||||
(None, _check_command_installation),
|
||||
('External Tools', _check_git_and_rg),
|
||||
(None, _check_terminal_backend),
|
||||
(None, _check_node_and_browser),
|
||||
(None, _check_npm_audit),
|
||||
('API Connectivity', _check_api_connectivity),
|
||||
('Tool Availability', _check_tool_availability),
|
||||
('Skills Hub', _check_skills_hub),
|
||||
('Memory Provider', _check_memory_provider),
|
||||
(None, _check_profiles),
|
||||
('Security Advisories', _check_security_advisories), ('MCP Server Security', _check_mcp_security),
|
||||
('Python Environment', _check_python_environment), ('SSL / CA Certificates', _check_certificates),
|
||||
('Required Packages', _check_required_packages), ('Configuration Files', _check_env_file),
|
||||
(None, _check_config_file), (None, _check_config_drift),
|
||||
('xAI Model Retirement (May 15, 2026)', _check_xai_retirement), ('Auth Providers', _check_auth_providers),
|
||||
('Directory Structure', _check_directory_structure), (None, _check_state_db),
|
||||
(None, _check_gateway_supervision), (None, _check_command_installation),
|
||||
('External Tools', _check_git_and_rg), (None, _check_terminal_backend), (None, _check_node_and_browser),
|
||||
(None, _check_npm_audit), ('API Connectivity', _check_api_connectivity),
|
||||
('Tool Availability', _check_tool_availability), ('Skills Hub', _check_skills_hub),
|
||||
('Memory Provider', _check_memory_provider), (None, _check_profiles),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -316,8 +316,7 @@ def run_probes(probes: list) -> list:
|
||||
try:
|
||||
# 8 workers is plenty — each probe is one HTTP call plus a TLS handshake.
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=8, thread_name_prefix="doctor-probe") as ex:
|
||||
futures = [ex.submit(fn) for _, fn in probes]
|
||||
return [f.result() for f in futures]
|
||||
return [f.result() for f in [ex.submit(fn) for _, fn in probes]]
|
||||
finally:
|
||||
if _imds_prev is None:
|
||||
os.environ.pop("AWS_EC2_METADATA_DISABLED", None)
|
||||
|
||||
@@ -28,10 +28,8 @@ def _dotenv_key_names() -> set[str]:
|
||||
if line.lower().startswith("export "):
|
||||
line = line[len("export "):].lstrip()
|
||||
name, _, value = line.partition("=")
|
||||
name = name.strip()
|
||||
# A bare `KEY=` (empty value) is effectively unset for the backend.
|
||||
if name and value.strip().strip("'\""):
|
||||
names.add(name)
|
||||
if name.strip() and value.strip().strip("'\""): # a bare `KEY=` is effectively unset for the backend
|
||||
names.add(name.strip())
|
||||
return names
|
||||
|
||||
|
||||
@@ -40,11 +38,9 @@ def _git_output(project_root: Path, *args: str) -> str:
|
||||
try:
|
||||
result = subprocess.run(["git", *args], capture_output=True, text=True, encoding='utf-8', errors='replace',
|
||||
timeout=5, cwd=str(project_root))
|
||||
if result.returncode == 0:
|
||||
return result.stdout.strip()
|
||||
return result.stdout.strip() if result.returncode == 0 else ""
|
||||
except Exception:
|
||||
pass
|
||||
return ""
|
||||
return ""
|
||||
|
||||
|
||||
def _get_git_commit(project_root: Path) -> str:
|
||||
|
||||
Reference in New Issue
Block a user