全中文输出 + 积极正向的状态提示
所有 doctor 输出和渠道描述改为中文。 状态提示从'不支持/需要配置'的语气改为'配置一下就能用'。 Before: ⬜ Reddit posts — Need config: reddit_proxy After: ⬜ Reddit 帖子和评论 — 配个代理就能用 Before: ⬜ XiaoHongShu — Need config: xhs_cookie After: ⬜ 小红书笔记 — 导入浏览器 Cookie 就能用
This commit is contained in:
@@ -108,25 +108,25 @@ agent-eyes configure proxy http://用户名:密码@IP:端口
|
|||||||
```
|
```
|
||||||
$ agent-eyes doctor
|
$ agent-eyes doctor
|
||||||
|
|
||||||
👁️ Agent Eyes Status
|
👁️ Agent Eyes 状态
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
✅ Ready (no setup needed):
|
✅ 装好即用:
|
||||||
✅ GitHub repos and code — Public repos only
|
✅ GitHub 仓库和代码 — 公开仓库可用。配置 github_token 可访问私有仓库
|
||||||
✅ Twitter/X posts — Full access (search + timeline + threads)
|
✅ Twitter/X 推文 — 可读取推文。安装 birdx + 配置 Cookie 可解锁搜索和发推
|
||||||
✅ YouTube video transcripts — yt-dlp
|
✅ YouTube 视频字幕 — yt-dlp
|
||||||
⚠️ Bilibili video info — May be blocked on servers
|
⚠️ B站视频信息和字幕 — 服务器 IP 可能被封,配置代理即可解决
|
||||||
✅ RSS and Atom feeds — feedparser
|
✅ RSS/Atom 订阅源 — feedparser
|
||||||
✅ Web pages (any URL) — Jina Reader API
|
✅ 网页(任意 URL) — Jina Reader API
|
||||||
|
|
||||||
🔍 Search (need free Exa API key):
|
🔍 搜索(免费 Exa Key 即可解锁):
|
||||||
⬜ Semantic web search
|
⬜ 全网语义搜索 — 注册 exa.ai 获取免费 Key,配置一下就能用
|
||||||
|
|
||||||
🔧 Optional (advanced setup):
|
🔧 配置后可用:
|
||||||
⬜ Reddit posts and comments — Need proxy
|
⬜ Reddit 帖子和评论 — 配个代理就能用
|
||||||
⬜ XiaoHongShu (小红书) — Need cookies
|
⬜ 小红书笔记 — 导入浏览器 Cookie 就能用
|
||||||
|
|
||||||
Status: 6/9 channels active
|
状态:6/9 个渠道可用
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -118,14 +118,14 @@ class Channel(ABC):
|
|||||||
# Check required tools
|
# Check required tools
|
||||||
for tool in self.requires_tools:
|
for tool in self.requires_tools:
|
||||||
if not shutil.which(tool):
|
if not shutil.which(tool):
|
||||||
return "off", f"Install: pip install {tool}"
|
return "off", f"需要安装:pip install {tool}"
|
||||||
|
|
||||||
# Check required config
|
# Check required config
|
||||||
for key in self.requires_config:
|
for key in self.requires_config:
|
||||||
if config and not config.get(key):
|
if config and not config.get(key):
|
||||||
return "off", f"Need config: {key}. Run: agent-eyes setup"
|
return "off", f"需要配置 {key},运行 agent-eyes setup"
|
||||||
|
|
||||||
return "ok", f"{', '.join(self.backends) if self.backends else 'built-in'}"
|
return "ok", f"{'、'.join(self.backends) if self.backends else '内置'}"
|
||||||
|
|
||||||
async def search(self, query: str, config=None, **kwargs) -> List[SearchResult]:
|
async def search(self, query: str, config=None, **kwargs) -> List[SearchResult]:
|
||||||
"""Search this platform. Override if supported."""
|
"""Search this platform. Override if supported."""
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from .base import Channel, ReadResult
|
|||||||
|
|
||||||
class BilibiliChannel(Channel):
|
class BilibiliChannel(Channel):
|
||||||
name = "bilibili"
|
name = "bilibili"
|
||||||
description = "Bilibili video info and subtitles"
|
description = "B站视频信息和字幕"
|
||||||
backends = ["Bilibili API"]
|
backends = ["Bilibili API"]
|
||||||
tier = 0
|
tier = 0
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ class BilibiliChannel(Channel):
|
|||||||
def check(self, config=None):
|
def check(self, config=None):
|
||||||
proxy = config.get("bilibili_proxy") if config else None
|
proxy = config.get("bilibili_proxy") if config else None
|
||||||
if proxy:
|
if proxy:
|
||||||
return "ok", "Via proxy"
|
return "ok", "已配置代理,完整可用"
|
||||||
# Detect if we're on a server (same logic as cli._detect_environment)
|
# Detect if we're on a server (same logic as cli._detect_environment)
|
||||||
import os
|
import os
|
||||||
indicators = [
|
indicators = [
|
||||||
@@ -34,8 +34,8 @@ class BilibiliChannel(Channel):
|
|||||||
]
|
]
|
||||||
is_server = any(indicators)
|
is_server = any(indicators)
|
||||||
if is_server:
|
if is_server:
|
||||||
return "warn", "May be blocked on servers. Fix: agent-eyes configure proxy URL"
|
return "warn", "服务器 IP 可能被封,配置代理即可解决:agent-eyes configure proxy URL"
|
||||||
return "ok", "Local access"
|
return "ok", "本地直连可用"
|
||||||
|
|
||||||
async def read(self, url: str, config=None) -> ReadResult:
|
async def read(self, url: str, config=None) -> ReadResult:
|
||||||
# Proxy support (Bilibili blocks server IPs)
|
# Proxy support (Bilibili blocks server IPs)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from typing import List
|
|||||||
|
|
||||||
class ExaSearchChannel(Channel):
|
class ExaSearchChannel(Channel):
|
||||||
name = "exa_search"
|
name = "exa_search"
|
||||||
description = "Semantic web search (powers Reddit/Twitter search too)"
|
description = "全网语义搜索(同时支持 Reddit/Twitter 搜索)"
|
||||||
backends = ["Exa API"]
|
backends = ["Exa API"]
|
||||||
requires_config = ["exa_api_key"]
|
requires_config = ["exa_api_key"]
|
||||||
tier = 1
|
tier = 1
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from typing import List
|
|||||||
|
|
||||||
class GitHubChannel(Channel):
|
class GitHubChannel(Channel):
|
||||||
name = "github"
|
name = "github"
|
||||||
description = "GitHub repos and code"
|
description = "GitHub 仓库和代码"
|
||||||
backends = ["GitHub API"]
|
backends = ["GitHub API"]
|
||||||
tier = 0
|
tier = 0
|
||||||
|
|
||||||
@@ -29,8 +29,8 @@ class GitHubChannel(Channel):
|
|||||||
def check(self, config=None):
|
def check(self, config=None):
|
||||||
token = config.get("github_token") if config else None
|
token = config.get("github_token") if config else None
|
||||||
if token:
|
if token:
|
||||||
return "ok", "Full access (authenticated)"
|
return "ok", "已认证,可访问私有仓库"
|
||||||
return "ok", "Public repos only. Set github_token for private repos + higher rate limits"
|
return "ok", "公开仓库可用。配置 github_token 可访问私有仓库"
|
||||||
|
|
||||||
def can_handle(self, url: str) -> bool:
|
def can_handle(self, url: str) -> bool:
|
||||||
domain = urlparse(url).netloc.lower()
|
domain = urlparse(url).netloc.lower()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from .base import Channel, ReadResult
|
|||||||
|
|
||||||
class RedditChannel(Channel):
|
class RedditChannel(Channel):
|
||||||
name = "reddit"
|
name = "reddit"
|
||||||
description = "Reddit posts and comments"
|
description = "Reddit 帖子和评论"
|
||||||
backends = ["Reddit JSON API"]
|
backends = ["Reddit JSON API"]
|
||||||
requires_config = ["reddit_proxy"]
|
requires_config = ["reddit_proxy"]
|
||||||
tier = 2
|
tier = 2
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from .base import Channel, ReadResult
|
|||||||
|
|
||||||
class RSSChannel(Channel):
|
class RSSChannel(Channel):
|
||||||
name = "rss"
|
name = "rss"
|
||||||
description = "RSS and Atom feeds"
|
description = "RSS/Atom 订阅源"
|
||||||
backends = ["feedparser"]
|
backends = ["feedparser"]
|
||||||
tier = 0
|
tier = 0
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import requests
|
|||||||
|
|
||||||
class TwitterChannel(Channel):
|
class TwitterChannel(Channel):
|
||||||
name = "twitter"
|
name = "twitter"
|
||||||
description = "Twitter/X posts"
|
description = "Twitter/X 推文"
|
||||||
backends = ["birdx", "Jina Reader"]
|
backends = ["birdx", "Jina Reader"]
|
||||||
tier = 0 # Single tweet reading is zero-config
|
tier = 0 # Single tweet reading is zero-config
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ class TwitterChannel(Channel):
|
|||||||
def check(self, config=None):
|
def check(self, config=None):
|
||||||
# Basic reading always works (Jina fallback)
|
# Basic reading always works (Jina fallback)
|
||||||
if shutil.which("birdx"):
|
if shutil.which("birdx"):
|
||||||
return "ok", "Full access (search + timeline + threads)"
|
return "ok", "搜索、时间线、发推全部可用"
|
||||||
return "ok", "Read-only (single tweets via Jina). Install birdx for search + timelines"
|
return "ok", "可读取推文。安装 birdx + 配置 Cookie 可解锁搜索和发推"
|
||||||
return "ok", "Jina Reader (single tweets only)"
|
return "ok", "Jina Reader (single tweets only)"
|
||||||
|
|
||||||
async def read(self, url: str, config=None) -> ReadResult:
|
async def read(self, url: str, config=None) -> ReadResult:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from .base import Channel, ReadResult
|
|||||||
|
|
||||||
class WebChannel(Channel):
|
class WebChannel(Channel):
|
||||||
name = "web"
|
name = "web"
|
||||||
description = "Web pages (any URL)"
|
description = "网页(任意 URL)"
|
||||||
backends = ["Jina Reader API"]
|
backends = ["Jina Reader API"]
|
||||||
tier = 0
|
tier = 0
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from .base import Channel, ReadResult
|
|||||||
|
|
||||||
class XiaoHongShuChannel(Channel):
|
class XiaoHongShuChannel(Channel):
|
||||||
name = "xiaohongshu"
|
name = "xiaohongshu"
|
||||||
description = "XiaoHongShu (小红书) notes"
|
description = "小红书笔记"
|
||||||
backends = ["XHS Web API"]
|
backends = ["XHS Web API"]
|
||||||
requires_config = ["xhs_cookie"]
|
requires_config = ["xhs_cookie"]
|
||||||
tier = 2
|
tier = 2
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from .base import Channel, ReadResult
|
|||||||
|
|
||||||
class YouTubeChannel(Channel):
|
class YouTubeChannel(Channel):
|
||||||
name = "youtube"
|
name = "youtube"
|
||||||
description = "YouTube video transcripts"
|
description = "YouTube 视频字幕"
|
||||||
backends = ["yt-dlp"]
|
backends = ["yt-dlp"]
|
||||||
requires_tools = ["yt-dlp"]
|
requires_tools = ["yt-dlp"]
|
||||||
tier = 0
|
tier = 0
|
||||||
|
|||||||
+20
-10
@@ -27,7 +27,7 @@ def check_all(config: Config) -> Dict[str, dict]:
|
|||||||
def format_report(results: Dict[str, dict]) -> str:
|
def format_report(results: Dict[str, dict]) -> str:
|
||||||
"""Format results as a readable text report."""
|
"""Format results as a readable text report."""
|
||||||
lines = []
|
lines = []
|
||||||
lines.append("👁️ Agent Eyes Status")
|
lines.append("👁️ Agent Eyes 状态")
|
||||||
lines.append("=" * 40)
|
lines.append("=" * 40)
|
||||||
|
|
||||||
ok_count = sum(1 for r in results.values() if r["status"] == "ok")
|
ok_count = sum(1 for r in results.values() if r["status"] == "ok")
|
||||||
@@ -35,7 +35,7 @@ def format_report(results: Dict[str, dict]) -> str:
|
|||||||
|
|
||||||
# Tier 0 — zero config
|
# Tier 0 — zero config
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("✅ Ready (no setup needed):")
|
lines.append("✅ 装好即用:")
|
||||||
for key, r in results.items():
|
for key, r in results.items():
|
||||||
if r["tier"] == 0:
|
if r["tier"] == 0:
|
||||||
if r["status"] == "ok":
|
if r["status"] == "ok":
|
||||||
@@ -49,23 +49,33 @@ def format_report(results: Dict[str, dict]) -> str:
|
|||||||
tier1 = {k: r for k, r in results.items() if r["tier"] == 1}
|
tier1 = {k: r for k, r in results.items() if r["tier"] == 1}
|
||||||
if tier1:
|
if tier1:
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("🔍 Search (need free Exa API key):")
|
lines.append("🔍 搜索(免费 Exa Key 即可解锁):")
|
||||||
for key, r in tier1.items():
|
for key, r in tier1.items():
|
||||||
icon = "✅" if r["status"] == "ok" else "⬜"
|
if r["status"] == "ok":
|
||||||
lines.append(f" {icon} {r['name']}")
|
lines.append(f" ✅ {r['name']}")
|
||||||
|
else:
|
||||||
|
lines.append(f" ⬜ {r['name']} — 注册 exa.ai 获取免费 Key,配置一下就能用")
|
||||||
|
|
||||||
# Tier 2 — optional setup
|
# Tier 2 — optional setup
|
||||||
tier2 = {k: r for k, r in results.items() if r["tier"] == 2}
|
tier2 = {k: r for k, r in results.items() if r["tier"] == 2}
|
||||||
if tier2:
|
if tier2:
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("🔧 Optional (advanced setup):")
|
lines.append("🔧 配置后可用:")
|
||||||
for key, r in tier2.items():
|
for key, r in tier2.items():
|
||||||
icon = "✅" if r["status"] == "ok" else "⬜"
|
if r["status"] == "ok":
|
||||||
lines.append(f" {icon} {r['name']} — {r['message']}")
|
lines.append(f" ✅ {r['name']} — {r['message']}")
|
||||||
|
else:
|
||||||
|
# Friendly message about what to configure
|
||||||
|
if "proxy" in str(r.get("message", "")):
|
||||||
|
lines.append(f" ⬜ {r['name']} — 配个代理就能用:agent-eyes configure proxy URL")
|
||||||
|
elif "cookie" in str(r.get("message", "")):
|
||||||
|
lines.append(f" ⬜ {r['name']} — 导入浏览器 Cookie 就能用:agent-eyes configure --from-browser chrome")
|
||||||
|
else:
|
||||||
|
lines.append(f" ⬜ {r['name']} — {r['message']}")
|
||||||
|
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append(f"Status: {ok_count}/{total} channels active")
|
lines.append(f"状态:{ok_count}/{total} 个渠道可用")
|
||||||
if ok_count < total:
|
if ok_count < total:
|
||||||
lines.append("Run `agent-eyes setup` to unlock more!")
|
lines.append("运行 `agent-eyes setup` 解锁更多渠道")
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ class TestDoctor:
|
|||||||
report = format_report(results)
|
report = format_report(results)
|
||||||
assert "Agent Eyes" in report
|
assert "Agent Eyes" in report
|
||||||
assert "✅" in report
|
assert "✅" in report
|
||||||
assert "channels active" in report
|
assert "渠道可用" in report
|
||||||
|
|||||||
Reference in New Issue
Block a user