返回 Social Auto Upload
2026-03-25-browser-cli-unification-implementation.md
根目录 / docs / superpowers / plans / 2026-03-25-browser-cli-unification-implementation.md
1 # Browser CLI Unification Implementation Plan
2
3 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
5 **Goal:** 为抖音、快手、小红书三家浏览器平台统一 `sau` CLI 契约,补齐小红书 CLI/skill,并把视频正文统一为 `desc`、图文正文统一为 `note`。
6
7 **Architecture:** 保持现有 `sau_cli.py` 单文件主入口,不引入新的 CLI 框架。`sau_cli.py` 负责 parser、request dataclass、dispatch 和账号文件解析;各平台 uploader 只做最小字段接线;skill、README、CLI/install/update 文档、examples 同步切到统一契约,避免对外同时暴露多套命名。
8
9 **Tech Stack:** Python 3.10+, `argparse`, `asyncio`, `dataclasses`, `pathlib`, `unittest`, existing Patchright-based uploaders, repository markdown docs
10
11 ---
12
13 ## File Structure
14
15 ### New files
16
17 - `tests/test_sau_browser_cli.py`
18 - 覆盖三家浏览器平台统一 CLI parser / dispatch / request 映射
19 - `skills/xiaohongshu-upload/SKILL.md`
20 - 小红书 CLI skill 主说明
21 - `skills/xiaohongshu-upload/references/runtime-requirements.md`
22 - 小红书运行前提
23 - `skills/xiaohongshu-upload/references/cli-contract.md`
24 - 小红书 CLI 契约
25 - `skills/xiaohongshu-upload/references/troubleshooting.md`
26 - 小红书排障文档
27 - `skills/xiaohongshu-upload/scripts/examples/xiaohongshu_commands.ps1`
28 - PowerShell 示例命令
29 - `skills/xiaohongshu-upload/scripts/examples/xiaohongshu_commands.sh`
30 - shell 示例命令
31 - `skills/xiaohongshu-upload/scripts/examples/xiaohongshu_cli_template.py`
32 - Python 命令模板
33
34 ### Modified files
35
36 - `sau_cli.py`
37 - 新增 `xiaohongshu` parser / dispatch
38 - 改造抖音、快手 request dataclass 和参数模型
39 - `uploader/douyin_uploader/main.py`
40 - 给视频接入 `desc`
41 - 给图文接入 `title + note`
42 - `uploader/ks_uploader/main.py`
43 - 给视频接入 `desc`
44 - 给图文接入 `title + note`
45 - `uploader/xiaohongshu_uploader/main.py`
46 - 只补 CLI 接线所需的最小字段适配
47 - `README.md`
48 - 补小红书 CLI / skill,并统一三家参数说明
49 - `docs/CLI.md`
50 - 统一三家浏览器平台命令契约
51 - `docs/install.md`
52 - 补小红书 CLI 示例和统一参数说明
53 - `docs/update.md`
54 - 补小红书检查项与 skill 路径
55 - `skills/douyin-upload/SKILL.md`
56 - `skills/douyin-upload/references/cli-contract.md`
57 - `skills/douyin-upload/scripts/examples/douyin_commands.ps1`
58 - `skills/douyin-upload/scripts/examples/douyin_commands.sh`
59 - `skills/douyin-upload/scripts/examples/douyin_cli_template.py`
60 - `skills/kuaishou-upload/SKILL.md`
61 - `skills/kuaishou-upload/references/cli-contract.md`
62 - `skills/kuaishou-upload/scripts/examples/kuaishou_commands.ps1`
63 - `skills/kuaishou-upload/scripts/examples/kuaishou_commands.sh`
64 - `skills/kuaishou-upload/scripts/examples/kuaishou_cli_template.py`
65 - `examples/get_xiaohongshu_cookie.py`
66 - `examples/upload_video_to_xiaohongshu.py`
67
68 ## Task 1: 用测试锁定统一 CLI 契约
69
70 **Files:**
71 - Create: `tests/test_sau_browser_cli.py`
72 - Reference: `sau_cli.py`
73
74 - [ ] **Step 1: 写统一 CLI parser 测试**
75
76 在 `tests/test_sau_browser_cli.py` 里覆盖最小命令契约:
77
78 ```python
79 import asyncio
80 import unittest
81 from argparse import Namespace
82 from pathlib import Path
83 from unittest.mock import AsyncMock, patch
84
85 import sau_cli
86
87
88 class BrowserCliParserTests(unittest.TestCase):
89 def test_build_parser_accepts_xiaohongshu_login(self):
90 parser = sau_cli.build_parser()
91 args = parser.parse_args(["xiaohongshu", "login", "--account", "creator"])
92 self.assertEqual(args.platform, "xiaohongshu")
93 self.assertEqual(args.action, "login")
94
95 def test_douyin_upload_video_accepts_desc(self):
96 parser = sau_cli.build_parser()
97 args = parser.parse_args([
98 "douyin", "upload-video",
99 "--account", "creator",
100 "--file", "demo.mp4",
101 "--title", "标题",
102 "--desc", "视频简介",
103 ])
104 self.assertEqual(args.desc, "视频简介")
105
106 def test_kuaishou_upload_note_accepts_title_and_note(self):
107 parser = sau_cli.build_parser()
108 args = parser.parse_args([
109 "kuaishou", "upload-note",
110 "--account", "creator",
111 "--images", "1.png",
112 "--title", "图文标题",
113 "--note", "图文正文",
114 ])
115 self.assertEqual(args.title, "图文标题")
116 self.assertEqual(args.note, "图文正文")
117 ```
118
119 - [ ] **Step 2: 写 dispatch 路由测试**
120
121 继续在 `tests/test_sau_browser_cli.py` 中补最小 dispatch 验证:
122
123 ```python
124 class BrowserCliDispatchTests(unittest.TestCase):
125 def test_dispatch_xiaohongshu_check_prints_valid(self):
126 args = Namespace(platform="xiaohongshu", action="check", account="creator")
127 with patch("sau_cli.check_xiaohongshu_account", new=AsyncMock(return_value=True)):
128 code = asyncio.run(sau_cli.dispatch(args))
129 self.assertEqual(code, 0)
130
131 def test_dispatch_douyin_upload_note_uses_new_request_fields(self):
132 args = Namespace(
133 platform="douyin",
134 action="upload-note",
135 account="creator",
136 images=[Path("1.png")],
137 title="图文标题",
138 note="图文正文",
139 tags="测试,图文",
140 schedule=0,
141 debug=False,
142 headless=True,
143 )
144 with patch("sau_cli.upload_note", new=AsyncMock()) as mock_upload:
145 asyncio.run(sau_cli.dispatch(args))
146 request = mock_upload.await_args.args[0]
147 self.assertEqual(request.title, "图文标题")
148 self.assertEqual(request.note, "图文正文")
149 ```
150
151 - [ ] **Step 3: 先跑测试确认失败**
152
153 Run:
154
155 ```powershell
156 py -3 -m unittest tests.test_sau_browser_cli -v
157 ```
158
159 Expected:
160
161 - 因为 `sau_cli.py` 还没有 `xiaohongshu` 分支和统一字段而失败
162
163 - [ ] **Step 4: 提交测试脚手架**
164
165 ```powershell
166 git add tests/test_sau_browser_cli.py
167 git commit -m "test: define browser cli unification contract"
168 ```
169
170 ## Task 2: 改 `sau_cli.py`,补齐统一参数与小红书路由
171
172 **Files:**
173 - Modify: `sau_cli.py`
174 - Reference: `uploader/xiaohongshu_uploader/main.py`
175 - Reference: `uploader/douyin_uploader/main.py`
176 - Reference: `uploader/ks_uploader/main.py`
177
178 - [ ] **Step 1: 增加小红书 request dataclass**
179
180 在 `sau_cli.py` 中新增:
181
182 ```python
183 @dataclass(slots=True)
184 class XiaohongshuVideoUploadRequest:
185 account_name: str
186 video_file: Path
187 title: str
188 description: str
189 tags: list[str]
190 publish_date: datetime | int
191 thumbnail_file: Path | None = None
192 publish_strategy: str = XIAOHONGSHU_PUBLISH_STRATEGY_IMMEDIATE
193 debug: bool = True
194 headless: bool = True
195
196
197 @dataclass(slots=True)
198 class XiaohongshuNoteUploadRequest:
199 account_name: str
200 image_files: list[Path]
201 title: str
202 note: str
203 tags: list[str]
204 publish_date: datetime | int
205 publish_strategy: str = XIAOHONGSHU_PUBLISH_STRATEGY_IMMEDIATE
206 debug: bool = True
207 headless: bool = True
208 ```
209
210 - [ ] **Step 2: 改造抖音、快手 request dataclass**
211
212 把字段统一到以下模型:
213
214 ```python
215 class DouyinVideoUploadRequest:
216 title: str
217 description: str
218
219 class DouyinNoteUploadRequest:
220 title: str
221 note: str
222
223 class KuaishouVideoUploadRequest:
224 title: str
225 description: str
226
227 class KuaishouNoteUploadRequest:
228 title: str
229 note: str
230 ```
231
232 要求:
233
234 - 视频用 `description`
235 - 图文用 `note`
236 - 不再把图文 request 设计成只有历史 `note` 语义但没有 `title`
237
238 - [ ] **Step 3: 改 parser**
239
240 在 `build_parser()` 里完成:
241
242 - `douyin upload-video` 增加 `--desc`
243 - `kuaishou upload-video` 增加 `--desc`
244 - `douyin upload-note` 改成 `--title --note`
245 - `kuaishou upload-note` 改成 `--title --note`
246 - 新增整套 `xiaohongshu`:
247 - `login`
248 - `check`
249 - `upload-video --title --desc --tags --thumbnail`
250 - `upload-note --images --title --note --tags`
251
252 - [ ] **Step 4: 补小红书账号操作函数**
253
254 在 `sau_cli.py` 中新增:
255
256 ```python
257 async def login_xiaohongshu_account(account_name: str, headless: bool = True) -> dict: ...
258 async def check_xiaohongshu_account(account_name: str) -> bool: ...
259 async def upload_xiaohongshu_video(request: XiaohongshuVideoUploadRequest) -> Path: ...
260 async def upload_xiaohongshu_note(request: XiaohongshuNoteUploadRequest) -> Path: ...
261 ```
262
263 要求:
264
265 - 账号路径继续走 `resolve_account_file("xiaohongshu", account_name)`
266 - 登录 / 检查分别复用 `xiaohongshu_setup` 与 `cookie_auth`
267 - 上传前先做 `setup(handle=False)` 校验
268
269 - [ ] **Step 5: 改 dispatch**
270
271 要求:
272
273 - 三家浏览器平台都统一输出:
274 - `login` 成功时打印账号文件路径
275 - `check` 输出 `valid` / `invalid`
276 - `upload-video` 打印简洁摘要
277 - `upload-note` 打印图片数量摘要
278 - request 构造统一映射:
279 - 视频:`title + description`
280 - 图文:`title + note`
281
282 - [ ] **Step 6: 跑 CLI 测试确认通过**
283
284 Run:
285
286 ```powershell
287 py -3 -m unittest tests.test_sau_browser_cli -v
288 ```
289
290 Expected:
291
292 - `BrowserCliParserTests`
293 - `BrowserCliDispatchTests`
294
295 全部通过
296
297 - [ ] **Step 7: 跑最小 help 自检**
298
299 Run:
300
301 ```powershell
302 py -3 sau_cli.py douyin --help
303 py -3 sau_cli.py kuaishou --help
304 py -3 sau_cli.py xiaohongshu --help
305 py -3 sau_cli.py xiaohongshu upload-note --help
306 ```
307
308 Expected:
309
310 - 小红书子命令存在
311 - 图文命令展示 `--title`、`--note`
312 - 视频命令展示 `--desc`
313
314 - [ ] **Step 8: 提交 CLI 主线**
315
316 ```powershell
317 git add sau_cli.py tests/test_sau_browser_cli.py
318 git commit -m "feat: unify browser cli contracts"
319 ```
320
321 ## Task 3: 给 uploader 做最小字段接线
322
323 **Files:**
324 - Modify: `uploader/douyin_uploader/main.py`
325 - Modify: `uploader/ks_uploader/main.py`
326 - Modify: `uploader/xiaohongshu_uploader/main.py`
327 - Modify: `tests/test_xiaohongshu_uploader.py`
328
329 - [ ] **Step 1: 给抖音视频接入 `desc`**
330
331 在 `DouYinVideo.__init__()` 中补 `desc` 参数和 `self.desc`,并把发布页填写逻辑改成:
332
333 ```python
334 await self.fill_title_and_description(page, self.title, self.desc or self.title, self.tags)
335 ```
336
337 - [ ] **Step 2: 给抖音图文接入 `title + note`**
338
339 在 `DouYinNote.__init__()` 中补 `title` 参数,保留 `note` 作为图文正文,发布页填写改成:
340
341 ```python
342 await self.fill_title_and_description(page, self.title, self.note, self.tags)
343 ```
344
345 要求:
346
347 - `title` 必填
348 - `note` 可选但建议非空;如果现有逻辑要求非空,就继续保留校验
349
350 - [ ] **Step 3: 给快手视频接入 `desc`**
351
352 在 `KSVideo.__init__()` 中补 `desc` 参数和 `self.desc`,填写“描述”区域时改成:
353
354 ```python
355 await page.keyboard.type(self.desc or self.title)
356 ```
357
358 - [ ] **Step 4: 给快手图文接入 `title + note`**
359
360 在 `KSNote.__init__()` 中补 `title` 参数与 `self.title`,保留 `self.note` 作为正文。
361
362 要求:
363
364 - 图文上传校验里增加 `title` 必填
365 - 如果页面当前只有正文输入区,没有独立标题区,仍然要在 CLI / request / 构造函数层保持 `title` 字段,以便后续平台对齐
366
367 - [ ] **Step 5: 检查小红书 CLI 接线是否需要补充适配**
368
369 确认 `XiaoHongShuVideo` / `XiaoHongShuNote` 只需要以下映射即可:
370
371 ```python
372 title=request.title
373 desc=request.description # 视频
374 desc=request.note # 图文
375 ```
376
377 如果 `XiaoHongShuNote` 里还保留历史 `note` 兼容,不要删,只保持 CLI 层优先走 `title + note + tags`。
378
379 - [ ] **Step 6: 补一条小红书映射测试**
380
381 在 `tests/test_xiaohongshu_uploader.py` 中增加最小断言:
382
383 ```python
384 def test_note_title_defaults_do_not_override_explicit_title(self):
385 app = xhs_main.XiaoHongShuNote(
386 image_paths=["a.png"],
387 note="正文",
388 tags=[],
389 publish_date=0,
390 account_file="account.json",
391 title="显式标题",
392 desc="图文正文",
393 )
394 self.assertEqual(app.title, "显式标题")
395 self.assertEqual(app.desc, "图文正文")
396 ```
397
398 - [ ] **Step 7: 跑 uploader 相关测试**
399
400 Run:
401
402 ```powershell
403 py -3 -m unittest tests.test_xiaohongshu_uploader -v
404 ```
405
406 Expected:
407
408 - 小红书 uploader 相关单测通过
409
410 - [ ] **Step 8: 提交 uploader 接线**
411
412 ```powershell
413 git add uploader/douyin_uploader/main.py uploader/ks_uploader/main.py uploader/xiaohongshu_uploader/main.py tests/test_xiaohongshu_uploader.py
414 git commit -m "feat: align browser uploader metadata fields"
415 ```
416
417 ## Task 4: 新增小红书 skill,并同步更新抖音、快手 skill
418
419 **Files:**
420 - Create: `skills/xiaohongshu-upload/SKILL.md`
421 - Create: `skills/xiaohongshu-upload/references/runtime-requirements.md`
422 - Create: `skills/xiaohongshu-upload/references/cli-contract.md`
423 - Create: `skills/xiaohongshu-upload/references/troubleshooting.md`
424 - Create: `skills/xiaohongshu-upload/scripts/examples/xiaohongshu_commands.ps1`
425 - Create: `skills/xiaohongshu-upload/scripts/examples/xiaohongshu_commands.sh`
426 - Create: `skills/xiaohongshu-upload/scripts/examples/xiaohongshu_cli_template.py`
427 - Modify: `skills/douyin-upload/SKILL.md`
428 - Modify: `skills/douyin-upload/references/cli-contract.md`
429 - Modify: `skills/douyin-upload/scripts/examples/douyin_commands.ps1`
430 - Modify: `skills/douyin-upload/scripts/examples/douyin_commands.sh`
431 - Modify: `skills/douyin-upload/scripts/examples/douyin_cli_template.py`
432 - Modify: `skills/kuaishou-upload/SKILL.md`
433 - Modify: `skills/kuaishou-upload/references/cli-contract.md`
434 - Modify: `skills/kuaishou-upload/scripts/examples/kuaishou_commands.ps1`
435 - Modify: `skills/kuaishou-upload/scripts/examples/kuaishou_commands.sh`
436 - Modify: `skills/kuaishou-upload/scripts/examples/kuaishou_cli_template.py`
437
438 - [ ] **Step 1: 复制现有 skill 目录结构作为小红书骨架**
439
440 要求:
441
442 - 风格对齐 `skills/douyin-upload/`
443 - 默认优先走 `sau xiaohongshu ...`
444 - 明确小红书支持:
445 - `login`
446 - `check`
447 - `upload-video`
448 - `upload-note`
449
450 - [ ] **Step 2: 写小红书 CLI 契约**
451
452 至少写清:
453
454 ```bash
455 sau xiaohongshu login --account <account>
456 sau xiaohongshu check --account <account>
457 sau xiaohongshu upload-video --account <account> --file <video> --title "<title>" [--desc "..."] [--tags ...]
458 sau xiaohongshu upload-note --account <account> --images <img...> --title "<title>" [--note "..."] [--tags ...]
459 ```
460
461 - [ ] **Step 3: 更新抖音、快手 skill 契约**
462
463 要求:
464
465 - 视频示例命令补 `--desc`
466 - 图文示例命令改成 `--title --note`
467 - 所有模板文件同步更新,不要只改文档不改脚本
468
469 - [ ] **Step 4: 做 skill 文件级自检**
470
471 Run:
472
473 ```powershell
474 Get-ChildItem skills\xiaohongshu-upload -Recurse
475 Get-Content skills\douyin-upload\references\cli-contract.md
476 Get-Content skills\kuaishou-upload\references\cli-contract.md
477 ```
478
479 Expected:
480
481 - 小红书 skill 目录完整
482 - 抖音、快手 skill 契约都已经切到新参数模型
483
484 - [ ] **Step 5: 提交 skill 变更**
485
486 ```powershell
487 git add skills/xiaohongshu-upload skills/douyin-upload skills/kuaishou-upload
488 git commit -m "feat: add xiaohongshu skill and align browser skill contracts"
489 ```
490
491 ## Task 5: 更新 examples 和 README / docs
492
493 **Files:**
494 - Modify: `examples/get_xiaohongshu_cookie.py`
495 - Modify: `examples/upload_video_to_xiaohongshu.py`
496 - Modify: `README.md`
497 - Modify: `docs/CLI.md`
498 - Modify: `docs/install.md`
499 - Modify: `docs/update.md`
500
501 - [ ] **Step 1: 改小红书 examples**
502
503 要求:
504
505 - `examples/get_xiaohongshu_cookie.py` 明确指向 `sau xiaohongshu login --account <account_name>` 这一主线
506 - `examples/upload_video_to_xiaohongshu.py` 说明当前主线优先走 CLI
507 - 如果继续保留 uploader 直连示例,注释里标明“调试入口 / 历史直连路径”
508
509 - [ ] **Step 2: 更新 README 平台表和快速开始**
510
511 README 至少要改这些位置:
512
513 - 平台能力表里把小红书改成:
514 - `CLI ✅`
515 - `Skill ✅`
516 - 快速开始里的浏览器平台命令改成统一契约
517 - 抖音、快手图文示例改成:
518
519 ```bash
520 sau douyin upload-note --account <account_name> --images videos/1.png videos/2.png --title "图文标题" --note "图文正文"
521 sau kuaishou upload-note --account <account_name> --images videos/1.png videos/2.png --title "图文标题" --note "图文正文"
522 sau xiaohongshu upload-note --account <account_name> --images videos/1.png videos/2.png --title "图文标题" --note "图文正文"
523 ```
524
525 - [ ] **Step 3: 更新 `docs/CLI.md`**
526
527 要求:
528
529 - 补 `xiaohongshu` 小节
530 - 把三家浏览器平台整理成一致说明:
531 - 视频:`title + desc + tags`
532 - 图文:`title + note + tags`
533 - 登录二维码说明补小红书
534
535 - [ ] **Step 4: 更新 `docs/install.md` 和 `docs/update.md`**
536
537 要求:
538
539 - 安装文档里补 `sau xiaohongshu --help`
540 - 更新文档里补小红书自检命令和 skill 路径
541 - 文档统一写 `account_name`
542
543 - [ ] **Step 5: 做文档与示例核对**
544
545 Run:
546
547 ```powershell
548 Get-Content README.md | Select-String -Pattern "xiaohongshu|upload-note|--note|--desc" -Context 1,2
549 Get-Content docs\CLI.md | Select-String -Pattern "xiaohongshu|--note|--desc" -Context 1,2
550 Get-Content docs\install.md | Select-String -Pattern "xiaohongshu" -Context 1,2
551 Get-Content docs\update.md | Select-String -Pattern "xiaohongshu" -Context 1,2
552 ```
553
554 Expected:
555
556 - README、CLI、install、update 都出现小红书 CLI
557 - 图文正文写成 `--note`
558 - 视频描述写成 `--desc`
559
560 - [ ] **Step 6: 跑最终最小验证**
561
562 Run:
563
564 ```powershell
565 py -3 -m unittest tests.test_sau_browser_cli tests.test_xiaohongshu_uploader -v
566 py -3 sau_cli.py xiaohongshu --help
567 py -3 sau_cli.py xiaohongshu upload-video --help
568 py -3 sau_cli.py xiaohongshu upload-note --help
569 ```
570
571 Expected:
572
573 - 单测通过
574 - 小红书 CLI 帮助存在
575 - 视频命令显示 `--desc`
576 - 图文命令显示 `--note`
577
578 - [ ] **Step 7: 提交收尾**
579
580 ```powershell
581 git add examples/get_xiaohongshu_cookie.py examples/upload_video_to_xiaohongshu.py README.md docs/CLI.md docs/install.md docs/update.md
582 git commit -m "docs: align browser cli docs and examples"
583 ```
584
584 lines MARKDOWN