知乎首次设置与草稿验收¶
本页是 ChatPost 0.1.0 的完整验收 runbook。日常最短路径请先看 Quickstart:从登录到发送草稿;本页用于复刻已验证的 Playwright-cache + Profile + Wechatsync 路线,并把制品与任务责任拆到 ChatUp/ChatPost。
最终边界¶
ChatUp 0.2.4
-> 安装 exact Playwright package + browser revision
-> chatup.playwright.resolve(...)
ChatPost 0.1.0
-> 持久 Profile + 浏览器生命周期
-> exact extension + loopback CDP/bridge
-> 登录 checkpoint / auth / dry-run / 单次 create / receipt
Wechatsync
-> 知乎 adapter 与草稿写入
这条链路:
- user-level;
- 不需要 Docker 或 root;
- 不读取或导出 Cookie/LocalStorage;
- 只创建草稿,不点击最终发布;
RESULT_UNKNOWN后禁止自动重试;- 尚不支持 same-ID update。
1. 安装 Python 包¶
python3 -m venv "$HOME/.chatarch/venvs/chatpost"
"$HOME/.chatarch/venvs/chatpost/bin/python" -m pip install --upgrade pip
"$HOME/.chatarch/venvs/chatpost/bin/python" -m pip install \
"chatup==0.2.4" \
"chatpost==0.1.0"
CHATUP="$HOME/.chatarch/venvs/chatpost/bin/chatup"
CHATPOST="$HOME/.chatarch/venvs/chatpost/bin/chatpost"
"$CHATUP" --version
"$CHATPOST" --version
2. 准备 Node.js 与 Playwright browser¶
"$CHATUP" nodejs -I
# 按 ChatUp 输出刷新当前 shell 后确认 node/npm 可用。
node --version
npm --version
"$CHATUP" playwright install 1.61.1 \
--browser chromium \
--output json \
-I
"$CHATUP" playwright doctor 1.61.1 \
--browser chromium \
--output json \
-I
ChatUp 将 package 与 browser 安装到 ~/.chatarch/playwright/1.61.1/。ChatPost 只解析该安装,不隐式下载或升级。
当前任务实测组合:
3. 准备 Wechatsync adapter¶
当前打通版本来自 ChatArch 的知乎草稿 CLI 分支:
git clone https://github.com/ChatArch/Wechatsync.git "$HOME/.chatarch/src/Wechatsync"
cd "$HOME/.chatarch/src/Wechatsync"
git checkout 0073787cfbff0f7af4d1b427da3adbb16d92eeb8
corepack enable
pnpm install --frozen-lockfile
pnpm build
test -f packages/cli/dist/index.js
test -f packages/extension/dist/manifest.json
ChatPost 不复制 Wechatsync 的知乎业务逻辑,只编排其 dry-run CLI parser、扩展 MCP bridge 和回执;create 通过 extension MCP direct bridge 发送 syncArticle。
4. 创建 Profile 与私密 bridge env¶
RUNNER_HOME="$HOME/.chatarch/chatpost/runners/zhihu-primary"
install -d -m 700 "$RUNNER_HOME/profile"
install -d -m 700 "$RUNNER_HOME/run"
生成本地 bridge token,不在终端输出:
RUNNER_HOME="$RUNNER_HOME" python3 - <<'PY'
import os
import secrets
from pathlib import Path
path = Path(os.environ["RUNNER_HOME"]) / "bridge.env"
path.write_text(
"WECHATSYNC_TOKEN=" + secrets.token_urlsafe(32) + "\n",
encoding="utf-8",
)
path.chmod(0o600)
PY
bridge token 只鉴权本机扩展与 CLI,不是知乎密码。不要把 env、Profile、Cookie、LocalStorage、二维码或验证码加入 Git、文档或日志。
5. 写 Runner TOML¶
从仓库示例复制:
cp examples/zhihu/runner.toml.example "$RUNNER_HOME/runner.toml"
chmod 600 "$RUNNER_HOME/runner.toml"
把示例中的路径改成当前机器的绝对路径。核心字段:
[zhihu]
playwright_version = "1.61.1"
playwright_home = "/home/user/.chatarch/playwright"
profile_dir = "/home/user/.chatarch/chatpost/runners/zhihu-primary/profile"
extension_dir = "/home/user/.chatarch/src/Wechatsync/packages/extension/dist"
node_bin = "/absolute/path/to/node"
wechatsync_cli = "/home/user/.chatarch/src/Wechatsync/packages/cli/dist/index.js"
env_file = "/home/user/.chatarch/chatpost/runners/zhihu-primary/bridge.env"
cdp_host = "127.0.0.1"
cdp_port = 9227
bridge_host = "127.0.0.1"
bridge_port = 9527
extension_id = "dipgimoobbhdefncjomgehikkbaklgii"
headless = true
browser_args = ["--disable-dev-shm-usage"]
macOS 通常可把 browser_args 设为空数组。Linux 是否需要额外参数应以该机器真实 Chrome smoke 为准;不要默认公开端口或关闭安全边界。
6. 运行静态 preflight¶
只有 status=READY 才继续。它会验证:
- exact ChatUp Playwright installation;
- Profile 存在且不向 group/other 开放;
- Node、Wechatsync CLI 与扩展 manifest;
- env 权限和 token 是否存在,但不显示值;
- CDP/bridge 均显式绑定数值 IPv4 loopback
127.0.0.1且端口尚未被占用;拒绝localhost和 IPv6 loopback,避免连接 readiness 与 listener PID ownership 命中不同 socket。
7. 首次人工登录¶
已有登录 Profile 可先做一次只读检查:
若未登录,启动登录 checkpoint:
login 保持同一浏览器/Profile,打开知乎登录页,并循环执行只读 auth;扫码或验证码成功后返回 READY。有桌面的机器可设置 headless=false。服务器必须使用经过授权的本机显示/隧道或受控截图流程;不得把 CDP、VNC 或 bridge 暴露到公网。
登录后再运行一次 auth,确认同一 Profile 可复用。ChatPost 不读取 Profile 内的 Cookie。
8. Dry-run¶
ARTICLE=/absolute/path/to/article.md
"$CHATPOST" zhihu draft zhihu-personal "$ARTICLE" \
--registry "$RUNNER_HOME/accounts.toml" \
--dry-run \
--output json \
-I
从 JSON 的 preview 字段确认标题、正文、图片引用和固定 marker 正确。preview 最多返回 8000 个字符,并已按私有 env 中的值脱敏。dry-run 不启动浏览器、不连接扩展、不写知乎。
9. 只创建一次草稿¶
RECEIPT="$RUNNER_HOME/run/zhihu-draft-receipt.json"
"$CHATPOST" zhihu draft zhihu-personal "$ARTICLE" \
--registry "$RUNNER_HOME/accounts.toml" \
--receipt "$RECEIPT" \
--output json \
-I
成功条件:
status=DRAFT_CREATED;- 有知乎 draft ID 与
/editreview URL; - receipt 权限为
0600; - 打开编辑页能回读期望标题和 marker;
- 停在草稿箱,未最终发布。
每次 browser 启动都会生成随机 data:text/plain,chatpost-run-* marker。ChatPost 只有在配置的 loopback 端口同时看到该 marker 和对应 browser WebSocket UUID 后,才把 CDP 绑定为本次进程所有。
扩展发现、Target.attachToTarget、扩展求值和登录页创建全部通过这个已捕获的 browser WebSocket 完成。Target.createTarget 返回本次 popup ID;ChatPost 将这个 exact identity 贯穿 browser session,并在 attach 前重验其精确 popup URL 和 page / background_page 类型。恢复出来的旧 popup、其他 stale popup 和 service worker 均不会被选择,也不会跟随后来从可复用 CDP 端口发现的 target-level WebSocket。create 时 ChatPost 自己在配置的 loopback bridge 端口上服务一次有界 Wechatsync extension MCP 请求,唤醒扩展后发送 syncArticle;端口占用、连接失败、MCP 请求失败或缺少 review URL 都按 RESULT_UNKNOWN 保守处理,不得自动重试。
正常清理通过启动时捕获的 browser WebSocket endpoint 发送 CDP Browser.close,不会重新发现后来可能占用同一端口的其他浏览器,也不会发送进程终止信号。若草稿结果已经明确、但清理失败,receipt 仍保留 DRAFT_CREATED,并附带 cleanup_status=MANUAL_RECOVERY_REQUIRED;此时人工恢复进程,不能再次执行 create。browser 启动失败时,ChatPost 会先等待 stderr drain,再返回限长诊断;Profile 路径、私密赋值、URL/连接信息和运行 marker 均经过脱敏。若私有 env 在 preflight 后消失、不可读或不再包含预期 token,diagnostics 会 fail-closed 为 [REDACTED],外围错误仍保留 browser 退出码。
receipt 分别记录 browser cleanup_status、本次 popup extension_cleanup_status 与 adapter adapter_cleanup_status;只有需要人工恢复时才写对应 error 字段。该契约同时适用于 DRAFT_CREATED 与 RESULT_UNKNOWN,包括 MCP 请求失败、create 返回失败、成功返回但缺 review URL,以及 browser/popup cleanup 失败。cleanup 会通过 browser-level Target.closeTarget 重验并只关闭本次创建的 popup,然后请求 Browser.close;popup cleanup 失败会被独立记录,不会阻止 browser close 尝试,也不会覆盖 authoritative result。source_sha256 在 browser 或 adapter 启动前捕获;成功或歧义 receipt 均复用该值,因此后续 source 文件被修改、删除或无法读取都不会覆盖 authoritative result。receipt 不写入 target ID、browser endpoint、token 或连接信息。
如果 authoritative result 已经产生但 receipt 无法落盘,ChatPost 不会用普通文件系统异常覆盖主结果:明确成功时先输出 DRAFT_CREATED;歧义写入时继续明确 RESULT_UNKNOWN;随后报告 receipt 无法写入,并明确不得自动重试。
adapter diagnostic tail 除了替换私有 env 的精确值,还会结构化遮蔽动态私密赋值、WebSocket URL、loopback 连接信息和 ownership marker;RESULT_UNKNOWN receipt 只保留有界、脱敏后的 adapter_output_tail 供人工恢复。
图片上传失败可以与草稿创建成功同时发生;必须按编辑页实际内容报告,不能把 CLI exit 0 当成图片完整证明。
10. 歧义恢复¶
若 receipt 为:
立即停止自动化:
- 不重新运行不带
--dry-run的draft; - 在同一知乎账号草稿箱按标题、marker 与时间查找;
- 找到后补录唯一 draft ID/review URL;
- 确认不存在后也要人工决定是否重新创建。
两仓协作结论¶
- ChatUp 只向上提供 Playwright package/browser substrate;不创建 Profile、不启动浏览器、不懂知乎。
- ChatPost 面向任务管理 Profile、进程、CDP、extension MCP bridge、登录 checkpoint、单次写入和 receipt;不复制 Playwright 下载逻辑,也不读取登录数据库。
- Wechatsync 是知乎 adapter;其协议变化应在 ChatPost adapter 边界显式兼容。
- 文章 update 必须基于已保存的 draft/article ID 另开验收,不能用标题匹配或再次 create 冒充 update。