diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1fa1840 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,65 @@ +# Changelog / 更新日志 + +All notable changes to this project will be documented in this file. + +本项目的所有重要变更都会记录在此文件中。 + +--- + +## [1.1.0] - 2025-02-25 + +### 🆕 New Channels / 新增渠道 + +#### 📷 Instagram +- Read public posts and profiles via [instaloader](https://github.com/instaloader/instaloader) +- Search via Exa (free, no API key) +- Optional cookie login for private content +- 通过 instaloader 读取公开帖子和 Profile +- 搜索通过 Exa(免费,无需 API Key) +- 可选 Cookie 登录解锁私密内容 + +#### 💼 LinkedIn +- Read person profiles, company pages, and job details via [linkedin-scraper-mcp](https://github.com/stickerdaniel/linkedin-mcp-server) +- Search people and jobs via MCP, with Exa fallback +- Fallback to Jina Reader when MCP is not configured +- 通过 linkedin-scraper-mcp 读取个人 Profile、公司页面、职位详情 +- 通过 MCP 搜索人才和职位,Exa 兜底 +- 未配置 MCP 时自动 fallback 到 Jina Reader + +#### 🏢 Boss直聘 +- QR code login via [mcp-bosszp](https://github.com/mucsbr/mcp-bosszp) +- Job search and recruiter greeting via MCP +- Fallback to Jina Reader for reading job pages +- 通过 mcp-bosszp 扫码登录 +- MCP 搜索职位、向 HR 打招呼 +- Jina Reader 兜底读取职位页面 + +### 📈 Improvements / 改进 + +- Channel count: 9 → 12 +- `agent-reach doctor` now detects all 12 channels +- CLI: added `search-instagram`, `search-linkedin`, `search-bosszhipin` subcommands +- Updated install guide with setup instructions for new channels +- 渠道数量:9 → 12 +- `agent-reach doctor` 现在检测全部 12 个渠道 +- CLI:新增 `search-instagram`、`search-linkedin`、`search-bosszhipin` 子命令 +- 安装指南新增三个渠道的配置说明 + +--- + +## [1.0.0] - 2025-02-24 + +### 🎉 Initial Release / 首次发布 + +- 9 channels: Web, Twitter/X, YouTube, Bilibili, GitHub, Reddit, XiaoHongShu, RSS, Exa Search +- CLI with `read`, `search`, `doctor`, `install` commands +- Unified channel interface — each platform is a single pluggable Python file +- Auto-detection of local vs server environments +- Built-in diagnostics via `agent-reach doctor` +- Skill registration for Claude Code / OpenClaw / Cursor +- 9 个渠道:网页、Twitter/X、YouTube、B站、GitHub、Reddit、小红书、RSS、Exa 搜索 +- CLI 支持 `read`、`search`、`doctor`、`install` 命令 +- 统一渠道接口 — 每个平台一个独立可插拔的 Python 文件 +- 自动检测本地/服务器环境 +- 内置诊断 `agent-reach doctor` +- Skill 注册支持 Claude Code / OpenClaw / Cursor diff --git a/README.md b/README.md index 7d19f8d..621e4d4 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,12 @@ AI Agent 已经能帮你写代码、改文档、管项目——但你让它去 就这一步。Agent 会自己完成剩下的所有事情。 +> 🛡️ **担心安全?** 可以用安全模式——不会自动装系统包,只告诉你需要什么: +> ``` +> 帮我安装 Agent Reach(安全模式):https://raw.githubusercontent.com/Panniantong/agent-reach/main/docs/install.md +> 安装时使用 --safe 参数 +> ``` +
它会做什么?(点击展开) @@ -169,6 +175,32 @@ channels/ --- +## 安全性 + +Agent Reach 在设计上重视安全: + +| 措施 | 说明 | +|------|------| +| 🔒 **凭据本地存储** | Cookie、Token 只存在你本机 `~/.agent-reach/config.yaml`,文件权限 600(仅所有者可读写),不上传不外传 | +| 🛡️ **安全模式** | `agent-reach install --safe` 不会自动修改系统,只列出需要什么,由你决定装不装 | +| 👀 **完全开源** | 代码透明,随时可审查。所有依赖工具也是开源项目 | +| 🔍 **Dry Run** | `agent-reach install --dry-run` 预览所有操作,不做任何改动 | +| 🧩 **可插拔架构** | 不信任某个组件?换掉对应的 channel 文件即可,不影响其他 | + +### 🍪 Cookie 安全建议 + +需要 Cookie 的平台(Twitter、小红书、Instagram)建议使用**专用小号**,不要用主账号。Cookie 等同于完整登录权限,用小号可以在凭据泄露时限制影响范围。 + +### 📦 安装方式 + +| 方式 | 命令 | 适合场景 | +|------|------|---------| +| 一键全自动(默认) | `agent-reach install --env=auto` | 个人电脑、开发环境 | +| 安全模式 | `agent-reach install --env=auto --safe` | 生产服务器、多人共用机器 | +| 仅预览 | `agent-reach install --env=auto --dry-run` | 先看看会做什么 | + +--- + ## 贡献 这个项目是纯 vibe coding 出来的 🎸 可能会有一些不完美的地方,如果遇到问题请多多包涵。有 bug 尽管提 [Issue](https://github.com/Panniantong/agent-reach/issues),我都会尽快修复。 @@ -195,7 +227,7 @@ Star 一下,下次需要的时候能找到。⭐ ## 致谢 -[Jina Reader](https://github.com/jina-ai/reader) · [yt-dlp](https://github.com/yt-dlp/yt-dlp) · [bird](https://www.npmjs.com/package/@steipete/bird) · [Exa](https://exa.ai) · [mcporter](https://github.com/steipete/mcporter) · [feedparser](https://github.com/kurtmckee/feedparser) · [xiaohongshu-mcp](https://github.com/xpzouying/xiaohongshu-mcp) +[Jina Reader](https://github.com/jina-ai/reader) · [yt-dlp](https://github.com/yt-dlp/yt-dlp) · [bird](https://www.npmjs.com/package/@steipete/bird) · [Exa](https://exa.ai) · [mcporter](https://github.com/steipete/mcporter) · [feedparser](https://github.com/kurtmckee/feedparser) · [xiaohongshu-mcp](https://github.com/xpzouying/xiaohongshu-mcp) · [instaloader](https://github.com/instaloader/instaloader) · [linkedin-scraper-mcp](https://github.com/stickerdaniel/linkedin-mcp-server) · [mcp-bosszp](https://github.com/mucsbr/mcp-bosszp) ## License diff --git a/agent_reach/cli.py b/agent_reach/cli.py index 08a12ab..f02f7fd 100644 --- a/agent_reach/cli.py +++ b/agent_reach/cli.py @@ -113,6 +113,10 @@ def main(): help="Environment: local, server, or auto-detect") p_install.add_argument("--proxy", default="", help="Residential proxy for Reddit/Bilibili (http://user:pass@ip:port)") + p_install.add_argument("--safe", action="store_true", + help="Safe mode: skip automatic system changes, show what's needed instead") + p_install.add_argument("--dry-run", action="store_true", + help="Show what would be done without making any changes") # ── configure ── p_conf = sub.add_parser("configure", help="Set a config value or auto-extract from browser") @@ -178,11 +182,21 @@ def _cmd_install(args): from agent_reach.config import Config from agent_reach.doctor import check_all, format_report + safe_mode = args.safe + dry_run = args.dry_run + config = Config() print() print("👁️ Agent Reach Installer") print("=" * 40) + if dry_run: + print("🔍 DRY RUN — showing what would be done (no changes)") + print() + if safe_mode: + print("🛡️ SAFE MODE — skipping automatic system changes") + print() + # Auto-detect environment env = args.env if env == "auto": @@ -195,20 +209,33 @@ def _cmd_install(args): # Apply explicit flags if args.proxy: - config.set("reddit_proxy", args.proxy) - config.set("bilibili_proxy", args.proxy) - print(f"✅ Proxy configured for Reddit + Bilibili") + if dry_run: + print(f"[dry-run] Would configure proxy for Reddit + Bilibili") + else: + config.set("reddit_proxy", args.proxy) + config.set("bilibili_proxy", args.proxy) + print(f"✅ Proxy configured for Reddit + Bilibili") # ── Install system dependencies ── print() - _install_system_deps() + if dry_run: + _install_system_deps_dryrun() + elif safe_mode: + _install_system_deps_safe() + else: + _install_system_deps() # ── mcporter (for Exa search + XiaoHongShu) ── print() - _install_mcporter() + if dry_run: + print("📦 [dry-run] Would install mcporter and configure Exa search") + elif safe_mode: + _install_mcporter_safe() + else: + _install_mcporter() # Auto-import cookies on local computers - if env == "local": + if env == "local" and not safe_mode and not dry_run: print() print("🍪 Trying to import cookies from browser...") try: @@ -230,6 +257,13 @@ def _cmd_install(args): print(" ⬜ No cookies found (normal if you haven't logged into these sites)") except Exception: print(" ⬜ Could not read browser cookies (browser might be open)") + elif env == "local" and safe_mode: + print() + print("🍪 Skipped auto cookie import (safe mode).") + print(" To import manually: agent-reach configure --from-browser chrome") + elif env == "local" and dry_run: + print() + print("🍪 [dry-run] Would try to import cookies from Chrome/Firefox") # Environment-specific advice if env == "server": @@ -240,25 +274,29 @@ def _cmd_install(args): print(" Cheap option: https://www.webshare.io ($1/month)") # Test channels - print() - print("Testing channels...") - results = check_all(config) - ok = sum(1 for r in results.values() if r["status"] == "ok") - total = len(results) - - # Final status - print() - print(format_report(results)) - print() - - # ── Install agent skill ── - _install_skill() - - print(f"✅ Installation complete! {ok}/{total} channels active.") - - if ok < total: + if not dry_run: print() - print("🤖 Some channels are not active yet. If you're an AI Agent, try to fix them yourself — then run `agent-reach doctor` again.") + print("Testing channels...") + results = check_all(config) + ok = sum(1 for r in results.values() if r["status"] == "ok") + total = len(results) + + # Final status + print() + print(format_report(results)) + print() + + # ── Install agent skill ── + _install_skill() + + print(f"✅ Installation complete! {ok}/{total} channels active.") + + if ok < total: + print() + print("🤖 Some channels are not active yet. If you're an AI Agent, try to fix them yourself — then run `agent-reach doctor` again.") + else: + print() + print("🔍 Dry run complete. No changes were made.") def _install_skill(): @@ -403,6 +441,58 @@ def _install_system_deps(): print(" ⬜ instaloader install failed (optional — try: pip install instaloader)") +def _install_system_deps_safe(): + """Safe mode: check what's installed, print instructions for what's missing.""" + import shutil + + print("🔧 Checking system dependencies (safe mode — no auto-install)...") + + deps = [ + ("gh", ["gh"], "GitHub CLI", "https://cli.github.com — or: apt install gh / brew install gh"), + ("node", ["node", "npm"], "Node.js", "https://nodejs.org — or: apt install nodejs npm"), + ("bird", ["bird", "birdx"], "bird CLI (Twitter)", "npm install -g @steipete/bird"), + ("instaloader", ["instaloader"], "instaloader (Instagram)", "pip install instaloader"), + ] + + missing = [] + for name, binaries, label, install_hint in deps: + found = any(shutil.which(b) for b in binaries) + if found: + print(f" ✅ {label} already installed") + else: + print(f" ⬜ {label} not found") + missing.append((label, install_hint)) + + if missing: + print() + print(" To install missing dependencies manually:") + for label, hint in missing: + print(f" {label}: {hint}") + else: + print(" All system dependencies are installed!") + + +def _install_system_deps_dryrun(): + """Dry-run: just show what would be checked/installed.""" + import shutil + + print("🔧 [dry-run] System dependency check:") + + checks = [ + ("gh CLI", ["gh"], "apt install gh / brew install gh"), + ("Node.js", ["node"], "curl NodeSource setup | bash + apt install nodejs"), + ("bird CLI", ["bird", "birdx"], "npm install -g @steipete/bird"), + ("instaloader", ["instaloader"], "pip install instaloader"), + ] + + for label, binaries, method in checks: + found = any(shutil.which(b) for b in binaries) + if found: + print(f" ✅ {label}: already installed, skip") + else: + print(f" 📥 {label}: would install via: {method}") + + def _install_mcporter(): """Install mcporter and configure Exa + XiaoHongShu MCP servers.""" import shutil @@ -474,6 +564,21 @@ def _install_mcporter(): pass +def _install_mcporter_safe(): + """Safe mode: check mcporter status, print instructions.""" + import shutil + + print("📦 Checking mcporter (safe mode)...") + + if shutil.which("mcporter"): + print(" ✅ mcporter already installed") + print(" To configure Exa search: mcporter config add exa https://mcp.exa.ai/mcp") + else: + print(" ⬜ mcporter not installed") + print(" To install: npm install -g mcporter") + print(" Then configure Exa: mcporter config add exa https://mcp.exa.ai/mcp") + + def _detect_environment(): """Auto-detect if running on local computer or server.""" import os diff --git a/agent_reach/config.py b/agent_reach/config.py index 008214e..59b7c74 100644 --- a/agent_reach/config.py +++ b/agent_reach/config.py @@ -51,6 +51,12 @@ class Config: self._ensure_dir() with open(self.config_path, "w") as f: yaml.dump(self.data, f, default_flow_style=False, allow_unicode=True) + # Restrict permissions — config may contain credentials + try: + import stat + self.config_path.chmod(stat.S_IRUSR | stat.S_IWUSR) # 0o600 + except OSError: + pass # Windows or permission edge cases def get(self, key: str, default: Any = None) -> Any: """Get a config value. Also checks environment variables (uppercase).""" diff --git a/docs/README_en.md b/docs/README_en.md index 17eef58..ca2553c 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -224,7 +224,7 @@ This project was entirely vibe-coded 🎸 There might be rough edges here and th ## Credits -[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) +[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) · [instaloader](https://github.com/instaloader/instaloader) · [linkedin-scraper-mcp](https://github.com/stickerdaniel/linkedin-mcp-server) · [mcp-bosszp](https://github.com/mucsbr/mcp-bosszp) ## License diff --git a/docs/install.md b/docs/install.md index 44c2113..352a73a 100644 --- a/docs/install.md +++ b/docs/install.md @@ -8,6 +8,12 @@ Copy this to your AI Agent: 帮我安装 Agent Reach:https://raw.githubusercontent.com/Panniantong/agent-reach/main/docs/install.md ``` +> 🛡️ **Security-conscious?** Use safe mode — it won't auto-install system packages: +> ``` +> 帮我安装 Agent Reach(安全模式):https://raw.githubusercontent.com/Panniantong/agent-reach/main/docs/install.md +> 安装时使用 --safe 参数 +> ``` + --- ## For AI Agents @@ -16,8 +22,20 @@ Copy this to your AI Agent: Install Agent Reach and make sure **all channels are working**. Your user expects you to have full internet access capabilities when this is done — reading tweets, searching Reddit, watching YouTube, browsing GitHub, and more. +### ⚠️ Boundaries + +When fixing issues, stay within these limits: + +- **DO NOT** run commands with `sudo` unless the user explicitly approved +- **DO NOT** modify system files outside `~/.agent-reach/` +- **DO NOT** install packages not listed in this guide +- **DO NOT** disable firewalls, security settings, or system protections +- If something requires elevated permissions, **tell the user** what's needed and let them decide + ### Step 1: Install +**Default (fully automatic):** + ```bash pip install https://github.com/Panniantong/agent-reach/archive/main.zip agent-reach install --env=auto @@ -25,6 +43,25 @@ agent-reach install --env=auto This auto-installs system dependencies (gh CLI, Node.js, mcporter, bird), configures Exa search, detects environment, and tests all channels. +**Safe mode (no automatic system changes):** + +If the user asked for safe mode, or you're on a production machine: + +```bash +pip install https://github.com/Panniantong/agent-reach/archive/main.zip +agent-reach install --env=auto --safe +``` + +Safe mode checks what's installed and tells you what's missing, but doesn't auto-install system packages. You can then install each dependency manually. + +**Dry run (preview only):** + +```bash +agent-reach install --env=auto --dry-run +``` + +Shows what would be done without making any changes. + **Read the output carefully**, then run: ```bash @@ -35,14 +72,16 @@ agent-reach doctor `agent-reach doctor` shows you which channels are active and which aren't. -**Your goal is to get as many channels to ✅ as possible.** If something failed during install or shows as ❌/⚠️ in doctor, fix it yourself. You're a capable AI Agent — diagnose the problem, try alternative approaches, and iterate until it works. +Try to get as many channels to ✅ as possible. If something failed during install or shows as ❌/⚠️ in doctor, diagnose the problem and try to fix it — but stay within the boundaries above. If a fix requires elevated permissions or system changes, ask the user first. -Only ask the user when you genuinely need their input (see below). +Only ask the user when you genuinely need their input (credentials, permissions, etc.). ### Step 3: Configure things that need user input Some channels need credentials only the user can provide. Based on the doctor output, ask for what's missing: +> 🔒 **Security tip:** For platforms that need cookies (Twitter, XiaoHongShu, Instagram), we recommend using a **dedicated/secondary account** rather than your main account. Cookie-based auth grants full account access — using a separate account limits the blast radius if credentials are ever compromised. + **Twitter search & posting (server users):** > "To unlock Twitter search, I need your Twitter cookies. Install the Cookie-Editor Chrome extension, go to x.com/twitter.com, click the extension → Export → Header String, and paste it to me." > Extension: https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm @@ -183,6 +222,8 @@ If the user wants a different agent to handle it, let them choose. | Command | What it does | |---------|-------------| | `agent-reach install --env=auto` | Full auto-setup (installs deps + configures) | +| `agent-reach install --env=auto --safe` | Safe setup (no auto system changes) | +| `agent-reach install --env=auto --dry-run` | Preview what would be done | | `agent-reach doctor` | Show channel status | | `agent-reach watch` | Quick health + update check (for scheduled tasks) | | `agent-reach check-update` | Check for new versions | diff --git a/pyproject.toml b/pyproject.toml index 791da01..054160b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agent-reach" -version = "1.0.0" +version = "1.1.0" description = "Give your AI Agent eyes to see the entire internet. Search + Read 10+ platforms." readme = "README.md" license = {text = "MIT"}