fix: birdx → bird CLI (npm @steipete/bird)
birdx 从来不是 PyPI 包,pip install birdx 必然失败。 实际工具是 npm 包 @steipete/bird,一个 Twitter GraphQL CLI。 变更: - 安装器改用 npm install -g @steipete/bird - twitter.py 直接调 bird,通过环境变量传 AUTH_TOKEN/CT0 - 兼容已有的 birdx wrapper(shutil.which 回退) - 更新所有文档引用 - 重写 setup-twitter.md 指南
This commit is contained in:
@@ -50,7 +50,7 @@ AI Agent 已经能帮你写代码、改文档、管项目——但你让它去
|
|||||||
|---|---|
|
|---|---|
|
||||||
| 💰 **完全免费** | 所有工具开源、所有 API 免费。唯一可能花钱的是服务器代理($1/月),本地电脑不需要 |
|
| 💰 **完全免费** | 所有工具开源、所有 API 免费。唯一可能花钱的是服务器代理($1/月),本地电脑不需要 |
|
||||||
| 🔒 **隐私安全** | Cookie 只存在你本地,不上传不外传。代码完全开源,随时可审查 |
|
| 🔒 **隐私安全** | Cookie 只存在你本地,不上传不外传。代码完全开源,随时可审查 |
|
||||||
| 🔄 **持续更新** | 底层工具(yt-dlp、birdx、Jina Reader 等)定期追踪更新到最新版,你不用自己盯 |
|
| 🔄 **持续更新** | 底层工具(yt-dlp、bird、Jina Reader 等)定期追踪更新到最新版,你不用自己盯 |
|
||||||
| 🤖 **兼容所有 Agent** | Claude Code、OpenClaw、Cursor、Windsurf……任何能跑命令行的 Agent 都能用 |
|
| 🤖 **兼容所有 Agent** | Claude Code、OpenClaw、Cursor、Windsurf……任何能跑命令行的 Agent 都能用 |
|
||||||
| 🩺 **自带诊断** | `agent-reach doctor` 一条命令告诉你哪个通、哪个不通、怎么修 |
|
| 🩺 **自带诊断** | `agent-reach doctor` 一条命令告诉你哪个通、哪个不通、怎么修 |
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ Agent Reach 做的事情很简单:**帮你把这些选型和配置的活儿做
|
|||||||
```
|
```
|
||||||
channels/
|
channels/
|
||||||
├── web.py → Jina Reader ← 可以换成 Firecrawl、Crawl4AI……
|
├── web.py → Jina Reader ← 可以换成 Firecrawl、Crawl4AI……
|
||||||
├── twitter.py → birdx ← 可以换成 Nitter、官方 API……
|
├── twitter.py → bird ← 可以换成 Nitter、官方 API……
|
||||||
├── youtube.py → yt-dlp ← 可以换成 YouTube API、Whisper……
|
├── youtube.py → yt-dlp ← 可以换成 YouTube API、Whisper……
|
||||||
├── github.py → gh CLI ← 可以换成 REST API、PyGithub……
|
├── github.py → gh CLI ← 可以换成 REST API、PyGithub……
|
||||||
├── bilibili.py → yt-dlp ← 可以换成 bilibili-api……
|
├── bilibili.py → yt-dlp ← 可以换成 bilibili-api……
|
||||||
@@ -146,7 +146,7 @@ channels/
|
|||||||
| 场景 | 选型 | 为什么选它 |
|
| 场景 | 选型 | 为什么选它 |
|
||||||
|------|------|-----------|
|
|------|------|-----------|
|
||||||
| 读网页 | [Jina Reader](https://github.com/jina-ai/reader) | 9.8K Star,免费,不需要 API Key |
|
| 读网页 | [Jina Reader](https://github.com/jina-ai/reader) | 9.8K Star,免费,不需要 API Key |
|
||||||
| 读推特 | [birdx](https://github.com/runesleo/birdx) | Cookie 登录,免费。官方 API 按量付费(读一条 $0.005) |
|
| 读推特 | [bird](https://github.com/steipete/bird) | Cookie 登录,免费。官方 API 按量付费(读一条 $0.005) |
|
||||||
| 视频字幕 + 搜索 | [yt-dlp](https://github.com/yt-dlp/yt-dlp) | 148K Star,YouTube + B站 + 1800 站通吃 |
|
| 视频字幕 + 搜索 | [yt-dlp](https://github.com/yt-dlp/yt-dlp) | 148K Star,YouTube + B站 + 1800 站通吃 |
|
||||||
| 搜全网 | [Exa](https://exa.ai) via [mcporter](https://github.com/nicepkg/mcporter) | AI 语义搜索,MCP 接入免 Key |
|
| 搜全网 | [Exa](https://exa.ai) via [mcporter](https://github.com/nicepkg/mcporter) | AI 语义搜索,MCP 接入免 Key |
|
||||||
| GitHub | [gh CLI](https://cli.github.com) | 官方工具,认证后完整 API 能力 |
|
| GitHub | [gh CLI](https://cli.github.com) | 官方工具,认证后完整 API 能力 |
|
||||||
@@ -183,7 +183,7 @@ Star 一下,下次需要的时候能找到。⭐
|
|||||||
|
|
||||||
## 致谢
|
## 致谢
|
||||||
|
|
||||||
[Jina Reader](https://github.com/jina-ai/reader) · [yt-dlp](https://github.com/yt-dlp/yt-dlp) · [birdx](https://github.com/runesleo/birdx) · [Exa](https://exa.ai) · [feedparser](https://github.com/kurtmckee/feedparser)
|
[Jina Reader](https://github.com/jina-ai/reader) · [yt-dlp](https://github.com/yt-dlp/yt-dlp) · [bird](https://github.com/steipete/bird) · [Exa](https://exa.ai) · [feedparser](https://github.com/kurtmckee/feedparser)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Twitter/X — via birdx CLI (free) or Jina Reader fallback.
|
"""Twitter/X — via bird CLI (free) or Jina Reader fallback.
|
||||||
|
|
||||||
Backend: birdx (https://github.com/runesleo/birdx) for search/timeline
|
Backend: bird (@steipete/bird npm package) for search/timeline
|
||||||
Jina Reader for single tweets
|
Jina Reader for single tweets
|
||||||
Swap to: any Twitter access tool
|
Swap to: any Twitter access tool
|
||||||
"""
|
"""
|
||||||
@@ -14,10 +14,29 @@ from typing import List
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
def _bird_cmd():
|
||||||
|
"""Find bird CLI binary."""
|
||||||
|
return shutil.which("bird") or shutil.which("birdx")
|
||||||
|
|
||||||
|
|
||||||
|
def _bird_env(config=None):
|
||||||
|
"""Build env dict with Twitter cookies for bird CLI."""
|
||||||
|
import os
|
||||||
|
env = os.environ.copy()
|
||||||
|
if config:
|
||||||
|
auth_token = config.get("twitter_auth_token")
|
||||||
|
ct0 = config.get("twitter_ct0")
|
||||||
|
if auth_token:
|
||||||
|
env["AUTH_TOKEN"] = auth_token
|
||||||
|
if ct0:
|
||||||
|
env["CT0"] = ct0
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
class TwitterChannel(Channel):
|
class TwitterChannel(Channel):
|
||||||
name = "twitter"
|
name = "twitter"
|
||||||
description = "Twitter/X 推文"
|
description = "Twitter/X 推文"
|
||||||
backends = ["birdx", "Jina Reader"]
|
backends = ["bird", "Jina Reader"]
|
||||||
tier = 0 # Single tweet reading is zero-config
|
tier = 0 # Single tweet reading is zero-config
|
||||||
|
|
||||||
def can_handle(self, url: str) -> bool:
|
def can_handle(self, url: str) -> bool:
|
||||||
@@ -26,21 +45,23 @@ 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 _bird_cmd():
|
||||||
return "ok", "搜索、时间线、发推全部可用"
|
return "ok", "搜索、时间线、发推全部可用"
|
||||||
return "ok", "可读取推文。安装 birdx + 配置 Cookie 可解锁搜索和发推"
|
return "ok", "可读取推文。安装 bird + 配置 Cookie 可解锁搜索和发推"
|
||||||
|
|
||||||
async def read(self, url: str, config=None) -> ReadResult:
|
async def read(self, url: str, config=None) -> ReadResult:
|
||||||
# Try birdx first
|
# Try bird first
|
||||||
if shutil.which("birdx"):
|
bird = _bird_cmd()
|
||||||
return await self._read_birdx(url)
|
if bird:
|
||||||
|
return await self._read_bird(url, bird, config)
|
||||||
# Fallback: Jina Reader
|
# Fallback: Jina Reader
|
||||||
return await self._read_jina(url)
|
return await self._read_jina(url)
|
||||||
|
|
||||||
async def _read_birdx(self, url: str) -> ReadResult:
|
async def _read_bird(self, url: str, bird: str, config=None) -> ReadResult:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["birdx", "read", url],
|
[bird, "read", url],
|
||||||
capture_output=True, text=True, timeout=30,
|
capture_output=True, text=True, timeout=30,
|
||||||
|
env=_bird_env(config),
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
return await self._read_jina(url)
|
return await self._read_jina(url)
|
||||||
@@ -84,7 +105,7 @@ class TwitterChannel(Channel):
|
|||||||
"The tweet may have been deleted, or the account is private.\n\n"
|
"The tweet may have been deleted, or the account is private.\n\n"
|
||||||
"Tips:\n"
|
"Tips:\n"
|
||||||
"- Make sure the URL is correct\n"
|
"- Make sure the URL is correct\n"
|
||||||
"- Try: birdx read <url> (if birdx is installed)\n"
|
"- Try: bird read <url> (if bird CLI is installed)\n"
|
||||||
"- For protected tweets, configure Twitter cookies: "
|
"- For protected tweets, configure Twitter cookies: "
|
||||||
"agent-reach configure twitter-cookies AUTH_TOKEN CT0",
|
"agent-reach configure twitter-cookies AUTH_TOKEN CT0",
|
||||||
url=url,
|
url=url,
|
||||||
@@ -105,7 +126,7 @@ class TwitterChannel(Channel):
|
|||||||
"The tweet may have been deleted, or the account is private.\n\n"
|
"The tweet may have been deleted, or the account is private.\n\n"
|
||||||
"Tips:\n"
|
"Tips:\n"
|
||||||
"- Make sure the URL is correct\n"
|
"- Make sure the URL is correct\n"
|
||||||
"- Try: birdx read <url> (if birdx is installed)\n"
|
"- Try: bird read <url> (if bird CLI is installed)\n"
|
||||||
"- For protected tweets, configure Twitter cookies: "
|
"- For protected tweets, configure Twitter cookies: "
|
||||||
"agent-reach configure twitter-cookies AUTH_TOKEN CT0",
|
"agent-reach configure twitter-cookies AUTH_TOKEN CT0",
|
||||||
url=url,
|
url=url,
|
||||||
@@ -115,27 +136,29 @@ class TwitterChannel(Channel):
|
|||||||
async def search(self, query: str, config=None, **kwargs) -> List[SearchResult]:
|
async def search(self, query: str, config=None, **kwargs) -> List[SearchResult]:
|
||||||
limit = kwargs.get("limit", 10)
|
limit = kwargs.get("limit", 10)
|
||||||
|
|
||||||
if shutil.which("birdx"):
|
bird = _bird_cmd()
|
||||||
return await self._search_birdx(query, limit)
|
if bird:
|
||||||
|
return await self._search_bird(query, limit, bird, config)
|
||||||
|
|
||||||
# Fallback to Exa
|
# Fallback to Exa
|
||||||
return await self._search_exa(query, limit, config)
|
return await self._search_exa(query, limit, config)
|
||||||
|
|
||||||
async def _search_birdx(self, query: str, limit: int) -> List[SearchResult]:
|
async def _search_bird(self, query: str, limit: int, bird: str, config=None) -> List[SearchResult]:
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["birdx", "search", query, "-n", str(limit)],
|
[bird, "search", query, "-n", str(limit)],
|
||||||
capture_output=True, text=True, timeout=30,
|
capture_output=True, text=True, timeout=30,
|
||||||
|
env=_bird_env(config),
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
return self._parse_birdx_output(result.stdout)
|
return self._parse_bird_output(result.stdout)
|
||||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _parse_birdx_output(self, text: str) -> List[SearchResult]:
|
def _parse_bird_output(self, text: str) -> List[SearchResult]:
|
||||||
"""Parse birdx text output into SearchResults."""
|
"""Parse bird text output into SearchResults."""
|
||||||
results = []
|
results = []
|
||||||
current = {}
|
current = {}
|
||||||
text_lines = []
|
text_lines = []
|
||||||
|
|||||||
+28
-20
@@ -305,23 +305,24 @@ def _install_system_deps():
|
|||||||
except Exception:
|
except Exception:
|
||||||
print(" ⚠️ Node.js install failed. Try: apt install nodejs npm, or nvm install 22, or download from https://nodejs.org")
|
print(" ⚠️ Node.js install failed. Try: apt install nodejs npm, or nvm install 22, or download from https://nodejs.org")
|
||||||
|
|
||||||
# ── birdx (for Twitter search) ──
|
# ── bird CLI (for Twitter search) ──
|
||||||
if shutil.which("birdx"):
|
if shutil.which("bird") or shutil.which("birdx"):
|
||||||
print(" ✅ birdx already installed")
|
print(" ✅ bird CLI already installed")
|
||||||
else:
|
else:
|
||||||
if shutil.which("pip3") or shutil.which("pip"):
|
if shutil.which("npm"):
|
||||||
pip_cmd = "pip3" if shutil.which("pip3") else "pip"
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[pip_cmd, "install", "-q", "birdx"],
|
["npm", "install", "-g", "@steipete/bird"],
|
||||||
capture_output=True, text=True, timeout=120,
|
capture_output=True, text=True, timeout=120,
|
||||||
)
|
)
|
||||||
if shutil.which("birdx"):
|
if shutil.which("bird"):
|
||||||
print(" ✅ birdx installed (Twitter search + timeline)")
|
print(" ✅ bird CLI installed (Twitter search + timeline)")
|
||||||
else:
|
else:
|
||||||
print(" ⬜ birdx install failed (optional — Twitter reading still works via Jina)")
|
print(" ⬜ bird CLI install failed (optional — Twitter reading still works via Jina)")
|
||||||
except Exception:
|
except Exception:
|
||||||
print(" ⬜ birdx install failed (optional — Twitter reading still works via Jina)")
|
print(" ⬜ bird CLI install failed (optional — Twitter reading still works via Jina)")
|
||||||
|
else:
|
||||||
|
print(" ⬜ bird CLI requires Node.js (optional — Twitter reading still works via Jina)")
|
||||||
|
|
||||||
|
|
||||||
def _install_mcporter():
|
def _install_mcporter():
|
||||||
@@ -435,6 +436,7 @@ def _detect_environment():
|
|||||||
|
|
||||||
def _cmd_configure(args):
|
def _cmd_configure(args):
|
||||||
"""Set a config value and test it, or auto-extract from browser."""
|
"""Set a config value and test it, or auto-extract from browser."""
|
||||||
|
import shutil
|
||||||
from agent_reach.config import Config
|
from agent_reach.config import Config
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
@@ -525,17 +527,23 @@ def _cmd_configure(args):
|
|||||||
print("Testing Twitter access...", end=" ")
|
print("Testing Twitter access...", end=" ")
|
||||||
try:
|
try:
|
||||||
import subprocess
|
import subprocess
|
||||||
result = subprocess.run(
|
bird = shutil.which("bird") or shutil.which("birdx")
|
||||||
["birdx", "search", "test", "-n", "1",
|
if not bird:
|
||||||
"--auth-token", auth_token, "--ct0", ct0],
|
print("⚠️ bird CLI not installed. Run: npm install -g @steipete/bird")
|
||||||
capture_output=True, text=True, timeout=15,
|
|
||||||
)
|
|
||||||
if result.returncode == 0 and result.stdout.strip():
|
|
||||||
print("✅ Twitter Advanced works!")
|
|
||||||
else:
|
else:
|
||||||
print(f"⚠️ Test returned no results (cookies might be wrong)")
|
import os
|
||||||
except FileNotFoundError:
|
env = os.environ.copy()
|
||||||
print("⚠️ birdx not installed. Run: pip install birdx")
|
env["AUTH_TOKEN"] = auth_token
|
||||||
|
env["CT0"] = ct0
|
||||||
|
result = subprocess.run(
|
||||||
|
[bird, "search", "test", "-n", "1"],
|
||||||
|
capture_output=True, text=True, timeout=15,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
if result.returncode == 0 and result.stdout.strip():
|
||||||
|
print("✅ Twitter Advanced works!")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Test returned no results (cookies might be wrong)")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Failed: {e}")
|
print(f"❌ Failed: {e}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class Config:
|
|||||||
FEATURE_REQUIREMENTS = {
|
FEATURE_REQUIREMENTS = {
|
||||||
"exa_search": ["exa_api_key"],
|
"exa_search": ["exa_api_key"],
|
||||||
"reddit_proxy": ["reddit_proxy"],
|
"reddit_proxy": ["reddit_proxy"],
|
||||||
"twitter_birdx": ["twitter_auth_token", "twitter_ct0"],
|
"twitter_bird": ["twitter_auth_token", "twitter_ct0"],
|
||||||
"groq_whisper": ["groq_api_key"],
|
"groq_whisper": ["groq_api_key"],
|
||||||
"github_token": ["github_token"],
|
"github_token": ["github_token"],
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -78,7 +78,7 @@ class AgentReach:
|
|||||||
return [r.to_dict() for r in results]
|
return [r.to_dict() for r in results]
|
||||||
|
|
||||||
async def search_twitter(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
|
async def search_twitter(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
|
||||||
"""Search Twitter. Uses birdx if available, else Exa."""
|
"""Search Twitter. Uses bird CLI if available, else Exa."""
|
||||||
ch = get_channel("twitter")
|
ch = get_channel("twitter")
|
||||||
results = await ch.search(query, config=self.config, limit=limit)
|
results = await ch.search(query, config=self.config, limit=limit)
|
||||||
return [r.to_dict() for r in results]
|
return [r.to_dict() for r in results]
|
||||||
|
|||||||
@@ -1,44 +1,36 @@
|
|||||||
# Twitter 高级功能配置指南(birdx)
|
# Twitter 高级功能配置指南(bird CLI)
|
||||||
|
|
||||||
## 功能说明
|
## 功能说明
|
||||||
基础 Twitter 功能(搜索+读单条推文)无需配置,开箱即用。
|
基础 Twitter 功能(搜索+读单条推文)无需配置,开箱即用。
|
||||||
|
|
||||||
高级功能需要 birdx:
|
高级功能需要 bird CLI:
|
||||||
- 查看用户时间线
|
- 查看用户时间线
|
||||||
- 深度搜索(更精确、更多结果)
|
- 深度搜索(更精确、更多结果)
|
||||||
- 读取完整线程(thread)
|
- 读取完整线程(thread)
|
||||||
- 查看关注列表推文
|
- 查看关注列表推文
|
||||||
|
|
||||||
birdx 是免费开源工具,但需要你的 Twitter 账号 cookie。
|
bird 是免费开源工具(npm 包 @steipete/bird),但需要你的 Twitter 账号 cookie。
|
||||||
|
|
||||||
## Agent 可自动完成的步骤
|
## Agent 可自动完成的步骤
|
||||||
|
|
||||||
1. 检查 birdx 是否安装:
|
1. 检查 bird 是否安装:
|
||||||
```bash
|
```bash
|
||||||
which birdx && echo "installed" || echo "not installed"
|
which bird && echo "installed" || echo "not installed"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 安装 birdx:
|
2. 安装 bird:
|
||||||
```bash
|
```bash
|
||||||
pip install birdx
|
npm install -g @steipete/bird
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 检查是否已配置 cookie:
|
3. 测试(需要先配置 cookie):
|
||||||
```bash
|
```bash
|
||||||
birdx whoami 2>&1
|
AUTH_TOKEN="xxx" CT0="yyy" bird search "test" -n 1
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 如果用户提供了 cookie,配置 birdx:
|
4. 用 agent-reach 配置 cookie(推荐):
|
||||||
```bash
|
```bash
|
||||||
# birdx 的 cookie 配置文件位置
|
agent-reach configure twitter-cookies "auth_token=xxx; ct0=yyy"
|
||||||
# 通常在 ~/.birdx/cookies.json 或通过环境变量
|
|
||||||
export TWITTER_AUTH_TOKEN="用户提供的auth_token"
|
|
||||||
export TWITTER_CT0="用户提供的ct0"
|
|
||||||
```
|
|
||||||
|
|
||||||
5. 测试:
|
|
||||||
```bash
|
|
||||||
birdx search "test" -n 1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 需要用户手动做的步骤
|
## 需要用户手动做的步骤
|
||||||
@@ -47,7 +39,13 @@ birdx search "test" -n 1
|
|||||||
|
|
||||||
> Twitter 高级功能需要你的 Twitter 账号 cookie(完全免费)。
|
> Twitter 高级功能需要你的 Twitter 账号 cookie(完全免费)。
|
||||||
>
|
>
|
||||||
> 步骤:
|
> **最简单的方式:**
|
||||||
|
> 1. 安装 Chrome 插件 [Cookie-Editor](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm)
|
||||||
|
> 2. 打开 https://x.com 并确保已登录
|
||||||
|
> 3. 点击 Cookie-Editor 插件图标 → Export → Header String
|
||||||
|
> 4. 把导出的内容发给我
|
||||||
|
>
|
||||||
|
> **手动方式:**
|
||||||
> 1. 用 Chrome 打开 https://x.com 并确保你已登录
|
> 1. 用 Chrome 打开 https://x.com 并确保你已登录
|
||||||
> 2. 按 **F12** 打开开发者工具(Mac 按 Cmd+Option+I)
|
> 2. 按 **F12** 打开开发者工具(Mac 按 Cmd+Option+I)
|
||||||
> 3. 点击顶部的 **Application**(应用)标签
|
> 3. 点击顶部的 **Application**(应用)标签
|
||||||
@@ -62,8 +60,8 @@ birdx search "test" -n 1
|
|||||||
|
|
||||||
## Agent 收到 cookie 后的操作
|
## Agent 收到 cookie 后的操作
|
||||||
|
|
||||||
1. 安装 birdx(如果没装):`pip install birdx`
|
1. 安装 bird(如果没装):`npm install -g @steipete/bird`
|
||||||
2. 配置 cookie:写入 birdx 配置
|
2. 配置 cookie:`agent-reach configure twitter-cookies "粘贴的内容"`
|
||||||
3. 测试:`birdx whoami` 确认身份
|
3. 测试:运行 `agent-reach doctor` 确认 Twitter 状态
|
||||||
4. 反馈:"✅ Twitter 高级功能已开启!你的账号是 @xxx。现在我可以查看时间线、读取线程了。"
|
4. 反馈:"✅ Twitter 高级功能已开启!现在可以搜索推文、查看时间线了。"
|
||||||
5. 如果失败:"❌ Cookie 无效或已过期,请重新导出。"
|
5. 如果失败:"❌ Cookie 无效或已过期,请重新导出。"
|
||||||
|
|||||||
+5
-5
@@ -45,7 +45,7 @@ Copy that to your Agent. A few minutes later, it can read tweets, search Reddit,
|
|||||||
|---|---|
|
|---|---|
|
||||||
| 💰 **Completely free** | All tools are open source, all APIs are free. The only possible cost is a server proxy ($1/month) — local computers don't need one |
|
| 💰 **Completely free** | All tools are open source, all APIs are free. The only possible cost is a server proxy ($1/month) — local computers don't need one |
|
||||||
| 🔒 **Privacy safe** | Cookies stay local. Never uploaded. Fully open source — audit anytime |
|
| 🔒 **Privacy safe** | Cookies stay local. Never uploaded. Fully open source — audit anytime |
|
||||||
| 🔄 **Kept up to date** | Upstream tools (yt-dlp, birdx, Jina Reader, etc.) are tracked and updated regularly |
|
| 🔄 **Kept up to date** | Upstream tools (yt-dlp, bird, Jina Reader, etc.) are tracked and updated regularly |
|
||||||
| 🤖 **Works with any Agent** | Claude Code, OpenClaw, Cursor, Windsurf… any Agent that can run commands |
|
| 🤖 **Works with any Agent** | Claude Code, OpenClaw, Cursor, Windsurf… any Agent that can run commands |
|
||||||
| 🩺 **Built-in diagnostics** | `agent-reach doctor` — one command shows what works, what doesn't, and how to fix it |
|
| 🩺 **Built-in diagnostics** | `agent-reach doctor` — one command shows what works, what doesn't, and how to fix it |
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ Copy that to your Agent. A few minutes later, it can read tweets, search Reddit,
|
|||||||
| Platform | Capabilities | Setup | Notes |
|
| Platform | Capabilities | Setup | Notes |
|
||||||
|----------|-------------|:-----:|-------|
|
|----------|-------------|:-----:|-------|
|
||||||
| 🌐 **Web** | Read | Zero config | Any URL → clean Markdown ([Jina Reader](https://github.com/jina-ai/reader) ⭐9.8K) |
|
| 🌐 **Web** | Read | Zero config | Any URL → clean Markdown ([Jina Reader](https://github.com/jina-ai/reader) ⭐9.8K) |
|
||||||
| 🐦 **Twitter/X** | Read · Search | Zero config / Cookie | Single tweets readable out of the box. Cookie unlocks search, timeline, posting ([birdx](https://github.com/runesleo/birdx)) |
|
| 🐦 **Twitter/X** | Read · Search | Zero config / Cookie | Single tweets readable out of the box. Cookie unlocks search, timeline, posting ([bird](https://github.com/steipete/bird)) |
|
||||||
| 📕 **XiaoHongShu** | Read · Search · **Post · Comment · Like** | mcporter | Via [xiaohongshu-mcp](https://github.com/user/xiaohongshu-mcp) internal API, install and go |
|
| 📕 **XiaoHongShu** | Read · Search · **Post · Comment · Like** | mcporter | Via [xiaohongshu-mcp](https://github.com/user/xiaohongshu-mcp) internal API, install and go |
|
||||||
| 🔍 **Web Search** | Search | Auto-configured | Auto-configured during install, free, no API key ([Exa](https://exa.ai) via [mcporter](https://github.com/nicepkg/mcporter)) |
|
| 🔍 **Web Search** | Search | Auto-configured | Auto-configured during install, free, no API key ([Exa](https://exa.ai) via [mcporter](https://github.com/nicepkg/mcporter)) |
|
||||||
| 📦 **GitHub** | Read · Search | Zero config | [gh CLI](https://cli.github.com) powered. Public repos work immediately. `gh auth login` unlocks Fork, Issue, PR |
|
| 📦 **GitHub** | Read · Search | Zero config | [gh CLI](https://cli.github.com) powered. Public repos work immediately. `gh auth login` unlocks Fork, Issue, PR |
|
||||||
@@ -164,7 +164,7 @@ Each platform is a single Python file implementing a unified interface. **Backen
|
|||||||
```
|
```
|
||||||
channels/
|
channels/
|
||||||
├── web.py → Jina Reader ← swap to Firecrawl, Crawl4AI…
|
├── web.py → Jina Reader ← swap to Firecrawl, Crawl4AI…
|
||||||
├── twitter.py → birdx ← swap to Nitter, official API…
|
├── twitter.py → bird ← swap to Nitter, official API…
|
||||||
├── youtube.py → yt-dlp ← swap to YouTube API, Whisper…
|
├── youtube.py → yt-dlp ← swap to YouTube API, Whisper…
|
||||||
├── github.py → gh CLI ← swap to REST API, PyGithub…
|
├── github.py → gh CLI ← swap to REST API, PyGithub…
|
||||||
├── bilibili.py → yt-dlp ← swap to bilibili-api…
|
├── bilibili.py → yt-dlp ← swap to bilibili-api…
|
||||||
@@ -180,7 +180,7 @@ channels/
|
|||||||
| Scenario | Tool | Why |
|
| Scenario | Tool | Why |
|
||||||
|----------|------|-----|
|
|----------|------|-----|
|
||||||
| Read web pages | [Jina Reader](https://github.com/jina-ai/reader) | 9.8K stars, free, no API key needed |
|
| Read web pages | [Jina Reader](https://github.com/jina-ai/reader) | 9.8K stars, free, no API key needed |
|
||||||
| Read tweets | [birdx](https://github.com/runesleo/birdx) | Cookie auth, free. Official API is pay-per-use ($0.005/post read) |
|
| Read tweets | [bird](https://github.com/steipete/bird) | Cookie auth, free. Official API is pay-per-use ($0.005/post read) |
|
||||||
| Video subtitles + search | [yt-dlp](https://github.com/yt-dlp/yt-dlp) | 148K stars, YouTube + Bilibili + 1800 sites |
|
| Video subtitles + search | [yt-dlp](https://github.com/yt-dlp/yt-dlp) | 148K stars, YouTube + Bilibili + 1800 sites |
|
||||||
| Search the web | [Exa](https://exa.ai) via [mcporter](https://github.com/nicepkg/mcporter) | AI semantic search, MCP integration, no API key |
|
| Search the web | [Exa](https://exa.ai) via [mcporter](https://github.com/nicepkg/mcporter) | AI semantic search, MCP integration, no API key |
|
||||||
| GitHub | [gh CLI](https://cli.github.com) | Official tool, full API after auth |
|
| GitHub | [gh CLI](https://cli.github.com) | Official tool, full API after auth |
|
||||||
@@ -203,7 +203,7 @@ This project was entirely vibe-coded 🎸 There might be rough edges here and th
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
[Jina Reader](https://github.com/jina-ai/reader) · [yt-dlp](https://github.com/yt-dlp/yt-dlp) · [birdx](https://github.com/runesleo/birdx) · [Exa](https://exa.ai) · [feedparser](https://github.com/kurtmckee/feedparser)
|
[Jina Reader](https://github.com/jina-ai/reader) · [yt-dlp](https://github.com/yt-dlp/yt-dlp) · [bird](https://github.com/steipete/bird) · [Exa](https://exa.ai) · [feedparser](https://github.com/kurtmckee/feedparser)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ pip install https://github.com/Panniantong/agent-reach/archive/main.zip
|
|||||||
agent-reach install --env=auto
|
agent-reach install --env=auto
|
||||||
```
|
```
|
||||||
|
|
||||||
This auto-installs system dependencies (gh CLI, Node.js, mcporter, birdx), configures Exa search, detects environment, and tests all channels.
|
This auto-installs system dependencies (gh CLI, Node.js, mcporter, bird), configures Exa search, detects environment, and tests all channels.
|
||||||
|
|
||||||
**Read the output carefully**, then run:
|
**Read the output carefully**, then run:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user