diff --git a/agent_eyes/cli.py b/agent_eyes/cli.py index fe24ccd..7354b99 100644 --- a/agent_eyes/cli.py +++ b/agent_eyes/cli.py @@ -80,11 +80,15 @@ def main(): help="Exa API key (get free at https://exa.ai)") # ── configure ── - p_conf = sub.add_parser("configure", help="Set a config value") - p_conf.add_argument("key", choices=["exa-key", "proxy", "github-token", "groq-key", - "twitter-cookies", "xhs-cookie", "youtube-cookies"], - help="What to configure") - p_conf.add_argument("value", nargs="+", help="The value(s) to set") + p_conf = sub.add_parser("configure", help="Set a config value or auto-extract from browser") + p_conf.add_argument("key", nargs="?", default=None, + choices=["exa-key", "proxy", "github-token", "groq-key", + "twitter-cookies", "xhs-cookie", "youtube-cookies"], + help="What to configure (omit if using --from-browser)") + p_conf.add_argument("value", nargs="*", help="The value(s) to set") + p_conf.add_argument("--from-browser", metavar="BROWSER", + choices=["chrome", "firefox", "edge", "brave", "opera"], + help="Auto-extract ALL platform cookies from browser (chrome/firefox/edge/brave/opera)") # ── doctor ── sub.add_parser("doctor", help="Check platform availability") @@ -224,11 +228,46 @@ def _detect_environment(): def _cmd_configure(args): - """Set a config value and test it.""" + """Set a config value and test it, or auto-extract from browser.""" from agent_eyes.config import Config config = Config() - value = " ".join(args.value) if isinstance(args.value, list) else args.value + + # ── Auto-extract from browser ── + if args.from_browser: + from agent_eyes.cookie_extract import configure_from_browser + + browser = args.from_browser + print(f"🔍 Extracting cookies from {browser}...") + print() + + results = configure_from_browser(browser, config) + + found_any = False + for platform, success, message in results: + if success: + print(f" ✅ {platform}: {message}") + found_any = True + else: + print(f" ⬜ {platform}: {message}") + + print() + if found_any: + print("✅ Cookies configured! Run `agent-eyes doctor` to see updated status.") + else: + print(f"No cookies found. Make sure you're logged into the platforms in {browser}.") + return + + # ── Manual configure ── + if not args.key: + print("Usage: agent-eyes configure ") + print(" or: agent-eyes configure --from-browser chrome") + return + + value = " ".join(args.value) if args.value else "" + if not value: + print(f"Missing value for {args.key}") + return if args.key == "proxy": config.set("reddit_proxy", value) diff --git a/agent_eyes/cookie_extract.py b/agent_eyes/cookie_extract.py new file mode 100644 index 0000000..1155d95 --- /dev/null +++ b/agent_eyes/cookie_extract.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +"""Auto-extract cookies from local browsers for all supported platforms. + +Supports: Chrome, Firefox, Edge, Brave, Opera +Extracts: Twitter, XiaoHongShu, Bilibili cookies in one shot. + +Usage: + agent-eyes configure --from-browser chrome +""" + +import sys +from typing import Dict, List, Optional, Tuple + + +# Platform cookie specs: (platform_name, domain_pattern, needed_cookies) +PLATFORM_SPECS = [ + { + "name": "Twitter/X", + "domains": [".x.com", ".twitter.com"], + "cookies": ["auth_token", "ct0"], + "config_key": "twitter", + }, + { + "name": "XiaoHongShu", + "domains": [".xiaohongshu.com"], + "cookies": None, # None = grab all cookies as header string + "config_key": "xhs", + }, + { + "name": "Bilibili", + "domains": [".bilibili.com"], + "cookies": ["SESSDATA", "bili_jct"], + "config_key": "bilibili", + }, +] + + +def extract_all(browser: str = "chrome") -> Dict[str, dict]: + """ + Extract cookies for all supported platforms from the specified browser. + + Returns: + { + "twitter": {"auth_token": "xxx", "ct0": "yyy"}, + "xhs": {"cookie_string": "a=1; b=2; ..."}, + "bilibili": {"SESSDATA": "xxx", "bili_jct": "yyy"}, + } + """ + try: + import browser_cookie3 + except ImportError: + raise RuntimeError( + "browser_cookie3 not installed. Run: pip install browser-cookie3" + ) + + # Get browser cookie jar + browser_funcs = { + "chrome": browser_cookie3.chrome, + "firefox": browser_cookie3.firefox, + "edge": browser_cookie3.edge, + "brave": browser_cookie3.brave, + "opera": browser_cookie3.opera, + } + + browser = browser.lower() + if browser not in browser_funcs: + raise ValueError( + f"Unsupported browser: {browser}. " + f"Supported: {', '.join(browser_funcs.keys())}" + ) + + try: + cookie_jar = browser_funcs[browser]() + except Exception as e: + raise RuntimeError( + f"Could not read {browser} cookies: {e}\n" + f"Make sure {browser} is closed and you have permission to read its data." + ) + + results = {} + + for spec in PLATFORM_SPECS: + platform_cookies = {} + all_cookies_for_domain = [] + + for cookie in cookie_jar: + # Check if cookie belongs to this platform + domain_match = any( + cookie.domain.endswith(d) or cookie.domain == d.lstrip(".") + for d in spec["domains"] + ) + if not domain_match: + continue + + all_cookies_for_domain.append(cookie) + + if spec["cookies"] is not None: + if cookie.name in spec["cookies"]: + platform_cookies[cookie.name] = cookie.value + + if spec["cookies"] is None: + # Grab all as header string + if all_cookies_for_domain: + cookie_str = "; ".join( + f"{c.name}={c.value}" for c in all_cookies_for_domain + ) + results[spec["config_key"]] = {"cookie_string": cookie_str} + else: + if platform_cookies: + results[spec["config_key"]] = platform_cookies + + return results + + +def configure_from_browser(browser: str, config) -> List[Tuple[str, bool, str]]: + """ + Extract cookies and configure all found platforms. + + Returns list of (platform_name, success, message) tuples. + """ + results_list = [] + + try: + extracted = extract_all(browser) + except Exception as e: + return [("Browser", False, str(e))] + + if not extracted: + return [("All platforms", False, + f"No platform cookies found in {browser}. " + f"Make sure you're logged into Twitter, XiaoHongShu, etc. in {browser}.")] + + # Configure each found platform + if "twitter" in extracted: + tc = extracted["twitter"] + if "auth_token" in tc and "ct0" in tc: + config.set("twitter_auth_token", tc["auth_token"]) + config.set("twitter_ct0", tc["ct0"]) + results_list.append(("Twitter/X", True, "auth_token + ct0")) + else: + found = ", ".join(tc.keys()) + missing = [k for k in ["auth_token", "ct0"] if k not in tc] + results_list.append(("Twitter/X", False, + f"Found {found}, but missing: {', '.join(missing)}. " + f"Make sure you're logged into x.com in {browser}.")) + + if "xhs" in extracted: + cookie_str = extracted["xhs"].get("cookie_string", "") + if cookie_str: + config.set("xhs_cookie", cookie_str) + n_cookies = len(cookie_str.split(";")) + results_list.append(("XiaoHongShu", True, f"{n_cookies} cookies")) + + if "bilibili" in extracted: + bc = extracted["bilibili"] + if "SESSDATA" in bc: + config.set("bilibili_sessdata", bc["SESSDATA"]) + if "bili_jct" in bc: + config.set("bilibili_csrf", bc["bili_jct"]) + results_list.append(("Bilibili", True, "SESSDATA" + + (" + bili_jct" if "bili_jct" in bc else ""))) + else: + results_list.append(("Bilibili", False, + f"No SESSDATA found. Make sure you're logged into bilibili.com in {browser}.")) + + return results_list diff --git a/docs/install.md b/docs/install.md index 7b976bb..4698d34 100644 --- a/docs/install.md +++ b/docs/install.md @@ -12,7 +12,7 @@ Install Agent Eyes: https://raw.githubusercontent.com/Panniantong/agent-eyes/mai ## For AI Agents -Install Agent Eyes and set it up for the user. Minimize friction — ask as few questions as possible. +Install Agent Eyes and set it up with minimal user effort. ### Step 1: Install and check @@ -21,103 +21,75 @@ pip install git+https://github.com/Panniantong/agent-eyes.git || pip3 install gi agent-eyes install --env=auto ``` -That's it. This installs the package, auto-detects the environment, and shows what's working. +### Step 2: One-click cookie import (LOCAL COMPUTERS ONLY) -### Step 2: Tell user what's ready + offer ONE upgrade +If the environment was detected as **local**, offer to grab all cookies at once: -Tell the user what already works, then offer the single highest-value upgrade: +> "I can automatically import your cookies from Chrome to unlock Twitter, XiaoHongShu, and Bilibili — all at once, one command. Want me to do that? +> (Make sure Chrome is closed first)" -> "✅ Agent Eyes installed! Here's what I can already do: -> - Read web pages, GitHub repos, RSS feeds, YouTube transcripts -> - Read individual tweets -> -> **Want me to unlock search too?** One free API key lets me search the entire web, Reddit, and Twitter for you. -> Just go to https://exa.ai → sign up (free, 1000 searches/month) → send me the API key. -> -> Or say 'skip' and I'm ready to use as-is." +If yes: +```bash +pip install browser-cookie3 +agent-eyes configure --from-browser chrome +``` -If the user provides a key: +This extracts cookies for ALL supported platforms in one shot. No F12, no manual copying. + +If user uses a different browser: +```bash +agent-eyes configure --from-browser firefox # or edge, brave, opera +``` + +If the environment is **server** or the user declines, skip to Step 3. + +### Step 3: Offer search upgrade + +> "Want to unlock web search? One free API key lets me search the entire web, Reddit, and Twitter. +> → Go to https://exa.ai → sign up (free) → send me the API key. +> Or say 'skip'." + +If user provides key: ```bash agent-eyes configure exa-key THE_KEY ``` -If the user says skip, move to Step 4. +### Step 4: Server-only — offer proxy -### Step 3: Offer additional channels +Only if auto-detected as server: -After Exa is configured (or skipped), briefly mention what else is available: - -> "I can also unlock these with cookies from your browser (2 min each): -> 🐦 **Twitter** deep search — timelines, threads, advanced search -> 📕 **XiaoHongShu** — read notes and comments -> -> Want any of these? Or say 'done' and we're good to go." - -Only proceed with channels the user explicitly asks for. - -#### How to get cookies (for any platform) - -When the user wants to set up a cookie-based channel, give them the SIMPLEST method: - -**Method 1 — Browser extension (easiest):** -> "Install the 'Cookie-Editor' extension for Chrome/Firefox (free). Open [the site], click the extension icon, click 'Export' → 'Header String'. Paste it to me." - -**Method 2 — Manual (if they don't want to install an extension):** -> "Open [the site] in Chrome. Press F12. Click 'Application' tab at the top. Click 'Cookies' on the left. Find and copy the values I need." - -#### Twitter setup -Cookie-Editor export, or manually copy **auth_token** and **ct0** from x.com cookies. -```bash -agent-eyes configure twitter-cookies AUTH_TOKEN CT0 -``` - -#### XiaoHongShu setup -Cookie-Editor export the full cookie string from xiaohongshu.com. -```bash -agent-eyes configure xhs-cookie COOKIE_STRING -``` - -#### YouTube (only mention if user has age-restricted video needs) -No cookies needed for public videos. For age-restricted/member content: -```bash -agent-eyes configure youtube-cookies chrome -``` -(yt-dlp reads cookies directly from the browser — just tell it which browser) - -#### Bilibili -- Local computer: already works -- Server: needs proxy (see below) - -#### Reddit full reader + Bilibili on server -Only offer this if `agent-eyes install` detected server environment: -> "Reddit and Bilibili block server IPs. Reddit search already works (via Exa), but for reading full posts you'd need a $1/month proxy from https://webshare.io. Want to set that up?" +> "Reddit and Bilibili block server IPs. Reddit search still works free via Exa, but for full access you'd need a cheap proxy ($1/month). +> → Get one at https://webshare.io if interested. Or skip — search still works!" +If user provides proxy: ```bash agent-eyes configure proxy http://user:pass@ip:port ``` -### Step 4: Done +### Step 5: Done ```bash agent-eyes doctor ``` -Summarize naturally: -> "✅ Ready! Here's everything I can do: [list channels]. Try asking me to search or read something!" +> "✅ All set! [list what's active]. Try asking me to search or read something!" -**Remember**: the user can always come back and say "set up Twitter" or "add a proxy" later. You just run the corresponding `agent-eyes configure` command. +**Users can always configure more later** by saying things like "set up Twitter for Agent Eyes" or "add my XiaoHongShu cookies". --- -## Quick Reference +## Manual configure (one channel at a time) + +These commands are for manual setup or adding individual channels later: | Command | What it does | |---------|-------------| -| `agent-eyes doctor` | Show all channels and status | +| `agent-eyes configure --from-browser chrome` | Import ALL cookies at once | | `agent-eyes configure exa-key KEY` | Unlock search (web + Reddit + Twitter) | -| `agent-eyes configure twitter-cookies TOKEN CT0` | Unlock Twitter deep search | -| `agent-eyes configure xhs-cookie COOKIE` | Unlock XiaoHongShu | -| `agent-eyes configure youtube-cookies BROWSER` | Unlock age-restricted YouTube | -| `agent-eyes configure proxy URL` | Unlock Reddit reader + Bilibili (server) | +| `agent-eyes configure twitter-cookies TOKEN CT0` | Twitter deep search (manual) | +| `agent-eyes configure xhs-cookie COOKIE` | XiaoHongShu (manual) | +| `agent-eyes configure youtube-cookies chrome` | Age-restricted YouTube | +| `agent-eyes configure proxy URL` | Reddit + Bilibili on servers | +| `agent-eyes doctor` | Show all channels and status | | `agent-eyes read URL` | Read any URL | | `agent-eyes search "query"` | Search the web | diff --git a/pyproject.toml b/pyproject.toml index 20e3cc9..350eb80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,9 @@ dependencies = [ [project.optional-dependencies] browser = ["playwright>=1.40"] +cookies = ["browser-cookie3>=0.19"] mcp = ["mcp[cli]>=1.0"] -all = ["playwright>=1.40", "mcp[cli]>=1.0"] +all = ["playwright>=1.40", "mcp[cli]>=1.0", "browser-cookie3>=0.19"] [project.scripts] agent-eyes = "agent_eyes.cli:main"