diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index b5c4188..2ccbf75 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -90,12 +90,12 @@ Plans:
2. 扫码失败时用户可以手动设置 Cookie 进行认证
3. Cookie 自动保存到本地文件,下次运行自动复用
4. Cookie 过期时系统自动检测并提示用户重新登录
-**Plans**: TBD
+**Plans**: 3 plans
Plans:
-- [ ] 07-01: 二维码扫码登录实现 (AUTH-01)
-- [ ] 07-02: 手动 Cookie 设置和持久化 (AUTH-02, AUTH-03)
-- [ ] 07-03: Cookie 过期检测机制 (AUTH-04)
+- [ ] 07-01-PLAN.md — 二维码扫码登录核心逻辑 (AUTH-01) + Wave 0 测试脚手架
+- [ ] 07-02-PLAN.md — Cookie 导入和持久化管理 (AUTH-02, AUTH-03)
+- [ ] 07-03-PLAN.md — Cookie 验证 + CLI login 子命令 (AUTH-04)
#### Phase 8: 内容爬取实现
**Goal**: 实现知乎问答、专栏/文章、评论的完整爬取功能,包含网络反爬措施
@@ -140,6 +140,6 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
| 4. Pipeline | v1.0 | 2/2 | Complete | 2026-03-15 |
| 5. 归档与目录优化 | v1.1 | 3/3 | Complete | 2026-03-16 |
| 6. 代码整合 | v1.1 | 3/3 | Complete | 2026-03-16 |
-| 7. 登录认证体系 | v1.2 | 0/3 | Not started | - |
+| 7. 登录认证体系 | v1.2 | 0/3 | Ready to execute | - |
| 8. 内容爬取实现 | v1.2 | 0/3 | Not started | - |
| 9. Pipeline 整合 | v1.2 | 0/1 | Not started | - |
diff --git a/.planning/phases/07-login-auth/07-01-PLAN.md b/.planning/phases/07-login-auth/07-01-PLAN.md
new file mode 100644
index 0000000..948c93a
--- /dev/null
+++ b/.planning/phases/07-login-auth/07-01-PLAN.md
@@ -0,0 +1,203 @@
+---
+phase: 07-login-auth
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - kb_workflow/auth/__init__.py
+ - kb_workflow/auth/zhihu_login.py
+ - tests/test_auth/__init__.py
+ - tests/test_auth/conftest.py
+ - tests/test_auth/test_qrcode_login.py
+autonomous: true
+requirements:
+ - AUTH-01
+user_setup: []
+
+must_haves:
+ truths:
+ - "用户可以访问知乎登录页面"
+ - "系统可以从 Canvas 元素截取二维码图片"
+ - "二维码图片保存到中间目录"
+ - "系统可以轮询检测 z_c0 Cookie 判断登录状态"
+ artifacts:
+ - path: "kb_workflow/auth/zhihu_login.py"
+ provides: "二维码登录核心逻辑"
+ min_lines: 100
+ exports: ["ZhihuLogin", "ZhihuLoginResult"]
+ - path: "tests/test_auth/test_qrcode_login.py"
+ provides: "登录逻辑单元测试"
+ exports: ["test_login_page_access", "test_qrcode_screenshot", "test_cookie_detection"]
+ key_links:
+ - from: "kb_workflow/auth/zhihu_login.py"
+ to: "PlaywrightBrowser"
+ via: "import from kb_workflow.metadata.zhihu_extractor"
+ pattern: "from kb_workflow.metadata.zhihu_extractor import PlaywrightBrowser"
+ - from: "ZhihuLogin.login_by_qrcode()"
+ to: "canvas.screenshot()"
+ via: "Playwright element screenshot API"
+ pattern: "canvas.*screenshot"
+---
+
+
+实现知乎二维码扫码登录核心逻辑。
+
+Purpose: 提供二维码登录能力,访问登录页 → Canvas 截图 → 轮询检测 Cookie
+Output: kb_workflow/auth/zhihu_login.py 模块 + Wave 0 测试脚手架
+
+
+
+@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
+@/home/starmen1265/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/07-login-auth/07-CONTEXT.md
+@.planning/phases/07-login-auth/07-RESEARCH.md
+
+
+
+
+From kb_workflow/metadata/zhihu_extractor.py:
+```python
+class PlaywrightBrowser:
+ def __init__(self, headless: bool = True, timeout: int = 30)
+ def start(self, cookie: str = None)
+ def close(self)
+ def goto(self, url: str, wait_until: str = "networkidle")
+ def wait_for_selector(self, selector: str, timeout: int = None)
+ def query_selector(self, selector: str)
+ def evaluate(self, script: str)
+ # context 属性可用于获取 cookies
+ self.context # BrowserContext
+ self.page # Page
+```
+
+From kb_workflow/pipeline/config.py:
+```python
+class KBSettings(BaseSettings):
+ intermediate_path: str # 中间目录路径
+ zhihu_cookie: SecretStr # 知乎 Cookie
+```
+
+From kb_workflow/pipeline/schemas.py:
+```python
+class StepOutput(BaseModel):
+ success: bool
+ error_message: Optional[str]
+ completed_at: datetime
+```
+
+
+
+
+
+
+ Task 1: 创建测试模块和 fixtures
+ tests/test_auth/__init__.py, tests/test_auth/conftest.py
+
+创建 Wave 0 测试脚手架:
+
+1. 创建 `tests/test_auth/__init__.py` 空文件
+
+2. 创建 `tests/test_auth/conftest.py` 包含:
+ - `mock_browser` fixture: 使用 unittest.mock 模拟 PlaywrightBrowser
+ - `mock_env_file` fixture: 使用 tmp_path 创建临时 ~/.kb_env
+ - `sample_cookies` fixture: 返回测试用 Cookie 数据
+
+参考 pytest fixture 模式,确保 mock_browser 可以:
+- 模拟 page.wait_for_selector() 返回 mock canvas
+- 模拟 canvas.screenshot() 返回 bytes
+- 模拟 context.cookies() 返回 Cookie 列表
+
+
+ python -c "from tests.test_auth.conftest import *; print('fixtures loaded')"
+
+ 测试模块和 fixtures 可被 pytest 加载
+
+
+
+ Task 2: 实现 ZhihuLogin 核心类
+ kb_workflow/auth/__init__.py, kb_workflow/auth/zhihu_login.py, tests/test_auth/test_qrcode_login.py
+
+ - Test 1: login_page_access - 访问登录页成功,URL 正确
+ - Test 2: qrcode_screenshot - Canvas 截图返回 bytes,保存到正确路径
+ - Test 3: cookie_detection - 检测 z_c0 Cookie 存在返回 True,不存在返回 False
+ - Test 4: login_timeout - 60秒超时后抛出 TimeoutError
+ - Test 5: cookie_extraction - 登录成功后提取 Cookie 字符串
+
+
+创建 `kb_workflow/auth/` 模块并实现 ZhihuLogin 类:
+
+1. 创建 `kb_workflow/auth/__init__.py`:
+ ```python
+ from .zhihu_login import ZhihuLogin, ZhihuLoginResult
+ __all__ = ["ZhihuLogin", "ZhihuLoginResult"]
+ ```
+
+2. 创建 `kb_workflow/auth/zhihu_login.py`:
+ - `ZhihuLoginResult` Pydantic model: success, cookie_str, qrcode_path, error_message
+ - `ZhihuLogin` 类:
+ - `LOGIN_URL = "https://www.zhihu.com/signin"`
+ - `CANVAS_SELECTORS = ["canvas.SignIn-qrcode", "canvas[class*='qrcode']", "canvas"]`
+ - `__init__(self, intermediate_path: str, headless: bool = True, timeout: int = 60)`
+ - `async def _find_qrcode_canvas(self) -> ElementHandle` - 尝试多个选择器
+ - `async def _save_qrcode_image(self, screenshot: bytes) -> Path` - 保存到中间目录
+ - `async def check_login_cookie(self) -> bool` - 检测 z_c0 Cookie
+ - `async def get_cookies_str(self) -> str` - 提取 Cookie 字符串
+ - `async def login_by_qrcode(self) -> ZhihuLoginResult` - 主流程
+
+使用 tenacity 的 @retry 装饰器实现 60 秒轮询:
+```python
+from tenacity import retry, stop_after_delay, wait_fixed, RetryError
+
+@retry(stop=stop_after_delay(60), wait=wait_fixed(1), reraise=True)
+async def _wait_for_login(self):
+ if not await self.check_login_cookie():
+ raise Exception("Not logged in yet")
+```
+
+二维码图片路径格式: `~/work/intermediate/zhihu_qrcode_{timestamp}.png`
+
+3. 创建 `tests/test_auth/test_qrcode_login.py`:
+ - test_login_page_access: mock browser.goto 验证 URL
+ - test_qrcode_screenshot: mock canvas.screenshot 验证保存
+ - test_cookie_detection: mock context.cookies 验证检测逻辑
+ - test_login_timeout: mock check_login_cookie 始终返回 False
+ - test_cookie_extraction: 验证 Cookie 字符串格式
+
+安装依赖: pip install tenacity
+
+
+ pytest tests/test_auth/test_qrcode_login.py -x -v
+
+
+ - ZhihuLogin 类可实例化
+ - 所有单元测试通过
+ - 二维码截图功能可用
+ - Cookie 检测功能可用
+
+
+
+
+
+
+1. 单元测试全部通过: `pytest tests/test_auth/test_qrcode_login.py -v`
+2. ZhihuLogin 类可导入: `python -c "from kb_workflow.auth import ZhihuLogin; print(ZhihuLogin)"`
+3. tenacity 依赖已安装: `pip show tenacity`
+
+
+
+- Wave 0 测试脚手架就绪 (tests/test_auth/)
+- ZhihuLogin 类实现二维码截图和 Cookie 检测
+- 5 个单元测试全部通过
+- 代码符合现有项目模式 (pydantic, Playwright)
+
+
+
diff --git a/.planning/phases/07-login-auth/07-02-PLAN.md b/.planning/phases/07-login-auth/07-02-PLAN.md
new file mode 100644
index 0000000..9b04a64
--- /dev/null
+++ b/.planning/phases/07-login-auth/07-02-PLAN.md
@@ -0,0 +1,299 @@
+---
+phase: 07-login-auth
+plan: 02
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - kb_workflow/auth/cookie_manager.py
+ - tests/test_auth/test_cookie_import.py
+ - tests/test_auth/test_cookie_persistence.py
+autonomous: true
+requirements:
+ - AUTH-02
+ - AUTH-03
+user_setup: []
+
+must_haves:
+ truths:
+ - "用户可以从 JSON 文件导入 Cookie"
+ - "Cookie 可以保存到 ~/.kb_env 文件"
+ - "保存的 Cookie 下次运行时可以自动加载"
+ - "JSON Cookie 格式被正确解析为字符串格式"
+ artifacts:
+ - path: "kb_workflow/auth/cookie_manager.py"
+ provides: "Cookie 导入和持久化"
+ min_lines: 80
+ exports: ["CookieManager", "parse_json_cookies"]
+ - path: "tests/test_auth/test_cookie_import.py"
+ provides: "Cookie 导入测试"
+ - path: "tests/test_auth/test_cookie_persistence.py"
+ provides: "Cookie 持久化测试"
+ key_links:
+ - from: "CookieManager.save_cookie()"
+ to: "~/.kb_env"
+ via: "文件写入"
+ pattern: "Path\\.home\\(\\).*\\.kb_env"
+ - from: "parse_json_cookies()"
+ to: "Chrome Cookie Editor JSON"
+ via: "json.load + 过滤 zhihu.com"
+ pattern: "zhihu\\.com.*domain"
+---
+
+
+实现 Cookie 导入和持久化管理。
+
+Purpose: 支持 JSON Cookie 导入和 ~/.kb_env 文件持久化
+Output: kb_workflow/auth/cookie_manager.py 模块 + 单元测试
+
+
+
+@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
+@/home/starmen1265/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/07-login-auth/07-CONTEXT.md
+@.planning/phases/07-login-auth/07-RESEARCH.md
+
+
+
+
+From kb_workflow/pipeline/config.py:
+```python
+class KBSettings(BaseSettings):
+ zhihu_cookie: SecretStr = Field(default=SecretStr(""), description="知乎 Cookie")
+
+ model_config = SettingsConfigDict(
+ env_file=str(Path.home() / ".kb_env"),
+ env_file_encoding="utf-8",
+ env_prefix="KB_",
+ extra="ignore",
+ )
+
+@lru_cache
+def get_settings() -> KBSettings:
+ return KBSettings()
+```
+
+Chrome Cookie Editor JSON 格式示例:
+```json
+[
+ {"name": "z_c0", "value": "xxx", "domain": ".zhihu.com"},
+ {"name": "_zap", "value": "yyy", "domain": ".zhihu.com"}
+]
+```
+
+目标存储格式 (~/.kb_env):
+```
+ZHIHU_COOKIE="z_c0=xxx; _zap=yyy"
+```
+
+
+
+
+
+
+ Task 1: 实现 Cookie JSON 解析功能
+ kb_workflow/auth/cookie_manager.py, tests/test_auth/test_cookie_import.py
+
+ - Test 1: parse_json_cookies - 解析 Chrome Cookie Editor JSON 格式
+ - Test 2: parse_json_cookies_filters_domain - 只保留 zhihu.com 域名
+ - Test 3: parse_json_cookies_empty - 空数组返回空字符串
+ - Test 4: parse_json_cookies_malformed - 格式错误抛出 ValueError
+
+
+在 `kb_workflow/auth/cookie_manager.py` 中实现 Cookie 解析功能:
+
+1. 创建 `kb_workflow/auth/cookie_manager.py`:
+
+```python
+"""Cookie 管理模块
+
+处理 Cookie 的导入、转换和持久化。
+"""
+import json
+from pathlib import Path
+from typing import List, Dict, Any
+
+def parse_json_cookies(json_path: str) -> str:
+ """从 JSON 文件解析 Cookie 为字符串格式
+
+ 支持 Chrome Cookie Editor 导出的 JSON 格式:
+ [{"name": "z_c0", "value": "xxx", "domain": ".zhihu.com"}, ...]
+
+ Args:
+ json_path: JSON 文件路径
+
+ Returns:
+ Cookie 字符串,格式: "name1=value1; name2=value2"
+
+ Raises:
+ ValueError: JSON 格式无效或文件不存在
+ """
+ path = Path(json_path)
+ if not path.exists():
+ raise ValueError(f"Cookie file not found: {json_path}")
+
+ try:
+ with open(path, 'r', encoding='utf-8') as f:
+ cookies = json.load(f)
+ except json.JSONDecodeError as e:
+ raise ValueError(f"Invalid JSON format: {e}")
+
+ if not isinstance(cookies, list):
+ raise ValueError("Expected JSON array")
+
+ # 过滤知乎域名,转换为 name=value 格式
+ zhihu_cookies = []
+ for c in cookies:
+ if not isinstance(c, dict):
+ continue
+ domain = c.get('domain', '')
+ if 'zhihu.com' in domain:
+ name = c.get('name', '')
+ value = c.get('value', '')
+ if name and value:
+ zhihu_cookies.append(f"{name}={value}")
+
+ return '; '.join(zhihu_cookies)
+```
+
+2. 创建 `tests/test_auth/test_cookie_import.py`:
+ - 使用 tmp_path fixture 创建临时 JSON 文件
+ - 测试正常解析、域名过滤、空数组、格式错误等情况
+
+
+ pytest tests/test_auth/test_cookie_import.py -x -v
+
+
+ - parse_json_cookies 函数可用
+ - 4 个单元测试全部通过
+ - 正确过滤 zhihu.com 域名
+
+
+
+
+ Task 2: 实现 Cookie 持久化管理器
+ kb_workflow/auth/cookie_manager.py, tests/test_auth/test_cookie_persistence.py
+
+ - Test 1: save_cookie_creates_file - 保存 Cookie 创建 ~/.kb_env 文件
+ - Test 2: save_cookie_updates_existing - 更新现有 ZHIHU_COOKIE 行
+ - Test 3: save_cookie_appends_new - 不存在时追加新行
+ - Test 4: save_cookie_preserves_other_lines - 保留其他配置行
+ - Test 5: load_cookie_returns_saved - 加载已保存的 Cookie
+ - Test 6: load_cookie_empty_if_not_exists - 文件不存在返回空字符串
+
+
+扩展 `kb_workflow/auth/cookie_manager.py` 添加 CookieManager 类:
+
+```python
+from functools import lru_cache
+
+class CookieManager:
+ """Cookie 持久化管理器
+
+ 管理 Cookie 在 ~/.kb_env 文件中的存储和读取。
+ """
+
+ ENV_FILE_NAME = ".kb_env"
+ COOKIE_KEY = "ZHIHU_COOKIE"
+
+ def __init__(self, env_path: Path = None):
+ """初始化管理器
+
+ Args:
+ env_path: 环境文件路径,默认 ~/.kb_env
+ """
+ self.env_path = env_path or (Path.home() / self.ENV_FILE_NAME)
+
+ def save_cookie(self, cookie_str: str) -> None:
+ """保存 Cookie 到环境文件
+
+ 格式: ZHIHU_COOKIE="name1=value1; name2=value2"
+
+ Args:
+ cookie_str: Cookie 字符串
+ """
+ # 读取现有内容
+ lines = []
+ if self.env_path.exists():
+ lines = self.env_path.read_text(encoding='utf-8').splitlines()
+
+ # 更新或添加 ZHIHU_COOKIE 行
+ cookie_line = f'{self.COOKIE_KEY}="{cookie_str}"'
+ found = False
+ for i, line in enumerate(lines):
+ if line.startswith(f"{self.COOKIE_KEY}="):
+ lines[i] = cookie_line
+ found = True
+ break
+ if not found:
+ lines.append(cookie_line)
+
+ # 写回文件,设置权限 600
+ self.env_path.write_text('\n'.join(lines) + '\n', encoding='utf-8')
+ self.env_path.chmod(0o600)
+
+ # 清除配置缓存
+ from kb_workflow.pipeline.config import get_settings
+ get_settings.cache_clear()
+
+ def load_cookie(self) -> str:
+ """从环境文件加载 Cookie
+
+ Returns:
+ Cookie 字符串,不存在时返回空字符串
+ """
+ if not self.env_path.exists():
+ return ""
+
+ content = self.env_path.read_text(encoding='utf-8')
+ for line in content.splitlines():
+ if line.startswith(f"{self.COOKIE_KEY}="):
+ # 提取引号内的值
+ value = line[len(f"{self.COOKIE_KEY}="):]
+ if value.startswith('"') and value.endswith('"'):
+ value = value[1:-1]
+ return value
+ return ""
+```
+
+创建 `tests/test_auth/test_cookie_persistence.py`:
+- 使用 tmp_path fixture 创建临时环境文件
+- 测试保存、更新、追加、加载等功能
+- 测试文件权限设置
+
+
+ pytest tests/test_auth/test_cookie_persistence.py -x -v
+
+
+ - CookieManager 类可用
+ - 6 个单元测试全部通过
+ - Cookie 可正确保存到 ~/.kb_env
+ - 保存后自动清除配置缓存
+
+
+
+
+
+
+1. Cookie 导入测试通过: `pytest tests/test_auth/test_cookie_import.py -v`
+2. Cookie 持久化测试通过: `pytest tests/test_auth/test_cookie_persistence.py -v`
+3. 模块可导入: `python -c "from kb_workflow.auth.cookie_manager import CookieManager, parse_json_cookies; print('OK')"`
+
+
+
+- parse_json_cookies 函数支持 Chrome Cookie Editor 格式
+- CookieManager 类支持保存和加载 Cookie
+- ~/.kb_env 文件权限正确设置为 600
+- 保存后自动清除 get_settings 缓存
+- 10 个单元测试全部通过
+
+
+
diff --git a/.planning/phases/07-login-auth/07-03-PLAN.md b/.planning/phases/07-login-auth/07-03-PLAN.md
new file mode 100644
index 0000000..66191b4
--- /dev/null
+++ b/.planning/phases/07-login-auth/07-03-PLAN.md
@@ -0,0 +1,341 @@
+---
+phase: 07-login-auth
+plan: 03
+type: execute
+wave: 2
+depends_on:
+ - 07-01
+ - 07-02
+files_modified:
+ - kb_workflow/auth/zhihu_login.py
+ - kb_workflow/auth/cookie_manager.py
+ - kb_workflow/kb_workflow.py
+ - tests/test_auth/test_cookie_validation.py
+ - tests/test_auth/test_cli_login.py
+autonomous: false
+requirements:
+ - AUTH-04
+user_setup: []
+
+must_haves:
+ truths:
+ - "用户可以通过 python3 kb-workflow/kb_workflow.py login 启动登录流程"
+ - "登录成功后 Cookie 自动保存到 ~/.kb_env"
+ - "Cookie 过期时系统自动检测并提示用户"
+ - "用户可以手动导入 Cookie 文件"
+ - "用户可以查看当前 Cookie 状态"
+ artifacts:
+ - path: "kb_workflow/auth/zhihu_login.py"
+ provides: "Cookie 验证方法"
+ exports: ["validate_cookie"]
+ - path: "kb_workflow/kb_workflow.py"
+ provides: "login CLI 子命令"
+ contains: "subparsers.add_parser('login')"
+ key_links:
+ - from: "kb_workflow.py login"
+ to: "ZhihuLogin.login_by_qrcode()"
+ via: "asyncio.run()"
+ pattern: "login_by_qrcode"
+ - from: "ZhihuLogin.login_by_qrcode()"
+ to: "CookieManager.save_cookie()"
+ via: "登录成功后保存"
+ pattern: "save_cookie"
+ - from: "validate_cookie()"
+ to: "/api/v4/me"
+ via: "Playwright request API"
+ pattern: "api/v4/me"
+---
+
+
+实现 Cookie 验证机制和 CLI login 子命令。
+
+Purpose: 整合登录功能到 CLI,支持 Cookie 验证和状态查询
+Output: 完整的 login 命令 + Cookie 验证 + 端到端测试
+
+
+
+@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
+@/home/starmen1265/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/07-login-auth/07-CONTEXT.md
+@.planning/phases/07-login-auth/07-RESEARCH.md
+@.planning/phases/07-login-auth/07-01-PLAN.md
+@.planning/phases/07-login-auth/07-02-PLAN.md
+
+
+
+
+From kb_workflow/auth/zhihu_login.py (Plan 01):
+```python
+class ZhihuLogin:
+ async def login_by_qrcode(self) -> ZhihuLoginResult
+ async def check_login_cookie(self) -> bool
+ async def get_cookies_str(self) -> str
+```
+
+From kb_workflow/auth/cookie_manager.py (Plan 02):
+```python
+def parse_json_cookies(json_path: str) -> str
+
+class CookieManager:
+ def save_cookie(self, cookie_str: str) -> None
+ def load_cookie(self) -> str
+```
+
+From kb_workflow/kb_workflow.py (existing):
+```python
+def process_command(url: str, ...) -> Dict[str, Any]
+
+def main():
+ subparsers = parser.add_subparsers(dest='command')
+ # 添加 login 子命令到此处
+```
+
+知乎 API:
+- 验证接口: GET https://www.zhihu.com/api/v4/me
+- 成功返回: {"id": 123, "name": "用户名", ...}
+- 失败返回: 401 状态码
+
+
+
+
+
+
+ Task 1: 实现 Cookie 验证功能
+ kb_workflow/auth/zhihu_login.py, tests/test_auth/test_cookie_validation.py
+
+ - Test 1: validate_cookie_valid - 有效 Cookie 返回 valid=True 和用户信息
+ - Test 2: validate_cookie_expired - 过期 Cookie 返回 valid=False
+ - Test 3: validate_cookie_empty - 空 Cookie 返回 valid=False
+ - Test 4: validate_cookie_malformed - 格式错误 Cookie 返回 valid=False
+
+
+扩展 `kb_workflow/auth/zhihu_login.py` 添加 Cookie 验证功能:
+
+1. 添加 `CookieValidationResult` Pydantic model:
+```python
+from pydantic import BaseModel
+from typing import Optional
+
+class CookieValidationResult(BaseModel):
+ """Cookie 验证结果"""
+ valid: bool
+ username: Optional[str] = None
+ user_id: Optional[str] = None
+ reason: Optional[str] = None
+```
+
+2. 在 `ZhihuLogin` 类中添加验证方法:
+```python
+VALIDATION_URL = "https://www.zhihu.com/api/v4/me"
+
+async def validate_cookie(self, cookie_str: str = None) -> CookieValidationResult:
+ """验证 Cookie 有效性
+
+ 通过请求知乎用户信息接口检测 Cookie 是否有效。
+
+ Args:
+ cookie_str: 要验证的 Cookie 字符串,为空时使用已加载的 Cookie
+
+ Returns:
+ CookieValidationResult 包含验证状态和用户信息
+ """
+ # 如果提供了 cookie_str,需要重新启动浏览器并设置 Cookie
+ if cookie_str:
+ if self.browser.browser:
+ self.browser.close()
+ self.browser.start(cookie=cookie_str)
+ elif not self.browser.page:
+ return CookieValidationResult(valid=False, reason="No cookie provided")
+
+ try:
+ # 使用 Playwright 的 request API
+ response = await self.browser.page.request.get(self.VALIDATION_URL)
+
+ if response.status == 200:
+ user_info = await response.json()
+ return CookieValidationResult(
+ valid=True,
+ username=user_info.get("name", ""),
+ user_id=str(user_info.get("id", ""))
+ )
+ elif response.status == 401:
+ return CookieValidationResult(valid=False, reason="Cookie expired")
+ else:
+ return CookieValidationResult(
+ valid=False,
+ reason=f"Validation failed with status {response.status}"
+ )
+ except Exception as e:
+ return CookieValidationResult(valid=False, reason=str(e))
+```
+
+3. 创建 `tests/test_auth/test_cookie_validation.py`:
+- 使用 mock 模拟 page.request.get() 返回不同状态码
+- 测试有效、过期、空、格式错误等场景
+
+
+ pytest tests/test_auth/test_cookie_validation.py -x -v
+
+
+ - validate_cookie 方法可用
+ - 4 个单元测试全部通过
+ - 返回 CookieValidationResult 结构
+
+
+
+
+ Task 2: 实现 login CLI 子命令
+ kb_workflow/kb_workflow.py, tests/test_auth/test_cli_login.py
+
+ - Test 1: login_default_mode - 默认启动二维码登录
+ - Test 2: login_import_cookie - --import-cookie 参数导入 JSON
+ - Test 3: login_status - --status 参数检查 Cookie 状态
+ - Test 4: login_integration - 登录成功后 Cookie 保存到文件
+
+
+扩展 `kb_workflow/kb_workflow.py` 添加 login 子命令:
+
+1. 添加 login_command 函数:
+```python
+def login_command(
+ import_cookie: Optional[str] = None,
+ status: bool = False
+) -> Dict[str, Any]:
+ """Execute login command
+
+ Args:
+ import_cookie: Path to JSON cookie file
+ status: Check current cookie status
+
+ Returns:
+ Dictionary with login result
+ """
+ try:
+ from kb_workflow.auth import ZhihuLogin
+ from kb_workflow.auth.cookie_manager import CookieManager, parse_json_cookies
+ from kb_workflow.pipeline.config import get_settings
+
+ settings = get_settings()
+ manager = CookieManager()
+
+ # 检查 Cookie 状态
+ if status:
+ current_cookie = settings.zhihu_cookie.get_secret_value()
+ if not current_cookie:
+ return {"valid": False, "reason": "No cookie found in ~/.kb_env"}
+
+ async def check_status():
+ login = ZhihuLogin(intermediate_path=settings.intermediate_path)
+ try:
+ login.browser.start(cookie=current_cookie)
+ return await login.validate_cookie(current_cookie)
+ finally:
+ login.browser.close()
+
+ result = asyncio.run(check_status())
+ return result.model_dump()
+
+ # 导入 Cookie
+ if import_cookie:
+ cookie_str = parse_json_cookies(import_cookie)
+ manager.save_cookie(cookie_str)
+ return {"success": True, "message": f"Cookie imported from {import_cookie}"}
+
+ # 二维码登录
+ async def qrcode_login():
+ login = ZhihuLogin(intermediate_path=settings.intermediate_path)
+ try:
+ result = await login.login_by_qrcode()
+ if result.success and result.cookie_str:
+ manager.save_cookie(result.cookie_str)
+ return result.model_dump()
+ finally:
+ if login.browser.browser:
+ login.browser.close()
+
+ result = asyncio.run(qrcode_login())
+ return result
+
+ except Exception as e:
+ return {"success": False, "error": str(e)}
+```
+
+2. 在 main() 函数中添加 login 子命令:
+```python
+# Login subcommand
+login_parser = subparsers.add_parser('login', help='Zhihu login authentication')
+login_parser.add_argument('--import-cookie', '-i', type=str,
+ help='Import cookie from JSON file')
+login_parser.add_argument('--status', action='store_true',
+ help='Check current cookie status')
+
+# Handle login subcommand
+if args.command == 'login':
+ result = login_command(
+ import_cookie=args.import_cookie,
+ status=args.status
+ )
+ print(json.dumps(result, ensure_ascii=False, indent=2))
+```
+
+3. 创建 `tests/test_auth/test_cli_login.py`:
+- 使用 mock 模拟 ZhihuLogin 和 CookieManager
+- 测试各种命令行参数组合
+
+
+ pytest tests/test_auth/test_cli_login.py -x -v
+
+
+ - login 子命令可用
+ - 支持 --import-cookie 和 --status 参数
+ - 4 个单元测试全部通过
+
+
+
+
+
+完整的知乎登录认证系统:
+- 二维码扫码登录 (python3 kb-workflow/kb_workflow.py login)
+- JSON Cookie 导入 (python3 kb-workflow/kb_workflow.py login --import-cookie cookies.json)
+- Cookie 状态检查 (python3 kb-workflow/kb_workflow.py login --status)
+- Cookie 自动持久化到 ~/.kb_env
+
+
+1. 运行 `python3 kb-workflow/kb_workflow.py login`
+2. 验证二维码图片生成在 `~/work/intermediate/zhihu_qrcode_*.png`
+3. 使用知乎 APP 扫码确认登录
+4. 验证命令返回 `{"success": true, "cookie_str": "..."}`
+5. 检查 `~/.kb_env` 文件包含 `ZHIHU_COOKIE="..."` 行
+6. 运行 `python3 kb-workflow/kb_workflow.py login --status`
+7. 验证返回用户信息
+
+ Type "approved" if login works, or describe issues
+
+
+
+
+
+1. 所有测试通过: `pytest tests/test_auth/ -v`
+2. login 命令可用: `python3 kb-workflow/kb_workflow.py login --help`
+3. Cookie 验证功能: `python3 -c "from kb_workflow.auth import ZhihuLogin; print(ZhihuLogin.validate_cookie)"`
+
+
+
+- 用户可以通过命令行扫码登录知乎
+- 扫码成功后 Cookie 自动保存
+- 用户可以手动导入 JSON Cookie
+- 用户可以查看 Cookie 状态
+- Cookie 过期时系统提示重新登录
+- 所有单元测试通过 (18+ tests)
+- 手动验证登录流程成功
+
+
+