Compare commits
4 Commits
main
...
1ee30ecf28
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ee30ecf28 | |||
| 61d6536754 | |||
| 27b2cd2d02 | |||
| 7eae32d0b7 |
+35
-16
@@ -1143,19 +1143,19 @@ def _parse_twitter_cookie_input(value: str):
|
||||
|
||||
|
||||
def _configure_xhs_cookies(value):
|
||||
"""Import cookies into xiaohongshu-mcp Docker container.
|
||||
"""Import cookies for xhs-cli, with legacy xiaohongshu-mcp support.
|
||||
|
||||
Accepts two formats:
|
||||
1. Cookie-Editor JSON export (array of cookie objects)
|
||||
2. Header String: "name1=value1; name2=value2; ..."
|
||||
|
||||
The xiaohongshu-mcp container stores cookies at $COOKIES_PATH
|
||||
(default: /app/data/cookies.json or cookies.json in workdir).
|
||||
Format: JSON array of {name, value, domain, path, expires, httpOnly, secure, sameSite}.
|
||||
xhs-cli stores cookies as a name/value dict at ~/.xiaohongshu-cli/cookies.json.
|
||||
The legacy Docker MCP stores Cookie-Editor style arrays at $COOKIES_PATH.
|
||||
"""
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
value = value.strip()
|
||||
if not value:
|
||||
@@ -1165,6 +1165,7 @@ def _configure_xhs_cookies(value):
|
||||
|
||||
# Detect format and parse
|
||||
cookies_json = None
|
||||
cookies = []
|
||||
|
||||
# Try JSON format first (Cookie-Editor JSON export)
|
||||
if value.startswith("["):
|
||||
@@ -1174,6 +1175,7 @@ def _configure_xhs_cookies(value):
|
||||
# Validate it looks like cookie objects
|
||||
first = parsed[0]
|
||||
if isinstance(first, dict) and "name" in first and "value" in first:
|
||||
cookies = parsed
|
||||
cookies_json = json.dumps(parsed)
|
||||
print(f" Parsed {len(parsed)} cookies from JSON format")
|
||||
else:
|
||||
@@ -1188,7 +1190,6 @@ def _configure_xhs_cookies(value):
|
||||
|
||||
# Header String format: "key1=val1; key2=val2; ..."
|
||||
if cookies_json is None and "=" in value:
|
||||
cookies = []
|
||||
for part in value.split(";"):
|
||||
part = part.strip()
|
||||
if "=" not in part:
|
||||
@@ -1222,17 +1223,37 @@ def _configure_xhs_cookies(value):
|
||||
print(' 2. Header String: "key1=val1; key2=val2; ..."')
|
||||
return
|
||||
|
||||
# Primary path: configure xhs-cli directly.
|
||||
xhs_cookie_map = {
|
||||
str(c.get("name", "")).strip(): str(c.get("value", ""))
|
||||
for c in cookies
|
||||
if isinstance(c, dict) and str(c.get("name", "")).strip()
|
||||
}
|
||||
if xhs_cookie_map.get("a1"):
|
||||
xhs_config_dir = os.path.expanduser("~/.xiaohongshu-cli")
|
||||
os.makedirs(xhs_config_dir, exist_ok=True)
|
||||
xhs_cookie_path = os.path.join(xhs_config_dir, "cookies.json")
|
||||
with open(xhs_cookie_path, "w", encoding="utf-8") as f:
|
||||
json.dump({**xhs_cookie_map, "saved_at": time.time()}, f, indent=2)
|
||||
os.chmod(xhs_cookie_path, 0o600)
|
||||
print(f"✅ xhs-cli cookies saved to {xhs_cookie_path}")
|
||||
print(" Run `xhs status` or `agent-reach doctor` to verify.")
|
||||
else:
|
||||
print("[!] Cookie input does not include the required `a1` cookie.")
|
||||
print(" xhs-cli will not treat this as a logged-in session. Export all xiaohongshu.com cookies from Cookie-Editor.")
|
||||
|
||||
# Keep a legacy Cookie-Editor array for users still running xiaohongshu-mcp.
|
||||
legacy_cookie_path = os.path.expanduser("~/.agent-reach/xhs-cookies.json")
|
||||
os.makedirs(os.path.dirname(legacy_cookie_path), exist_ok=True)
|
||||
with open(legacy_cookie_path, "w") as f:
|
||||
f.write(cookies_json)
|
||||
os.chmod(legacy_cookie_path, 0o600)
|
||||
print(f" Legacy MCP cookie array saved to {legacy_cookie_path}")
|
||||
|
||||
# Find the container
|
||||
docker = shutil.which("docker")
|
||||
if not docker:
|
||||
# No Docker - write to a local file for manual import
|
||||
cookie_path = os.path.expanduser("~/.agent-reach/xhs-cookies.json")
|
||||
with open(cookie_path, "w") as f:
|
||||
f.write(cookies_json)
|
||||
os.chmod(cookie_path, 0o600)
|
||||
print(f" Cookies saved to {cookie_path}")
|
||||
print(" Docker not found. Copy manually:")
|
||||
print(f" docker cp {cookie_path} xiaohongshu-mcp:/app/data/cookies.json")
|
||||
print(" Docker not found; skipping xiaohongshu-mcp import.")
|
||||
return
|
||||
|
||||
# Check if xiaohongshu-mcp container is running
|
||||
@@ -1243,9 +1264,7 @@ def _configure_xhs_cookies(value):
|
||||
)
|
||||
container_name = result.stdout.strip()
|
||||
if not container_name:
|
||||
print("[X] xiaohongshu-mcp container is not running.")
|
||||
print(" Start it first:")
|
||||
print(" docker run -d --name xiaohongshu-mcp -p 18060:18060 xpzouying/xiaohongshu-mcp")
|
||||
print(" xiaohongshu-mcp container is not running; skipping legacy Docker import.")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"[X] Could not check Docker: {e}")
|
||||
|
||||
@@ -39,7 +39,9 @@ agent-reach doctor
|
||||
> 3. 点击 Cookie-Editor 图标 → Export → Header String
|
||||
> 4. 把导出的字符串发给 Agent,运行:`agent-reach configure xhs-cookies "导出的cookie字符串"`
|
||||
>
|
||||
> **注意**:不要依赖 QR 扫码登录,Cookie-Editor 导出方式最简单可靠。
|
||||
> **注意**:不要依赖 QR 扫码登录,Cookie-Editor 导出方式最简单可靠。这个方式也适合 WSL、SSH、容器等无法直接读取桌面浏览器 Cookie 的环境。
|
||||
|
||||
`agent-reach configure xhs-cookies` 会把 Cookie 同步到 `~/.xiaohongshu-cli/cookies.json`,供 `xhs status/search/read` 直接使用;如果你还在用旧的 `xiaohongshu-mcp` Docker 方案,也会保留兼容导入文件。
|
||||
|
||||
## 使用示例
|
||||
|
||||
@@ -69,6 +71,9 @@ A: 推荐使用住宅代理:`export HTTP_PROXY="http://user:pass@ip:port"`。
|
||||
**Q: xhs-cli 不支持我的系统?**
|
||||
A: 确保 Python 3.10+ 和 pipx 已安装。运行 `pipx install xiaohongshu-cli` 即可。
|
||||
|
||||
**Q: WSL 里 `xhs login` 读不到 Windows 浏览器怎么办?**
|
||||
A: 这是常见限制。WSL 里没有直接可读的 Linux 浏览器 Cookie 时,`xhs login` 自动提取会失败。推荐在 Windows 浏览器用 Cookie-Editor 导出 `xiaohongshu.com` 的 Header String,然后在 WSL 里运行 `agent-reach configure xhs-cookies "..."`,再用 `xhs status` 验证。
|
||||
|
||||
## 备选方案:Docker MCP
|
||||
|
||||
如果你已经在使用 [xiaohongshu-mcp](https://github.com/xpzouying/xiaohongshu-mcp) Docker 方案,它也能正常工作:
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for Agent Reach CLI."""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from unittest.mock import patch
|
||||
@@ -42,6 +45,24 @@ class TestCLI:
|
||||
assert auth_token == "token123"
|
||||
assert ct0 == "ct0abc"
|
||||
|
||||
def test_configure_xhs_cookies_writes_xhs_cli_cookie_file(self, tmp_path, monkeypatch, capsys):
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setattr("shutil.which", lambda _name: None)
|
||||
|
||||
cli._configure_xhs_cookies("a1=token123; web_session=session456; other=value")
|
||||
|
||||
cookie_path = tmp_path / ".xiaohongshu-cli" / "cookies.json"
|
||||
data = json.loads(cookie_path.read_text())
|
||||
assert data["a1"] == "token123"
|
||||
assert data["web_session"] == "session456"
|
||||
assert "saved_at" in data
|
||||
assert oct(os.stat(cookie_path).st_mode & 0o777) == "0o600"
|
||||
|
||||
legacy_path = tmp_path / ".agent-reach" / "xhs-cookies.json"
|
||||
assert legacy_path.exists()
|
||||
captured = capsys.readouterr()
|
||||
assert "xhs-cli cookies saved" in captured.out
|
||||
|
||||
|
||||
class TestCheckUpdateRetry:
|
||||
def test_retry_timeout_classification(self):
|
||||
|
||||
Reference in New Issue
Block a user