docs(07): create phase 7 login auth plans

This commit is contained in:
2026-03-17 17:15:30 +08:00
parent 1d1f3309b2
commit 14434df4eb
4 changed files with 848 additions and 5 deletions
+5 -5
View File
@@ -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 | - |
@@ -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"
---
<objective>
实现知乎二维码扫码登录核心逻辑。
Purpose: 提供二维码登录能力,访问登录页 → Canvas 截图 → 轮询检测 Cookie
Output: kb_workflow/auth/zhihu_login.py 模块 + Wave 0 测试脚手架
</objective>
<execution_context>
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
<!-- Key interfaces from existing codebase -->
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
```
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: 创建测试模块和 fixtures</name>
<files>tests/test_auth/__init__.py, tests/test_auth/conftest.py</files>
<action>
创建 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 列表
</action>
<verify>
<automated>python -c "from tests.test_auth.conftest import *; print('fixtures loaded')"</automated>
</verify>
<done>测试模块和 fixtures 可被 pytest 加载</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: 实现 ZhihuLogin 核心类</name>
<files>kb_workflow/auth/__init__.py, kb_workflow/auth/zhihu_login.py, tests/test_auth/test_qrcode_login.py</files>
<behavior>
- 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 字符串
</behavior>
<action>
创建 `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
</action>
<verify>
<automated>pytest tests/test_auth/test_qrcode_login.py -x -v</automated>
</verify>
<done>
- ZhihuLogin 类可实例化
- 所有单元测试通过
- 二维码截图功能可用
- Cookie 检测功能可用
</done>
</task>
</tasks>
<verification>
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`
</verification>
<success_criteria>
- Wave 0 测试脚手架就绪 (tests/test_auth/)
- ZhihuLogin 类实现二维码截图和 Cookie 检测
- 5 个单元测试全部通过
- 代码符合现有项目模式 (pydantic, Playwright)
</success_criteria>
<output>
After completion, create `.planning/phases/07-login-auth/07-01-SUMMARY.md`
</output>
@@ -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"
---
<objective>
实现 Cookie 导入和持久化管理。
Purpose: 支持 JSON Cookie 导入和 ~/.kb_env 文件持久化
Output: kb_workflow/auth/cookie_manager.py 模块 + 单元测试
</objective>
<execution_context>
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
<!-- Key interfaces from existing codebase -->
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"
```
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: 实现 Cookie JSON 解析功能</name>
<files>kb_workflow/auth/cookie_manager.py, tests/test_auth/test_cookie_import.py</files>
<behavior>
- 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
</behavior>
<action>
在 `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 文件
- 测试正常解析、域名过滤、空数组、格式错误等情况
</action>
<verify>
<automated>pytest tests/test_auth/test_cookie_import.py -x -v</automated>
</verify>
<done>
- parse_json_cookies 函数可用
- 4 个单元测试全部通过
- 正确过滤 zhihu.com 域名
</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: 实现 Cookie 持久化管理器</name>
<files>kb_workflow/auth/cookie_manager.py, tests/test_auth/test_cookie_persistence.py</files>
<behavior>
- 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 - 文件不存在返回空字符串
</behavior>
<action>
扩展 `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 创建临时环境文件
- 测试保存、更新、追加、加载等功能
- 测试文件权限设置
</action>
<verify>
<automated>pytest tests/test_auth/test_cookie_persistence.py -x -v</automated>
</verify>
<done>
- CookieManager 类可用
- 6 个单元测试全部通过
- Cookie 可正确保存到 ~/.kb_env
- 保存后自动清除配置缓存
</done>
</task>
</tasks>
<verification>
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')"`
</verification>
<success_criteria>
- parse_json_cookies 函数支持 Chrome Cookie Editor 格式
- CookieManager 类支持保存和加载 Cookie
- ~/.kb_env 文件权限正确设置为 600
- 保存后自动清除 get_settings 缓存
- 10 个单元测试全部通过
</success_criteria>
<output>
After completion, create `.planning/phases/07-login-auth/07-02-SUMMARY.md`
</output>
@@ -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"
---
<objective>
实现 Cookie 验证机制和 CLI login 子命令。
Purpose: 整合登录功能到 CLI,支持 Cookie 验证和状态查询
Output: 完整的 login 命令 + Cookie 验证 + 端到端测试
</objective>
<execution_context>
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
<!-- Key interfaces from prior plans -->
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 状态码
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: 实现 Cookie 验证功能</name>
<files>kb_workflow/auth/zhihu_login.py, tests/test_auth/test_cookie_validation.py</files>
<behavior>
- 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
</behavior>
<action>
扩展 `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() 返回不同状态码
- 测试有效、过期、空、格式错误等场景
</action>
<verify>
<automated>pytest tests/test_auth/test_cookie_validation.py -x -v</automated>
</verify>
<done>
- validate_cookie 方法可用
- 4 个单元测试全部通过
- 返回 CookieValidationResult 结构
</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: 实现 login CLI 子命令</name>
<files>kb_workflow/kb_workflow.py, tests/test_auth/test_cli_login.py</files>
<behavior>
- Test 1: login_default_mode - 默认启动二维码登录
- Test 2: login_import_cookie - --import-cookie 参数导入 JSON
- Test 3: login_status - --status 参数检查 Cookie 状态
- Test 4: login_integration - 登录成功后 Cookie 保存到文件
</behavior>
<action>
扩展 `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
- 测试各种命令行参数组合
</action>
<verify>
<automated>pytest tests/test_auth/test_cli_login.py -x -v</automated>
</verify>
<done>
- login 子命令可用
- 支持 --import-cookie 和 --status 参数
- 4 个单元测试全部通过
</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
完整的知乎登录认证系统:
- 二维码扫码登录 (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
</what-built>
<how-to-verify>
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. 验证返回用户信息
</how-to-verify>
<resume-signal>Type "approved" if login works, or describe issues</resume-signal>
</task>
</tasks>
<verification>
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)"`
</verification>
<success_criteria>
- 用户可以通过命令行扫码登录知乎
- 扫码成功后 Cookie 自动保存
- 用户可以手动导入 JSON Cookie
- 用户可以查看 Cookie 状态
- Cookie 过期时系统提示重新登录
- 所有单元测试通过 (18+ tests)
- 手动验证登录流程成功
</success_criteria>
<output>
After completion, create `.planning/phases/07-login-auth/07-03-SUMMARY.md`
</output>