OpenCLI 小红书图文发布实战:publish workflow 从命令到 shadow DOM 自动化的完整解析
【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI
导读
本文基于 OpenCLI 仓库中sitemaps/xiaohongshu/workflows/publish.md这一站点工作流文档,系统讲解如何让 AI Agent 借助opencli xiaohongshu publish命令,在已登录的浏览器中自动发布小红书图文笔记(标题 + 正文 + 1-9 张本地图片 + 可选话题)。你将掌握:publish 命令的完整参数与执行前提、最佳路径与降级路径(Fallback)的切换逻辑、creator center 独立域与 closed shadow DOM 发布按钮两大核心难点、中断恢复检查点(Re-entry checkpoints)与发布结果验证方法,以及文档中逐条列出的安全红线与失效标记(Stale markers)。文末还会深入clis/xiaohongshu/publish.js源码与测试用例,说明每条动作背后的实现原理。
背景:publish workflow 在整个小红书 sitemap 中的位置
在 OpenCLI 的站点文档体系中,sitemaps/xiaohongshu/SITE.md将小红书(xiaohongshu.com)定义为"图文笔记 + 短视频 social feed",并明确指出 Agent 的主要任务是搜笔记 / 发笔记 / 读笔记内容 + 评论 / 看 creator 数据。围绕这些目标,仓库提供了三类工作流文档:
- 搜索笔记
- 发布图文笔记(本文主题,
workflows/publish.md) - 评论笔记
同时,发布流程依赖两个关键页面/坑位文档:
- 发布表单页面 compose.md:描述 creator center 发布表单的视觉锚点(Visual anchors)与 5 个 action(上传图片、填文字、加话题、提交发布、存草稿);
- 站点级坑位 pitfalls.md:汇总 task-executing agent 执行工作流时会撞的坑,其中
creator_center_is_different_host、publish_button_shadow_dom、title_input_has_hidden_decoy、security_block_on_repeated_access与发布直接相关。
本文聚焦 publish workflow 本身,其余站点能力(feed / search / note detail)仅作为验证环节的背景提及,不展开。
发布目标(Goal)与范围边界
publish workflow 的目标非常明确:
发一篇图文笔记:标题(≤20 字)+ 正文 + 1-9 张本地图片,可选 topic 话题。
文档同时划定了v1 PoC 的明确边界,以下能力不在本次范围:
- 视频笔记 / 长文 / 私密 / 定时发布 / @用户 / 位置 均不支持;
- 草稿保存不等于发布成功:草稿走同一页面上的
save_draftaction,通过--draftflag 触发,不算 workflow 完成。
状态签名(State signature)
工作流文档用"状态签名"描述进入与成功的判定条件:
- entry(入口):任意页面,
logged_in,本地图片路径 ready,标题 / 正文 string ready; - success(成功):
opencli xiaohongshu creator-notes列表里出现新笔记(title 匹配),或 publish toast发布成功。
这条签名在源码中被实现为发布后的双重校验(见 publish.js 的 Step 8):
- 页面出现成功标记文案(发布模式匹配
发布成功/上传成功;草稿模式匹配草稿已保存/暂存成功/保存成功/保存于/图文笔记(); - 或当前 URL 已离开
/publish/publish(navigatedAway)。
两者任一成立即判定成功,随后返回status: ✅ 发布成功。
最佳路径(Best path):一条命令直发
前置条件
adapter: opencli xiaohongshu publish adapter_health: healthy preconditions: - logged_in (creator.xiaohongshu.com 同 cookie 共享) - title <= 20 chars - 1-9 local image paths (jpg/png/webp, <10MB each) - body text ready estimated_turns: 1命令格式与完整参数
opencli xiaohongshu publish --title "<title>" "<body>" \ --images /path/a.jpg,/path/b.jpg \ [--topics 生活,旅行]结合 publish.js 的 args 定义,完整参数表如下:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
<content> | positional | 是 | 笔记正文(string) |
--title | string | 是 | 笔记标题,最多 20 字 |
--images | string | 否 | 图片路径,逗号分隔,最多 9 张,支持 jpg/png/gif/webp |
--card-text | string | 否 | 文字配图卡片文字,多张卡片用\|\|\|分隔,卡内换行用\n |
--card-style | string | 否 | 文字配图卡片样式,运行时按页面实际选项匹配;找不到会失败;省略时使用基础 |
--topics | string | 否 | 话题标签,逗号分隔,不含#号 |
--draft | bool | 否 | 保存为草稿,不直接发布(默认 false) |
adapter 内部执行:navigate creator host → CDP file upload → shadow DOM submit(见 publish.js 头注释 的 5 步流程)。
参数校验细节(源码级)
adapter 在导航前会做快速失败(fast-fail)校验,见 publish.js:
--title缺失 →ArgumentError;标题超过 20 字符(MAX_TITLE_LEN = 20)→ArgumentError;- positional
<content>缺失 →ArgumentError; --images与--card-text都没给 →ArgumentError;- 图片数量超过
MAX_IMAGES = 9→ArgumentError; - 文字配图模式下追加的图片不支持
.gif(编辑器图片入口只接受 jpg/jpeg/png/webp)→ArgumentError; - 图片路径不存在或扩展名不在
SUPPORTED_EXTENSIONS = { .jpg, .jpeg, .png, .gif, .webp }内 →ArgumentError(validateImagePaths 在导航前就把坏路径挡掉,避免白跑一趟页面)。
登录态与 creator 域校验
执行中 adapter 会先page.goto(PUBLISH_URL)(即https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image),随后检查location.href是否仍包含creator.xiaohongshu.com;若被重定向走(session 过期),会抛出错误提示重新捕获登录:
"Redirected away from creator center — session may have expired. Re-capture browser login via:
opencli xiaohongshu creator-profile"
这条防线来自 pitfalls 中的creator_center_is_different_host:creator center 与主站是两套 DOM,虽然共享 SSO(同一web_sessioncookie),但发布入口只存在于creator.xiaohongshu.com。
降级路径(Fallback path):adapter 失败后的手操流程
当 adapter 抛出 typed error(AuthRequiredError/CommandExecutionError)或卡死超过 60s 时,Agent 切换至 Fallback path:
on_adapter_fail: - adapter_health_update: opencli xiaohongshu publish -> suspect - if AuthRequiredError: - recovery: opencli xiaohongshu login # pending: codex task #276 - 登录后 retry adapter 一次 - opencli browser state (verify host + URL) - if host != creator.xiaohongshu.com: goto https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image - action:upload_images in pages/compose.md (CDP DOM.setFileInputFiles) - wait 3s for upload settle - action:fill_text in pages/compose.md (title visible filter + body contenteditable) - 如果 topics 非空: action:add_topics in pages/compose.md - action:submit_publish in pages/compose.md - **注意**: publish button 是 closed shadow DOM, host-level click 不响应 -> 必须 evaluate 实例方法 `_onPublish` / `onPublish` / `_onSubmit` / `_handlePublish` - verify URL redirect 到 /creator/notes 或 toast "发布成功" - cross-verify: opencli xiaohongshu creator-notes --limit 1 看顶部是否是新发的 estimated_turns: 8关于 login 命令的现状
workflow 文档标注opencli xiaohongshu login为 pending(codex task #276)。从当前仓库看,登录命令已经由 site-auth.js 中的registerSiteAuthCommands统一注册:
opencli xiaohongshu whoami:读取命令,探测当前登录账号(auth.js 通过访问creator.xiaohongshu.com/new/home并 fetch/api/galaxy/creator/home/personal_info验证身份,返回 username + followers;未登录抛AuthRequiredError);opencli xiaohongshu login:写命令,先探测是否已登录(already_logged_in),否则打开登录页并轮询等待人工完成登录,默认超时 300 秒,成功返回login_complete;quickCheck只检查web_sessioncookie 是否存在(weak 预检,见 SITE.md 的 cookie_probe)。
Fallback 中的四个关键动作(对应 compose.md)
Fallback 手操流程逐条对应 compose.md 的 Actions:
- upload_images:通过 CDP
DOM.setFileInputFiles设置文件输入;图片预览区 3s 内出现缩略图视为 settle;失败时检查图片格式(jpg/png/webp <10MB)、最多 9 张(MAX_IMAGES),adapter 内置 base64 fallback; - fill_text:向标题输入框(visible filter)输入标题 + 向正文 contenteditable 输入正文;标题超过 20 字 / 正文超过 1000 字需要截断;
- add_topics:话题非空时触发
#话题联想并点击选项;话题添加失败可跳过(非必需),但 persistent fail 要把 adapter_health 标 suspect; - submit_publish:必须调用 shadow DOM 实例方法(见下节),成功后 redirect 到
/creator/notes或出现发布成功toast。
核心难点一:closed shadow DOM 发布按钮
这是整个发布流程最容易踩的坑。creator center 的发布/保存按钮被封装在<xhs-publish-btn>自定义元素中,背后是 closed shadow root(publish.js 注释):
Host-level
.click()无法 dispatch 进内部 handler。
因此 adapter 使用实例方法调用(instance method invocation),依次尝试:
- 发布:
['_onPublish', 'onPublish', '_onSubmit', '_handlePublish'](PUBLISH_METHOD_NAMES); - 存草稿:
['_onSave', '_onSaveDraft', '_onDraft'](DRAFT_METHOD_NAMES)。
发布提交逻辑 分两路:
- Path 1(主路):遍历页面上所有可见的
xhs-publish-btnhost 元素 × 全部候选方法名,逐个host[name]()调用;即使某个方法抛错也不立刻放弃(多个 host 可能并存,后面的方法名可能成功),最终返回{ ok: true, via: 'method', name }; - Path 2(兜底):若方法调用全部 miss,退化为按可见文本匹配
<button>/[role="button"]并.click()(匹配发布/发布笔记,或草稿模式的暂存离开/存草稿)。
草稿模式还有第三层兜底:点击返回/关闭/取消/离开触发"离开并暂存"流程,再点击暂存离开/存草稿/保存草稿,甚至检测页面是否已自动保存(出现草稿箱(/保存于/编辑于标记)。
对应的测试用例见 publish.test.js:uses the shadow-DOM method-invoke path when xhs-publish-btn handler succeeds。pitfalls.md 中publish_button_shadow_dom明确警告:不要手 click;如果 adapter broken 必须手动,用opencli browser evaluate调实例方法而不是 click。
核心难点二:标题输入框的 hidden decoy(可见性过滤)
creator center 的标题输入框存在两套同 class 的实现:一对 4px 宽的隐藏 scaffolding input(空 placeholder,submit 时 v-model 不 commit),加上真正可见的输入框(pitfall:title_input_has_hidden_decoy)。若用 page-level selector 或nth-child直接选,输入会丢进隐藏框,标题永远为空。
解法分两层:
- adapter 层:
TITLE_SELECTORS按优先级排列(publish.js),placeholder 类 selector(含"标题"或"title")优先于 class 类 selector,从根源上避开隐藏 decoy; - fallback 层:按 placeholder 文本
请输入标题加 visible filter(offsetWidth > 50)选可见输入框,不要按 nth-child / first match(compose.md Visual anchors)。
fillField 的实现 进一步做了三阶段保护:locate(跳过offsetParent === null的隐藏元素)→ apply(用原生 setter +beforeinput/input/change事件序列模拟真实输入,contenteditable 走document.execCommand('insertText')或page.insertText)→ verify(输入后校验el.value/innerText与期望值一致,不一致则截图到/tmp/xhs_publish_<field>_debug.png并抛错)。测试用例aborts when the title does not stick after filling与falls back to in-page insertion when contenteditable native insertText fails正是覆盖这两条路径。
核心难点三:话题的"内联 # 流"添加方式
旧版 creator center 有独立的"添加话题"按钮 + 搜索输入框,新版编辑器已移除该入口。现在必须走编辑器原生的内联流程(addTopics 实现):
- focus 正文 contenteditable 并把光标移到末尾;
- 用 CDP 原生
page.insertText输入#话题(不能用execCommand,否则 XHS 不触发 keyup 监听、联想下拉不弹出); - 等待 1.2s 让联想下拉渲染(下拉位于 closed shadow root 内,light-DOM 无法枚举,但 XHS 会自动高亮第一个匹配项,所以直接
pressKey('Enter')接受); - 校验"话题实体"真实生成:通过正文 innerText 中的稳定标记
#话题[话题]数量是否增加来判断(topicMarkerCountScript)。
这里有个重要的写侧后置条件(write-side postcondition):如果请求的话题最终没有生成真实话题实体(只有裸#text),adapter在发布前就失败,而不是静默发出带裸#的笔记。测试attaches topics via Enter to accept the inline suggestion (shadow-DOM dropdown)、fails typed when XHS does not render the topic chip marker after Enter、does not accept a pre-existing topic marker as proof of a new attached topic三个用例完整覆盖了这一行为。
文字配图(--card-text / --card-style)扩展能力
虽然 v1 workflow 文档聚焦"标题 + 正文 + 本地图片",publish adapter 还实现了完整的文字配图子流程(runTextImageFlow):
- 点击
文字配图入口 → 等待 tiptap/ProseMirror 卡片编辑器出现(.tiptap.ProseMirrorselector); - 逐卡输入文字(多卡用
\|\|\|分隔,卡内换行用\n,每行通过pressKey('Enter')产生真实换行;shell 单引号传入的\n字面量会被归一化为真实换行); - 点击
生成图片等待进入预览图片步骤(生成图片在卡片文字注册进编辑器模型前会 no-op,因此带重试,且"点击后按钮消失"保证不会重复添加); - 可选
--card-style:样式条(.cover-list-container)是虚拟化列表,一次只渲染视口附近的 ~10/20 个选项,因此selectCardStyle逐步滚动累加所有标签,目标样式出现后scrollIntoView再点击;样式是内容相关的动态子集,没有静态白名单,CARD_STYLE_GUIDE仅供--help展示; - 点击
下一步回到标准编辑器,之后照常填标题 / 正文 / 话题 / 提交。
CARD_STYLE_GUIDE中预置了 21 种样式及适用场景(基础/边框/备忘/清新/涂写/便签/光影/涂鸦/简约/手写/插图/美漫/弥散/柔和/印刷/科技/贺卡/札记/书摘/手帐/几何)。测试覆盖单卡、多卡、\n换行、字面量\n等场景(publish.test.js)。
Avoid:禁止清单与安全红线
workflow 文档明确列出 6 条 Avoid,均可在 pitfalls.md 中找到对应依据:
- 不要在
www.xiaohongshu.com找发布入口—pitfall:creator_center_is_different_host:主站顶 nav 的"发布"只是跳到 creator host,手 click 容易撞 popup / iframe / sso refresh 链; - 不要 host-level
.click()<xhs-publish-btn>—pitfall:publish_button_shadow_dom:看似点击成功但表单不提交; - 不要用 page-level selector 找 title input—
pitfall:title_input_has_hidden_decoy:必须加 visible filter(offsetWidth > 50); - 安全验证 modal(CAPTCHA / 滑块 / 短信 2FA)弹出即停手报 user,不要尝试自动解 — 这是 human-only 红线;
- publish 失败后不要立刻 retry 5 次—
pitfall:security_block_on_repeated_access:短时间高频访问/重试会触发安全限制/访问链接异常(URL 含website-login/error或error_code=300017|300031);触发后 60s 内不重试同 URL,workflow 内用 1-2s wait 隔开连续请求; - 不要把
--draft当 fallback— 草稿 != 已发布,不算 workflow 成功。
中断恢复检查点(Re-entry checkpoints)
Agent 被中断后醒来,应按opencli browser state的 URL +creator-notes比对判断当前进度(publish.md):
| 当前状态 | 处理方式 |
|---|---|
| 非 creator host | 重新走 Best path |
在/publish/publish?...,图片已上传但 title/body 未填 | 从 Fallback 的fill_text步继续 |
已在/creator/notes或 toast发布成功已显示 | 已完成,用creator-notes交叉验证顶部 |
| 安全验证 modal 可见 | 停手报 user |
状态验证(State validation)与交叉验证
workflow 的最终成功判定,三选一即可(publish.md):
opencli xiaohongshu creator-notes --limit 5顶部出现 title 匹配的新行;creator.xiaohongshu.com/creator/notesURL 上看到新笔记 card(visible text 匹配);- publish toast
发布成功出现过。
creator-notes 的读取原理
creator-notes.js 采用三级降级读取(fetchCreatorNotes):
- Capture 路径(首选):在 dashboard 根页安装
window.__xhsCapture钩子(同时 hookfetch与XMLHttpRequest,只捕获/api/galaxy/签名接口),因为从page.evaluate直接fetch()会绕过 x-s 签名导致 406;随后通过history.pushState+popstate触发 SPA 导航发出page_num=1的签名请求,再翻页收集全部note_infos; - API 路径:直接 fetch
/api/galaxy/creator/datacenter/note/analyze/list(失败时用 interceptor 捕获); - DOM 兜底:访问
new/note-manager滚动解析div.note卡片(新发布笔记在 analyze API 中title为空,需从 note-manager 的 DOM 补标题)。
返回字段:rank / id / title / date / views / likes / collects / comments / url,其中 url 为creator.xiaohongshu.com/statistics/note-detail?noteId=...。
草稿的验证
--draft保存成功后,可用opencli xiaohongshu drafts列出本地草稿箱(drafts.js,支持--type image/video/article/audio,默认 image),成功标记见源码['草稿已保存', '暂存成功', '保存成功', '保存于', '图文笔记(']。
失效标记(Stale markers):何时判定页面结构漂移
workflow 文档给出三个"页面结构漂移"信号,一旦命中应把 adapter_health 标为 suspect(publish.md):
- shadow DOM publish button 实例方法名变化(
_onPublishfamily):adapter 内部 fallback 链全 miss,task agent 会在 Fallback step 卡住; - title input class 漂 / hidden decoy 结构换:visible filter 阈值(
offsetWidth > 50)需要调整; - creator center URL 的
from=/target=query 变化:goto URL 需要更新。
这些信号与 compose.md 的 Page pitfalls 一一对应,是维护者判断"页面又改版了"的第一手依据。
从文档到代码:完整调用链一览
将 workflow 文档与源码对照,publish 的端到端调用链为:
opencli xiaohongshu publish→ publish.jscli()定义,access: 'write'、strategy: Strategy.COOKIE、browser: true、domain: creator.xiaohongshu.com;- 参数校验(title ≤ 20、images ≤ 9、格式校验)→
page.goto(PUBLISH_URL)并验证仍在 creator 域; - 图文 tab 选择 → 上传(CDP
DOM.setFileInputFiles优先,base64 DataTransfer 兜底,>500KB 的 base64 载荷会警告升级 extension v1.6+)→waitForUploads轮询上传进度条消失; waitForEditForm等待编辑器渲染(新版 UI 上传图片后才渲染编辑表单);fillField填标题与正文(三阶段:locate/apply/verify)→addTopics内联话题;- shadow DOM 实例方法提交 → 成功标记 + URL 离开校验;
opencli xiaohongshu creator-notes --limit 1交叉验证新笔记出现在列表顶部。
全套行为由 publish.test.js(90+ 处与 publish 相关的断言)守护,覆盖 CDP 上传、DataTransfer 兜底、shadow-DOM 方法调用、hidden decoy 过滤、话题实体校验、参数快速失败、文字配图多卡等场景。
小结
workflows/publish.md是一份面向 AI Agent 的可执行工作流契约:它以opencli xiaohongshu publish为单一入口,把"发图文笔记"压缩成一条命令;又以精确的 Fallback 步骤、Avoid 清单、Re-entry checkpoints 和 Stale markers,把页面结构漂移、登录失效、安全风控等现实风险显式化。理解这份文档,等于同时理解了 OpenCLI 的站点适配哲学——"human authenticates, machine verifies",以及面对 closed shadow DOM、hidden decoy、签名 API 等现代前端对抗手段时的工程化应对方式。对希望基于 OpenCLI 构建小红书内容自动化管线的开发者,本文列出的命令参数、源码路径与测试用例即是可直接复用的起点。
【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考