Files
Agent-Reach/agent_reach/doctor.py
T
Pnant 75bc98a7eb feat(install): split into core + optional channels with --channels flag (#237)
Base install now only sets up lightweight zero-config channels (Web, YouTube,
GitHub, RSS, Exa, V2EX, Bilibili basic). Optional channels (Twitter, Weibo,
WeChat, Xiaoyuzhou, XiaoHongShu, Reddit, Bilibili full, Douyin, LinkedIn)
are installed on demand via --channels flag.

- Add --channels param to install subcommand
- Extract twitter/xhs/reddit/bili into independent install functions
- Remove heavy deps (weibo/xiaoyuzhou/wechat/twitter-cli/xhs-cli) from
  default _install_system_deps() and _install_mcporter()
- Cookie import only triggers when cookie-needing channels are selected
- Doctor output: inactive optional channels summarized in one line
- install.md: two-step flow (basics → ask user which channels)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 18:19:59 +08:00

109 lines
4.1 KiB
Python

# -*- coding: utf-8 -*-
"""Environment health checker — powered by channels.
Each channel knows how to check itself. Doctor just collects the results.
"""
from typing import Dict
from agent_reach.config import Config
from agent_reach.channels import get_all_channels
def check_all(config: Config) -> Dict[str, dict]:
"""Check all channels and return status dict."""
results = {}
for ch in get_all_channels():
status, message = ch.check(config)
results[ch.name] = {
"status": status,
"name": ch.description,
"message": message,
"tier": ch.tier,
"backends": ch.backends,
}
return results
def format_report(results: Dict[str, dict]) -> str:
"""Format results as a readable text report (with Rich markup)."""
try:
from rich.markup import escape
except ImportError:
escape = lambda x: x
lines = []
lines.append("[bold cyan]Agent Reach 状态[/bold cyan]")
lines.append("[cyan]" + "=" * 40 + "[/cyan]")
ok_count = sum(1 for r in results.values() if r["status"] == "ok")
total = len(results)
# Tier 0 — zero config
lines.append("")
lines.append("[bold]✅ 装好即用:[/bold]")
for key, r in results.items():
if r["tier"] == 0:
name_msg = f"[bold]{escape(r['name'])}[/bold] — {escape(r['message'])}"
if r["status"] == "ok":
lines.append(f" [green]✅[/green] {name_msg}")
elif r["status"] == "warn":
lines.append(f" [yellow][!][/yellow] {name_msg}")
elif r["status"] in ("off", "error"):
lines.append(f" [red][X][/red] {name_msg}")
# Tier 1 — needs free key / login
tier1 = {k: r for k, r in results.items() if r["tier"] == 1}
tier1_active = {k: r for k, r in tier1.items() if r["status"] == "ok"}
tier1_inactive = {k: r for k, r in tier1.items() if r["status"] != "ok"}
if tier1_active:
lines.append("")
lines.append("[bold]可选渠道(已安装):[/bold]")
for key, r in tier1_active.items():
name_msg = f"[bold]{escape(r['name'])}[/bold] — {escape(r['message'])}"
lines.append(f" [green]✅[/green] {name_msg}")
# Tier 2 — optional complex setup
tier2 = {k: r for k, r in results.items() if r["tier"] == 2}
tier2_active = {k: r for k, r in tier2.items() if r["status"] == "ok"}
tier2_inactive = {k: r for k, r in tier2.items() if r["status"] != "ok"}
if tier2_active:
if not tier1_active:
lines.append("")
lines.append("[bold]可选渠道(已安装):[/bold]")
for key, r in tier2_active.items():
name_msg = f"[bold]{escape(r['name'])}[/bold] — {escape(r['message'])}"
lines.append(f" [green]✅[/green] {name_msg}")
lines.append("")
status_color = "green" if ok_count == total else ("yellow" if ok_count > 0 else "red")
lines.append(f"状态:[{status_color}]{ok_count}/{total}[/{status_color}] 个渠道可用")
# Summarize inactive optional channels in one line instead of listing each
all_inactive = list(tier1_inactive.values()) + list(tier2_inactive.values())
if all_inactive:
names = [r["name"] for r in all_inactive]
lines.append(
f"还有 {len(names)} 个可选渠道可以解锁({''.join(names)}),"
"告诉你的 Agent「帮我装 XXX」即可"
)
# Security check: config file permissions (Unix only)
import os
import stat
import sys
config_path = Config.CONFIG_DIR / "config.yaml"
if config_path.exists() and sys.platform != "win32":
try:
mode = config_path.stat().st_mode
if mode & (stat.S_IRGRP | stat.S_IROTH):
lines.append("")
lines.append(
"[bold red][!] 安全提示:config.yaml 权限过宽(其他用户可读)[/bold red]"
)
lines.append(" 修复:chmod 600 ~/.agent-reach/config.yaml")
except OSError:
pass
return "\n".join(lines)